Skip to content

Commit b02e98c

Browse files
committed
delete saved cards feature
1 parent e38e4c5 commit b02e98c

File tree

18 files changed

+317
-79
lines changed

18 files changed

+317
-79
lines changed

app/src/main/java/com/flutterwave/rave_android/MainActivity.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,16 @@ public void onSavedCardsLookupFailed(String message) {
575575
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
576576
}
577577

578+
@Override
579+
public void onRemoveSavedCardRequestSuccessful() {
580+
581+
}
582+
583+
@Override
584+
public void onRemoveSavedCardRequestFailed(String message) {
585+
586+
}
587+
578588
@Override
579589
public void collectOtpForSaveCardCharge() {
580590
collectOtp("Otp for saved card");
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package com.flutterwave.raveandroid;
2+
3+
import android.content.Context;
4+
import android.graphics.Canvas;
5+
import android.graphics.Color;
6+
import android.graphics.Paint;
7+
import android.graphics.PorterDuff;
8+
import android.graphics.PorterDuffXfermode;
9+
import android.graphics.drawable.ColorDrawable;
10+
import android.graphics.drawable.Drawable;
11+
import android.view.View;
12+
13+
import androidx.annotation.NonNull;
14+
import androidx.core.content.ContextCompat;
15+
import androidx.recyclerview.widget.ItemTouchHelper;
16+
import androidx.recyclerview.widget.RecyclerView;
17+
18+
abstract public class SwipeToDeleteCallback extends ItemTouchHelper.Callback {
19+
20+
Context mContext;
21+
private Paint mClearPaint;
22+
private ColorDrawable mBackground;
23+
private int backgroundColor;
24+
private Drawable deleteDrawable;
25+
private int intrinsicWidth;
26+
private int intrinsicHeight;
27+
28+
29+
public SwipeToDeleteCallback(Context context) {
30+
mContext = context;
31+
mBackground = new ColorDrawable();
32+
backgroundColor = Color.parseColor("#b80f0a");
33+
mClearPaint = new Paint();
34+
mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
35+
deleteDrawable = ContextCompat.getDrawable(mContext, R.drawable.ic_delete);
36+
intrinsicWidth = deleteDrawable.getIntrinsicWidth();
37+
intrinsicHeight = deleteDrawable.getIntrinsicHeight();
38+
}
39+
40+
41+
@Override
42+
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
43+
return makeMovementFlags(0, ItemTouchHelper.LEFT);
44+
}
45+
46+
@Override
47+
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder1) {
48+
return false;
49+
}
50+
51+
@Override
52+
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
53+
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
54+
55+
View itemView = viewHolder.itemView;
56+
int itemHeight = itemView.getHeight();
57+
58+
boolean isCancelled = dX == 0 && !isCurrentlyActive;
59+
60+
if (isCancelled) {
61+
clearCanvas(c, itemView.getRight() + dX, (float) itemView.getTop(), (float) itemView.getRight(), (float) itemView.getBottom());
62+
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
63+
return;
64+
}
65+
66+
mBackground.setColor(backgroundColor);
67+
mBackground.setBounds(itemView.getRight() + (int) dX, itemView.getTop(), itemView.getRight(), itemView.getBottom());
68+
mBackground.draw(c);
69+
70+
int deleteIconTop = itemView.getTop() + (itemHeight - intrinsicHeight) / 2;
71+
int deleteIconMargin = (itemHeight - intrinsicHeight) / 2;
72+
int deleteIconLeft = itemView.getRight() - deleteIconMargin - intrinsicWidth;
73+
int deleteIconRight = itemView.getRight() - deleteIconMargin;
74+
int deleteIconBottom = deleteIconTop + intrinsicHeight;
75+
76+
77+
deleteDrawable.setBounds(deleteIconLeft, deleteIconTop, deleteIconRight, deleteIconBottom);
78+
deleteDrawable.draw(c);
79+
80+
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
81+
82+
83+
}
84+
85+
private void clearCanvas(Canvas c, Float left, Float top, Float right, Float bottom) {
86+
c.drawRect(left, top, right, bottom, mClearPaint);
87+
88+
}
89+
90+
@Override
91+
public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
92+
return 0.7f;
93+
}
94+
}
95+

rave_android/src/main/java/com/flutterwave/raveandroid/card/CardFragment.java

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,19 @@
2222
import android.widget.TextView;
2323
import android.widget.Toast;
2424

25+
import androidx.annotation.NonNull;
2526
import androidx.appcompat.app.AlertDialog;
2627
import androidx.appcompat.widget.SwitchCompat;
2728
import androidx.core.widget.NestedScrollView;
2829
import androidx.fragment.app.Fragment;
30+
import androidx.recyclerview.widget.ItemTouchHelper;
2931
import androidx.recyclerview.widget.LinearLayoutManager;
3032
import androidx.recyclerview.widget.RecyclerView;
3133

3234
import com.flutterwave.raveandroid.R;
3335
import com.flutterwave.raveandroid.RavePayActivity;
3436
import com.flutterwave.raveandroid.RavePayInitializer;
37+
import com.flutterwave.raveandroid.SwipeToDeleteCallback;
3538
import com.flutterwave.raveandroid.ViewObject;
3639
import com.flutterwave.raveandroid.card.savedcards.SavedCardRecyclerAdapter;
3740
import com.flutterwave.raveandroid.card.savedcards.SavedCardsActivity;
@@ -520,13 +523,6 @@ public void showOTPLayoutForSavedCard(Payload payload, String authInstruction) {
520523
@Override
521524
public void showSavedCardsLayout(List<SavedCard> savedCardsList) {
522525
switchToSaveCards(true);
523-
// Intent intent = new Intent(getContext(), SavedCardsActivity.class);
524-
// Type savedCardsListType = new TypeToken<List<SavedCard>>() {
525-
// }.getType();
526-
// intent.putExtra(SavedCardsFragment.EXTRA_SAVED_CARDS,
527-
// (new Gson()).toJson(savedCardsList, savedCardsListType));
528-
// intent.putExtra(SavedCardsActivity.ACTIVITY_MOTIVE, SavedCardsFragment.SAVED_CARD_MOTIVE);
529-
// startActivityForResult(intent, FOR_SAVED_CARDS);
530526
}
531527

532528
@Override
@@ -538,12 +534,13 @@ public void setHasSavedCards(boolean b, List<SavedCard> savedCards) {
538534
setUpSavedCardsAdapter(savedCards);
539535
} else {
540536
useASavedCardTv.setVisibility(GONE);
537+
if(savedCards == null) savedCards = new ArrayList();
538+
setUpSavedCardsAdapter(savedCards);
541539
}
542540
}
543541

544-
private void setUpSavedCardsAdapter(List<SavedCard> savedCards){
545-
if (savedCards == null) savedCards = new ArrayList<>();
546-
SavedCardRecyclerAdapter adapter = new SavedCardRecyclerAdapter();
542+
private void setUpSavedCardsAdapter(final List<SavedCard> savedCards){
543+
final SavedCardRecyclerAdapter adapter = new SavedCardRecyclerAdapter();
547544
adapter.set(savedCards);
548545
adapter.setSavedCardSelectedListener(new Callbacks.SavedCardSelectedListener() {
549546
@Override
@@ -553,6 +550,17 @@ public void onCardSelected(SavedCard savedCard) {
553550
});
554551
RecyclerView recyclerView = (RecyclerView) v.findViewById(R.id.rave_recycler);
555552
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
553+
554+
SwipeToDeleteCallback swipeHandler = new SwipeToDeleteCallback(requireContext()) {
555+
@Override
556+
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
557+
SavedCard savedCard = adapter.getCards().get(viewHolder.getAdapterPosition());
558+
presenter.deleteASavedCard(savedCard.getCardHash(), ravePayInitializer.getPhoneNumber(), ravePayInitializer.getPublicKey());
559+
}
560+
};
561+
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(swipeHandler);
562+
itemTouchHelper.attachToRecyclerView(recyclerView);
563+
556564
recyclerView.setAdapter(adapter);
557565
}
558566

@@ -594,7 +602,7 @@ public void collectOtpForSaveCardCharge(Payload payload) {
594602
@Override
595603
public void onCardSaveSuccessful(SaveCardResponse response, String phoneNumber) {
596604
// Perform lookup of saved savedCards and save to phone storage
597-
presenter.lookupSavedCards(ravePayInitializer.getPublicKey(), phoneNumber);
605+
presenter.lookupSavedCards(ravePayInitializer.getPublicKey(), phoneNumber, false);
598606

599607
}
600608

@@ -614,6 +622,17 @@ public void onCardSaveFailed(String message) {
614622

615623
}
616624

625+
@Override
626+
public void onSavedCardRemoveSuccessful() {
627+
presenter.lookupSavedCards(ravePayInitializer.getPublicKey(), ravePayInitializer.getPhoneNumber(), true);
628+
}
629+
630+
@Override
631+
public void onSavedCardRemoveFailed(String message) {
632+
((RecyclerView) v.findViewById(R.id.rave_recycler)).getAdapter().notifyDataSetChanged();
633+
showToast(message);
634+
}
635+
617636
@Override
618637
public void onSavedCardsLookupSuccessful(List<SavedCard> cards, String phoneNumber) {
619638
if (cards != null && cards.size() != 0) hasSavedCards = true;
@@ -638,6 +657,7 @@ public void onSavedCardsLookupSuccessful(List<SavedCard> cards, String phoneNumb
638657

639658
@Override
640659
public void onSavedCardsLookupFailed(String message) {
660+
setHasSavedCards(false, new ArrayList<SavedCard>());
641661
Intent intent = new Intent();
642662
intent.putExtra("response", responseAsJsonString);
643663

rave_android/src/main/java/com/flutterwave/raveandroid/card/CardUiPresenter.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -302,9 +302,7 @@ public void checkForSavedCardsInMemory(RavePayInitializer ravePayInitializer) {
302302
retrieveSavedCardsFromMemory(ravePayInitializer.getPhoneNumber(), ravePayInitializer.getPublicKey());
303303
}
304304

305-
if (!savedCards.isEmpty()) {
306-
mView.setHasSavedCards(true, savedCards);
307-
}
305+
mView.setHasSavedCards(!savedCards.isEmpty(), savedCards);
308306
}
309307

310308
@Override
@@ -344,7 +342,7 @@ public void init(RavePayInitializer ravePayInitializer) {
344342
if (ravePayInitializer.getPhoneNumber() != null) {
345343
if (ravePayInitializer.getPhoneNumber().length() > 0) {
346344
lookupSavedCards(ravePayInitializer.getPublicKey(),
347-
ravePayInitializer.getPhoneNumber());
345+
ravePayInitializer.getPhoneNumber(), false);
348346
mView.onPhoneNumberValidated(ravePayInitializer.getPhoneNumber());
349347
}
350348
}

rave_android/src/main/java/com/flutterwave/raveandroid/card/NullUiCardView.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ public void showProgressIndicator(boolean active) {
2626

2727
}
2828

29+
@Override
30+
public void onSavedCardRemoveSuccessful() {
31+
32+
}
33+
34+
@Override
35+
public void onSavedCardRemoveFailed(String message) {
36+
37+
}
38+
2939
@Override
3040
public void setHasSavedCards(boolean b, List<SavedCard> savedCards) {
3141

rave_android/src/main/java/com/flutterwave/raveandroid/card/savedcards/SavedCardRecyclerAdapter.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ public void set(List<SavedCard> cards) {
2727
this.cards = cards;
2828
}
2929

30+
public List<SavedCard> getCards(){
31+
return cards;
32+
}
33+
3034

3135
@Override
3236
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<vector android:height="20dp" android:tint="#FFFFFF"
2+
android:viewportHeight="24.0" android:viewportWidth="24.0"
3+
android:width="20dp" xmlns:android="http://schemas.android.com/apk/res/android">
4+
<path android:fillColor="#FF000000" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
5+
</vector>
Lines changed: 40 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,49 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2+
<androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
android:id="@+id/card_view"
35
android:layout_width="match_parent"
4-
android:layout_height="wrap_content">
5-
6-
<androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
7-
android:id="@+id/card_view"
6+
android:layout_height="wrap_content"
7+
android:layout_margin="8dp"
8+
card_view:contentPadding="8dp"
9+
card_view:contentPaddingLeft="16dp"
10+
card_view:contentPaddingRight="16dp"
11+
card_view:cardCornerRadius="4dp">
12+
13+
<LinearLayout
814
android:layout_width="match_parent"
9-
android:layout_height="wrap_content"
10-
android:layout_gravity="center"
11-
android:layout_margin="8dp"
12-
card_view:contentPadding="8dp"
13-
card_view:contentPaddingLeft="16dp"
14-
card_view:contentPaddingRight="16dp"
15-
card_view:cardCornerRadius="4dp">
15+
android:layout_height="wrap_content">
1616

1717
<LinearLayout
18-
android:layout_width="match_parent"
19-
android:layout_height="wrap_content">
20-
21-
<LinearLayout
22-
android:layout_width="0dp"
18+
android:layout_width="0dp"
19+
android:layout_height="wrap_content"
20+
android:layout_weight="1"
21+
android:orientation="vertical">
22+
23+
<TextView
24+
android:id="@+id/card_type_tv"
25+
style="@style/TextAppearance.AppCompat.Subhead"
26+
android:layout_width="wrap_content"
2327
android:layout_height="wrap_content"
24-
android:layout_weight="1"
25-
android:orientation="vertical">
26-
27-
<TextView
28-
android:id="@+id/card_type_tv"
29-
style="@style/TextAppearance.AppCompat.Subhead"
30-
android:layout_width="wrap_content"
31-
android:layout_height="wrap_content"
32-
android:paddingBottom="6dp"
33-
android:text="YOUR "
34-
android:textSize="16sp" />
35-
36-
<TextView
37-
android:id="@+id/masked_pan_tv"
38-
style="@style/TextAppearance.AppCompat.Subhead"
39-
android:layout_width="wrap_content"
40-
android:layout_height="wrap_content"
41-
android:text="424242******4242"
42-
android:textSize="18sp" />
43-
44-
</LinearLayout>
28+
android:paddingBottom="6dp"
29+
android:text="YOUR "
30+
android:textSize="16sp" />
31+
32+
<TextView
33+
android:id="@+id/masked_pan_tv"
34+
style="@style/TextAppearance.AppCompat.Subhead"
35+
android:layout_width="wrap_content"
36+
android:layout_height="wrap_content"
37+
android:text="424242******4242"
38+
android:textSize="18sp" />
4539

46-
<ImageView
47-
android:id="@+id/rave_card_brand_imageview"
48-
android:layout_width="40dp"
49-
android:layout_height="24dp"
50-
android:layout_gravity="center_vertical"
51-
android:src="@drawable/creditcard" />
5240
</LinearLayout>
53-
</androidx.cardview.widget.CardView>
5441

55-
</LinearLayout>
42+
<ImageView
43+
android:id="@+id/rave_card_brand_imageview"
44+
android:layout_width="40dp"
45+
android:layout_height="24dp"
46+
android:layout_gravity="center_vertical"
47+
android:src="@drawable/creditcard" />
48+
</LinearLayout>
49+
</androidx.cardview.widget.CardView>

rave_cache/src/main/java/com/flutterwave/raveandroid/rave_cache/SharedPrefsRepo.java

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,8 @@ public void saveCardToSharedPreference(List<SavedCard> cardsToSave, String phone
4343

4444
savePhoneNumber(phoneNumber);
4545

46-
List<SavedCard> savedCards = getSavedCards(phoneNumber, publicKey);
47-
List<SavedCard> repeatedCards = new ArrayList<>();
48-
for (SavedCard s : savedCards) {
49-
for (SavedCard c : cardsToSave) {
50-
if (s.getCardHash().equalsIgnoreCase(c.getCardHash())) {
51-
repeatedCards.add(s);
52-
break;
53-
}
54-
}
55-
}
56-
savedCards.removeAll(repeatedCards);
57-
savedCards.addAll(cardsToSave);
58-
5946
Type type = new TypeToken<List<SavedCard>>() {}.getType();
60-
String savedCardsJson = gson.toJson(savedCards, type);
47+
String savedCardsJson = gson.toJson(cardsToSave, type);
6148

6249
editor.putString(SAVED_CARDS_PREFIX + phoneNumber + publicKey, savedCardsJson).apply();
6350
}

0 commit comments

Comments
 (0)