diff --git a/CHANGELOG.md b/CHANGELOG.md index d9665b690..37d2787b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Changelog ========= +[1.0.0-beta08](https://github.com/hyperwallet/hyperwallet-android-ui-sdk/releases/tag/1.0.0-beta08) +------------------- +* Added support for update Transfer method +* Enhancements + [1.0.0-beta07](https://github.com/hyperwallet/hyperwallet-android-ui-sdk/releases/tag/1.0.0-beta07) ------------------- * Added Prepaid Card support diff --git a/README.md b/README.md index 310ae0dd1..38c0910c3 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,9 @@ Note that this SDK is geared towards those who need both backend data and UI fea To install Hyperwallet UI SDK, you just need to add the dependencies into your build.gradle file in Android Studio (or Gradle). For example: ```bash -api 'com.hyperwallet.android.ui:transfermethodui:1.0.0-beta07' -api 'com.hyperwallet.android.ui:receiptui:1.0.0-beta07' -api 'com.hyperwallet.android.ui:transferui:1.0.0-beta07' +api 'com.hyperwallet.android.ui:transfermethodui:1.0.0-beta08' +api 'com.hyperwallet.android.ui:receiptui:1.0.0-beta08' +api 'com.hyperwallet.android.ui:transferui:1.0.0-beta08' ``` ### Proguard @@ -171,6 +171,21 @@ public void onClick(View view) { } ``` +### Update a transfer method +The second argument is a token that represents a Transfer method. +The third argument is boolean flag to control view orientation, lockScreenToPortrait set to true +The form fields are based on the country, currency, user's profile type, and transfer method type should be passed to this Activity to create a new Transfer Method for those values. +```java +@Override +public void onClick(View view) { + Intent intent = mHyperwalletTransferMethodUi.getIntentUpdateTransferMethodActivity( + MainActivity.this, + "trm-12345", + false); + startActivity(intent); +} +``` + ### List the user's receipts The second argument is boolean flag to control view orientation, lockScreenToPortrait set to true will lock the screen to Portrait orientation otherwise it will just follow device orientation. diff --git a/build.gradle b/build.gradle index e7155ef05..59b28a3a8 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,7 @@ allprojects { } - project.version = "1.0.0-beta07" + project.version = "1.0.0-beta08" } @@ -38,7 +38,7 @@ subprojects { targetVersion = 29 codeVersion = 1 - hyperwalletCoreVersion = '1.0.0-beta07' + hyperwalletCoreVersion = '1.0.0-beta08' hyperwalletInsightVersion = '1.0.0-beta02' // androidMaterialVersion = '1.0.0' diff --git a/commonui/src/main/java/com/hyperwallet/android/ui/common/intent/HyperwalletIntent.java b/commonui/src/main/java/com/hyperwallet/android/ui/common/intent/HyperwalletIntent.java index d87c5d13d..992ddfb3f 100644 --- a/commonui/src/main/java/com/hyperwallet/android/ui/common/intent/HyperwalletIntent.java +++ b/commonui/src/main/java/com/hyperwallet/android/ui/common/intent/HyperwalletIntent.java @@ -65,6 +65,11 @@ private HyperwalletIntent() { */ public static final short SELECT_TRANSFER_SOURCE_REQUEST_CODE = 104; + /** + * Update Transfer method request code + */ + public static final short UPDATE_TRANSFER_METHOD_REQUEST_CODE = 105; + /** * SDK Broadcast payload error */ @@ -81,5 +86,10 @@ private HyperwalletIntent() { */ public static final String EXTRA_TRANSFER_METHOD_ADDED = "EXTRA_TRANSFER_METHOD_ADDED"; + /** + * Transfer method updated, extra activity parcelable transfer method payload + */ + public static final String EXTRA_TRANSFER_METHOD_UPDATED = "EXTRA_TRANSFER_METHOD_UPDATED"; + } diff --git a/commonui/src/main/java/com/hyperwallet/android/ui/common/view/TransferMethodUtils.java b/commonui/src/main/java/com/hyperwallet/android/ui/common/view/TransferMethodUtils.java index f94c774bf..6da2bc392 100644 --- a/commonui/src/main/java/com/hyperwallet/android/ui/common/view/TransferMethodUtils.java +++ b/commonui/src/main/java/com/hyperwallet/android/ui/common/view/TransferMethodUtils.java @@ -20,6 +20,7 @@ import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodFields.CARD_BRAND; import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodFields.CARD_NUMBER; import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodFields.EMAIL; +import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodFields.POSTAL_CODE; import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodFields.TYPE; import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodFields.VENMO_ACCOUNT_ID; import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.BANK_ACCOUNT; @@ -172,6 +173,8 @@ public static String getTransferMethodDetail(@NonNull Context context, case VENMO_ACCOUNT: return getFourDigitsIdentification(context, transferMethod, VENMO_ACCOUNT_ID, R.string.endingIn); + case PAPER_CHECK: + return context.getString(R.string.to, transferMethod.getField(POSTAL_CODE)); default: return ""; } @@ -205,4 +208,30 @@ private static String getFourDigitsIdentificationWithCardBrand(@NonNull final Co return cardBrand + "\u0020\u2022\u2022\u2022\u2022\u0020" + identificationText; } + + public static int getAdjustCollapseTitleStyle(String title) { + int count = title.length(); + if (count <= 24) { + return R.style.TextAppearance_Hyperwallet_Title_Collapse_Large; + } else if (count <= 34) { + return R.style.TextAppearance_Hyperwallet_Title_Collapse_Medium; + } else if (count <= 36) { + return R.style.TextAppearance_Hyperwallet_Title_Collapse_Small; + } else { + return R.style.TextAppearance_Hyperwallet_Title_Collapse_ExtraSmall; + } + } + + public static int getAdjustExpandTitleStyle(String title) { + int count = title.length(); + if (count <= 24) { + return R.style.TextAppearance_Hyperwallet_Title_Expanded_Large; + } else if (count <= 34) { + return R.style.TextAppearance_Hyperwallet_Title_Expanded_Medium; + } else if (count <= 40) { + return R.style.TextAppearance_Hyperwallet_Title_Expanded_Small; + } else { + return R.style.TextAppearance_Hyperwallet_Title_Expanded_ExtraSmall; + } + } } diff --git a/commonui/src/main/res/drawable/ic_edit.xml b/commonui/src/main/res/drawable/ic_edit.xml new file mode 100644 index 000000000..bf16bd5cc --- /dev/null +++ b/commonui/src/main/res/drawable/ic_edit.xml @@ -0,0 +1,12 @@ + + + diff --git a/commonui/src/main/res/drawable/ic_trash.xml b/commonui/src/main/res/drawable/ic_trash.xml index 5fdcd6dfe..caa047ad8 100644 --- a/commonui/src/main/res/drawable/ic_trash.xml +++ b/commonui/src/main/res/drawable/ic_trash.xml @@ -1,20 +1,12 @@ - - + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + diff --git a/commonui/src/main/res/font/hw_mobile_ui_sdk_icons.ttf b/commonui/src/main/res/font/hw_mobile_ui_sdk_icons.ttf index 34086f178..d90500355 100644 Binary files a/commonui/src/main/res/font/hw_mobile_ui_sdk_icons.ttf and b/commonui/src/main/res/font/hw_mobile_ui_sdk_icons.ttf differ diff --git a/commonui/src/main/res/values-bg/strings.xml b/commonui/src/main/res/values-bg/strings.xml index 7c3a1d60c..26ba6cd94 100644 --- a/commonui/src/main/res/values-bg/strings.xml +++ b/commonui/src/main/res/values-bg/strings.xml @@ -23,5 +23,5 @@ Грешка при удостоверяване Отмяна Премахване - Сигурни ли сте? + Сигурни ли сте? diff --git a/commonui/src/main/res/values-cs/strings.xml b/commonui/src/main/res/values-cs/strings.xml index c212016d7..6b9ab9e6e 100644 --- a/commonui/src/main/res/values-cs/strings.xml +++ b/commonui/src/main/res/values-cs/strings.xml @@ -23,5 +23,5 @@ Chyba ověření Zrušit Odebrat - Jste si jistí? + Jste si jistí? diff --git a/commonui/src/main/res/values-de/strings.xml b/commonui/src/main/res/values-de/strings.xml index b986f2dd5..213570492 100644 --- a/commonui/src/main/res/values-de/strings.xml +++ b/commonui/src/main/res/values-de/strings.xml @@ -23,5 +23,5 @@ Authentifizierungsfehler Abbrechen Entfernen - Sind Sie sicher? + Sind Sie sicher? diff --git a/commonui/src/main/res/values-el/strings.xml b/commonui/src/main/res/values-el/strings.xml index 961e65484..1239481a2 100644 --- a/commonui/src/main/res/values-el/strings.xml +++ b/commonui/src/main/res/values-el/strings.xml @@ -23,5 +23,5 @@ Σφάλμα ελέγχου ταυτότητας Ακύρωση Κατάργηση - Είστε βέβαιος/-η; + Είστε βέβαιος/-η; diff --git a/commonui/src/main/res/values-es/strings.xml b/commonui/src/main/res/values-es/strings.xml index 33a0b358e..ea48415ff 100644 --- a/commonui/src/main/res/values-es/strings.xml +++ b/commonui/src/main/res/values-es/strings.xml @@ -23,5 +23,5 @@ Error de autenticación Cancelar Eliminar - ¿Está seguro? + ¿Está seguro? diff --git a/commonui/src/main/res/values-fi/strings.xml b/commonui/src/main/res/values-fi/strings.xml index 6a9c9d2d9..00a236c95 100644 --- a/commonui/src/main/res/values-fi/strings.xml +++ b/commonui/src/main/res/values-fi/strings.xml @@ -23,5 +23,5 @@ Varmennusvirhe Peruuta Poista - Oletko varma? + Oletko varma? diff --git a/commonui/src/main/res/values-fr/strings.xml b/commonui/src/main/res/values-fr/strings.xml index 90bdee31e..cdb9de381 100644 --- a/commonui/src/main/res/values-fr/strings.xml +++ b/commonui/src/main/res/values-fr/strings.xml @@ -23,5 +23,5 @@ Erreur d’authentification Annuler Supprimer - Êtes-vous sûr(e) ? + Êtes-vous sûr(e) ? diff --git a/commonui/src/main/res/values-hr/strings.xml b/commonui/src/main/res/values-hr/strings.xml index d567c7e6d..f86001c2b 100644 --- a/commonui/src/main/res/values-hr/strings.xml +++ b/commonui/src/main/res/values-hr/strings.xml @@ -23,5 +23,5 @@ Pogreška provjere autentičnosti Poništite Uklonite - Jeste li sigurni? + Jeste li sigurni? diff --git a/commonui/src/main/res/values-hu/strings.xml b/commonui/src/main/res/values-hu/strings.xml index 4554d9337..c7c633bc8 100644 --- a/commonui/src/main/res/values-hu/strings.xml +++ b/commonui/src/main/res/values-hu/strings.xml @@ -23,5 +23,5 @@ Hitelesítési hiba Mégse Eltávolítás - Biztos benne? + Biztos benne? diff --git a/commonui/src/main/res/values-in/strings.xml b/commonui/src/main/res/values-in/strings.xml index 7b7c3209c..afe3f1ce5 100644 --- a/commonui/src/main/res/values-in/strings.xml +++ b/commonui/src/main/res/values-in/strings.xml @@ -23,5 +23,5 @@ Kesalahan Autentikasi Batalkan Hapus - Apa Anda yakin? + Apa Anda yakin? diff --git a/commonui/src/main/res/values-is/strings.xml b/commonui/src/main/res/values-is/strings.xml index f0eedcf9c..31f8ef0cf 100644 --- a/commonui/src/main/res/values-is/strings.xml +++ b/commonui/src/main/res/values-is/strings.xml @@ -23,5 +23,5 @@ Sannvottunarvilla Hætta við Fjarlægja - Ertu viss? + Ertu viss? diff --git a/commonui/src/main/res/values-it/strings.xml b/commonui/src/main/res/values-it/strings.xml index 06fb00bd5..d54ff502a 100644 --- a/commonui/src/main/res/values-it/strings.xml +++ b/commonui/src/main/res/values-it/strings.xml @@ -23,5 +23,5 @@ Errore di autenticazione Annulla Rimuovi - Sei sicuro/a? + Sei sicuro/a? diff --git a/commonui/src/main/res/values-ja/strings.xml b/commonui/src/main/res/values-ja/strings.xml index fe144cbb8..ebc759aff 100644 --- a/commonui/src/main/res/values-ja/strings.xml +++ b/commonui/src/main/res/values-ja/strings.xml @@ -23,5 +23,5 @@ 認証エラー キャンセル 削除 - よろしいですか? + よろしいですか? diff --git a/commonui/src/main/res/values-ko/strings.xml b/commonui/src/main/res/values-ko/strings.xml index 17d445eec..ca06f4a85 100644 --- a/commonui/src/main/res/values-ko/strings.xml +++ b/commonui/src/main/res/values-ko/strings.xml @@ -23,5 +23,5 @@ 인증 오류 취소 삭제 - 계속 진행하시겠습니까? + 계속 진행하시겠습니까? diff --git a/commonui/src/main/res/values-lv/strings.xml b/commonui/src/main/res/values-lv/strings.xml index eb32cd3a4..18b2ea15c 100644 --- a/commonui/src/main/res/values-lv/strings.xml +++ b/commonui/src/main/res/values-lv/strings.xml @@ -23,5 +23,5 @@ Autorizācijas kļūda Atcelt Dzēst - Vai tiešām? + Vai tiešām? diff --git a/commonui/src/main/res/values-mk/strings.xml b/commonui/src/main/res/values-mk/strings.xml index 28fbfd90d..33adf1f8f 100644 --- a/commonui/src/main/res/values-mk/strings.xml +++ b/commonui/src/main/res/values-mk/strings.xml @@ -23,5 +23,5 @@ Грешка во автентикација Откажи Отстрани - Дали сте сигурни? + Дали сте сигурни? diff --git a/commonui/src/main/res/values-mn/strings.xml b/commonui/src/main/res/values-mn/strings.xml index 55eec52fc..927f4df1d 100644 --- a/commonui/src/main/res/values-mn/strings.xml +++ b/commonui/src/main/res/values-mn/strings.xml @@ -23,5 +23,5 @@ Баталгаажуулалтын алдаа Цуцлах Устгах - Та итгэлтэй байна уу? + Та итгэлтэй байна уу? diff --git a/commonui/src/main/res/values-ms/strings.xml b/commonui/src/main/res/values-ms/strings.xml index dd7e898d1..b6d6a3b37 100644 --- a/commonui/src/main/res/values-ms/strings.xml +++ b/commonui/src/main/res/values-ms/strings.xml @@ -23,5 +23,5 @@ Ralat Pengesahan Batal Alih keluar - Adakah anda pasti? + Adakah anda pasti? diff --git a/commonui/src/main/res/values-nl/strings.xml b/commonui/src/main/res/values-nl/strings.xml index ee8071f10..ffa8fcbf6 100644 --- a/commonui/src/main/res/values-nl/strings.xml +++ b/commonui/src/main/res/values-nl/strings.xml @@ -23,5 +23,5 @@ Verificatiefout Annuleren Verwijderen - Weet u het zeker? + Weet u het zeker? diff --git a/commonui/src/main/res/values-pl/strings.xml b/commonui/src/main/res/values-pl/strings.xml index 60a0497df..79dad118d 100644 --- a/commonui/src/main/res/values-pl/strings.xml +++ b/commonui/src/main/res/values-pl/strings.xml @@ -23,5 +23,5 @@ Błąd uwierzytelniania Anuluj Usuń - Na pewno? + Na pewno? diff --git a/commonui/src/main/res/values-pt-rPT/strings.xml b/commonui/src/main/res/values-pt-rPT/strings.xml index 28bf5cc96..4b951758a 100644 --- a/commonui/src/main/res/values-pt-rPT/strings.xml +++ b/commonui/src/main/res/values-pt-rPT/strings.xml @@ -23,5 +23,5 @@ Erro de autenticação Cancelar Remover - Tem a certeza? + Tem a certeza? diff --git a/commonui/src/main/res/values-pt/strings.xml b/commonui/src/main/res/values-pt/strings.xml index a2407ceba..2a709e362 100644 --- a/commonui/src/main/res/values-pt/strings.xml +++ b/commonui/src/main/res/values-pt/strings.xml @@ -23,5 +23,5 @@ Erro de autenticação Cancelar Remover - Você tem certeza? + Você tem certeza? diff --git a/commonui/src/main/res/values-ro/strings.xml b/commonui/src/main/res/values-ro/strings.xml index b4c691741..2385fb326 100644 --- a/commonui/src/main/res/values-ro/strings.xml +++ b/commonui/src/main/res/values-ro/strings.xml @@ -23,5 +23,5 @@ Eroare de autentificare Anulați Eliminați - Sunteți sigur(ă)? + Sunteți sigur(ă)? diff --git a/commonui/src/main/res/values-ru/strings.xml b/commonui/src/main/res/values-ru/strings.xml index 970e7fd59..31ca3d546 100644 --- a/commonui/src/main/res/values-ru/strings.xml +++ b/commonui/src/main/res/values-ru/strings.xml @@ -23,5 +23,5 @@ Ошибка аутентификации Отмена Удалить - Вы уверены? + Вы уверены? diff --git a/commonui/src/main/res/values-sk/strings.xml b/commonui/src/main/res/values-sk/strings.xml index 55f557d5b..eebd79573 100644 --- a/commonui/src/main/res/values-sk/strings.xml +++ b/commonui/src/main/res/values-sk/strings.xml @@ -23,5 +23,5 @@ Chyba overenia Zrušiť Odstrániť - Naozaj? + Naozaj? diff --git a/commonui/src/main/res/values-sl/strings.xml b/commonui/src/main/res/values-sl/strings.xml index 5ec5f5e68..978f9f623 100644 --- a/commonui/src/main/res/values-sl/strings.xml +++ b/commonui/src/main/res/values-sl/strings.xml @@ -23,5 +23,5 @@ Napaka pri avtentifikaciji Prekliči Odstrani - Ali ste prepričani? + Ali ste prepričani? diff --git a/commonui/src/main/res/values-sv/strings.xml b/commonui/src/main/res/values-sv/strings.xml index 38250a574..cd42f9a6f 100644 --- a/commonui/src/main/res/values-sv/strings.xml +++ b/commonui/src/main/res/values-sv/strings.xml @@ -23,5 +23,5 @@ Autentiseringsfel Avbryt Ta bort - Är du säker? + Är du säker? diff --git a/commonui/src/main/res/values-th/strings.xml b/commonui/src/main/res/values-th/strings.xml index 219032f19..a1dee2f50 100644 --- a/commonui/src/main/res/values-th/strings.xml +++ b/commonui/src/main/res/values-th/strings.xml @@ -23,5 +23,5 @@ ข้อผิดพลาดในการพิสูจน์ตัวตนถูกต้อง ยกเลิก ลบ - คุณแน่ใจหรือไม่ + คุณแน่ใจหรือไม่ diff --git a/commonui/src/main/res/values-tr/strings.xml b/commonui/src/main/res/values-tr/strings.xml index 32fe22892..a6ca18635 100644 --- a/commonui/src/main/res/values-tr/strings.xml +++ b/commonui/src/main/res/values-tr/strings.xml @@ -23,5 +23,5 @@ Doğrulama Hatası İptal et Kaldır - Emin misiniz? + Emin misiniz? diff --git a/commonui/src/main/res/values-uk/strings.xml b/commonui/src/main/res/values-uk/strings.xml index ac7f15639..981a24050 100644 --- a/commonui/src/main/res/values-uk/strings.xml +++ b/commonui/src/main/res/values-uk/strings.xml @@ -23,5 +23,5 @@ Помилка автентифікації Скасувати Видалити - Ви впевнені? + Ви впевнені? diff --git a/commonui/src/main/res/values-vi/strings.xml b/commonui/src/main/res/values-vi/strings.xml index 426cdda4b..bda3650a0 100644 --- a/commonui/src/main/res/values-vi/strings.xml +++ b/commonui/src/main/res/values-vi/strings.xml @@ -23,5 +23,5 @@ Lỗi xác thực Huỷ bỏ Xóa - Bạn có chắc không? + Bạn có chắc không? diff --git a/commonui/src/main/res/values-xxxhdpi/dimens.xml b/commonui/src/main/res/values-xxxhdpi/dimens.xml new file mode 100644 index 000000000..4fe15ada6 --- /dev/null +++ b/commonui/src/main/res/values-xxxhdpi/dimens.xml @@ -0,0 +1,12 @@ + + + 12dp + 16dp + 18dp + 16dp + + 14dp + 18dp + 20dp + 28dp + \ No newline at end of file diff --git a/commonui/src/main/res/values-zh-rCN/strings.xml b/commonui/src/main/res/values-zh-rCN/strings.xml index d2b34d2e5..a280d401c 100644 --- a/commonui/src/main/res/values-zh-rCN/strings.xml +++ b/commonui/src/main/res/values-zh-rCN/strings.xml @@ -23,5 +23,5 @@ 验证错误 取消 删除 - 是否确定? + 是否确定? diff --git a/commonui/src/main/res/values-zh-rTW/strings.xml b/commonui/src/main/res/values-zh-rTW/strings.xml index a40d7fb00..1733eefdf 100644 --- a/commonui/src/main/res/values-zh-rTW/strings.xml +++ b/commonui/src/main/res/values-zh-rTW/strings.xml @@ -23,5 +23,5 @@ 驗證錯誤 取消 移除 - 是否確定? + 是否確定? diff --git a/commonui/src/main/res/values-zh/strings.xml b/commonui/src/main/res/values-zh/strings.xml index a40d7fb00..1733eefdf 100644 --- a/commonui/src/main/res/values-zh/strings.xml +++ b/commonui/src/main/res/values-zh/strings.xml @@ -23,5 +23,5 @@ 驗證錯誤 取消 移除 - 是否確定? + 是否確定? diff --git a/commonui/src/main/res/values/strings.xml b/commonui/src/main/res/values/strings.xml index d1ece3372..da5cebe47 100644 --- a/commonui/src/main/res/values/strings.xml +++ b/commonui/src/main/res/values/strings.xml @@ -26,6 +26,7 @@ \ue906 \ue909 \ue905 + \ue90b ending in %s to %s @@ -33,6 +34,7 @@ Authentication Error Cancel + Edit Remove - Are you sure? + Remove this transfer method? diff --git a/commonui/src/main/res/values/styles.xml b/commonui/src/main/res/values/styles.xml index ebe4137fe..fa2027845 100644 --- a/commonui/src/main/res/values/styles.xml +++ b/commonui/src/main/res/values/styles.xml @@ -14,9 +14,9 @@ true - - - @@ -60,29 +58,24 @@ @color/regularColorPrimary - - - - - @@ -169,8 +162,7 @@ @color/colorBackgroundDisabled - - - - - @@ -225,8 +213,7 @@ @android:color/transparent - - - - - @@ -292,4 +275,44 @@ + + + + + + + + + + + + + + diff --git a/commonui/src/test/java/com/hyperwallet/android/ui/common/view/TransferMethodUtilsTest.java b/commonui/src/test/java/com/hyperwallet/android/ui/common/view/TransferMethodUtilsTest.java index 36cf5625b..5b23302c0 100644 --- a/commonui/src/test/java/com/hyperwallet/android/ui/common/view/TransferMethodUtilsTest.java +++ b/commonui/src/test/java/com/hyperwallet/android/ui/common/view/TransferMethodUtilsTest.java @@ -22,6 +22,7 @@ import com.hyperwallet.android.model.transfermethod.BankAccount; import com.hyperwallet.android.model.transfermethod.BankCard; +import com.hyperwallet.android.model.transfermethod.PaperCheck; import com.hyperwallet.android.model.transfermethod.PayPalAccount; import com.hyperwallet.android.model.transfermethod.TransferMethod; import com.hyperwallet.android.model.transfermethod.VenmoAccount; @@ -197,11 +198,9 @@ public void getTransferMethodDetail_returnsBankAccountDetails() { @Test public void getTransferMethodDetail_returnsPaperCheckDetails() { - TransferMethod transferMethod = new TransferMethod(); - - String actual = getTransferMethodDetail(mContext, transferMethod, PAPER_CHECK); - assertThat(actual, is("")); - verify(mContext, never()).getString(ArgumentMatchers.eq(R.string.endingIn), - anyString()); + TransferMethod transferMethod = new PaperCheck.Builder().transferMethodCurrency("USD").transferMethodCountry("CA").build(); + assertThat(transferMethod.getField(TransferMethod.TransferMethodFields.TRANSFER_METHOD_COUNTRY), is("CA")); + assertThat(transferMethod.getField(TransferMethod.TransferMethodFields.TRANSFER_METHOD_CURRENCY), is("USD")); + assertThat(transferMethod.getField(TransferMethod.TransferMethodFields.TYPE), is("PAPER_CHECK")); } } diff --git a/receiptui/src/androidTest/java/com/hyperwallet/android/ui/receipt/ListPrepaidCardReceiptsTest.java b/receiptui/src/androidTest/java/com/hyperwallet/android/ui/receipt/ListPrepaidCardReceiptsTest.java index 302f1b9a7..821ce0a7d 100644 --- a/receiptui/src/androidTest/java/com/hyperwallet/android/ui/receipt/ListPrepaidCardReceiptsTest.java +++ b/receiptui/src/androidTest/java/com/hyperwallet/android/ui/receipt/ListPrepaidCardReceiptsTest.java @@ -12,6 +12,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; import static java.net.HttpURLConnection.HTTP_NO_CONTENT; @@ -309,13 +310,14 @@ public void testListPrepaidCardReceipt_checkDateTextOnLocaleChange() { mActivityTestRule.launchActivity(null); // assert onView(withId(R.id.list_receipts)) - .check(matches(atPosition(0, hasDescendant(withText("Juni 2019"))))); + .check(matches(atPosition(0, hasDescendant(withText(containsString("Juni 2019")))))); onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText(com.hyperwallet.android.ui.receipt.R.string.debit))))); onView(withId(R.id.list_receipts)).check( matches(atPosition(0, hasDescendant(withText(R.string.adjustment))))); + onView(withId(R.id.list_receipts)).check( - matches(atPosition(0, hasDescendant(withText(debitSymbol + usdCurrencySymbol + "8.90"))))); + matches(atPosition(0, hasDescendant(withText(containsString(debitSymbol + "8,90")))))); onView(withId(R.id.list_receipts)).check( matches(atPosition(0, hasDescendant(withText("1. Juni 2019"))))); onView(withId(R.id.list_receipts)).check( @@ -394,7 +396,7 @@ public void testListPrepaidCardReceipt_clickTransactionDisplaysDetails() { onView(withId(R.id.receipt_id_label)).check(matches(withText(R.string.mobileJournalNumberLabel))); onView(withId(R.id.receipt_id_value)).check(matches(withText("FISVL_5240220"))); onView(withId(R.id.date_label)).check(matches(withText(R.string.date))); - onView(withId(R.id.date_value)).check(matches(withText("Thu, June 6, 2019, 3:48 PM PDT"))); + onView(withId(R.id.date_value)).check(matches(withText("Jun 6, 2019, 3:48 PM PDT"))); onView(withId(R.id.client_id_label)).check(matches(withText(R.string.mobileMerchantTxnLabel))); onView(withId(R.id.client_id_value)).check(matches(withText("AOxXefx9"))); @@ -448,7 +450,7 @@ public void testListPrepaidCardReceipt_clickTransactionDisplaysDetailsWithoutFee onView(withId(R.id.receipt_id_label)).check(matches(withText(R.string.mobileJournalNumberLabel))); onView(withId(R.id.receipt_id_value)).check(matches(withText("FISVL_5240221"))); onView(withId(R.id.date_label)).check(matches(withText(R.string.date))); - onView(withId(R.id.date_value)).check(matches(withText("Thu, June 6, 2019, 3:48 PM PDT"))); + onView(withId(R.id.date_value)).check(matches(withText("Jun 6, 2019, 3:48 PM PDT"))); onView(withId(R.id.client_id_label)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); onView(withId(R.id.client_id_value)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); diff --git a/receiptui/src/androidTest/java/com/hyperwallet/android/ui/receipt/ListUserReceiptsTest.java b/receiptui/src/androidTest/java/com/hyperwallet/android/ui/receipt/ListUserReceiptsTest.java index 0bc149ec7..ac990fc48 100644 --- a/receiptui/src/androidTest/java/com/hyperwallet/android/ui/receipt/ListUserReceiptsTest.java +++ b/receiptui/src/androidTest/java/com/hyperwallet/android/ui/receipt/ListUserReceiptsTest.java @@ -149,7 +149,7 @@ public void testListReceipt_userHasMultipleTransactions() { onView(withId(R.id.list_receipts)).check( matches(atPosition(3, hasDescendant(withText(R.string.transfer_to_prepaid_card))))); onView(withId(R.id.list_receipts)).check( - matches(atPosition(3, hasDescendant(withText(debitSymbol + krwCurrencySymbol + "40000"))))); + matches(atPosition(3, hasDescendant(withText(debitSymbol + krwCurrencySymbol + "40,000"))))); onView(withId(R.id.list_receipts)).check( matches(atPosition(3, hasDescendant(withText("December 1, 2018"))))); @@ -184,7 +184,7 @@ public void testListReceipt_userHasMultipleTransactionCurrencyFormat() { onView(withId(R.id.list_receipts)).check(matches(atPosition(0, hasDescendant(withText(KRW.first.toString()))))); onView(withId(R.id.list_receipts)) - .check(matches(atPosition(1, hasDescendant(withText(monthLabel1))))); + .check(matches(atPosition(1, hasDescendant(withText("June 2, 2019"))))); onView(withId(R.id.list_receipts)).check(matches(atPosition(1, hasDescendant(withText(R.string.credit))))); onView(withId(R.id.list_receipts)).check( @@ -196,7 +196,7 @@ public void testListReceipt_userHasMultipleTransactionCurrencyFormat() { onView(withId(R.id.list_receipts)).check(matches(atPosition(1, hasDescendant(withText("USD"))))); onView(withId(R.id.list_receipts)).check(matches(atPosition(2, - hasDescendant(withText(R.string.credit))))); + hasDescendant(withText(R.string.debit))))); onView(withId(R.id.list_receipts)).check( matches(atPosition(2, hasDescendant(withText(R.string.card_activation_fee))))); onView(withId(R.id.list_receipts)).check( @@ -444,7 +444,7 @@ public void testListReceipt_clickTransactionDisplaysDetails() { onView(withId(R.id.receipt_id_label)).check(matches(withText(R.string.mobileJournalNumberLabel))); onView(withId(R.id.receipt_id_value)).check(matches(withText("3051579"))); onView(withId(R.id.date_label)).check(matches(withText(R.string.date))); - onView(withId(R.id.date_value)).check(matches(withText("Fri, June 7, 2019, 10:08 AM PDT"))); + onView(withId(R.id.date_value)).check(matches(withText("Jun 7, 2019, 10:08 AM PDT"))); onView(withId(R.id.client_id_label)).check(matches(withText(R.string.mobileMerchantTxnLabel))); onView(withId(R.id.client_id_value)).check(matches(withText("8OxXefx5"))); @@ -498,7 +498,7 @@ public void testListReceipt_clickTransactionDisplaysDetailsWithoutFees() { onView(withId(R.id.receipt_id_label)).check(matches(withText(R.string.mobileJournalNumberLabel))); onView(withId(R.id.receipt_id_value)).check(matches(withText("3051590"))); onView(withId(R.id.date_label)).check(matches(withText(R.string.date))); - onView(withId(R.id.date_value)).check(matches(withText("Sat, December 1, 2018, 9:12 AM PST"))); + onView(withId(R.id.date_value)).check(matches(withText("Dec 1, 2018, 9:12 AM PST"))); onView(withId(R.id.client_id_label)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); onView(withId(R.id.client_id_value)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); diff --git a/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ListPrepaidCardReceiptActivity.java b/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ListPrepaidCardReceiptActivity.java index 7ad58abe3..3f126a101 100644 --- a/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ListPrepaidCardReceiptActivity.java +++ b/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ListPrepaidCardReceiptActivity.java @@ -21,6 +21,7 @@ import android.os.Bundle; import android.text.TextUtils; import android.view.View; +import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; @@ -51,6 +52,8 @@ public class ListPrepaidCardReceiptActivity extends AppCompatActivity implements @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); + setContentView(R.layout.activity_list_prepaid_card_receipt); Toolbar toolbar = findViewById(R.id.toolbar); diff --git a/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ListReceiptFragment.java b/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ListReceiptFragment.java index 071e1dd5c..237640deb 100644 --- a/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ListReceiptFragment.java +++ b/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ListReceiptFragment.java @@ -43,6 +43,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.hyperwallet.android.model.receipt.Receipt; +import com.hyperwallet.android.ui.common.util.CurrencyParser; import com.hyperwallet.android.ui.common.util.DateUtils; import com.hyperwallet.android.ui.common.view.OneClickListener; import com.hyperwallet.android.ui.receipt.R; @@ -285,8 +286,9 @@ public void onOneClick(View v) { if (CREDIT.equals(receipt.getEntry())) { transactionAmount.setTextColor(transactionAmount.getContext() .getResources().getColor(R.color.positiveColor)); - transactionAmount.setText(transactionAmount.getContext() - .getString(R.string.credit_sign, currencySymbol, receipt.getAmount())); + transactionAmount.setText( + CurrencyParser.getInstance(itemView.getContext()).formatCurrency(receipt.getCurrency(), + receipt.getAmount())); transactionTypeIcon.setTextColor(transactionTypeIcon.getContext() .getResources().getColor(R.color.positiveColor)); transactionTypeIcon.setText(transactionTypeIcon.getContext().getText(R.string.credit)); @@ -295,8 +297,9 @@ public void onOneClick(View v) { .getResources().getColor(R.color.negativeColor)); transactionTypeIcon.setTextColor(transactionTypeIcon.getContext() .getResources().getColor(R.color.negativeColor)); - transactionAmount.setText(transactionAmount.getContext() - .getString(R.string.debit_sign, currencySymbol, receipt.getAmount())); + transactionAmount.setText(transactionAmount.getContext().getString(R.string.debit_sign, + CurrencyParser.getInstance(itemView.getContext()).formatCurrency(receipt.getCurrency(), + receipt.getAmount()))); transactionTypeIcon.setText(transactionTypeIcon.getContext().getText(R.string.debit)); } diff --git a/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ListReceiptsFragment.java b/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ListReceiptsFragment.java index 5a5745bde..33cfc0144 100644 --- a/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ListReceiptsFragment.java +++ b/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ListReceiptsFragment.java @@ -334,7 +334,9 @@ public void onOneClick(View v) { if (CREDIT.equals(receipt.getEntry())) { transactionAmount.setTextColor(transactionAmount.getContext() .getResources().getColor(R.color.positiveColor)); - transactionAmount.setText(CurrencyParser.getInstance(itemView.getContext()).formatCurrency(receipt.getCurrency(), receipt.getAmount())); + transactionAmount.setText( + CurrencyParser.getInstance(itemView.getContext()).formatCurrency(receipt.getCurrency(), + receipt.getAmount())); transactionTypeIcon.setTextColor(transactionTypeIcon.getContext() .getResources().getColor(R.color.positiveColor)); transactionTypeIcon.setText(transactionTypeIcon.getContext().getText(R.string.credit)); @@ -343,8 +345,9 @@ public void onOneClick(View v) { .getResources().getColor(R.color.negativeColor)); transactionTypeIcon.setTextColor(transactionTypeIcon.getContext() .getResources().getColor(R.color.negativeColor)); - transactionAmount.setText(transactionAmount.getContext().getString(R.string.debit_sign_receipts, - CurrencyParser.getInstance(itemView.getContext()).formatCurrency(receipt.getCurrency(), receipt.getAmount()))); + transactionAmount.setText(transactionAmount.getContext().getString(R.string.debit_sign, + CurrencyParser.getInstance(itemView.getContext()).formatCurrency(receipt.getCurrency(), + receipt.getAmount()))); transactionTypeIcon.setText(transactionTypeIcon.getContext().getText(R.string.debit)); } diff --git a/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ListUserReceiptActivity.java b/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ListUserReceiptActivity.java index 3e2ce59ed..08f2e1142 100644 --- a/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ListUserReceiptActivity.java +++ b/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ListUserReceiptActivity.java @@ -20,6 +20,7 @@ import android.content.pm.ActivityInfo; import android.os.Bundle; import android.view.View; +import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -50,6 +51,8 @@ public class ListUserReceiptActivity extends AppCompatActivity implements OnNetw @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); + setContentView(R.layout.activity_list_user_receipt); Toolbar toolbar = findViewById(R.id.toolbar); diff --git a/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ReceiptDetailActivity.java b/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ReceiptDetailActivity.java index 970826462..f8966609a 100644 --- a/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ReceiptDetailActivity.java +++ b/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ReceiptDetailActivity.java @@ -41,6 +41,8 @@ public class ReceiptDetailActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); + setContentView(R.layout.activity_receipt_detail); Toolbar toolbar = findViewById(R.id.toolbar); diff --git a/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ReceiptDetailFragment.java b/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ReceiptDetailFragment.java index 79a9bb46a..71c4753a8 100644 --- a/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ReceiptDetailFragment.java +++ b/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/ReceiptDetailFragment.java @@ -16,6 +16,7 @@ */ package com.hyperwallet.android.ui.receipt.view; +import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; import static android.text.format.DateUtils.FORMAT_ABBREV_WEEKDAY; import static android.text.format.DateUtils.FORMAT_SHOW_DATE; import static android.text.format.DateUtils.FORMAT_SHOW_TIME; @@ -119,15 +120,18 @@ private void setTransactionView(@NonNull final Receipt receipt, @NonNull final V if (CREDIT.equals(receipt.getEntry())) { transactionAmount.setTextColor(transactionAmount.getContext() .getResources().getColor(R.color.positiveColor)); - transactionAmount.setText(CurrencyParser.getInstance(view.getContext()).formatCurrency(receipt.getCurrency(), receipt.getAmount())); + transactionAmount.setText( + CurrencyParser.getInstance(view.getContext()).formatCurrency(receipt.getCurrency(), + receipt.getAmount())); transactionTypeIcon.setTextColor(transactionTypeIcon.getContext() .getResources().getColor(R.color.positiveColor)); transactionTypeIcon.setText(transactionTypeIcon.getContext().getText(R.string.credit)); } else if (DEBIT.equals(receipt.getEntry())) { transactionAmount.setTextColor(transactionAmount.getContext() .getResources().getColor(R.color.negativeColor)); - transactionAmount.setText(transactionAmount.getContext().getString(R.string.debit_sign_receipts, - CurrencyParser.getInstance(view.getContext()).formatCurrency(receipt.getCurrency(), receipt.getAmount()))); + transactionAmount.setText(transactionAmount.getContext().getString(R.string.debit_sign, + CurrencyParser.getInstance(view.getContext()).formatCurrency(receipt.getCurrency(), + receipt.getAmount()))); transactionTypeIcon.setTextColor(transactionTypeIcon.getContext() .getResources().getColor(R.color.negativeColor)); transactionTypeIcon.setText(transactionTypeIcon.getContext().getText(R.string.debit)); @@ -161,13 +165,16 @@ private void setFeeDetailsView(@NonNull final Receipt receipt, @NonNull final Vi String currencySymbol = Currency.getInstance(receipt.getCurrency()).getSymbol(Locale.getDefault()); TextView amountView = view.findViewById(R.id.details_amount_value); - amountView.setText(CurrencyParser.getInstance(view.getContext()).formatCurrency(receipt.getCurrency(), receipt.getAmount()) + " " + receipt.getCurrency()); + amountView.setText(CurrencyParser.getInstance(view.getContext()).formatCurrency(receipt.getCurrency(), + receipt.getAmount()) + " " + receipt.getCurrency()); TextView fee = view.findViewById(R.id.details_fee_value); - fee.setText(CurrencyParser.getInstance(view.getContext()).formatCurrency(receipt.getCurrency(), receipt.getFee()) + " " + receipt.getCurrency()); + fee.setText(CurrencyParser.getInstance(view.getContext()).formatCurrency(receipt.getCurrency(), + receipt.getFee()) + " " + receipt.getCurrency()); TextView transfer = view.findViewById(R.id.details_transfer_amount_value); - transfer.setText(CurrencyParser.getInstance(view.getContext()).formatCurrency(receipt.getCurrency(), transferAmountTotal) + " " + receipt.getCurrency()); + transfer.setText(CurrencyParser.getInstance(view.getContext()).formatCurrency(receipt.getCurrency(), + transferAmountTotal) + " " + receipt.getCurrency()); } } @@ -181,7 +188,7 @@ private void setDetailsView(@NonNull final Receipt receipt, @NonNull final View dateView.setText(view.getContext().getString(R.string.concat_date_string_view_format, formatDateTime(view.getContext(), date.getTime(), FORMAT_SHOW_DATE | FORMAT_SHOW_TIME | FORMAT_SHOW_YEAR - | FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_WEEKDAY), timezone)); + | FORMAT_ABBREV_MONTH), timezone)); if (receipt.getDetails() != null) { ReceiptDetails receiptDetails = receipt.getDetails(); diff --git a/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/TabbedListReceiptsActivity.java b/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/TabbedListReceiptsActivity.java index 2d011e93e..c5cc2bd69 100644 --- a/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/TabbedListReceiptsActivity.java +++ b/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/TabbedListReceiptsActivity.java @@ -19,6 +19,7 @@ import android.content.pm.ActivityInfo; import android.os.Bundle; import android.view.View; +import android.view.WindowManager; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; @@ -50,6 +51,8 @@ public class TabbedListReceiptsActivity extends AppCompatActivity implements OnN @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); + setContentView(R.layout.activity_tabbed_list_receipt); Toolbar toolbar = findViewById(R.id.toolbar); diff --git a/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/TabbedListReceiptsFragment.java b/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/TabbedListReceiptsFragment.java index be268b1df..ed0805574 100644 --- a/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/TabbedListReceiptsFragment.java +++ b/receiptui/src/main/java/com/hyperwallet/android/ui/receipt/view/TabbedListReceiptsFragment.java @@ -51,7 +51,6 @@ public class TabbedListReceiptsFragment extends Fragment { private ListReceiptsViewPagerAdapter mListReceiptsViewPagerAdapter; private ViewPager mViewPager; private TabLayout mTabLayout; - private LinearLayout mHeader; private TabbedListReceiptsViewModel mTabbedListReceiptsViewModel; @@ -80,7 +79,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat super.onViewCreated(view, savedInstanceState); mViewPager = view.findViewById(R.id.receipts_pager); mTabLayout = view.findViewById(R.id.tab_layout); - mHeader = view.findViewById(R.id.transactions_header); } void retry() { @@ -90,9 +88,6 @@ void retry() { @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - if (getActivity() instanceof TabbedListReceiptsActivity) { - mHeader.setVisibility(View.GONE); - } mTabbedListReceiptsViewModel = ViewModelProviders.of(requireActivity()).get(TabbedListReceiptsViewModel.class); mListReceiptsViewPagerAdapter = new ListReceiptsViewPagerAdapter(getFragmentManager(), FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT, new ArrayList(), diff --git a/receiptui/src/main/res/layout/fragment_receipt_detail.xml b/receiptui/src/main/res/layout/fragment_receipt_detail.xml index 3d4421c6e..53689e370 100644 --- a/receiptui/src/main/res/layout/fragment_receipt_detail.xml +++ b/receiptui/src/main/res/layout/fragment_receipt_detail.xml @@ -23,6 +23,7 @@ style="@style/HeaderTitleAppearance" android:layout_width="match_parent" android:layout_height="wrap_content" + android:orientation="vertical" android:paddingTop="@dimen/grid_padding_top"> + android:layout_height="wrap_content"> @@ -103,8 +104,6 @@ android:layout_alignParentTop="true" android:layout_alignParentEnd="true" android:gravity="end" - android:ellipsize="end" - android:maxLines="1" android:layout_marginStart="@dimen/grid_margin_left" android:layout_toEndOf="@+id/receipt_id_label" android:layout_marginTop="@dimen/grid_margin_top" @@ -138,8 +137,6 @@ android:layout_alignParentEnd="true" android:layout_marginStart="@dimen/grid_margin_left" android:gravity="end" - android:ellipsize="end" - android:maxLines="1" android:paddingTop="@dimen/item_text_top_bottom_margin" tools:text="@string/text_placeholder" android:textAppearance="@style/TextAppearance.Hyperwallet.Caption" /> @@ -176,8 +173,6 @@ android:paddingEnd="@dimen/item_text_top_bottom_margin" android:layout_marginTop="@dimen/item_text_top_bottom_margin" android:layout_toEndOf="@+id/client_id_label" - android:ellipsize="end" - android:maxLines="1" android:gravity="end" tools:text="@string/text_placeholder" android:textAppearance="@style/TextAppearance.Hyperwallet.Caption" @@ -213,8 +208,6 @@ android:layout_marginStart="@dimen/item_text_top_bottom_margin" android:layout_marginTop="@dimen/item_text_top_bottom_margin" android:layout_toEndOf="@+id/charity_label" - android:ellipsize="end" - android:maxLines="1" android:gravity="end" tools:text="@string/text_placeholder" android:textAppearance="@style/TextAppearance.Hyperwallet.Caption" @@ -251,8 +244,6 @@ android:layout_marginStart="@dimen/item_text_top_bottom_margin" android:layout_marginTop="@dimen/item_text_top_bottom_margin" android:layout_toEndOf="@+id/check_number_label" - android:ellipsize="end" - android:maxLines="1" android:gravity="end" tools:text="@string/text_placeholder" android:textAppearance="@style/TextAppearance.Hyperwallet.Caption" @@ -289,8 +280,6 @@ android:layout_marginStart="@dimen/item_text_top_bottom_margin" android:layout_marginTop="@dimen/item_text_top_bottom_margin" android:layout_toEndOf="@+id/website_label" - android:ellipsize="end" - android:maxLines="1" android:gravity="end" tools:text="@string/text_placeholder" android:textAppearance="@style/TextAppearance.Hyperwallet.Caption" @@ -397,8 +386,6 @@ android:layout_alignParentTop="true" android:layout_alignParentEnd="true" android:gravity="end" - android:ellipsize="end" - android:maxLines="1" android:layout_marginStart="@dimen/grid_margin_left" android:layout_toEndOf="@+id/details_amount_label" android:layout_marginTop="@dimen/grid_margin_top" @@ -437,8 +424,6 @@ android:layout_alignParentTop="true" android:layout_alignParentEnd="true" android:gravity="end" - android:ellipsize="end" - android:maxLines="1" android:layout_marginStart="@dimen/grid_margin_left" android:layout_toEndOf="@+id/details_fee_label" android:layout_marginTop="@dimen/grid_margin_top" @@ -477,8 +462,6 @@ android:layout_alignParentTop="true" android:layout_alignParentEnd="true" android:gravity="end" - android:ellipsize="end" - android:maxLines="1" android:layout_marginStart="@dimen/grid_margin_left" android:layout_toEndOf="@+id/details_transfer_amount_label" android:layout_marginTop="@dimen/grid_margin_top" diff --git a/receiptui/src/main/res/layout/fragment_tabbed_list_receipt.xml b/receiptui/src/main/res/layout/fragment_tabbed_list_receipt.xml index 342a5e04c..81c468f23 100644 --- a/receiptui/src/main/res/layout/fragment_tabbed_list_receipt.xml +++ b/receiptui/src/main/res/layout/fragment_tabbed_list_receipt.xml @@ -4,34 +4,13 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginStart="@dimen/default_margin" - android:layout_marginTop="@dimen/default_margin" android:layout_marginEnd="@dimen/default_margin"> - - - - - + app:layout_constraintBottom_toBottomOf="parent"> + android:layout_height="wrap_content"> - + + android:layout_height="wrap_content"> diff --git a/receiptui/src/main/res/layout/receipt.xml b/receiptui/src/main/res/layout/receipt.xml index 9fd3af32e..06617caa2 100644 --- a/receiptui/src/main/res/layout/receipt.xml +++ b/receiptui/src/main/res/layout/receipt.xml @@ -14,19 +14,19 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:text="@string/text_placeholder" - android:ellipsize="end" - android:maxLines="1" style="@style/ReceiptTitle" app:layout_constraintEnd_toStartOf="@+id/transaction_amount" app:layout_constraintStart_toEndOf="@+id/transaction_type_icon" app:layout_constraintTop_toTopOf="parent" /> - diff --git a/receiptui/src/main/res/values/dimens.xml b/receiptui/src/main/res/values/dimens.xml index 7f97cbc6b..9f2d14c16 100644 --- a/receiptui/src/main/res/values/dimens.xml +++ b/receiptui/src/main/res/values/dimens.xml @@ -3,5 +3,4 @@ 10dp 8dp 80dp - 48dp diff --git a/receiptui/src/main/res/values/strings.xml b/receiptui/src/main/res/values/strings.xml index ba2d0da16..4c3f2a448 100644 --- a/receiptui/src/main/res/values/strings.xml +++ b/receiptui/src/main/res/values/strings.xml @@ -28,8 +28,7 @@ \ue908 \ue907 - -%s%s - -%s + -%s %s%s diff --git a/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodConfigurationRepositoryImpl.java b/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodConfigurationRepositoryImpl.java index c953ff758..a6a2793a9 100644 --- a/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodConfigurationRepositoryImpl.java +++ b/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodConfigurationRepositoryImpl.java @@ -184,6 +184,11 @@ class FieldMapKey { this.mTransferMethodType = mTransferMethodType; } + FieldMapKey() { + this.mCountry = null; + this.mCurrency = null; + this.mTransferMethodType = null; + } private String getCountry() { return mCountry; } diff --git a/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepository.java b/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepository.java index f11f896c5..d94d7279c 100644 --- a/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepository.java +++ b/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepository.java @@ -46,6 +46,17 @@ void createTransferMethod(@NonNull TransferMethod transferMethod, */ void loadTransferMethods(@NonNull LoadTransferMethodListCallback callback); + + /** + * Update transfer method specified. + * + * @param transferMethod transfer method to deactivate @see {@link TransferMethod} + * @param callback @see {@link DeactivateTransferMethodCallback} + */ + void updateTransferMethod(@NonNull final TransferMethod transferMethod, + @NonNull LoadTransferMethodCallback callback); + + /** * Load latest transfer methods available, associated with current context * diff --git a/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepositoryFactory.java b/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepositoryFactory.java index ed9360c6f..daede99ff 100644 --- a/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepositoryFactory.java +++ b/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepositoryFactory.java @@ -20,10 +20,12 @@ public class TransferMethodRepositoryFactory { private static TransferMethodRepositoryFactory sInstance; private TransferMethodRepository mTransferMethodRepository; private TransferMethodConfigurationRepository mTransferMethodConfigurationRepository; + private TransferMethodUpdateConfigurationRepository mTransferMethodUpdateConfigurationRepository; private TransferMethodRepositoryFactory() { mTransferMethodRepository = new TransferMethodRepositoryImpl(); mTransferMethodConfigurationRepository = new TransferMethodConfigurationRepositoryImpl(); + mTransferMethodUpdateConfigurationRepository = new TransferMethodUpdateConfigurationRepositoryImpl(); } public static synchronized TransferMethodRepositoryFactory getInstance() { @@ -44,4 +46,9 @@ public TransferMethodRepository getTransferMethodRepository() { public TransferMethodConfigurationRepository getTransferMethodConfigurationRepository() { return mTransferMethodConfigurationRepository; } + + public TransferMethodUpdateConfigurationRepository getTransferMethodUpdateConfigurationRepository() { + return mTransferMethodUpdateConfigurationRepository; + } + } diff --git a/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepositoryImpl.java b/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepositoryImpl.java index eec04ee35..5252bfa8a 100644 --- a/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepositoryImpl.java +++ b/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepositoryImpl.java @@ -22,6 +22,7 @@ import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodFields.TYPE; import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.BANK_ACCOUNT; import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.BANK_CARD; +import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.PAPER_CHECK; import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.PAYPAL_ACCOUNT; import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.VENMO_ACCOUNT; import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.WIRE_ACCOUNT; @@ -41,6 +42,7 @@ import com.hyperwallet.android.model.paging.PageList; import com.hyperwallet.android.model.transfermethod.BankAccount; import com.hyperwallet.android.model.transfermethod.BankCard; +import com.hyperwallet.android.model.transfermethod.PaperCheck; import com.hyperwallet.android.model.transfermethod.PayPalAccount; import com.hyperwallet.android.model.transfermethod.TransferMethod; import com.hyperwallet.android.model.transfermethod.TransferMethodQueryParam; @@ -80,6 +82,9 @@ public void createTransferMethod(@NonNull final TransferMethod transferMethod, case VENMO_ACCOUNT: createVenmoAccount(transferMethod, callback); break; + case PAPER_CHECK: + createPaperCheck(transferMethod, callback); + break; default: // error on unknown transfer type callback.onError(getErrorsOnUnsupportedTransferType()); } @@ -118,6 +123,31 @@ public Handler getHandler() { }); } + @Override + public void updateTransferMethod(@NonNull TransferMethod transferMethod, + @NonNull LoadTransferMethodCallback callback) { + switch (transferMethod.getField(TYPE)) { + case BANK_ACCOUNT: + case WIRE_ACCOUNT: + updateBankAccount(transferMethod, callback); + break; + case BANK_CARD: + updateBankCard(transferMethod, callback); + break; + case PAYPAL_ACCOUNT: + updatePayPalAccount(transferMethod, callback); + break; + case VENMO_ACCOUNT: + updateVenmoAccount(transferMethod, callback); + break; + case PAPER_CHECK: + updatePaperCheck(transferMethod, callback); + break; + default: // error on unknown transfer type + callback.onError(getErrorsOnUnsupportedTransferType()); + } + } + /** * @see TransferMethodRepository#loadLatestTransferMethod(LoadTransferMethodCallback) */ @@ -170,6 +200,9 @@ public void deactivateTransferMethod(@NonNull final TransferMethod transferMetho case VENMO_ACCOUNT: deactivateVenmoAccount(transferMethod, callback); break; + case PAPER_CHECK: + deactivatePaperCheck(transferMethod, callback); + break; default: // error on unknown transfer type callback.onError(getErrorsOnUnsupportedTransferType()); } @@ -239,7 +272,7 @@ public Handler getHandler() { } private void deactivateVenmoAccount(@NonNull final TransferMethod transferMethod, - @NonNull final DeactivateTransferMethodCallback callback) { + @NonNull final DeactivateTransferMethodCallback callback) { getHyperwallet().deactivateVenmoAccount(transferMethod.getField(TOKEN), null, new HyperwalletListener() { @Override @@ -259,8 +292,29 @@ public Handler getHandler() { }); } + private void deactivatePaperCheck(@NonNull final TransferMethod transferMethod, + @NonNull final DeactivateTransferMethodCallback callback) { + getHyperwallet().deactivatePaperCheck(transferMethod.getField(TOKEN), null, + new HyperwalletListener() { + @Override + public void onSuccess(@Nullable StatusTransition result) { + callback.onTransferMethodDeactivated(result); + } + + @Override + public void onFailure(HyperwalletException exception) { + callback.onError(exception.getErrors()); + } + + @Override + public Handler getHandler() { + return mHandler; + } + }); + } + private void createBankAccount(final TransferMethod transferMethod, - final LoadTransferMethodCallback callback) { + final LoadTransferMethodCallback callback) { BankAccount bankAccount = (BankAccount) transferMethod; getHyperwallet().createBankAccount(bankAccount, new HyperwalletListener() { @@ -304,7 +358,7 @@ public Handler getHandler() { } private void createPayPalAccount(@NonNull final TransferMethod transferMethod, - @NonNull final LoadTransferMethodCallback callback) { + @NonNull final LoadTransferMethodCallback callback) { PayPalAccount payPalAccount = (PayPalAccount) transferMethod; getHyperwallet().createPayPalAccount(payPalAccount, new HyperwalletListener() { @@ -326,7 +380,7 @@ public Handler getHandler() { } private void createVenmoAccount(@NonNull final TransferMethod transferMethod, - @NonNull final LoadTransferMethodCallback callback) { + @NonNull final LoadTransferMethodCallback callback) { VenmoAccount venmoAccount = (VenmoAccount) transferMethod; getHyperwallet().createVenmoAccount(venmoAccount, new HyperwalletListener() { @@ -347,6 +401,28 @@ public Handler getHandler() { }); } + private void createPaperCheck(@NonNull final TransferMethod transferMethod, + @NonNull final LoadTransferMethodCallback callback) { + PaperCheck paperCheck = (PaperCheck) transferMethod; + + getHyperwallet().createPaperCheck(paperCheck, new HyperwalletListener() { + @Override + public void onSuccess(@Nullable PaperCheck result) { + callback.onTransferMethodLoaded(result); + } + + @Override + public void onFailure(HyperwalletException exception) { + callback.onError(exception.getErrors()); + } + + @Override + public Handler getHandler() { + return mHandler; + } + }); + } + // Note: This way of surfacing error is not ideal but rather a workaround please have a look on other options, // before resulting into this pattern private Errors getErrorsOnUnsupportedTransferType() { @@ -354,4 +430,115 @@ private Errors getErrorsOnUnsupportedTransferType() { EC_UNEXPECTED_EXCEPTION); return new Errors(Collections.singletonList(error)); } + + private void updateBankAccount(final TransferMethod transferMethod, + final LoadTransferMethodCallback callback) { + BankAccount bankAccount = (BankAccount) transferMethod; + + getHyperwallet().updateBankAccount(bankAccount, new HyperwalletListener() { + @Override + public void onSuccess(@Nullable BankAccount result) { + callback.onTransferMethodLoaded(result); + } + + @Override + public void onFailure(HyperwalletException exception) { + callback.onError(exception.getErrors()); + } + + @Override + public Handler getHandler() { + return mHandler; + } + }); + } + + private void updateBankCard(@NonNull final TransferMethod transferMethod, + @NonNull final LoadTransferMethodCallback callback) { + BankCard bankCard = (BankCard) transferMethod; + + getHyperwallet().updateBankCard(bankCard, new HyperwalletListener() { + @Override + public void onSuccess(@Nullable BankCard result) { + callback.onTransferMethodLoaded(result); + } + + @Override + public void onFailure(HyperwalletException exception) { + callback.onError(exception.getErrors()); + } + + @Override + public Handler getHandler() { + return mHandler; + } + }); + } + + private void updatePayPalAccount(@NonNull final TransferMethod transferMethod, + @NonNull final LoadTransferMethodCallback callback) { + PayPalAccount payPalAccount = (PayPalAccount) transferMethod; + + getHyperwallet().updatePayPalAccount(payPalAccount, new HyperwalletListener() { + @Override + public void onSuccess(@Nullable PayPalAccount result) { + callback.onTransferMethodLoaded(result); + } + + @Override + public void onFailure(HyperwalletException exception) { + callback.onError(exception.getErrors()); + } + + @Override + public Handler getHandler() { + return mHandler; + } + }); + } + + private void updateVenmoAccount(@NonNull final TransferMethod transferMethod, + @NonNull final LoadTransferMethodCallback callback) { + VenmoAccount venmoAccount = (VenmoAccount) transferMethod; + + getHyperwallet().updateVenmoAccount(venmoAccount, new HyperwalletListener() { + @Override + public void onSuccess(@Nullable VenmoAccount result) { + callback.onTransferMethodLoaded(result); + } + + @Override + public void onFailure(HyperwalletException exception) { + callback.onError(exception.getErrors()); + } + + @Override + public Handler getHandler() { + return mHandler; + } + }); + } + + private void updatePaperCheck(@NonNull final TransferMethod transferMethod, + @NonNull final LoadTransferMethodCallback callback) { + PaperCheck paperCheck = (PaperCheck) transferMethod; + + getHyperwallet().updatePaperCheck(paperCheck, new HyperwalletListener() { + @Override + public void onSuccess(@Nullable PaperCheck result) { + callback.onTransferMethodLoaded(result); + } + + @Override + public void onFailure(HyperwalletException exception) { + callback.onError(exception.getErrors()); + } + + @Override + public Handler getHandler() { + return mHandler; + } + }); + } + } diff --git a/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodUpdateConfigurationRepository.java b/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodUpdateConfigurationRepository.java new file mode 100644 index 000000000..5110c62f5 --- /dev/null +++ b/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodUpdateConfigurationRepository.java @@ -0,0 +1,49 @@ +/* + * Copyright 2018 Hyperwallet + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO + * EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.hyperwallet.android.ui.transfermethod.repository; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.hyperwallet.android.model.Errors; +import com.hyperwallet.android.model.graphql.HyperwalletTransferMethodConfigurationField; + +public interface TransferMethodUpdateConfigurationRepository { + + void getFields(@NonNull final String transferMethodToken, + @NonNull final LoadFieldsCallback loadFieldsCallback); + + + void refreshFields(); + + interface LoadFieldsCallback { + + void onFieldsLoaded(@Nullable final HyperwalletTransferMethodConfigurationField field); + + void onError(@NonNull final Errors errors); + } +} diff --git a/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodUpdateConfigurationRepositoryImpl.java b/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodUpdateConfigurationRepositoryImpl.java new file mode 100644 index 000000000..59ba2a218 --- /dev/null +++ b/transfermethodrepository/src/main/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodUpdateConfigurationRepositoryImpl.java @@ -0,0 +1,113 @@ +/* + * Copyright 2018 Hyperwallet + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO + * EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +package com.hyperwallet.android.ui.transfermethod.repository; + +import android.os.Handler; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +import com.hyperwallet.android.Hyperwallet; +import com.hyperwallet.android.exception.HyperwalletException; +import com.hyperwallet.android.listener.HyperwalletListener; +import com.hyperwallet.android.model.graphql.HyperwalletTransferMethodConfigurationField; +import com.hyperwallet.android.model.graphql.HyperwalletTransferMethodConfigurationKey; +import com.hyperwallet.android.model.graphql.query.TransferMethodUpdateConfigurationFieldQuery; +import com.hyperwallet.android.ui.common.repository.EspressoIdlingResource; + +import java.util.HashMap; +import java.util.Map; + +public class TransferMethodUpdateConfigurationRepositoryImpl implements TransferMethodUpdateConfigurationRepository { + private final Handler mHandler; + private final Map mFieldMap; + + public TransferMethodUpdateConfigurationRepositoryImpl() { + mHandler = new Handler(); + mFieldMap = new HashMap<>(); + } + + @VisibleForTesting() + protected TransferMethodUpdateConfigurationRepositoryImpl(@Nullable Handler handler, + Map fieldMap) { + mHandler = handler; + mFieldMap = fieldMap; + } + + @VisibleForTesting + Hyperwallet getHyperwallet() { + return Hyperwallet.getDefault(); + } + + @VisibleForTesting + void getTransferMethodConfigurationFieldResult( + @NonNull final String transferMethodToken, + @NonNull final LoadFieldsCallback loadFieldsCallback) { + TransferMethodUpdateConfigurationFieldQuery query = new TransferMethodUpdateConfigurationFieldQuery( + transferMethodToken); + EspressoIdlingResource.increment(); + + getHyperwallet().retrieveUpdateTransferMethodConfigurationFields( + query, + new HyperwalletListener() { + @Override + public void onSuccess(HyperwalletTransferMethodConfigurationField result) { + FieldMapKey fieldMapKey = new FieldMapKey(); + mFieldMap.put(fieldMapKey, result); + loadFieldsCallback.onFieldsLoaded(result); + EspressoIdlingResource.decrement(); + } + + @Override + public void onFailure(HyperwalletException exception) { + loadFieldsCallback.onError(exception.getErrors()); + EspressoIdlingResource.decrement(); + } + + @Override + public Handler getHandler() { + return mHandler; + } + }); + + } + + @Override + public synchronized void getFields( + @NonNull final String transferMethodToken, + @NonNull final LoadFieldsCallback loadFieldsCallback) { + + getTransferMethodConfigurationFieldResult(transferMethodToken, loadFieldsCallback); + } + + @Override + public void refreshFields() { + mFieldMap.clear(); + } + +} \ No newline at end of file diff --git a/transfermethodrepository/src/test/java/com/hyperwallet/android/ui/transfermethod/repository/FieldMapKeyTest.java b/transfermethodrepository/src/test/java/com/hyperwallet/android/ui/transfermethod/repository/FieldMapKeyTest.java index f0541bb9a..2b18445a9 100644 --- a/transfermethodrepository/src/test/java/com/hyperwallet/android/ui/transfermethod/repository/FieldMapKeyTest.java +++ b/transfermethodrepository/src/test/java/com/hyperwallet/android/ui/transfermethod/repository/FieldMapKeyTest.java @@ -13,6 +13,12 @@ public void testEquals_withSameReference() { assertEquals(thisKey, thisKey); } + @Test + public void testEquals_withSameReference_withAnotherConstructor() { + FieldMapKey thisKey = new FieldMapKey(); + assertEquals(thisKey, thisKey); + } + @Test public void testEquals_withDifferentObjectType() { FieldMapKey thisKey = new FieldMapKey("US", "USD", "BANK_ACCOUNT"); @@ -33,5 +39,4 @@ public void testEquals_withDifferentReferencesDifferentValues() { FieldMapKey thatKey = new FieldMapKey("CA", "CAD", "BANK_ACCOUNT"); assertNotEquals(thisKey, thatKey); } - } diff --git a/transfermethodrepository/src/test/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepositoryFactoryTest.java b/transfermethodrepository/src/test/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepositoryFactoryTest.java index 7e89c5a03..232f724f3 100644 --- a/transfermethodrepository/src/test/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepositoryFactoryTest.java +++ b/transfermethodrepository/src/test/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepositoryFactoryTest.java @@ -14,6 +14,7 @@ public void testGetInstance_verifyRepositoriesInitialized() { assertThat(repositoryFactory, is(notNullValue())); assertThat(repositoryFactory.getTransferMethodRepository(), is(notNullValue())); assertThat(repositoryFactory.getTransferMethodConfigurationRepository(), is(notNullValue())); + assertThat(repositoryFactory.getTransferMethodUpdateConfigurationRepository(), is(notNullValue())); } @Test @@ -23,10 +24,13 @@ public void testClearInstance_verifyRepositoryCleared() { TransferMethodRepository transferMethodRepository = repositoryFactory.getTransferMethodRepository(); TransferMethodConfigurationRepository configurationRepository = repositoryFactory.getTransferMethodConfigurationRepository(); + TransferMethodUpdateConfigurationRepository transferMethodUpdateConfigurationRepository = + repositoryFactory.getTransferMethodUpdateConfigurationRepository(); TransferMethodRepositoryFactory currentRepositoryFactory = TransferMethodRepositoryFactory.getInstance(); assertThat(repositoryFactory, is(currentRepositoryFactory)); assertThat(transferMethodRepository, is(currentRepositoryFactory.getTransferMethodRepository())); assertThat(configurationRepository, is(currentRepositoryFactory.getTransferMethodConfigurationRepository())); + assertThat(transferMethodUpdateConfigurationRepository,is(currentRepositoryFactory.getTransferMethodUpdateConfigurationRepository())); TransferMethodRepositoryFactory.clearInstance(); @@ -35,5 +39,6 @@ public void testClearInstance_verifyRepositoryCleared() { assertThat(transferMethodRepository, is(not(anotherRepositoryFactory.getTransferMethodRepository()))); assertThat(configurationRepository, is(not(anotherRepositoryFactory.getTransferMethodConfigurationRepository()))); + assertThat(transferMethodUpdateConfigurationRepository,is(not(anotherRepositoryFactory.getTransferMethodUpdateConfigurationRepository()))); } } \ No newline at end of file diff --git a/transfermethodrepository/src/test/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepositoryImplTest.java b/transfermethodrepository/src/test/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepositoryImplTest.java index 4df552eea..0018dff20 100644 --- a/transfermethodrepository/src/test/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepositoryImplTest.java +++ b/transfermethodrepository/src/test/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodRepositoryImplTest.java @@ -34,6 +34,7 @@ import com.hyperwallet.android.model.paging.PageList; import com.hyperwallet.android.model.transfermethod.BankAccount; import com.hyperwallet.android.model.transfermethod.BankCard; +import com.hyperwallet.android.model.transfermethod.PaperCheck; import com.hyperwallet.android.model.transfermethod.TransferMethod; import com.hyperwallet.android.model.transfermethod.TransferMethodQueryParam; import com.hyperwallet.android.model.transfermethod.PayPalAccount; @@ -87,6 +88,8 @@ public class TransferMethodRepositoryImplTest { @Captor private ArgumentCaptor mVenmoAccountArgumentCaptor; @Captor + private ArgumentCaptor mPaperCheckArgumentCaptor; + @Captor private ArgumentCaptor mStatusTransitionArgumentCaptor; @Captor private ArgumentCaptor> mListTransferMethodCaptor; @@ -141,6 +144,41 @@ public Object answer(InvocationOnMock invocation) { assertThat(transferMethod.getField(BANK_ACCOUNT_ID), is("3423423432")); } + @Test + public void testUpdateTransferMethod_bankAccountWithSuccess() { + BankAccount bankAccount = new BankAccount + .Builder("CA", "CAD", "3423423432") + .build(); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + BankAccount returnedBank = new BankAccount + .Builder("CA", "CAD", "3423423432") + .bankName("Mock Bank Response") + .build(); + listener.onSuccess(returnedBank); + return listener; + } + }).when(mHyperwallet).updateBankAccount(any(BankAccount.class), + ArgumentMatchers.>any()); + + // test + mTransferMethodRepository.updateTransferMethod(bankAccount, mLoadTransferMethodCallback); + + verify(mLoadTransferMethodCallback).onTransferMethodLoaded(mBankAccountArgumentCaptor.capture()); + verify(mLoadTransferMethodCallback, never()).onError(any(Errors.class)); + + BankAccount transferMethod = mBankAccountArgumentCaptor.getValue(); + assertThat(transferMethod, is(notNullValue())); + assertThat(transferMethod.getField(TYPE), is(TransferMethod.TransferMethodTypes.BANK_ACCOUNT)); + assertThat(transferMethod.getField(BANK_NAME), is("Mock Bank Response")); + assertThat(transferMethod.getField(TRANSFER_METHOD_COUNTRY), is("CA")); + assertThat(transferMethod.getField(TRANSFER_METHOD_CURRENCY), is("CAD")); + assertThat(transferMethod.getField(BANK_ACCOUNT_ID), is("3423423432")); + } + @Test public void testCreateTransferMethod_bankAccountWithError() { BankAccount bankAccount = new BankAccount @@ -171,6 +209,35 @@ public Object answer(InvocationOnMock invocation) { assertThat(mErrorsArgumentCaptor.getValue().getErrors(), hasItem(error)); } + @Test + public void testUpdateTransferMethod_bankAccountWithError() { + BankAccount bankAccount = new BankAccount + .Builder(COUNTRY_US, CURRENCY_USD, "23432432") + .build(); + + final Error error = new Error(TEST_MESSAGE, TEST_CODE); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + List errorList = new ArrayList<>(); + errorList.add(error); + Errors errors = new Errors(errorList); + HyperwalletException exception = new HyperwalletException(errors); + listener.onFailure(exception); + return listener; + } + }).when(mHyperwallet).updateBankAccount(any(BankAccount.class), + ArgumentMatchers.>any()); + + // test + mTransferMethodRepository.updateTransferMethod(bankAccount, mLoadTransferMethodCallback); + + verify(mLoadTransferMethodCallback).onError(mErrorsArgumentCaptor.capture()); + verify(mLoadTransferMethodCallback, never()).onTransferMethodLoaded(any(TransferMethod.class)); + assertThat(mErrorsArgumentCaptor.getValue().getErrors(), hasItem(error)); + } @Test public void testCreateTransferMethod_withUnsupportedTransferMethodType() { BankAccount bankAccount = new BankAccount @@ -483,6 +550,40 @@ public Object answer(InvocationOnMock invocation) { assertThat(transferMethod.getField(TransferMethod.TransferMethodFields.CARD_TYPE), is("cardType")); } + @Test + public void testUpdateTransferMethod_bankCardWithSuccess() { + BankCard bankCard = new BankCard + .Builder("CA", "CAD", "1232345456784", "2019-05", "234") + .build(); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + BankCard returnedBankCard = new BankCard + .Builder("CA", "CAD", "1232345456784", "2019-05", "234") + .cardBrand("Brand") + .cardType("cardType") + .build(); + listener.onSuccess(returnedBankCard); + return listener; + } + }).when(mHyperwallet).updateBankCard(any(BankCard.class), + ArgumentMatchers.>any()); + + // test + mTransferMethodRepository.updateTransferMethod(bankCard, mLoadTransferMethodCallback); + + verify(mLoadTransferMethodCallback).onTransferMethodLoaded(mBankCardArgumentCaptor.capture()); + verify(mLoadTransferMethodCallback, never()).onError(any(Errors.class)); + + BankCard transferMethod = mBankCardArgumentCaptor.getValue(); + assertThat(transferMethod, is(notNullValue())); + assertThat(transferMethod.getField(TYPE), is(TransferMethod.TransferMethodTypes.BANK_CARD)); + assertThat(transferMethod.getField(TransferMethod.TransferMethodFields.CARD_BRAND), is("Brand")); + assertThat(transferMethod.getField(TransferMethod.TransferMethodFields.CARD_TYPE), is("cardType")); + } + @Test public void testCreateTransferMethod_bankCardWithError() { BankCard bankCard = new BankCard @@ -512,7 +613,35 @@ public Object answer(InvocationOnMock invocation) { verify(mLoadTransferMethodCallback, never()).onTransferMethodLoaded(any(TransferMethod.class)); assertThat(mErrorsArgumentCaptor.getValue().getErrors(), hasItem(error)); } + @Test + public void testUpdateTransferMethod_bankCardWithError() { + BankCard bankCard = new BankCard + .Builder("CA", "CAD", "1232345456784", "2019-05", "234") + .build(); + + final Error error = new Error("bank card test message", "BANK_CARD_TEST_CODE"); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + List errorList = new ArrayList<>(); + errorList.add(error); + Errors errors = new Errors(errorList); + HyperwalletException exception = new HyperwalletException(errors); + listener.onFailure(exception); + return listener; + } + }).when(mHyperwallet).updateBankCard(any(BankCard.class), + ArgumentMatchers.>any()); + + // test + mTransferMethodRepository.updateTransferMethod(bankCard, mLoadTransferMethodCallback); + verify(mLoadTransferMethodCallback).onError(mErrorsArgumentCaptor.capture()); + verify(mLoadTransferMethodCallback, never()).onTransferMethodLoaded(any(TransferMethod.class)); + assertThat(mErrorsArgumentCaptor.getValue().getErrors(), hasItem(error)); + } @Test public void testLoadTransferMethod_returnsBankAccount() { @@ -720,6 +849,42 @@ public Object answer(InvocationOnMock invocation) { assertThat(payPalAccount.getField(TOKEN), is("trm-token-1342242314")); } + // @Test + public void testUpdateTransferMethod_payPalAccountWithSuccess() { + // prepare + final PayPalAccount returnedPayPalAccount = new PayPalAccount.Builder() + .email("money@mail.com") + .token("trm-token-1342242314") + .build(); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + returnedPayPalAccount.setField(STATUS, ACTIVATED); + listener.onSuccess(returnedPayPalAccount); + return listener; + } + }).when(mHyperwallet).updatePayPalAccount(any(PayPalAccount.class), + ArgumentMatchers.>any()); + + PayPalAccount parameter = new PayPalAccount.Builder().build(); + + // test + mTransferMethodRepository.updateTransferMethod(parameter, mLoadTransferMethodCallback); + + // verify + verify(mLoadTransferMethodCallback).onTransferMethodLoaded(mPayPalAccountArgumentCaptor.capture()); + verify(mLoadTransferMethodCallback, never()).onError(any(Errors.class)); + + // assert + PayPalAccount payPalAccount = mPayPalAccountArgumentCaptor.getValue(); + assertThat(payPalAccount, is(notNullValue())); + assertThat(payPalAccount.getEmail(), is("money@mail.com")); + assertThat(payPalAccount.getField(STATUS), is(ACTIVATED)); + assertThat(payPalAccount.getField(TOKEN), is("trm-token-1342242314")); + } + @Test public void testCreateTransferMethod_payPalAccountWithError() { // prepare @@ -751,6 +916,37 @@ public Object answer(InvocationOnMock invocation) { assertThat(mErrorsArgumentCaptor.getValue().getErrors(), hasItem(returnedError)); } + // @Test + public void testUpdateTransferMethod_payPalAccountWithError() { + // prepare + final Error returnedError = new Error("PayPal test message", "PAYPAL_TEST_CODE"); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + + List errorList = new ArrayList<>(); + errorList.add(returnedError); + + listener.onFailure(new HyperwalletException(new Errors(errorList))); + return listener; + } + }).when(mHyperwallet).updatePayPalAccount(any(PayPalAccount.class), + ArgumentMatchers.>any()); + PayPalAccount parameter = new PayPalAccount.Builder().build(); + + // test + mTransferMethodRepository.updateTransferMethod(parameter, mLoadTransferMethodCallback); + + // verify + verify(mLoadTransferMethodCallback, never()).onTransferMethodLoaded(any(TransferMethod.class)); + verify(mLoadTransferMethodCallback).onError(mErrorsArgumentCaptor.capture()); + + // assert + assertThat(mErrorsArgumentCaptor.getValue().getErrors(), hasItem(returnedError)); + } + @Test public void createTransferMethod_venmoAccountWithSuccess() { final VenmoAccount returnedVenmoAccount = buildVenmoAccount(); @@ -780,6 +976,89 @@ public Object answer(InvocationOnMock invocation) { assertThat(venmoAccount.getField(TOKEN), is(TEST_TOKEN)); } + public void updateTransferMethod_venmoAccountWithSuccess() { + final VenmoAccount returnedVenmoAccount = buildVenmoAccount(); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + returnedVenmoAccount.setField(STATUS, ACTIVATED); + listener.onSuccess(returnedVenmoAccount); + return listener; + } + }).when(mHyperwallet).updateVenmoAccount(any(VenmoAccount.class), any(HyperwalletListener.class)); + + VenmoAccount parameter = new VenmoAccount.Builder().build(); + + mTransferMethodRepository.updateTransferMethod(parameter, mLoadTransferMethodCallback); + + verify(mLoadTransferMethodCallback).onTransferMethodLoaded(mVenmoAccountArgumentCaptor.capture()); + verify(mLoadTransferMethodCallback, never()).onError(any(Errors.class)); + + VenmoAccount venmoAccount = mVenmoAccountArgumentCaptor.getValue(); + assertThat(venmoAccount, is(notNullValue())); + assertThat(venmoAccount.getCountry(), is(COUNTRY_US)); + assertThat(venmoAccount.getCurrency(), is(CURRENCY_USD)); + assertThat(venmoAccount.getAccountId(), is(VENMO_ACCOUNT_ID)); + assertThat(venmoAccount.getField(STATUS), is(ACTIVATED)); + assertThat(venmoAccount.getField(TOKEN), is(TEST_TOKEN)); + } + + @Test + public void createTransferMethod_paperCheckWithSuccess() { + final PaperCheck returnedPaperCheck = buildPaperCheck(); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + returnedPaperCheck.setField(STATUS, ACTIVATED); + listener.onSuccess(returnedPaperCheck); + return listener; + } + }).when(mHyperwallet).createPaperCheck(any(PaperCheck.class), any(HyperwalletListener.class)); + + PaperCheck parameter = new PaperCheck.Builder().build(); + + mTransferMethodRepository.createTransferMethod(parameter, mLoadTransferMethodCallback); + + verify(mLoadTransferMethodCallback).onTransferMethodLoaded(mPaperCheckArgumentCaptor.capture()); + verify(mLoadTransferMethodCallback, never()).onError(any(Errors.class)); + + PaperCheck paperCheck = mPaperCheckArgumentCaptor.getValue(); + assertThat(paperCheck, is(notNullValue())); + assertThat(paperCheck.getCountry(), is(COUNTRY_US)); + assertThat(paperCheck.getCurrency(), is(CURRENCY_USD)); + assertThat(paperCheck.getField(STATUS), is(ACTIVATED)); + assertThat(paperCheck.getField(TOKEN), is(TEST_TOKEN)); + } + + @Test + public void updateTransferMethod_paperCheckWithSuccess() { + final PaperCheck returnedPaperCheck = buildPaperCheck(); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + returnedPaperCheck.setField(STATUS, ACTIVATED); + listener.onSuccess(returnedPaperCheck); + return listener; + } + }).when(mHyperwallet).updatePaperCheck(any(PaperCheck.class), any(HyperwalletListener.class)); + + PaperCheck parameter = new PaperCheck.Builder().build(); + + mTransferMethodRepository.updateTransferMethod(parameter, mLoadTransferMethodCallback); + + verify(mLoadTransferMethodCallback).onTransferMethodLoaded(mPaperCheckArgumentCaptor.capture()); + verify(mLoadTransferMethodCallback, never()).onError(any(Errors.class)); + + PaperCheck paperCheck = mPaperCheckArgumentCaptor.getValue(); + assertThat(paperCheck, is(notNullValue())); + assertThat(paperCheck.getCountry(), is(COUNTRY_US)); + assertThat(paperCheck.getCurrency(), is(CURRENCY_USD)); + assertThat(paperCheck.getField(STATUS), is(ACTIVATED)); + assertThat(paperCheck.getField(TOKEN), is(TEST_TOKEN)); + } @Test public void createTransferMethod_venmoAccountWithError() { final Error returnedError = new Error(TEST_MESSAGE, TEST_CODE); @@ -804,6 +1083,78 @@ public Object answer(InvocationOnMock invocation) { assertThat(mErrorsArgumentCaptor.getValue().getErrors(), hasItem(returnedError)); } + @Test + public void updateTransferMethod_venmoAccountWithError() { + final Error returnedError = new Error(TEST_MESSAGE, TEST_CODE); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + + List errorList = new ArrayList<>(); + errorList.add(returnedError); + + listener.onFailure(new HyperwalletException(new Errors(errorList))); + return listener; + } + }).when(mHyperwallet).updateVenmoAccount(any(VenmoAccount.class), any(HyperwalletListener.class)); + VenmoAccount parameter = new VenmoAccount.Builder().build(); + + mTransferMethodRepository.updateTransferMethod(parameter, mLoadTransferMethodCallback); + + verify(mLoadTransferMethodCallback, never()).onTransferMethodLoaded(any(TransferMethod.class)); + verify(mLoadTransferMethodCallback).onError(mErrorsArgumentCaptor.capture()); + assertThat(mErrorsArgumentCaptor.getValue().getErrors(), hasItem(returnedError)); + } + + @Test + public void createTransferMethod_paperCheckWithError() { + final Error returnedError = new Error(TEST_MESSAGE, TEST_CODE); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + + List errorList = new ArrayList<>(); + errorList.add(returnedError); + + listener.onFailure(new HyperwalletException(new Errors(errorList))); + return listener; + } + }).when(mHyperwallet).createPaperCheck(any(PaperCheck.class), any(HyperwalletListener.class)); + PaperCheck parameter = new PaperCheck.Builder().build(); + + mTransferMethodRepository.createTransferMethod(parameter, mLoadTransferMethodCallback); + + verify(mLoadTransferMethodCallback, never()).onTransferMethodLoaded(any(TransferMethod.class)); + verify(mLoadTransferMethodCallback).onError(mErrorsArgumentCaptor.capture()); + assertThat(mErrorsArgumentCaptor.getValue().getErrors(), hasItem(returnedError)); + } + + @Test + public void updateTransferMethod_paperCheckWithError() { + final Error returnedError = new Error(TEST_MESSAGE, TEST_CODE); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + + List errorList = new ArrayList<>(); + errorList.add(returnedError); + + listener.onFailure(new HyperwalletException(new Errors(errorList))); + return listener; + } + }).when(mHyperwallet).updatePaperCheck(any(PaperCheck.class), any(HyperwalletListener.class)); + PaperCheck parameter = new PaperCheck.Builder().build(); + + mTransferMethodRepository.updateTransferMethod(parameter, mLoadTransferMethodCallback); + + verify(mLoadTransferMethodCallback, never()).onTransferMethodLoaded(any(TransferMethod.class)); + verify(mLoadTransferMethodCallback).onError(mErrorsArgumentCaptor.capture()); + assertThat(mErrorsArgumentCaptor.getValue().getErrors(), hasItem(returnedError)); + } + @Test public void testCreateTransferMethod_wireAccountWithSuccess() { BankAccount bankAccount = new BankAccount @@ -908,4 +1259,12 @@ private VenmoAccount buildVenmoAccount() { .token(TEST_TOKEN) .build(); } + + private PaperCheck buildPaperCheck() { + return new PaperCheck.Builder() + .transferMethodCountry(COUNTRY_US) + .transferMethodCurrency(CURRENCY_USD) + .token(TEST_TOKEN) + .build(); + } } diff --git a/transfermethodrepository/src/test/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodUpdateConfigurationRepositoryImplTest.java b/transfermethodrepository/src/test/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodUpdateConfigurationRepositoryImplTest.java new file mode 100644 index 000000000..96b65ed05 --- /dev/null +++ b/transfermethodrepository/src/test/java/com/hyperwallet/android/ui/transfermethod/repository/TransferMethodUpdateConfigurationRepositoryImplTest.java @@ -0,0 +1,177 @@ +package com.hyperwallet.android.ui.transfermethod.repository; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.hyperwallet.android.Hyperwallet; +import com.hyperwallet.android.exception.HyperwalletException; +import com.hyperwallet.android.listener.HyperwalletListener; +import com.hyperwallet.android.model.Error; +import com.hyperwallet.android.model.Errors; +import com.hyperwallet.android.model.TypeReference; +import com.hyperwallet.android.model.graphql.HyperwalletTransferMethodConfigurationField; +import com.hyperwallet.android.model.graphql.field.FieldGroup; +import com.hyperwallet.android.model.graphql.field.TransferMethodConfiguration; +import com.hyperwallet.android.model.graphql.field.TransferMethodConfigurationFieldResult; +import com.hyperwallet.android.model.graphql.field.TransferMethodUpdateConfigurationFieldResult; +import com.hyperwallet.android.model.graphql.query.TransferMethodUpdateConfigurationFieldQuery; +import com.hyperwallet.android.ui.testutils.rule.HyperwalletExternalResourceManager; +import com.hyperwallet.android.util.JsonUtils; + +import org.hamcrest.collection.IsEmptyCollection; +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.mockito.stubbing.Answer; +import org.robolectric.RobolectricTestRunner; + +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.List; + +@RunWith(RobolectricTestRunner.class) +public class TransferMethodUpdateConfigurationRepositoryImplTest { + + private static final String TRANSFER_TOKEN = "trm-fake"; + @Rule + public HyperwalletExternalResourceManager externalResourceManager = new HyperwalletExternalResourceManager(); + @Rule + public MockitoRule mockito = MockitoJUnit.rule(); + @Mock + private TransferMethodUpdateConfigurationRepository.LoadFieldsCallback loadFieldsCallback; + @Captor + private ArgumentCaptor fieldResultArgumentCaptor; + @Captor + private ArgumentCaptor mErrorsArgumentCaptor; + @Mock + private Hyperwallet mHyperwallet; + @Mock + private HashMap mFieldsMap; + @Spy + @InjectMocks + private TransferMethodUpdateConfigurationRepositoryImpl mTransferMethodUpdateConfigurationRepositoryImplMock; + + @Before + public void setUp() { + doReturn(mHyperwallet).when(mTransferMethodUpdateConfigurationRepositoryImplMock).getHyperwallet(); + } + + @Test + public void testGetFields_callsListenerWithFieldResultOnSuccess() + throws NoSuchMethodException, InstantiationException, IllegalAccessException, JSONException, + InvocationTargetException { + String responseBody = externalResourceManager.getResourceContent( + "successful_tmc_update_field_bank_account_response.json"); + final TransferMethodUpdateConfigurationFieldResult result = JsonUtils.fromJsonString(responseBody, + new TypeReference() { + }); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + listener.onSuccess(result); + return listener; + } + }).when(mHyperwallet).retrieveUpdateTransferMethodConfigurationFields( + ArgumentMatchers.any(), + ArgumentMatchers.>any()); + + mTransferMethodUpdateConfigurationRepositoryImplMock.getFields(TRANSFER_TOKEN,loadFieldsCallback); + + verify(loadFieldsCallback).onFieldsLoaded(fieldResultArgumentCaptor.capture()); + verify(loadFieldsCallback, never()).onError(any(Errors.class)); + + HyperwalletTransferMethodConfigurationField transferMethodConfigurationFieldResult = + fieldResultArgumentCaptor.getValue(); + assertNotNull(transferMethodConfigurationFieldResult); + TransferMethodConfiguration transferMethodConfiguration = + transferMethodConfigurationFieldResult.getFields(); + assertThat(transferMethodConfiguration, is(notNullValue())); + assertThat(transferMethodConfiguration.getFieldGroups(), + is(not(IsEmptyCollection.empty()))); + } + + @Test + public void testGetFields_callsListenerWithErrorResultOnFailure() + throws NoSuchMethodException, InstantiationException, IllegalAccessException, JSONException, + InvocationTargetException { + String responseBody = externalResourceManager.getResourceContent("error_tmc_keys_response.json"); + final Errors errors = JsonUtils.fromJsonString(responseBody, + new TypeReference() { + }); + final HyperwalletException exception = new HyperwalletException(errors); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + HyperwalletListener listener = (HyperwalletListener) invocation.getArguments()[1]; + + listener.onFailure(exception); + return listener; + } + }).when(mHyperwallet).retrieveUpdateTransferMethodConfigurationFields( + ArgumentMatchers.any(), + ArgumentMatchers.>any()); + + + mTransferMethodUpdateConfigurationRepositoryImplMock.getFields(TRANSFER_TOKEN, loadFieldsCallback); + + verify(loadFieldsCallback, never()).onFieldsLoaded(any(HyperwalletTransferMethodConfigurationField.class)); + verify(loadFieldsCallback).onError(mErrorsArgumentCaptor.capture()); + + Errors errorsList = mErrorsArgumentCaptor.getValue(); + assertNotNull(errorsList); + List inErrors = errorsList.getErrors(); + assertNotNull(errors); + Error inError = inErrors.get(0); + assertThat(errors.getErrors().get(0), is(inError)); + } + + @Test + public void testGetFields_callsListenerWithFieldResultFromCacheWhenNotNull() throws Exception { + String responseBody = externalResourceManager.getResourceContent( + "successful_tmc_update_field_bank_account_response.json"); + final TransferMethodUpdateConfigurationFieldResult result = JsonUtils.fromJsonString(responseBody, + new TypeReference() { + }); + + mTransferMethodUpdateConfigurationRepositoryImplMock.getFields(TRANSFER_TOKEN, loadFieldsCallback); + verify(loadFieldsCallback, never()).onError(any(Errors.class)); + } + + @Test + public void testRefreshFields_clearsFieldMapWhenNotEmpty() throws Exception { + String responseBody = externalResourceManager.getResourceContent( + "successful_tmc_update_field_bank_account_response.json"); + JSONObject jsonObject = new JSONObject(responseBody); + FieldMapKey fieldMapKey = new FieldMapKey(); + HashMap fieldMap = new HashMap<>(); + fieldMap.put(fieldMapKey, new TransferMethodUpdateConfigurationFieldResult(jsonObject)); + TransferMethodConfigurationRepositoryImpl repositoryWithCache = new TransferMethodConfigurationRepositoryImpl( + null, null, fieldMap); + repositoryWithCache.refreshFields(); + assertTrue(fieldMap.isEmpty()); + } +} diff --git a/transfermethodrepository/src/test/resources/successful_tmc_update_field_bank_account_response.json b/transfermethodrepository/src/test/resources/successful_tmc_update_field_bank_account_response.json new file mode 100644 index 000000000..7c5003d5e --- /dev/null +++ b/transfermethodrepository/src/test/resources/successful_tmc_update_field_bank_account_response.json @@ -0,0 +1,1192 @@ +{ + "data": { + "transferMethodUpdateUIConfigurations": { + "nodes": [ + { + "country": "US", + "currency": "USD", + "transferMethodType": "BANK_ACCOUNT", + "profile": "INDIVIDUAL", + "fieldGroups": { + "nodes": [ + { + "group": "ACCOUNT_INFORMATION", + "isEditable": true, + "instruction": { + "textBottom": "On a paper check, the routing number is printed as the first 9 digits on the bottom of the check, followed by 4-17 digits for the account number." + }, + "fields": [ + { + "category": "ACCOUNT", + "value": "021000021", + "dataType": "NUMBER", + "isRequired": true, + "isEditable": true, + "label": "Routing Number", + "maxLength": 9, + "minLength": 9, + "name": "branchId", + "placeholder": "", + "regularExpression": "^[0-9]{9}$", + "validationMessage": { + "length": "The exact length of this field is 9.", + "pattern": "is invalid length or format.", + "empty": "You must provide a value for this field" + }, + "fieldValueMasked": false + }, + { + "category": "ACCOUNT", + "value": "****", + "dataType": "NUMBER", + "isRequired": true, + "isEditable": true, + "label": "Account Number", + "maxLength": 17, + "minLength": 3, + "name": "bankAccountId", + "placeholder": "", + "regularExpression": "^(?![0-]+$)[0-9-]{3,17}$", + "validationMessage": { + "length": "The minimum length of this field is 3 and maximum length is 17.", + "pattern": "is invalid length or format.", + "empty": "You must provide a value for this field" + }, + "fieldValueMasked": true + }, + { + "category": "ACCOUNT", + "value": "SAVINGS", + "dataType": "SELECTION", + "isRequired": true, + "isEditable": true, + "label": "Account Type", + "name": "bankAccountPurpose", + "placeholder": "", + "fieldSelectionOptions": [ + { + "label": "Checking", + "value": "CHECKING" + }, + { + "label": "Savings", + "value": "SAVINGS" + } + ], + "validationMessage": { + "length": "", + "pattern": "is invalid length or format.", + "empty": "You must provide a value for this field" + }, + "fieldValueMasked": false + } + ] + }, + { + "group": "ACCOUNT_HOLDER", + "isEditable": true, + "instruction": { + }, + "fields": [ + { + "category": "PROFILE", + "value": "iOS Mobile", + "dataType": "TEXT", + "isRequired": true, + "isEditable": true, + "label": "First Name", + "name": "firstName", + "placeholder": "", + "regularExpression": "^[\\sa-zA-Z0-9\\-.,'\\u00C0-\\u00FF\\u0100-\\u017F\\u0180-\\u024F]{1,50}$", + "validationMessage": { + "length": "", + "pattern": "is invalid length or format.", + "empty": "You must provide a value for this field" + }, + "fieldValueMasked": false + }, + { + "category": "PROFILE", + "value": "mobile-qa", + "dataType": "TEXT", + "isRequired": false, + "isEditable": true, + "label": "Middle Name", + "name": "middleName", + "placeholder": "", + "regularExpression": "^[\\sa-zA-Z0-9\\-.,'\\u00C0-\\u00FF\\u0100-\\u017F\\u0180-\\u024F]{1,50}$", + "validationMessage": { + "length": "", + "pattern": "is invalid length or format.", + "empty": "You must provide a value for this field" + }, + "fieldValueMasked": false + }, + { + "category": "PROFILE", + "value": "UITest", + "dataType": "TEXT", + "isRequired": true, + "isEditable": true, + "label": "Last Name", + "name": "lastName", + "placeholder": "", + "regularExpression": "^[\\sa-zA-Z0-9\\-.,'\\u00C0-\\u00FF\\u0100-\\u017F\\u0180-\\u024F]{1,50}$", + "validationMessage": { + "length": "", + "pattern": "is invalid length or format.", + "empty": "You must provide a value for this field" + }, + "fieldValueMasked": false + } + ] + }, + { + "group": "ADDRESS", + "isEditable": true, + "instruction": { + }, + "fields": [ + { + "category": "ADDRESS", + "value": "CA", + "dataType": "SELECTION", + "isRequired": true, + "isEditable": true, + "label": "Country", + "name": "country", + "placeholder": "", + "fieldSelectionOptions": [ + { + "label": "Afghanistan", + "value": "AF" + }, + { + "label": "Åland Islands", + "value": "AX" + }, + { + "label": "Albania", + "value": "AL" + }, + { + "label": "Algeria", + "value": "DZ" + }, + { + "label": "American Samoa", + "value": "AS" + }, + { + "label": "Andorra", + "value": "AD" + }, + { + "label": "Angola", + "value": "AO" + }, + { + "label": "Anguilla", + "value": "AI" + }, + { + "label": "Antarctica", + "value": "AQ" + }, + { + "label": "Antigua & Barbuda", + "value": "AG" + }, + { + "label": "Argentina", + "value": "AR" + }, + { + "label": "Armenia", + "value": "AM" + }, + { + "label": "Aruba", + "value": "AW" + }, + { + "label": "Australia", + "value": "AU" + }, + { + "label": "Austria", + "value": "AT" + }, + { + "label": "Azerbaijan", + "value": "AZ" + }, + { + "label": "Bahamas", + "value": "BS" + }, + { + "label": "Bahrain", + "value": "BH" + }, + { + "label": "Bangladesh", + "value": "BD" + }, + { + "label": "Barbados", + "value": "BB" + }, + { + "label": "Belgium", + "value": "BE" + }, + { + "label": "Belize", + "value": "BZ" + }, + { + "label": "Benin", + "value": "BJ" + }, + { + "label": "Bermuda", + "value": "BM" + }, + { + "label": "Bhutan", + "value": "BT" + }, + { + "label": "Bolivia", + "value": "BO" + }, + { + "label": "Caribbean Netherlands", + "value": "BQ" + }, + { + "label": "Bosnia & Herzegovina", + "value": "BA" + }, + { + "label": "Botswana", + "value": "BW" + }, + { + "label": "Bouvet Island", + "value": "BV" + }, + { + "label": "Brazil", + "value": "BR" + }, + { + "label": "British Indian Ocean Territory", + "value": "IO" + }, + { + "label": "Brunei", + "value": "BN" + }, + { + "label": "Bulgaria", + "value": "BG" + }, + { + "label": "Burkina Faso", + "value": "BF" + }, + { + "label": "Burundi", + "value": "BI" + }, + { + "label": "Cambodia", + "value": "KH" + }, + { + "label": "Cameroon", + "value": "CM" + }, + { + "label": "Canada", + "value": "CA" + }, + { + "label": "Cape Verde", + "value": "CV" + }, + { + "label": "Cayman Islands", + "value": "KY" + }, + { + "label": "Chad", + "value": "TD" + }, + { + "label": "Chile", + "value": "CL" + }, + { + "label": "China", + "value": "CN" + }, + { + "label": "Christmas Island", + "value": "CX" + }, + { + "label": "Cocos (Keeling) Islands", + "value": "CC" + }, + { + "label": "Colombia", + "value": "CO" + }, + { + "label": "Comoros", + "value": "KM" + }, + { + "label": "Congo - Brazzaville", + "value": "CG" + }, + { + "label": "Cook Islands", + "value": "CK" + }, + { + "label": "Costa Rica", + "value": "CR" + }, + { + "label": "Côte d’Ivoire", + "value": "CI" + }, + { + "label": "Croatia", + "value": "HR" + }, + { + "label": "Curaçao", + "value": "CW" + }, + { + "label": "Cyprus", + "value": "CY" + }, + { + "label": "Czechia", + "value": "CZ" + }, + { + "label": "Denmark", + "value": "DK" + }, + { + "label": "Djibouti", + "value": "DJ" + }, + { + "label": "Dominica", + "value": "DM" + }, + { + "label": "Dominican Republic", + "value": "DO" + }, + { + "label": "Timor-Leste", + "value": "TL" + }, + { + "label": "Ecuador", + "value": "EC" + }, + { + "label": "Egypt", + "value": "EG" + }, + { + "label": "El Salvador", + "value": "SV" + }, + { + "label": "Equatorial Guinea", + "value": "GQ" + }, + { + "label": "Estonia", + "value": "EE" + }, + { + "label": "Ethiopia", + "value": "ET" + }, + { + "label": "Falkland Islands", + "value": "FK" + }, + { + "label": "Faroe Islands", + "value": "FO" + }, + { + "label": "Fiji", + "value": "FJ" + }, + { + "label": "Finland", + "value": "FI" + }, + { + "label": "France", + "value": "FR" + }, + { + "label": "French Guiana", + "value": "GF" + }, + { + "label": "French Polynesia", + "value": "PF" + }, + { + "label": "French Southern Territories", + "value": "TF" + }, + { + "label": "Gabon", + "value": "GA" + }, + { + "label": "Gambia", + "value": "GM" + }, + { + "label": "Georgia", + "value": "GE" + }, + { + "label": "Germany", + "value": "DE" + }, + { + "label": "Ghana", + "value": "GH" + }, + { + "label": "Gibraltar", + "value": "GI" + }, + { + "label": "Greece", + "value": "GR" + }, + { + "label": "Greenland", + "value": "GL" + }, + { + "label": "Grenada", + "value": "GD" + }, + { + "label": "Guadeloupe", + "value": "GP" + }, + { + "label": "Guam", + "value": "GU" + }, + { + "label": "Guatemala", + "value": "GT" + }, + { + "label": "Guernsey", + "value": "GG" + }, + { + "label": "Guinea", + "value": "GN" + }, + { + "label": "Guyana", + "value": "GY" + }, + { + "label": "Haiti", + "value": "HT" + }, + { + "label": "Heard & McDonald Islands", + "value": "HM" + }, + { + "label": "Honduras", + "value": "HN" + }, + { + "label": "Hong Kong SAR China", + "value": "HK" + }, + { + "label": "Hungary", + "value": "HU" + }, + { + "label": "Iceland", + "value": "IS" + }, + { + "label": "India", + "value": "IN" + }, + { + "label": "Indonesia", + "value": "ID" + }, + { + "label": "Ireland", + "value": "IE" + }, + { + "label": "Isle of Man", + "value": "IM" + }, + { + "label": "Israel", + "value": "IL" + }, + { + "label": "Italy", + "value": "IT" + }, + { + "label": "Jamaica", + "value": "JM" + }, + { + "label": "Japan", + "value": "JP" + }, + { + "label": "Jersey", + "value": "JE" + }, + { + "label": "Jordan", + "value": "JO" + }, + { + "label": "Kazakhstan", + "value": "KZ" + }, + { + "label": "Kenya", + "value": "KE" + }, + { + "label": "Kiribati", + "value": "KI" + }, + { + "label": "South Korea", + "value": "KR" + }, + { + "label": "Kosovo", + "value": "XK" + }, + { + "label": "Kuwait", + "value": "KW" + }, + { + "label": "Kyrgyzstan", + "value": "KG" + }, + { + "label": "Laos", + "value": "LA" + }, + { + "label": "Latvia", + "value": "LV" + }, + { + "label": "Lesotho", + "value": "LS" + }, + { + "label": "Liechtenstein", + "value": "LI" + }, + { + "label": "Lithuania", + "value": "LT" + }, + { + "label": "Luxembourg", + "value": "LU" + }, + { + "label": "Macao SAR China", + "value": "MO" + }, + { + "label": "North Macedonia", + "value": "MK" + }, + { + "label": "Madagascar", + "value": "MG" + }, + { + "label": "Malawi", + "value": "MW" + }, + { + "label": "Malaysia", + "value": "MY" + }, + { + "label": "Maldives", + "value": "MV" + }, + { + "label": "Mali", + "value": "ML" + }, + { + "label": "Malta", + "value": "MT" + }, + { + "label": "Marshall Islands", + "value": "MH" + }, + { + "label": "Martinique", + "value": "MQ" + }, + { + "label": "Mauritania", + "value": "MR" + }, + { + "label": "Mauritius", + "value": "MU" + }, + { + "label": "Mayotte", + "value": "YT" + }, + { + "label": "Mexico", + "value": "MX" + }, + { + "label": "Micronesia", + "value": "FM" + }, + { + "label": "Moldova", + "value": "MD" + }, + { + "label": "Monaco", + "value": "MC" + }, + { + "label": "Mongolia", + "value": "MN" + }, + { + "label": "Montenegro", + "value": "ME" + }, + { + "label": "Montserrat", + "value": "MS" + }, + { + "label": "Morocco", + "value": "MA" + }, + { + "label": "Mozambique", + "value": "MZ" + }, + { + "label": "Namibia", + "value": "NA" + }, + { + "label": "Nauru", + "value": "NR" + }, + { + "label": "Nepal", + "value": "NP" + }, + { + "label": "Netherlands", + "value": "NL" + }, + { + "label": "Curaçao", + "value": "AN" + }, + { + "label": "New Caledonia", + "value": "NC" + }, + { + "label": "New Zealand", + "value": "NZ" + }, + { + "label": "Nicaragua", + "value": "NI" + }, + { + "label": "Niger", + "value": "NE" + }, + { + "label": "Nigeria", + "value": "NG" + }, + { + "label": "Niue", + "value": "NU" + }, + { + "label": "Norfolk Island", + "value": "NF" + }, + { + "label": "Northern Mariana Islands", + "value": "MP" + }, + { + "label": "Norway", + "value": "NO" + }, + { + "label": "Oman", + "value": "OM" + }, + { + "label": "Pakistan", + "value": "PK" + }, + { + "label": "Palau", + "value": "PW" + }, + { + "label": "Palestinian Territories", + "value": "PS" + }, + { + "label": "Panama", + "value": "PA" + }, + { + "label": "Papua New Guinea", + "value": "PG" + }, + { + "label": "Paraguay", + "value": "PY" + }, + { + "label": "Peru", + "value": "PE" + }, + { + "label": "Philippines", + "value": "PH" + }, + { + "label": "Pitcairn Islands", + "value": "PN" + }, + { + "label": "Poland", + "value": "PL" + }, + { + "label": "Portugal", + "value": "PT" + }, + { + "label": "Puerto Rico", + "value": "PR" + }, + { + "label": "Qatar", + "value": "QA" + }, + { + "label": "Réunion", + "value": "RE" + }, + { + "label": "Romania", + "value": "RO" + }, + { + "label": "Russia", + "value": "RU" + }, + { + "label": "Rwanda", + "value": "RW" + }, + { + "label": "St. Kitts & Nevis", + "value": "KN" + }, + { + "label": "St. Lucia", + "value": "LC" + }, + { + "label": "St. Martin", + "value": "MF" + }, + { + "label": "St. Vincent & Grenadines", + "value": "VC" + }, + { + "label": "Samoa", + "value": "WS" + }, + { + "label": "San Marino", + "value": "SM" + }, + { + "label": "São Tomé & Príncipe", + "value": "ST" + }, + { + "label": "Saudi Arabia", + "value": "SA" + }, + { + "label": "Senegal", + "value": "SN" + }, + { + "label": "Serbia", + "value": "RS" + }, + { + "label": "Seychelles", + "value": "SC" + }, + { + "label": "Sierra Leone", + "value": "SL" + }, + { + "label": "Singapore", + "value": "SG" + }, + { + "label": "Sint Maarten", + "value": "SX" + }, + { + "label": "Slovakia", + "value": "SK" + }, + { + "label": "Slovenia", + "value": "SI" + }, + { + "label": "Solomon Islands", + "value": "SB" + }, + { + "label": "South Africa", + "value": "ZA" + }, + { + "label": "South Georgia & South Sandwich Islands", + "value": "GS" + }, + { + "label": "South Sudan", + "value": "SS" + }, + { + "label": "Spain", + "value": "ES" + }, + { + "label": "Sri Lanka", + "value": "LK" + }, + { + "label": "St. Barthélemy", + "value": "BL" + }, + { + "label": "St. Helena", + "value": "SH" + }, + { + "label": "St. Pierre & Miquelon", + "value": "PM" + }, + { + "label": "Suriname", + "value": "SR" + }, + { + "label": "Svalbard & Jan Mayen", + "value": "SJ" + }, + { + "label": "Eswatini", + "value": "SZ" + }, + { + "label": "Sweden", + "value": "SE" + }, + { + "label": "Switzerland", + "value": "CH" + }, + { + "label": "Taiwan", + "value": "TW" + }, + { + "label": "Tajikistan", + "value": "TJ" + }, + { + "label": "Tanzania", + "value": "TZ" + }, + { + "label": "Thailand", + "value": "TH" + }, + { + "label": "Togo", + "value": "TG" + }, + { + "label": "Tokelau", + "value": "TK" + }, + { + "label": "Tonga", + "value": "TO" + }, + { + "label": "Trinidad & Tobago", + "value": "TT" + }, + { + "label": "Tunisia", + "value": "TN" + }, + { + "label": "Turkey", + "value": "TR" + }, + { + "label": "Turkmenistan", + "value": "TM" + }, + { + "label": "Turks & Caicos Islands", + "value": "TC" + }, + { + "label": "Tuvalu", + "value": "TV" + }, + { + "label": "Uganda", + "value": "UG" + }, + { + "label": "Ukraine", + "value": "UA" + }, + { + "label": "United Arab Emirates", + "value": "AE" + }, + { + "label": "United Kingdom", + "value": "GB" + }, + { + "label": "United States", + "value": "US" + }, + { + "label": "U.S. Outlying Islands", + "value": "UM" + }, + { + "label": "Uruguay", + "value": "UY" + }, + { + "label": "Uzbekistan", + "value": "UZ" + }, + { + "label": "Vanuatu", + "value": "VU" + }, + { + "label": "Vatican City", + "value": "VA" + }, + { + "label": "Venezuela", + "value": "VE" + }, + { + "label": "Vietnam", + "value": "VN" + }, + { + "label": "British Virgin Islands", + "value": "VG" + }, + { + "label": "U.S. Virgin Islands", + "value": "VI" + }, + { + "label": "Wallis & Futuna", + "value": "WF" + }, + { + "label": "Western Sahara", + "value": "EH" + }, + { + "label": "Zambia", + "value": "ZM" + } + ], + "validationMessage": { + "length": "", + "pattern": "is invalid length or format.", + "empty": "You must provide a value for this field" + }, + "fieldValueMasked": false + }, + { + "category": "ADDRESS", + "value": "BC", + "dataType": "TEXT", + "isRequired": true, + "isEditable": true, + "label": "State/Province", + "name": "stateProvince", + "placeholder": "", + "regularExpression": "^[\\sa-zA-Z0-9\\-().,'\\u00C0-\\u00FF\\u0100-\\u017F\\u0180-\\u024F]{2,50}$", + "validationMessage": { + "length": "", + "pattern": "is invalid length or format.", + "empty": "You must provide a value for this field" + }, + "fieldValueMasked": false + }, + { + "category": "ADDRESS", + "value": "475 howe st", + "dataType": "TEXT", + "isRequired": true, + "isEditable": true, + "label": "Address Line 1", + "name": "addressLine1", + "placeholder": "", + "regularExpression": "^[\\sa-zA-Z0-9\\/\\-:().,#;'°&\\u00C0-\\u00FF\\u0100-\\u017F\\u0180-\\u024F]{2,100}$", + "validationMessage": { + "length": "", + "pattern": "is invalid length or format.", + "empty": "You must provide a value for this field" + }, + "fieldValueMasked": false + }, + { + "category": "ADDRESS", + "dataType": "TEXT", + "isRequired": false, + "isEditable": true, + "label": "Address Line 2", + "name": "addressLine2", + "placeholder": "", + "regularExpression": "^[\\sa-zA-Z0-9\\/\\-:().,#;'°&\\u00C0-\\u00FF\\u0100-\\u017F\\u0180-\\u024F]{2,100}$", + "validationMessage": { + "length": "", + "pattern": "is invalid length or format.", + "empty": "You must provide a value for this field" + }, + "fieldValueMasked": false + }, + { + "category": "ADDRESS", + "value": "vancouver", + "dataType": "TEXT", + "isRequired": true, + "isEditable": true, + "label": "City", + "name": "city", + "placeholder": "", + "regularExpression": "^[\\sa-zA-Z0-9\\-().,'\\u00C0-\\u00FF\\u0100-\\u017F\\u0180-\\u024F]{2,50}$", + "validationMessage": { + "length": "", + "pattern": "is invalid length or format.", + "empty": "You must provide a value for this field" + }, + "fieldValueMasked": false + }, + { + "category": "ADDRESS", + "value": "V6Z1L2", + "dataType": "TEXT", + "isRequired": true, + "isEditable": true, + "label": "Zip/Postal Code", + "name": "postalCode", + "placeholder": "", + "regularExpression": "^(?![\\-]+$)[\\sa-zA-Z0-9\\-]{2,16}$", + "validationMessage": { + "length": "", + "pattern": "is invalid length or format.", + "empty": "You must provide a value for this field" + }, + "fieldValueMasked": false + } + ] + } + ] + } + } + ] + } + } +} \ No newline at end of file diff --git a/transfermethodui/build.gradle b/transfermethodui/build.gradle index cd908659d..207b83eb2 100644 --- a/transfermethodui/build.gradle +++ b/transfermethodui/build.gradle @@ -25,6 +25,7 @@ dependencies { implementation project(':commonui') implementation project(":userrepository") implementation project(":transfermethodrepository") + implementation project(":transferui") androidTestImplementation "androidx.test:rules:$testRulesVersion" diff --git a/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/AddTransferMethodTest.java b/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/AddTransferMethodTest.java index 3a5c4064f..d932c232f 100644 --- a/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/AddTransferMethodTest.java +++ b/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/AddTransferMethodTest.java @@ -165,32 +165,6 @@ public void testAddTransferMethod_displaysUnexpectedErrorDialogOnException() { mActivityTestRule.getActivityResult().getResultCode(), is(RESULT_ERROR)); } - @Test - public void testAddTransferMethod_notSupportedExternalAccountTypeDisplaysUnexpectedErrorDialog() { - Intent intent = new Intent(ApplicationProvider.getApplicationContext(), - AddTransferMethodActivity.class); - intent.putExtra("TRANSFER_METHOD_TYPE", "PAPER_CHECK"); - intent.putExtra("TRANSFER_METHOD_COUNTRY", "US"); - intent.putExtra("TRANSFER_METHOD_CURRENCY", "USD"); - - mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager - .getResourceContent("successful_tmc_fields_paper_check_response.json")).mock(); - - mActivityTestRule.launchActivity(intent); - - onView(withId(R.id.add_transfer_method_button)).perform(nestedScrollTo(), click()); - - // check dialog content - onView(withText(R.string.error_dialog_unexpected_title)).inRoot(isDialog()).check(matches(isDisplayed())); - onView(withText(R.string.error_unsupported_transfer_type)).inRoot(isDialog()).check(matches(isDisplayed())); - onView(withId(android.R.id.button1)).check(matches(withText(R.string.ok))); - onView(withId(android.R.id.button1)).perform(click()); - - // verify activity is finished - assertThat("Result code is incorrect", - mActivityTestRule.getActivityResult().getResultCode(), is(RESULT_ERROR)); - } - @Test public void testAddTransferMethod_displaysNetworkErrorDialogOnConnectionTimeout() throws IOException { mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager diff --git a/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/EditTransferMethodTest.java b/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/EditTransferMethodTest.java new file mode 100644 index 000000000..cc7d9bca4 --- /dev/null +++ b/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/EditTransferMethodTest.java @@ -0,0 +1,614 @@ +package com.hyperwallet.android.ui.transfermethod; + +import android.app.Instrumentation; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.view.MenuItem; +import android.widget.ImageButton; +import android.widget.TextView; + +import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.espresso.IdlingRegistry; +import androidx.test.espresso.Root; +import androidx.test.espresso.action.ViewActions; +import androidx.test.espresso.assertion.ViewAssertions; +import androidx.test.espresso.matcher.ViewMatchers; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.rule.ActivityTestRule; + +import com.hyperwallet.android.model.StatusTransition; +import com.hyperwallet.android.ui.R; +import com.hyperwallet.android.ui.common.repository.EspressoIdlingResource; +import com.hyperwallet.android.ui.common.view.error.DefaultErrorDialogFragment; +import com.hyperwallet.android.ui.testutils.rule.HyperwalletExternalResourceManager; +import com.hyperwallet.android.ui.testutils.rule.HyperwalletMockWebServer; +import com.hyperwallet.android.ui.testutils.util.RecyclerViewCountAssertion; +import com.hyperwallet.android.ui.transfermethod.repository.TransferMethodRepositoryFactory; +import com.hyperwallet.android.ui.transfermethod.rule.HyperwalletInsightMockRule; +import com.hyperwallet.android.ui.transfermethod.view.AddTransferMethodActivity; +import com.hyperwallet.android.ui.transfermethod.view.ListTransferMethodActivity; + +import org.hamcrest.Matchers; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.util.concurrent.CountDownLatch; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.replaceText; +import static androidx.test.espresso.action.ViewActions.typeText; +import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.RootMatchers.isDialog; +import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; +import static androidx.test.espresso.matcher.ViewMatchers.hasSibling; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withParent; +import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static com.hyperwallet.android.model.StatusTransition.StatusDefinition.DE_ACTIVATED; +import static com.hyperwallet.android.ui.common.view.error.DefaultErrorDialogFragment.RESULT_ERROR; +import static com.hyperwallet.android.ui.testutils.util.EspressoUtils.atPosition; +import static com.hyperwallet.android.ui.testutils.util.EspressoUtils.nestedScrollTo; +import static com.hyperwallet.android.ui.testutils.util.EspressoUtils.withDrawable; +import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; +import static java.net.HttpURLConnection.HTTP_OK; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; + +@RunWith(AndroidJUnit4.class) +public class EditTransferMethodTest { + + @ClassRule + public static HyperwalletExternalResourceManager sResourceManager = new HyperwalletExternalResourceManager(); + @Rule + public HyperwalletInsightMockRule mHyperwalletInsightMockRule = new HyperwalletInsightMockRule(); + @Rule + public HyperwalletMockWebServer mMockWebServer = new HyperwalletMockWebServer(8080); + @Rule + public ActivityTestRule mActivityTestRule = + new ActivityTestRule<>(ListTransferMethodActivity.class, true, false); + + @Before + public void setup() { + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("authentication_token_response.json")).mock(); + IdlingRegistry.getInstance().register(EspressoIdlingResource.getIdlingResource()); + } + + @After + public void cleanup() { + TransferMethodRepositoryFactory.clearInstance(); + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.getIdlingResource()); + } + + + @Test + public void testUpdateTransferMethodFragment_verifyUpdateBankAccountTransferMethod() throws InterruptedException { + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("transfer_method_list_single_bank_account_response.json")).mock(); + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("transfer_method_update_bankacount_response.json")).mock(); + + + final CountDownLatch gate = new CountDownLatch(1); + final BroadcastReceiver br = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + gate.countDown(); + + StatusTransition statusTransition = intent.getParcelableExtra( + "hyperwallet-local-broadcast-payload"); + assertThat("Transition is not valid", statusTransition.getTransition(), is(DE_ACTIVATED)); + } + }; + + // run test + mActivityTestRule.launchActivity(null); + LocalBroadcastManager.getInstance(mActivityTestRule.getActivity().getApplicationContext()) + .registerReceiver(br, new IntentFilter("ACTION_HYPERWALLET_TRANSFER_METHOD_DEACTIVATED")); + + // assert + onView(withId(R.id.toolbar)).check(matches(isDisplayed())); + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); + onView(withId(R.id.fab)).check(matches(isDisplayed())); + + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.bank_account_font_icon))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.bank_account))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText("United States"))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(getEndingIn("1332")))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withDrawable(R.drawable.ic_three_dots_16dp))))); + + onView(allOf(instanceOf(ImageButton.class), hasSibling(withText(R.string.bank_account)))).perform(click()) + .inRoot(Matchers.instanceOf(MenuItem.class)); + + +// Assert both icons + onView(withDrawable(R.drawable.ic_trash)).check(matches(isDisplayed())); + onView(withText(R.string.edit)).check(matches(isDisplayed())).perform(click()); + + onView(withId(R.id.branchIdLabel)).check(matches(isDisplayed())); + + onView(withId(R.id.branchId)).perform(nestedScrollTo()).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(matches(withText("021000021"))); + + onView(ViewMatchers.withId(R.id.branchIdLabel)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); + + onView(ViewMatchers.withId(R.id.bankAccountId)) + .perform(nestedScrollTo()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(matches(withText("****"))); + + onView(ViewMatchers.withId(R.id.bankAccountIdLabel)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); + + // ACCOUNT HOLDER INFO + onView( + Matchers.allOf( + ViewMatchers.withId(R.id.section_header_title), + ViewMatchers.withText(R.string.account_holder) + ) + ) + .perform(nestedScrollTo()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); + + onView(ViewMatchers.withId(R.id.firstName)) + .perform(nestedScrollTo()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(matches(withText("Android Mobile"))); + + onView(ViewMatchers.withId(R.id.firstNameLabel)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); + + onView(ViewMatchers.withId(R.id.middleName)) + .perform(nestedScrollTo()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(matches(withText("mobile-qa"))); + + onView(ViewMatchers.withId(R.id.middleNameLabel)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); + + onView(ViewMatchers.withId(R.id.lastName)) + .perform(nestedScrollTo()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(matches(withText("UITest"))); + + onView(ViewMatchers.withId(R.id.lastNameLabel)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); + + // ADDRESS + onView( + Matchers.allOf( + ViewMatchers.withId(R.id.section_header_title), + ViewMatchers.withText(R.string.address) + ) + ).perform(nestedScrollTo()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); + + onView(ViewMatchers.withId(R.id.country)) + .perform(nestedScrollTo()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(matches(withText("Canada"))); + + onView(ViewMatchers.withId(R.id.countryLabel)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); + + onView(ViewMatchers.withId(R.id.stateProvince)) + .perform(nestedScrollTo()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(matches(withText("BC"))); + + onView(ViewMatchers.withId(R.id.stateProvinceLabel)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); + + onView(ViewMatchers.withId(R.id.addressLine1)) + .perform(nestedScrollTo()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(matches(withText("475 howe st"))); + + onView(ViewMatchers.withId(R.id.addressLine1Label)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); + + onView(ViewMatchers.withId(R.id.city)) + .perform(nestedScrollTo()) + .check(matches(withText("vancouver"))) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); + + onView(ViewMatchers.withId(R.id.cityLabel)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); + + onView(ViewMatchers.withId(R.id.postalCode)) + .perform(nestedScrollTo()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(matches(withText("V6Z1L2"))); + + onView(ViewMatchers.withId(R.id.postalCodeLabel)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); + + } + + @Test + public void testUpdateTransferMethodFragment_verifyUpdateBankcardTransferMethod() { + + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("transfer_method_list_bankcard_response.json")).mock(); + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("transfer_method_update_bankcard_response.json")).mock(); + + + final CountDownLatch gate = new CountDownLatch(1); + final BroadcastReceiver br = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + gate.countDown(); + + StatusTransition statusTransition = intent.getParcelableExtra( + "hyperwallet-local-broadcast-payload"); + assertThat("Transition is not valid", statusTransition.getTransition(), is(DE_ACTIVATED)); + } + }; + + // run test + mActivityTestRule.launchActivity(null); + LocalBroadcastManager.getInstance(mActivityTestRule.getActivity().getApplicationContext()) + .registerReceiver(br, new IntentFilter("ACTION_HYPERWALLET_TRANSFER_METHOD_DEACTIVATED")); + + // assert + onView(withId(R.id.toolbar)).check(matches(isDisplayed())); + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); + onView(withId(R.id.fab)).check(matches(isDisplayed())); + + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.bank_card_font_icon))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.bank_card))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText("Debit Card"))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(getEndingIn("0006")))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withDrawable(R.drawable.ic_three_dots_16dp))))); + + onView(allOf(instanceOf(ImageButton.class), hasSibling(withText(R.string.bank_card)))).perform(click()) + .inRoot(Matchers.instanceOf(MenuItem.class)); + +// Assert both icons + onView(withDrawable(R.drawable.ic_trash)).check(matches(isDisplayed())); + onView(withText(R.string.edit)).check(matches(isDisplayed())).perform(click()); + + onView(withId(R.id.cardNumber)).check(matches(isDisplayed())); + + onView(ViewMatchers.withId(R.id.cardNumber)) + .perform(nestedScrollTo()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(matches(withText("****0006"))); + + onView(ViewMatchers.withId(R.id.dateOfExpiry)) + .perform(nestedScrollTo()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(matches(withText("10/24"))); + + // cvv has no value when loaded for update + onView(ViewMatchers.withId(R.id.cvv)).perform(nestedScrollTo()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); + + onView(ViewMatchers.withId(R.id.cardNumber)) + .perform(nestedScrollTo(), ViewActions.replaceText("1111222233334444")); + + onView(ViewMatchers.withId(R.id.dateOfExpiry)) + .perform(nestedScrollTo(), ViewActions.replaceText("12/25")); + + onView(ViewMatchers.withId(R.id.cvv)) + .perform(nestedScrollTo(), ViewActions.replaceText("321")); + + // update transfer method + onView(ViewMatchers.withId(R.id.update_transfer_method_button)) + .perform(nestedScrollTo(), ViewActions.click()); + } + + @Test + public void testUpdateTransferMethodFragment_verifyWireTransferMethod() { + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("transfer_method_list_wireaccount_response.json")).mock(); + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("transfer_method_update_wireaccount_response.json")).mock(); + + + final CountDownLatch gate = new CountDownLatch(1); + final BroadcastReceiver br = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + gate.countDown(); + + StatusTransition statusTransition = intent.getParcelableExtra( + "hyperwallet-local-broadcast-payload"); + assertThat("Transition is not valid", statusTransition.getTransition(), is(DE_ACTIVATED)); + } + }; + + // run test + mActivityTestRule.launchActivity(null); + LocalBroadcastManager.getInstance(mActivityTestRule.getActivity().getApplicationContext()) + .registerReceiver(br, new IntentFilter("ACTION_HYPERWALLET_TRANSFER_METHOD_DEACTIVATED")); + + // assert + onView(withId(R.id.toolbar)).check(matches(isDisplayed())); + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); + onView(withId(R.id.fab)).check(matches(isDisplayed())); + + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.wire_account_font_icon))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.wire_account))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText("Wire Transfer Account"))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(getEndingIn("8888")))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withDrawable(R.drawable.ic_three_dots_16dp))))); + + onView(allOf(instanceOf(ImageButton.class), hasSibling(withText(R.string.wire_account)))).perform(click()) + .inRoot(Matchers.instanceOf(MenuItem.class)); + + // Assert both icons + onView(withDrawable(R.drawable.ic_trash)).check(matches(isDisplayed())); + onView(withText(R.string.edit)).check(matches(isDisplayed())).perform(click()); + + onView(withId(R.id.bankAccountId)).check(matches(isDisplayed())); + + + onView(withId(R.id.bankAccountId)) + .perform(nestedScrollTo(),ViewActions.replaceText("1234567890")); + + onView(withId(R.id.bankId)) + .perform(nestedScrollTo(),ViewActions.replaceText("ACMTCAXX")); + + // update transfer method + onView(ViewMatchers.withId(R.id.update_transfer_method_button)) + .perform(nestedScrollTo(), ViewActions.click()); + } + + @Test + public void testUpdateTransferMethodFragment_verifyUpdatePaypalTransferMethod() { + + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("transfer_method_list_paypal_response.json")).mock(); + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("transfer_method_update_paypal_response.json")).mock(); + + + final CountDownLatch gate = new CountDownLatch(1); + final BroadcastReceiver br = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + gate.countDown(); + + StatusTransition statusTransition = intent.getParcelableExtra( + "hyperwallet-local-broadcast-payload"); + assertThat("Transition is not valid", statusTransition.getTransition(), is(DE_ACTIVATED)); + } + }; + + // run test + mActivityTestRule.launchActivity(null); + LocalBroadcastManager.getInstance(mActivityTestRule.getActivity().getApplicationContext()) + .registerReceiver(br, new IntentFilter("ACTION_HYPERWALLET_TRANSFER_METHOD_DEACTIVATED")); + + // assert + onView(withId(R.id.toolbar)).check(matches(isDisplayed())); + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); + onView(withId(R.id.fab)).check(matches(isDisplayed())); + + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.paypal_account_font_icon))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.paypal_account))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText("PayPal Account"))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withDrawable(R.drawable.ic_three_dots_16dp))))); + + onView(allOf(instanceOf(ImageButton.class), hasSibling(withText(R.string.paypal_account)))).perform(click()) + .inRoot(Matchers.instanceOf(MenuItem.class)); + + // Assert both icons + onView(withDrawable(R.drawable.ic_trash)).check(matches(isDisplayed())); + onView(withText(R.string.edit)).check(matches(isDisplayed())).perform(click()); + + onView(withId(R.id.email)).check(matches(isDisplayed())); + + onView(withId(R.id.email)).perform(nestedScrollTo()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(matches(withText("hello@hw.com"))); + + onView(withId(R.id.emailLabel)).perform(nestedScrollTo()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); + + onView(ViewMatchers.withId(R.id.email)) + .perform(nestedScrollTo(), ViewActions.replaceText("update@test.com")); + + // update transfer method + onView(ViewMatchers.withId(R.id.update_transfer_method_button)) + .perform(nestedScrollTo(), ViewActions.click()); + + } + + @Test + public void testUpdateTransferMethodFragment_verifyUpdatePaperCheckTransferMethod() { + + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("transfer_method_list_paper_check_response.json")).mock(); + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("transfer_method_update_papercheck_response.json")).mock(); + + + final CountDownLatch gate = new CountDownLatch(1); + final BroadcastReceiver br = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + gate.countDown(); + + StatusTransition statusTransition = intent.getParcelableExtra( + "hyperwallet-local-broadcast-payload"); + assertThat("Transition is not valid", statusTransition.getTransition(), is(DE_ACTIVATED)); + } + }; + + // run test + mActivityTestRule.launchActivity(null); + LocalBroadcastManager.getInstance(mActivityTestRule.getActivity().getApplicationContext()) + .registerReceiver(br, new IntentFilter("ACTION_HYPERWALLET_TRANSFER_METHOD_DEACTIVATED")); + + // assert + onView(withId(R.id.toolbar)).check(matches(isDisplayed())); + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); + onView(withId(R.id.fab)).check(matches(isDisplayed())); + + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.paper_check_font_icon))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.paper_check))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText("Paper Check"))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withDrawable(R.drawable.ic_three_dots_16dp))))); + + onView(allOf(instanceOf(ImageButton.class), hasSibling(withText(R.string.paper_check)))).perform(click()) + .inRoot(Matchers.instanceOf(MenuItem.class)); + + // Assert both icons + onView(withDrawable(R.drawable.ic_trash)).check(matches(isDisplayed())); + onView(withText(R.string.edit)).check(matches(isDisplayed())).perform(click()); + + + onView(withId(com.hyperwallet.android.ui.R.id.shippingMethod)) + .check(ViewAssertions.matches(ViewMatchers.withText("Standard Mail"))); + + onView(withId(com.hyperwallet.android.ui.R.id.country)) + .check(ViewAssertions.matches(ViewMatchers.withText("Canada"))); + + onView(withId(com.hyperwallet.android.ui.R.id.stateProvince)) + .check(ViewAssertions.matches(ViewMatchers.withText("BC"))); + + onView(withId(com.hyperwallet.android.ui.R.id.addressLine1)) + .check(ViewAssertions.matches(ViewMatchers.withText("475 howe st"))); + + onView(withId(com.hyperwallet.android.ui.R.id.addressLine2)) + .check(ViewAssertions.matches(ViewMatchers.withText(""))); + + onView(withId(com.hyperwallet.android.ui.R.id.city)) + .check(ViewAssertions.matches(ViewMatchers.withText("vancouver"))); + + onView(withId(com.hyperwallet.android.ui.R.id.postalCode)) + .check(ViewAssertions.matches(ViewMatchers.withText("V6Z1L2"))); + + onView(ViewMatchers.withId(R.id.city)) + .perform(nestedScrollTo(), ViewActions.replaceText("UpdateCityTest")); + + // update transfer method + onView(ViewMatchers.withId(R.id.update_transfer_method_button)) + .perform(nestedScrollTo(), ViewActions.click()); + } + + @Test + public void testUpdateTransferMethodFragment_verifyUpdateVenmoTransferMethod() { + + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("transfer_method_list_venmo_response.json")).mock(); + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("transfer_method_update_venmo_response.json")).mock(); + + + final CountDownLatch gate = new CountDownLatch(1); + final BroadcastReceiver br = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + gate.countDown(); + + StatusTransition statusTransition = intent.getParcelableExtra( + "hyperwallet-local-broadcast-payload"); + assertThat("Transition is not valid", statusTransition.getTransition(), is(DE_ACTIVATED)); + } + }; + + // run test + mActivityTestRule.launchActivity(null); + LocalBroadcastManager.getInstance(mActivityTestRule.getActivity().getApplicationContext()) + .registerReceiver(br, new IntentFilter("ACTION_HYPERWALLET_TRANSFER_METHOD_DEACTIVATED")); + + // assert + onView(withId(R.id.toolbar)).check(matches(isDisplayed())); + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); + onView(withId(R.id.fab)).check(matches(isDisplayed())); + + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.venmo_account_font_icon))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.venmo_account))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText("Venmo Account"))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(getEndingIn("1234")))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withDrawable(R.drawable.ic_three_dots_16dp))))); + + onView(allOf(instanceOf(ImageButton.class), hasSibling(withText(R.string.venmo_account)))).perform(click()) + .inRoot(Matchers.instanceOf(MenuItem.class)); + + // Assert both icons + onView(withDrawable(R.drawable.ic_trash)).check(matches(isDisplayed())); + onView(withText(R.string.edit)).check(matches(isDisplayed())).perform(click()); + + onView(withId(R.id.accountIdLabel)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); + + onView(ViewMatchers.withId(R.id.accountIdLabel)) + .check(ViewAssertions.matches(ViewMatchers.hasDescendant(ViewMatchers.withText("5555555555")))); + + onView(ViewMatchers.withId(R.id.accountId)) + .perform(nestedScrollTo(), ViewActions.replaceText("1234567890")); + + // update transfer method + onView(ViewMatchers.withId(R.id.update_transfer_method_button)) + .perform(nestedScrollTo(), ViewActions.click()); + } + + private String getEndingIn(String ending) { + return String.format(InstrumentationRegistry.getInstrumentation().getTargetContext() + .getString(R.string.endingIn), ending); + } + +} diff --git a/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/ListTransferMethodTest.java b/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/ListTransferMethodTest.java index 9940d57fe..ead445e23 100644 --- a/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/ListTransferMethodTest.java +++ b/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/ListTransferMethodTest.java @@ -4,12 +4,10 @@ import static androidx.test.espresso.action.ViewActions.click; import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.RootMatchers.isDialog; import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; import static androidx.test.espresso.matcher.ViewMatchers.hasSibling; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withParent; import static androidx.test.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.MatcherAssert.assertThat; @@ -22,10 +20,8 @@ import static java.util.concurrent.TimeUnit.SECONDS; import static com.hyperwallet.android.model.StatusTransition.StatusDefinition.DE_ACTIVATED; -import static com.hyperwallet.android.ui.common.view.error.DefaultErrorDialogFragment.RESULT_ERROR; import static com.hyperwallet.android.ui.testutils.util.EspressoUtils.atPosition; import static com.hyperwallet.android.ui.testutils.util.EspressoUtils.withDrawable; -import static org.hamcrest.Matchers.not; import android.content.BroadcastReceiver; import android.content.Context; @@ -33,7 +29,6 @@ import android.content.IntentFilter; import android.view.MenuItem; import android.widget.ImageButton; -import android.widget.TextView; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.test.espresso.IdlingRegistry; @@ -99,8 +94,9 @@ public void testListTransferMethod_userHasMultipleTransferMethods() { mActivityTestRule.launchActivity(null); // assert - onView(allOf(instanceOf(TextView.class), withParent(withId(R.id.toolbar)))) - .check(matches(withText(R.string.mobileTransferMethodsHeader))); + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); onView(withId(R.id.fab)).check(matches(isDisplayed())); onView(withId(R.id.list_transfer_method_item)).check( @@ -142,7 +138,7 @@ public void testListTransferMethod_userHasMultipleTransferMethods() { matches(atPosition(3, hasDescendant(withText(R.string.paper_check))))); onView(withId(R.id.list_transfer_method_item)).check(matches(atPosition(3, hasDescendant(withText("Canada"))))); onView(withId(R.id.list_transfer_method_item)).check( - matches(atPosition(3, hasDescendant(withText(""))))); + matches(atPosition(3, hasDescendant(withText("to V6Z1L2"))))); onView(withId(R.id.list_transfer_method_item)).check( matches(atPosition(3, hasDescendant(withDrawable(R.drawable.ic_three_dots_16dp))))); @@ -180,8 +176,9 @@ public void testListTransferMethod_userHasSingleTransferMethod() { // assert onView(withId(R.id.toolbar)).check(matches(isDisplayed())); - onView(allOf(instanceOf(TextView.class), withParent(withId(R.id.toolbar)))) - .check(matches(withText(R.string.mobileTransferMethodsHeader))); + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); onView(withId(R.id.fab)).check(matches(isDisplayed())); onView(withId(R.id.list_transfer_method_item)).check( @@ -206,8 +203,9 @@ public void testListTransferMethod_userHasNoTransferMethods() { mActivityTestRule.launchActivity(null); onView(withId(R.id.toolbar)).check(matches(isDisplayed())); - onView(allOf(instanceOf(TextView.class), withParent(withId(R.id.toolbar)))) - .check(matches(withText(R.string.mobileTransferMethodsHeader))); + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); onView(withId(R.id.fab)).check(matches(isDisplayed())); onView(withId(R.id.empty_transfer_method_list_layout)).check(matches(isDisplayed())); @@ -243,8 +241,9 @@ public void onReceive(Context context, Intent intent) { // assert onView(withId(R.id.toolbar)).check(matches(isDisplayed())); - onView(allOf(instanceOf(TextView.class), withParent(withId(R.id.toolbar)))) - .check(matches(withText(R.string.mobileTransferMethodsHeader))); + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); onView(withId(R.id.fab)).check(matches(isDisplayed())); onView(withId(R.id.list_transfer_method_item)).check( @@ -264,11 +263,7 @@ public void onReceive(Context context, Intent intent) { onView(withText(R.string.remove)).check(matches(isDisplayed())).perform(click()); // confirmation dialog is shown before deletion - onView(withText(R.string.mobileAreYouSure)).check(matches(isDisplayed())); - String confirmMessageBody = getRemoveConfirmationMessage( - InstrumentationRegistry.getInstrumentation().getTargetContext() - .getString(R.string.bank_account)); - onView(withText(confirmMessageBody)).check(matches(isDisplayed())); + onView(withText(R.string.remove_transfer_method_title)).check(matches(isDisplayed())); onView(withId(android.R.id.button1)).check(matches(withText(R.string.remove))); onView(withId(android.R.id.button2)).check(matches(withText(R.string.cancelButtonLabel))); @@ -322,8 +317,9 @@ public void onReceive(Context context, Intent intent) { // assert onView(withId(R.id.toolbar)).check(matches(isDisplayed())); - onView(allOf(instanceOf(TextView.class), withParent(withId(R.id.toolbar)))) - .check(matches(withText(R.string.mobileTransferMethodsHeader))); + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); onView(withId(R.id.fab)).check(matches(isDisplayed())); onView(withId(R.id.list_transfer_method_item)).check( @@ -343,11 +339,7 @@ public void onReceive(Context context, Intent intent) { onView(withText(R.string.remove)).check(matches(isDisplayed())).perform(click()); // confirmation dialog is shown before deletion - onView(withText(R.string.mobileAreYouSure)).check(matches(isDisplayed())); - String confirmMessageBody = getRemoveConfirmationMessage( - InstrumentationRegistry.getInstrumentation().getTargetContext() - .getString(R.string.bank_card)); - onView(withText(confirmMessageBody)).check(matches(isDisplayed())); + onView(withText(R.string.remove_transfer_method_title)).check(matches(isDisplayed())); onView(withId(android.R.id.button1)).check(matches(withText(R.string.remove))); onView(withId(android.R.id.button2)).check(matches(withText(R.string.cancelButtonLabel))); @@ -391,8 +383,9 @@ public void onReceive(Context context, Intent intent) { // assert onView(withId(R.id.toolbar)).check(matches(isDisplayed())); - onView(allOf(instanceOf(TextView.class), withParent(withId(R.id.toolbar)))) - .check(matches(withText(R.string.mobileTransferMethodsHeader))); + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); onView(withId(R.id.fab)).check(matches(isDisplayed())); onView(withId(R.id.list_transfer_method_item)).check( @@ -410,11 +403,7 @@ public void onReceive(Context context, Intent intent) { onView(withText(R.string.remove)).check(matches(isDisplayed())).perform(click()); // confirmation dialog is shown before deletion - onView(withText(R.string.mobileAreYouSure)).check(matches(isDisplayed())); - String confirmMessageBody = getRemoveConfirmationMessage( - InstrumentationRegistry.getInstrumentation().getTargetContext() - .getString(R.string.paypal_account)); - onView(withText(confirmMessageBody)).check(matches(isDisplayed())); + onView(withText(R.string.remove_transfer_method_title)).check(matches(isDisplayed())); onView(withId(android.R.id.button1)).check(matches(withText(R.string.remove))); onView(withId(android.R.id.button2)).check(matches(withText(R.string.cancelButtonLabel))); @@ -439,8 +428,9 @@ public void testListTransferMethod_removeBankAccountClickCancel() { mActivityTestRule.launchActivity(null); // assert onView(withId(R.id.toolbar)).check(matches(isDisplayed())); - onView(allOf(instanceOf(TextView.class), withParent(withId(R.id.toolbar)))) - .check(matches(withText(R.string.mobileTransferMethodsHeader))); + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); onView(withId(R.id.fab)).check(matches(isDisplayed())); onView(withId(R.id.list_transfer_method_item)).check( @@ -460,19 +450,14 @@ public void testListTransferMethod_removeBankAccountClickCancel() { onView(withText(R.string.remove)).check(matches(isDisplayed())).perform(click()); // confirmation dialog is shown before deletion - onView(withText(R.string.mobileAreYouSure)).check(matches(isDisplayed())); - String confirmMessageBody = getRemoveConfirmationMessage( - InstrumentationRegistry.getInstrumentation().getTargetContext() - .getString(R.string.bank_account)); - onView(withText(confirmMessageBody)).check(matches(isDisplayed())); + onView(withText(R.string.remove_transfer_method_title)).check(matches(isDisplayed())); onView(withId(android.R.id.button1)).check(matches(withText(R.string.remove))); onView(withId(android.R.id.button2)).check(matches(withText(R.string.cancelButtonLabel))); onView(withId(android.R.id.button2)).perform(click()); // confirmation dialog should be dismissed and does not exist in ui - onView(withText(R.string.mobileAreYouSure)).check(doesNotExist()); - onView(withText(confirmMessageBody)).check(doesNotExist()); + onView(withText(R.string.remove_transfer_method_title)).check(doesNotExist()); // Assert the Transfer methods list is shown onView(withId(R.id.list_transfer_method_item)).check( @@ -486,8 +471,8 @@ public void testListTransferMethod_removeBankAccountClickCancel() { onView(withId(R.id.list_transfer_method_item)).check( matches(atPosition(0, hasDescendant(withDrawable(R.drawable.ic_three_dots_16dp))))); } - - @Test + //supported Paper check there is no method is un supported transfer method. + /* @Test public void testListTransferMethod_removeUnsupportedTypeDisplaysUnexpectedError() { mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager .getResourceContent("transfer_method_list_response.json")).mock(); @@ -517,7 +502,7 @@ public void testListTransferMethod_removeUnsupportedTypeDisplaysUnexpectedError( // verify activity is finished assertThat("Result code is incorrect", mActivityTestRule.getActivityResult().getResultCode(), is(RESULT_ERROR)); - } + }*/ @Test public void testListTransferMethod_prepaidCardTransferMethods() { @@ -528,8 +513,9 @@ public void testListTransferMethod_prepaidCardTransferMethods() { mActivityTestRule.launchActivity(null); // assert - onView(allOf(instanceOf(TextView.class), withParent(withId(R.id.toolbar)))) - .check(matches(withText(R.string.mobileTransferMethodsHeader))); + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); onView(withId(R.id.fab)).check(matches(isDisplayed())); onView(withId(R.id.list_transfer_method_item)).check( @@ -539,7 +525,7 @@ public void testListTransferMethod_prepaidCardTransferMethods() { String visa = InstrumentationRegistry.getInstrumentation().getTargetContext() .getString(R.string.visa); onView(withId(R.id.list_transfer_method_item)).check( - matches(atPosition(0, hasDescendant(withText(getCardBrandWithFourDigits(visa,"8766")))))); + matches(atPosition(0, hasDescendant(withText(getCardBrandWithFourDigits(visa, "8766")))))); onView(withId(R.id.list_transfer_method_item)).check( matches(atPosition(0, hasDescendant(withText(R.string.prepaidCardManagementInfo))))); @@ -550,7 +536,7 @@ public void testListTransferMethod_prepaidCardTransferMethods() { String master = InstrumentationRegistry.getInstrumentation().getTargetContext() .getString(R.string.mastercard); onView(withId(R.id.list_transfer_method_item)).check( - matches(atPosition(1, hasDescendant(withText(getCardBrandWithFourDigits(master,"8767")))))); + matches(atPosition(1, hasDescendant(withText(getCardBrandWithFourDigits(master, "8767")))))); onView(withId(R.id.list_transfer_method_item)).check( matches(atPosition(1, hasDescendant(withText(R.string.prepaidCardManagementInfo))))); @@ -565,8 +551,9 @@ public void testListTransferMethod_venmoTransferMethods() { mActivityTestRule.launchActivity(null); // assert - onView(allOf(instanceOf(TextView.class), withParent(withId(R.id.toolbar)))) - .check(matches(withText(R.string.mobileTransferMethodsHeader))); + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); onView(withId(R.id.fab)).check(matches(isDisplayed())); onView(withId(R.id.list_transfer_method_item)).check( @@ -581,6 +568,32 @@ public void testListTransferMethod_venmoTransferMethods() { matches(atPosition(0, hasDescendant(withDrawable(R.drawable.ic_three_dots_16dp))))); } + @Test + public void testListTransferMethod_paperCheckTransferMethods() { + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("transfer_method_list_paper_check_response.json")).mock(); + + // run test + mActivityTestRule.launchActivity(null); + + // assert + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); + onView(withId(R.id.fab)).check(matches(isDisplayed())); + + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.paper_check_font_icon))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.paper_check))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText("United States"))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText("to V6Z1L2"))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withDrawable(R.drawable.ic_three_dots_16dp))))); + } + @Test public void testListTransferMethod_removeVenmoAccount() throws InterruptedException { mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager @@ -609,8 +622,9 @@ public void onReceive(Context context, Intent intent) { // assert onView(withId(R.id.toolbar)).check(matches(isDisplayed())); - onView(allOf(instanceOf(TextView.class), withParent(withId(R.id.toolbar)))) - .check(matches(withText(R.string.mobileTransferMethodsHeader))); + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); onView(withId(R.id.fab)).check(matches(isDisplayed())); @@ -625,17 +639,81 @@ public void onReceive(Context context, Intent intent) { onView(withId(R.id.list_transfer_method_item)).check( matches(atPosition(0, hasDescendant(withDrawable(R.drawable.ic_three_dots_16dp))))); - onView(allOf(withDrawable(R.drawable.ic_three_dots_16dp), hasSibling(withText(R.string.venmo_account)))).perform(click()); + onView(allOf(withDrawable(R.drawable.ic_three_dots_16dp), + hasSibling(withText(R.string.venmo_account)))).perform(click()); onView(withDrawable(R.drawable.ic_trash)).check(matches(isDisplayed())); onView(withText(R.string.remove)).check(matches(isDisplayed())).perform(click()); // confirmation dialog is shown before deletion - onView(withText(R.string.mobileAreYouSure)).check(matches(isDisplayed())); - String confirmMessageBody = getRemoveConfirmationMessage( - InstrumentationRegistry.getInstrumentation().getTargetContext() - .getString(R.string.paypal_account)); - onView(withText(confirmMessageBody)).check(matches(isDisplayed())); + onView(withText(R.string.remove_transfer_method_title)).check(matches(isDisplayed())); + onView(withId(android.R.id.button1)).check(matches(withText(R.string.remove))); + onView(withId(android.R.id.button2)).check(matches(withText(R.string.cancelButtonLabel))); + + onView(withId(android.R.id.button1)).perform(click()); + + gate.await(5, SECONDS); + LocalBroadcastManager.getInstance(mActivityTestRule.getActivity().getApplicationContext()).unregisterReceiver( + br); + assertThat("Action is not broadcasted", gate.getCount(), is(0L)); + + onView(withId(R.id.empty_transfer_method_list_layout)).check(matches(isDisplayed())); + onView(withText(R.string.emptyStateAddTransferMethod)).check(matches(isDisplayed())); + + } + + @Test + public void testListTransferMethod_removePaperCheck() throws InterruptedException { + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("transfer_method_list_paper_check_response.json")).mock(); + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("transfer_method_deactivate_success.json")).mock(); + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_NO_CONTENT).withBody("").mock(); + + + final CountDownLatch gate = new CountDownLatch(1); + final BroadcastReceiver br = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + gate.countDown(); + + StatusTransition statusTransition = intent.getParcelableExtra( + "hyperwallet-local-broadcast-payload"); + assertThat("Transition is not valid", statusTransition.getTransition(), is(DE_ACTIVATED)); + } + }; + + // run test + mActivityTestRule.launchActivity(null); + LocalBroadcastManager.getInstance(mActivityTestRule.getActivity().getApplicationContext()) + .registerReceiver(br, new IntentFilter("ACTION_HYPERWALLET_TRANSFER_METHOD_DEACTIVATED")); + + // assert + onView(withId(R.id.toolbar)).check(matches(isDisplayed())); + onView(withId(R.id.toolbar)) + .check(matches( + hasDescendant(withText(R.string.mobileTransferMethodsHeader)))); + onView(withId(R.id.fab)).check(matches(isDisplayed())); + + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.paper_check_font_icon))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText(R.string.paper_check))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText("United States"))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withText("to V6Z1L2"))))); + onView(withId(R.id.list_transfer_method_item)).check( + matches(atPosition(0, hasDescendant(withDrawable(R.drawable.ic_three_dots_16dp))))); + + onView(allOf(withDrawable(R.drawable.ic_three_dots_16dp), hasSibling(withText(R.string.paper_check)))).perform( + click()); + + onView(withDrawable(R.drawable.ic_trash)).check(matches(isDisplayed())); + onView(withText(R.string.remove)).check(matches(isDisplayed())).perform(click()); + + // confirmation dialog is shown before deletion + onView(withText(R.string.remove_transfer_method_title)).check(matches(isDisplayed())); onView(withId(android.R.id.button1)).check(matches(withText(R.string.remove))); onView(withId(android.R.id.button2)).check(matches(withText(R.string.cancelButtonLabel))); @@ -661,8 +739,4 @@ private String getCardBrandWithFourDigits(String cardBrand, String endingDigits) return cardBrand + "\u0020\u2022\u2022\u2022\u2022\u0020" + endingDigits; } - private String getRemoveConfirmationMessage(String type) { - return String.format(InstrumentationRegistry.getInstrumentation().getTargetContext() - .getString(R.string.mobileRemoveEAconfirm), type); - } } diff --git a/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/PaperCheckTest.java b/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/PaperCheckTest.java new file mode 100644 index 000000000..cd297e5cc --- /dev/null +++ b/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/PaperCheckTest.java @@ -0,0 +1,172 @@ +package com.hyperwallet.android.ui.transfermethod; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.replaceText; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isEnabled; +import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +import static org.hamcrest.Matchers.not; + +import static java.net.HttpURLConnection.HTTP_OK; + +import static com.hyperwallet.android.ui.testutils.util.EspressoUtils.hasErrorText; +import static com.hyperwallet.android.ui.testutils.util.EspressoUtils.hasNoErrorText; +import static com.hyperwallet.android.ui.testutils.util.EspressoUtils.nestedScrollTo; + +import android.content.Intent; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.espresso.IdlingRegistry; +import androidx.test.espresso.matcher.ViewMatchers; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import com.hyperwallet.android.ui.R; +import com.hyperwallet.android.ui.common.repository.EspressoIdlingResource; +import com.hyperwallet.android.ui.testutils.rule.HyperwalletExternalResourceManager; +import com.hyperwallet.android.ui.testutils.rule.HyperwalletMockWebServer; +import com.hyperwallet.android.ui.transfermethod.repository.TransferMethodRepositoryFactory; +import com.hyperwallet.android.ui.transfermethod.rule.HyperwalletInsightMockRule; +import com.hyperwallet.android.ui.transfermethod.view.AddTransferMethodActivity; + +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class PaperCheckTest { + + @ClassRule + public static HyperwalletExternalResourceManager sResourceManager = new HyperwalletExternalResourceManager(); + @Rule + public HyperwalletInsightMockRule mHyperwalletInsightMockRule = new HyperwalletInsightMockRule(); + @Rule + public HyperwalletMockWebServer mMockWebServer = new HyperwalletMockWebServer(8080); + @Rule + public ActivityTestRule mActivityTestRule = + new ActivityTestRule(AddTransferMethodActivity.class, true, false) { + @Override + protected Intent getActivityIntent() { + Intent intent = new Intent(ApplicationProvider.getApplicationContext(), + AddTransferMethodActivity.class); + intent.putExtra("TRANSFER_METHOD_TYPE", "PAPER_CHECK"); + intent.putExtra("TRANSFER_METHOD_COUNTRY", "US"); + intent.putExtra("TRANSFER_METHOD_CURRENCY", "USD"); + intent.putExtra("TRANSFER_METHOD_PROFILE_TYPE", "INDIVIDUAL"); + return intent; + } + }; + + @Before + public void setup() { + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("authentication_token_response.json")).mock(); + mMockWebServer.mockResponse().withHttpResponseCode(HTTP_OK).withBody(sResourceManager + .getResourceContent("successful_tmc_fields_paper_check_response.json")).mock(); + IdlingRegistry.getInstance().register(EspressoIdlingResource.getIdlingResource()); + } + + @After + public void cleanup() { + TransferMethodRepositoryFactory.clearInstance(); + IdlingRegistry.getInstance().unregister(EspressoIdlingResource.getIdlingResource()); + } + + @Test + public void testAddTransferMethod_displaysFeeElementsOnTmcResponse() { + mActivityTestRule.launchActivity(null); + + onView(withId(R.id.add_transfer_method_static_container)).check( + matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); + + onView(withId(R.id.transfer_method_information)).check( + matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); + + onView(withId(R.id.transfer_method_information)).check( + matches(withText(R.string.mobileFeesAndProcessingTime))); + + onView(withId(R.id.add_transfer_method_information)).check( + matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); + onView(withId(R.id.add_transfer_method_information)).check( + matches(withText("US$0.25 fee \u2022 5 - 7 Business days"))); + } + + @Test + public void testAddTransferMethod_verifyDefaultValues() { + mActivityTestRule.launchActivity(null); + + onView(withId(R.id.shippingMethod)).check(matches(withText("Standard Mail"))); + onView(withId(R.id.country)).check(matches(withText("United States"))); + onView(withId(R.id.stateProvince)).check(matches(withText("BC"))); + onView(withId(R.id.addressLine1)).check(matches(withText("950 Granville Street"))); + onView(withId(R.id.city)).check(matches(withText("Vancouver"))); + onView(withId(R.id.postalCode)).check(matches(withText("12345"))); + } + + @Test + public void testAddTransferMethod_verifyEditableFields() { + mActivityTestRule.launchActivity(null); + + onView(withId(R.id.shippingMethod)).check(matches(isEnabled())); + onView(withId(R.id.country)).check(matches(not(isEnabled()))); + onView(withId(R.id.stateProvince)).check(matches(isEnabled())); + onView(withId(R.id.addressLine1)).check(matches(isEnabled())); + onView(withId(R.id.city)).check(matches(isEnabled())); + onView(withId(R.id.postalCode)).check(matches(isEnabled())); + } + + @Test + public void testAddTransferMethod_returnsErrorOnInvalidPattern() { + mActivityTestRule.launchActivity(null); + + onView(withId(R.id.addressLine1)).perform(nestedScrollTo(), replaceText("950 {G}ranville Street")); + onView(withId(R.id.city)).perform(nestedScrollTo(), replaceText("Vancouve{r}")); + onView(withId(R.id.stateProvince)).perform(nestedScrollTo(), replaceText("df{r}")); + onView(withId(R.id.postalCode)).perform(nestedScrollTo(), replaceText("123456")); + + onView(withId(R.id.add_transfer_method_button)).perform(nestedScrollTo(), click()); + + onView(withId(R.id.addressLine1Label)) + .check(matches(hasErrorText("is invalid length or format."))); + onView(withId(R.id.cityLabel)) + .check(matches(hasErrorText("is invalid length or format."))); + onView(withId(R.id.stateProvinceLabel)) + .check(matches(hasErrorText("is invalid length or format."))); + onView(withId(R.id.postalCodeLabel)) + .check(matches(hasErrorText("is invalid length or format."))); + + } + + @Test + public void testAddTransferMethod_returnsErrorOnInvalidPresence() { + mActivityTestRule.launchActivity(null); + + onView(withId(R.id.addressLine1)).perform(nestedScrollTo(), replaceText("")); + onView(withId(R.id.city)).perform(nestedScrollTo(), replaceText("")); + onView(withId(R.id.stateProvince)).perform(nestedScrollTo(), replaceText("")); + onView(withId(R.id.postalCode)).perform(nestedScrollTo(), replaceText("")); + + + onView(withId(R.id.add_transfer_method_button)).perform(nestedScrollTo(), click()); + + onView(withId(R.id.addressLine1Label)) + .check(matches(hasErrorText("You must provide a value for this field"))); + onView(withId(R.id.cityLabel)) + .check(matches(hasErrorText("You must provide a value for this field"))); + onView(withId(R.id.stateProvinceLabel)) + .check(matches(hasErrorText("You must provide a value for this field"))); + onView(withId(R.id.postalCodeLabel)) + .check(matches(hasErrorText("You must provide a value for this field"))); + + onView(withId(R.id.shippingMethodLabel)).check(matches(hasNoErrorText())); + onView(withId(R.id.countryLabel)).check(matches(hasNoErrorText())); + + } +} diff --git a/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/SelectTransferMethodTest.java b/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/SelectTransferMethodTest.java index c23920d6b..ec570fb8c 100644 --- a/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/SelectTransferMethodTest.java +++ b/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/SelectTransferMethodTest.java @@ -11,7 +11,6 @@ import static androidx.test.espresso.matcher.ViewMatchers.hasSibling; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; import static androidx.test.espresso.matcher.ViewMatchers.withChild; -import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withParent; import static androidx.test.espresso.matcher.ViewMatchers.withText; @@ -36,7 +35,6 @@ import androidx.test.espresso.IdlingRegistry; import androidx.test.espresso.contrib.RecyclerViewActions; import androidx.test.espresso.intent.rule.IntentsTestRule; -import androidx.test.espresso.matcher.ViewMatchers; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.rule.ActivityTestRule; @@ -210,7 +208,7 @@ public void testSelectTransferMethod_verifyTransferMethodsList() { matches(atPosition(0, hasDescendant(withText(R.string.bank_account))))); String bankCardFee = InstrumentationRegistry.getInstrumentation().getTargetContext() - .getString(R.string.feeAndProcessingTimeInformation,usdDollarSymbol + "2.00","1-2 Business days"); + .getString(R.string.feeAndProcessingTimeInformation, usdDollarSymbol + "2.00", "1-2 Business days"); onView(withId(R.id.select_transfer_method_types_list)).check( matches(atPosition(0, hasDescendant(withText(bankCardFee))))); @@ -221,7 +219,7 @@ public void testSelectTransferMethod_verifyTransferMethodsList() { matches(atPosition(1, hasDescendant(withText(R.string.bank_card))))); String debitCardFee = InstrumentationRegistry.getInstrumentation().getTargetContext() - .getString(R.string.feeAndProcessingTimeInformation,usdDollarSymbol + "1.75","IMMEDIATE"); + .getString(R.string.feeAndProcessingTimeInformation, usdDollarSymbol + "1.75", "IMMEDIATE"); onView(withId(R.id.select_transfer_method_types_list)).check( matches(atPosition(1, hasDescendant(withText(debitCardFee))))); @@ -236,7 +234,7 @@ public void testSelectTransferMethod_verifyTransferMethodsList() { matches(atPosition(2, hasDescendant(wireAccountViewMatchers)))); String wireTransferFee = InstrumentationRegistry.getInstrumentation().getTargetContext() - .getString(R.string.feeAndProcessingTimeInformation,usdDollarSymbol + "20.00","1-3 Business days"); + .getString(R.string.feeAndProcessingTimeInformation, usdDollarSymbol + "20.00", "1-3 Business days"); onView(withId(R.id.select_transfer_method_types_list)).check( matches(atPosition(2, hasDescendant(withText(wireTransferFee))))); @@ -244,10 +242,10 @@ public void testSelectTransferMethod_verifyTransferMethodsList() { onView(withId(R.id.select_transfer_method_types_list)).check( matches(atPosition(3, hasDescendant(withText(R.string.paypal_account_font_icon))))); onView(withId(R.id.select_transfer_method_types_list)).check( - matches(atPosition(3, hasDescendant(withText(R.string.paypal_account))))); + matches(atPosition(3, hasDescendant(withText("PayPal"))))); String paypalFee = InstrumentationRegistry.getInstrumentation().getTargetContext() - .getString(R.string.feeAndProcessingTimeInformation,usdDollarSymbol + "0.25","IMMEDIATE"); + .getString(R.string.feeAndProcessingTimeInformation, usdDollarSymbol + "0.25", "IMMEDIATE"); onView(withId(R.id.select_transfer_method_types_list)).check( matches(atPosition(3, hasDescendant(withText(paypalFee))))); @@ -292,7 +290,7 @@ public void testSelectTransferMethod_verifyTransferMethodsListEmptyProcessing() onView(withId(R.id.select_transfer_method_types_list)).check( matches(atPosition(0, hasDescendant(withText(R.string.bank_account))))); String bankCardFee = InstrumentationRegistry.getInstrumentation().getTargetContext() - .getString(R.string.feeInformation,usdDollarSymbol + "2.00"); + .getString(R.string.feeInformation, usdDollarSymbol + "2.00"); onView(withId(R.id.select_transfer_method_types_list)).check( matches(atPosition(0, hasDescendant(withText(bankCardFee))))); @@ -321,18 +319,18 @@ public void testSelectTransferMethod_verifyTransferMethodsListUpdatedOnSelection matches(atPosition(0, hasDescendant(withText(R.string.bank_account))))); String bankAccountFee = InstrumentationRegistry.getInstrumentation().getTargetContext() - .getString(R.string.feeAndProcessingTimeInformation,cdnDollarSymbol + "2.20","1-2 Business days"); + .getString(R.string.feeAndProcessingTimeInformation, cdnDollarSymbol + "2.20", "1-2 Business days"); onView(withId(R.id.select_transfer_method_types_list)).check( - matches(atPosition(0, hasDescendant(withText(bankAccountFee))))); + matches(atPosition(0, hasDescendant(withText(bankAccountFee))))); onView(withId(R.id.select_transfer_method_types_list)).check( matches(atPosition(1, hasDescendant(withText(R.string.paypal_account_font_icon))))); onView(withId(R.id.select_transfer_method_types_list)).check( - matches(atPosition(1, hasDescendant(withText(R.string.paypal_account))))); + matches(atPosition(1, hasDescendant(withText("PayPal"))))); String paypalFee = InstrumentationRegistry.getInstrumentation().getTargetContext() - .getString(R.string.feeAndProcessingTimeInformation,cdnDollarSymbol + "0.25","IMMEDIATE"); + .getString(R.string.feeAndProcessingTimeInformation, cdnDollarSymbol + "0.25", "IMMEDIATE"); onView(withId(R.id.select_transfer_method_types_list)).check( matches(atPosition(1, hasDescendant(withText(paypalFee))))); diff --git a/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/VenmoTest.java b/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/VenmoTest.java index 692272c8a..b2c965966 100644 --- a/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/VenmoTest.java +++ b/transfermethodui/src/androidTest/java/com/hyperwallet/android/ui/transfermethod/VenmoTest.java @@ -83,7 +83,7 @@ public void testAddTransferMethod_displaysElementsOnTmcResponse() { .check(matches(withText(R.string.venmo_account))); // Not working will investigate later - // onView(withId(R.id.mobileNumberLabel)).check(matches(isDisplayed())); + onView(withId(R.id.accountIdLabel)).check(matches(isDisplayed())); onView(withId(R.id.transfer_method_information)).check( matches(withText(R.string.mobileFeesAndProcessingTime))); diff --git a/transfermethodui/src/main/AndroidManifest.xml b/transfermethodui/src/main/AndroidManifest.xml index 2a97d4dee..d46f47fb2 100644 --- a/transfermethodui/src/main/AndroidManifest.xml +++ b/transfermethodui/src/main/AndroidManifest.xml @@ -12,6 +12,11 @@ android:theme="@style/AppTheme.NoActionBar" android:windowSoftInputMode="adjustResize"/> + + true screen will be locked to Portrait mode; + * otherwise false screen will follow whatever the + * device orientation is directed. + * @return an Intent with the data necessary to launch the {@link UpdateTransferMethodActivity} + */ + public Intent getIntentUpdateTransferMethodActivity(@NonNull final Context context, + @NonNull final String transferMethodToken, final boolean lockScreenToPortrait) { + Intent intent = new Intent(context, UpdateTransferMethodActivity.class); + intent.putExtra(EXTRA_TRANSFER_METHOD_TOKEN, transferMethodToken); + intent.putExtra(AddTransferMethodActivity.EXTRA_LOCK_SCREEN_ORIENTATION_TO_PORTRAIT, lockScreenToPortrait); + return intent; + } + public static void clearInstance() { sInstance = null; Hyperwallet.clearInstance(); diff --git a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/TransferMethodLocalBroadcast.java b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/TransferMethodLocalBroadcast.java index f0d88bca0..5d703d328 100644 --- a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/TransferMethodLocalBroadcast.java +++ b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/TransferMethodLocalBroadcast.java @@ -19,6 +19,7 @@ import static com.hyperwallet.android.ui.common.intent.HyperwalletIntent.HYPERWALLET_LOCAL_BROADCAST_PAYLOAD_KEY; import static com.hyperwallet.android.ui.transfermethod.TransferMethodLocalBroadcast.TransferMethodLocalBroadcastAction.ACTION_HYPERWALLET_TRANSFER_METHOD_ADDED; import static com.hyperwallet.android.ui.transfermethod.TransferMethodLocalBroadcast.TransferMethodLocalBroadcastAction.ACTION_HYPERWALLET_TRANSFER_METHOD_DEACTIVATED; +import static com.hyperwallet.android.ui.transfermethod.TransferMethodLocalBroadcast.TransferMethodLocalBroadcastAction.ACTION_HYPERWALLET_TRANSFER_METHOD_UPDATED; import android.content.Intent; import android.os.Parcelable; @@ -40,6 +41,12 @@ public static Intent createBroadcastIntentTransferMethodAdded( ACTION_HYPERWALLET_TRANSFER_METHOD_ADDED); } + public static Intent createBroadcastIntentTransferMethodUpdated( + @NonNull final TransferMethod transferMethod) { + return createBroadcastIntent(transferMethod, + ACTION_HYPERWALLET_TRANSFER_METHOD_UPDATED); + } + public static Intent createBroadcastIntentTransferMethodDeactivated( @NonNull final StatusTransition StatusTransition) { return createBroadcastIntent(StatusTransition, @@ -57,6 +64,7 @@ private static Intent createBroadcastIntent(@NonNull final Parcelable parcelable @Retention(RetentionPolicy.SOURCE) @StringDef({ ACTION_HYPERWALLET_TRANSFER_METHOD_ADDED, + ACTION_HYPERWALLET_TRANSFER_METHOD_UPDATED, ACTION_HYPERWALLET_TRANSFER_METHOD_DEACTIVATED }) public @interface TransferMethodLocalBroadcastActionType { @@ -69,5 +77,7 @@ private TransferMethodLocalBroadcastAction() {} "ACTION_HYPERWALLET_TRANSFER_METHOD_ADDED"; public static final String ACTION_HYPERWALLET_TRANSFER_METHOD_DEACTIVATED = "ACTION_HYPERWALLET_TRANSFER_METHOD_DEACTIVATED"; + public static final String ACTION_HYPERWALLET_TRANSFER_METHOD_UPDATED = + "ACTION_HYPERWALLET_TRANSFER_METHOD_UPDATED"; } } diff --git a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/AddTransferMethodActivity.java b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/AddTransferMethodActivity.java index b090895d4..f43ebaebf 100644 --- a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/AddTransferMethodActivity.java +++ b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/AddTransferMethodActivity.java @@ -27,6 +27,7 @@ import androidx.core.content.ContextCompat; import androidx.fragment.app.FragmentManager; +import com.google.android.material.appbar.CollapsingToolbarLayout; import com.hyperwallet.android.model.Error; import com.hyperwallet.android.ui.R; import com.hyperwallet.android.ui.common.util.PageGroups; @@ -58,16 +59,22 @@ public class AddTransferMethodActivity extends AppCompatActivity implements @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_add_transfer_method); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); + setContentView(R.layout.activity_add_transfer_method); + Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setTitle(TransferMethodUtils.getTransferMethodName(this, getIntent().getStringExtra(EXTRA_TRANSFER_METHOD_TYPE))); + CollapsingToolbarLayout collapsingToolbar = findViewById(R.id.collapsing_toolbar); + int titleStyleCollapse = TransferMethodUtils.getAdjustCollapseTitleStyle(getTitle().toString()); + collapsingToolbar.setCollapsedTitleTextAppearance(titleStyleCollapse); + int titleStyleExpanded = TransferMethodUtils.getAdjustExpandTitleStyle(getTitle().toString()); + collapsingToolbar.setExpandedTitleTextAppearance(titleStyleExpanded); + toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/AddTransferMethodFragment.java b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/AddTransferMethodFragment.java index ddd310afc..dde95a619 100644 --- a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/AddTransferMethodFragment.java +++ b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/AddTransferMethodFragment.java @@ -22,6 +22,7 @@ import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodFields.TYPE; import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.BANK_ACCOUNT; import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.BANK_CARD; +import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.PAPER_CHECK; import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.PAYPAL_ACCOUNT; import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.VENMO_ACCOUNT; import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.WIRE_ACCOUNT; @@ -59,6 +60,7 @@ import com.hyperwallet.android.model.graphql.field.FieldGroup; import com.hyperwallet.android.model.transfermethod.BankAccount; import com.hyperwallet.android.model.transfermethod.BankCard; +import com.hyperwallet.android.model.transfermethod.PaperCheck; import com.hyperwallet.android.model.transfermethod.PayPalAccount; import com.hyperwallet.android.model.transfermethod.TransferMethod; import com.hyperwallet.android.model.transfermethod.VenmoAccount; @@ -492,7 +494,7 @@ public void showInputErrors(@NonNull final List errors) { widget.showValidationError(error.getMessage()); widgetInputState.setErrorMessage(error.getMessage()); widgetInputState.setHasApiError(true); - }else{ + } else { widget.showValidationError(null); widgetInputState.setErrorMessage(null); } @@ -564,6 +566,12 @@ private void triggerSubmit() { .transferMethodCurrency(mCurrency) .build(); break; + case PAPER_CHECK: + mTransferMethod = new PaperCheck.Builder() + .transferMethodCountry(mCountry) + .transferMethodCurrency(mCurrency) + .build(); + break; default: mTransferMethod = new TransferMethod(); mTransferMethod.setField(TRANSFER_METHOD_COUNTRY, mCountry); @@ -620,6 +628,7 @@ private void setVisibleAndDisableFields() { /** * Use this to perform validation on an entire form, typically used during form submission. + * * @return true if the form is valid */ private boolean performValidation() { diff --git a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/ListTransferMethodActivity.java b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/ListTransferMethodActivity.java index 9af12b35a..332ad93b7 100644 --- a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/ListTransferMethodActivity.java +++ b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/ListTransferMethodActivity.java @@ -17,12 +17,14 @@ package com.hyperwallet.android.ui.transfermethod.view; import static com.hyperwallet.android.ui.common.intent.HyperwalletIntent.SELECT_TRANSFER_METHOD_REQUEST_CODE; +import static com.hyperwallet.android.ui.common.intent.HyperwalletIntent.UPDATE_TRANSFER_METHOD_REQUEST_CODE; import static com.hyperwallet.android.ui.transfermethod.view.ListTransferMethodFragment.ARGUMENT_IS_TRANSFER_METHODS_RELOAD_NEEDED; import android.content.Intent; import android.content.pm.ActivityInfo; import android.os.Bundle; import android.view.View; +import android.view.WindowManager; import android.widget.TextView; import androidx.annotation.NonNull; @@ -31,6 +33,7 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; +import com.google.android.material.appbar.CollapsingToolbarLayout; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.hyperwallet.android.model.Error; import com.hyperwallet.android.model.transfermethod.TransferMethod; @@ -48,7 +51,7 @@ public class ListTransferMethodActivity extends AppCompatActivity implements ListTransferMethodFragment.OnAddNewTransferMethodSelected, ListTransferMethodFragment.OnDeactivateTransferMethodNetworkErrorCallback, ListTransferMethodFragment.OnLoadTransferMethodNetworkErrorCallback, - ListTransferMethodFragment.OnTransferMethodContextMenuDeletionSelected, + ListTransferMethodFragment.OnTransferMethodContextMenuItemSelected, OnTransferMethodDeactivateCallback, OnNetworkErrorCallback { public static final String TAG = "transfer-method:list:list-transfer-methods"; @@ -67,12 +70,17 @@ public class ListTransferMethodActivity extends AppCompatActivity implements @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); + setContentView(R.layout.activity_list_transfer_method); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); + CollapsingToolbarLayout collapsingToolbar = findViewById(R.id.collapsing_toolbar); + int titleStyleExpanded = TransferMethodUtils.getAdjustExpandTitleStyle(getTitle().toString()); + collapsingToolbar.setExpandedTitleTextAppearance(titleStyleExpanded); TextView titleText = findViewById(R.id.toolbar_title); toolbar.setNavigationOnClickListener(new View.OnClickListener() { @@ -130,6 +138,9 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { if (fragment != null && fragment.getArguments() != null) { fragment.getArguments().putBoolean(ARGUMENT_IS_TRANSFER_METHODS_RELOAD_NEEDED, true); } + } else if (requestCode == UPDATE_TRANSFER_METHOD_REQUEST_CODE && resultCode == RESULT_OK) { + ActivityUtils.initFragment(this, ListTransferMethodFragment.newInstance(), + R.id.list_transfer_method_fragment); } } @@ -173,6 +184,15 @@ public void showConfirmationDialog(@NonNull TransferMethod transferMethod) { } } + @Override + public void invokeTransferMethodEdit(@NonNull TransferMethod transferMethod) { + String token = transferMethod.getField(TransferMethod.TransferMethodFields.TOKEN); + Intent intent = new Intent(this, UpdateTransferMethodActivity.class); + intent.putExtra(UpdateTransferMethodActivity.EXTRA_TRANSFER_METHOD_TOKEN, token); + intent.putExtra(UpdateTransferMethodActivity.EXTRA_LOCK_SCREEN_ORIENTATION_TO_PORTRAIT, true); + startActivityForResult(intent, UPDATE_TRANSFER_METHOD_REQUEST_CODE); + } + @Override public void confirm() { mRetryCode = RETRY_CONFIRM_DEACTIVATE_TRANSFER_METHOD; diff --git a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/ListTransferMethodFragment.java b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/ListTransferMethodFragment.java index 9f4ccb6a8..f5c185311 100644 --- a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/ListTransferMethodFragment.java +++ b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/ListTransferMethodFragment.java @@ -69,7 +69,7 @@ public class ListTransferMethodFragment extends Fragment implements ListTransfer private View mProgressBar; private ArrayList mTransferMethodList; private OnAddNewTransferMethodSelected mOnAddNewTransferMethodSelected; - private OnTransferMethodContextMenuDeletionSelected mOnTransferMethodContextMenuDeletionSelected; + private OnTransferMethodContextMenuItemSelected mOnTransferMethodContextMenuItemSelected; private OnDeactivateTransferMethodNetworkErrorCallback mOnDeactivateTransferMethodNetworkErrorCallback; private OnLoadTransferMethodNetworkErrorCallback mOnLoadTransferMethodNetworkErrorCallback; private boolean mIsTransferMethodsReloadNeeded; @@ -105,10 +105,10 @@ public void onAttach(Context context) { } try { - mOnTransferMethodContextMenuDeletionSelected = (OnTransferMethodContextMenuDeletionSelected) context; + mOnTransferMethodContextMenuItemSelected = (OnTransferMethodContextMenuItemSelected) context; } catch (ClassCastException e) { throw new ClassCastException(getActivity().toString() + " must implement " - + OnTransferMethodContextMenuDeletionSelected.class.getCanonicalName()); + + OnTransferMethodContextMenuItemSelected.class.getCanonicalName()); } try { @@ -144,7 +144,7 @@ public void onViewStateRestored(@Nullable Bundle savedInstanceState) { mIsTransferMethodsReloadNeeded = true; } mListTransferMethodAdapter = new ListTransferMethodAdapter(mTransferMethodList, - mOnTransferMethodContextMenuDeletionSelected); + mOnTransferMethodContextMenuItemSelected); recyclerView.setAdapter(mListTransferMethodAdapter); } @@ -187,7 +187,11 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { public void onResume() { super.onResume(); mIsTransferMethodsReloadNeeded = getArguments().getBoolean(ARGUMENT_IS_TRANSFER_METHODS_RELOAD_NEEDED, true); - if (mIsTransferMethodsReloadNeeded) { + loadTransferMethodsList(mIsTransferMethodsReloadNeeded); + } + + private void loadTransferMethodsList(boolean shouldReload) { + if (shouldReload) { getArguments().putBoolean(ARGUMENT_IS_TRANSFER_METHODS_RELOAD_NEEDED, false); mPresenter.loadTransferMethods(); } else { @@ -272,9 +276,11 @@ public void loadTransferMethods() { mPresenter.loadTransferMethods(); } - interface OnTransferMethodContextMenuDeletionSelected { + interface OnTransferMethodContextMenuItemSelected { void showConfirmationDialog(@NonNull final TransferMethod transferMethod); + + void invokeTransferMethodEdit(@NonNull final TransferMethod transferMethod); } interface OnAddNewTransferMethodSelected { @@ -294,12 +300,12 @@ interface OnLoadTransferMethodNetworkErrorCallback { private static class ListTransferMethodAdapter extends RecyclerView.Adapter { private List mTransferMethodList; - private OnTransferMethodContextMenuDeletionSelected mOnTransferMethodContextMenuDeletionSelected; + private OnTransferMethodContextMenuItemSelected mOnTransferMethodContextMenuItemSelected; ListTransferMethodAdapter(final List transferMethodList, - final OnTransferMethodContextMenuDeletionSelected onTransferMethodContextMenuSelection) { + final OnTransferMethodContextMenuItemSelected onTransferMethodContextMenuSelection) { mTransferMethodList = transferMethodList; - mOnTransferMethodContextMenuDeletionSelected = onTransferMethodContextMenuSelection; + mOnTransferMethodContextMenuItemSelected = onTransferMethodContextMenuSelection; } @NonNull @@ -388,7 +394,10 @@ public void onClick(View v) { @Override public boolean onMenuItemClick(MenuItem item) { if (item.getItemId() == R.id.remove_account_context_option) { - mOnTransferMethodContextMenuDeletionSelected.showConfirmationDialog(transferMethod); + mOnTransferMethodContextMenuItemSelected.showConfirmationDialog(transferMethod); + return true; + } if (item.getItemId() == R.id.edit_account_context_option) { + mOnTransferMethodContextMenuItemSelected.invokeTransferMethodEdit(transferMethod); return true; } return false; diff --git a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/SelectTransferMethodActivity.java b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/SelectTransferMethodActivity.java index 7c5488704..851c6c14d 100644 --- a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/SelectTransferMethodActivity.java +++ b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/SelectTransferMethodActivity.java @@ -33,10 +33,12 @@ import androidx.core.content.ContextCompat; import androidx.fragment.app.FragmentManager; +import com.google.android.material.appbar.CollapsingToolbarLayout; import com.hyperwallet.android.model.Error; import com.hyperwallet.android.ui.R; import com.hyperwallet.android.ui.common.util.PageGroups; import com.hyperwallet.android.ui.common.view.ActivityUtils; +import com.hyperwallet.android.ui.common.view.TransferMethodUtils; import com.hyperwallet.android.ui.common.view.error.OnNetworkErrorCallback; import java.util.List; @@ -66,10 +68,18 @@ public class SelectTransferMethodActivity extends AppCompatActivity implements @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); + setContentView(R.layout.activity_select_transfer_method); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); + CollapsingToolbarLayout collapsingToolbar = findViewById(R.id.collapsing_toolbar); + int titleStyleCollapse = TransferMethodUtils.getAdjustCollapseTitleStyle(getTitle().toString()); + collapsingToolbar.setCollapsedTitleTextAppearance(titleStyleCollapse); + int titleStyleExpanded = TransferMethodUtils.getAdjustExpandTitleStyle(getTitle().toString()); + collapsingToolbar.setExpandedTitleTextAppearance(titleStyleExpanded); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); diff --git a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/TransferMethodConfirmDeactivationDialogFragment.java b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/TransferMethodConfirmDeactivationDialogFragment.java index 604328d12..d999824c0 100644 --- a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/TransferMethodConfirmDeactivationDialogFragment.java +++ b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/TransferMethodConfirmDeactivationDialogFragment.java @@ -63,8 +63,7 @@ public void onDestroyView() { public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder( new ContextThemeWrapper(requireContext(), R.style.Theme_Hyperwallet_Confirmation_Dialog)); - builder.setMessage(requireContext().getString(R.string.mobileAreYouSure)) - .setTitle(R.string.mobileRemoveEAconfirm) + builder.setTitle(requireContext().getString(R.string.remove_transfer_method_title)) .setNegativeButton(R.string.cancelButtonLabel, null); if (getActivity() instanceof OnTransferMethodDeactivateCallback) { builder.setPositiveButton(R.string.remove, new DialogInterface.OnClickListener() { diff --git a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/UpdateTransferMethodActivity.java b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/UpdateTransferMethodActivity.java new file mode 100644 index 000000000..916aa0e40 --- /dev/null +++ b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/UpdateTransferMethodActivity.java @@ -0,0 +1,169 @@ +package com.hyperwallet.android.ui.transfermethod.view; + +import android.content.pm.ActivityInfo; +import android.os.Bundle; +import android.view.View; +import android.view.WindowManager; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.FragmentManager; + +import com.google.android.material.appbar.CollapsingToolbarLayout; +import com.hyperwallet.android.model.Error; +import com.hyperwallet.android.ui.R; +import com.hyperwallet.android.ui.common.util.PageGroups; +import com.hyperwallet.android.ui.common.view.ActivityUtils; +import com.hyperwallet.android.ui.common.view.TransferMethodUtils; +import com.hyperwallet.android.ui.common.view.error.OnNetworkErrorCallback; + +import java.util.List; + +public class UpdateTransferMethodActivity extends AppCompatActivity implements + WidgetSelectionDialogFragment.WidgetSelectionItemListener, + UpdateTransferMethodFragment.OnUpdateTransferMethodNetworkErrorCallback, + UpdateTransferMethodFragment.OnLoadTransferMethodConfigurationFieldsNetworkErrorCallback, + OnNetworkErrorCallback, WidgetDateDialogFragment.OnSelectedDateCallback { + + public static final String TAG = "transfer-method:update:collect-transfer-method-information"; + + public static final String EXTRA_TRANSFER_METHOD_TOKEN = "EXTRA_TRANSFER_METHOD_TOKEN"; + private static final String ARGUMENT_RETRY_ACTION = "ARGUMENT_RETRY_ACTION"; + public static final String EXTRA_LOCK_SCREEN_ORIENTATION_TO_PORTRAIT = "EXTRA_LOCK_SCREEN_ORIENTATION_TO_PORTRAIT"; + private static final short RETRY_SHOW_ERROR_UPDATE_TRANSFER_METHOD = 100; + private static final short RETRY_SHOW_ERROR_LOAD_TMC_FIELDS = 101; + + private short mRetryCode; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); + + setContentView(R.layout.activity_update_transfer_method); + + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setDisplayShowHomeEnabled(true); + CollapsingToolbarLayout collapsingToolbar = findViewById(R.id.collapsing_toolbar); + int titleStyleCollapse = TransferMethodUtils.getAdjustCollapseTitleStyle(getTitle().toString()); + collapsingToolbar.setCollapsedTitleTextAppearance(titleStyleCollapse); + int titleStyleExpanded = TransferMethodUtils.getAdjustExpandTitleStyle(getTitle().toString()); + collapsingToolbar.setExpandedTitleTextAppearance(titleStyleExpanded); + getSupportActionBar().setTitle(""); + + toolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); + + if (getIntent().getBooleanExtra(EXTRA_LOCK_SCREEN_ORIENTATION_TO_PORTRAIT, false)) { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } + if (savedInstanceState == null) { + ActivityUtils.initFragment(this, UpdateTransferMethodFragment.newInstance( + getIntent().getStringExtra(EXTRA_TRANSFER_METHOD_TOKEN) + ), R.id.update_transfer_method_fragment); + } else { + mRetryCode = savedInstanceState.getShort(ARGUMENT_RETRY_ACTION); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + outState.putShort(ARGUMENT_RETRY_ACTION, mRetryCode); + super.onSaveInstanceState(outState); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + + if (savedInstanceState != null) { + mRetryCode = savedInstanceState.getShort(ARGUMENT_RETRY_ACTION); + } + } + + @Override + public boolean onSupportNavigateUp() { + onBackPressed(); + return true; + } + + @Override + public void onBackPressed() { + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + getWindow().setStatusBarColor(ContextCompat.getColor(this, R.color.colorPrimaryDark)); + getWindow().getDecorView().setSystemUiVisibility(0); + super.onBackPressed(); + } + + @Override + public void retry() { + UpdateTransferMethodFragment fragment = getUpdateTransferFragment(); + switch (mRetryCode) { + case RETRY_SHOW_ERROR_UPDATE_TRANSFER_METHOD: + fragment.retryUpdateTransferMethod(); + break; + case RETRY_SHOW_ERROR_LOAD_TMC_FIELDS: + fragment.reloadTransferMethodConfigurationFields(); + break; + default: // no default action + } + } + + private UpdateTransferMethodFragment getUpdateTransferFragment() { + FragmentManager fragmentManager = getSupportFragmentManager(); + UpdateTransferMethodFragment fragment = (UpdateTransferMethodFragment) + fragmentManager.findFragmentById(R.id.update_transfer_method_fragment); + + if (fragment == null) { + fragment = UpdateTransferMethodFragment.newInstance( + getIntent().getStringExtra(EXTRA_TRANSFER_METHOD_TOKEN)); + } + return fragment; + } + + @Override + public void showErrorsLoadTransferMethodConfigurationFields(@NonNull List errors) { + mRetryCode = RETRY_SHOW_ERROR_LOAD_TMC_FIELDS; + ActivityUtils.showError(this, TAG, PageGroups.TRANSFER_METHOD, errors); + } + + @Override + public void showErrorsUpdateTransferMethod(@NonNull List errors) { + mRetryCode = RETRY_SHOW_ERROR_UPDATE_TRANSFER_METHOD; + ActivityUtils.showError(this, TAG, PageGroups.TRANSFER_METHOD, errors); + } + + @Override + public void setSelectedDateField(@NonNull String fieldName, String selectedValue) { + FragmentManager fragmentManager = getSupportFragmentManager(); + UpdateTransferMethodFragment updateTransferMethodFragment = + (UpdateTransferMethodFragment) fragmentManager.findFragmentById(R.id.update_transfer_method_fragment); + if (updateTransferMethodFragment != null) { + updateTransferMethodFragment.onDateSelected(selectedValue, fieldName); + } + } + + @Override + public void onWidgetSelectionItemClicked(@NonNull String selectedValue, @NonNull String fieldName) { + FragmentManager fragmentManager = getSupportFragmentManager(); + UpdateTransferMethodFragment updateTransferMethodFragment = + (UpdateTransferMethodFragment) fragmentManager.findFragmentById(R.id.update_transfer_method_fragment); + updateTransferMethodFragment.onWidgetSelectionItemClicked(selectedValue, fieldName); + + WidgetSelectionDialogFragment widgetSelectionDialogFragment = + (WidgetSelectionDialogFragment) fragmentManager.findFragmentById(android.R.id.content); + widgetSelectionDialogFragment.dismiss(); + getSupportFragmentManager().popBackStack(WidgetSelectionDialogFragment.TAG, + FragmentManager.POP_BACK_STACK_INCLUSIVE); + } +} diff --git a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/UpdateTransferMethodContract.java b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/UpdateTransferMethodContract.java new file mode 100644 index 000000000..b66bdda1f --- /dev/null +++ b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/UpdateTransferMethodContract.java @@ -0,0 +1,65 @@ +package com.hyperwallet.android.ui.transfermethod.view; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.hyperwallet.android.model.Error; +import com.hyperwallet.android.model.graphql.Fee; +import com.hyperwallet.android.model.graphql.HyperwalletTransferMethodConfigurationField; +import com.hyperwallet.android.model.graphql.ProcessingTime; +import com.hyperwallet.android.model.graphql.field.FieldGroup; +import com.hyperwallet.android.model.transfermethod.TransferMethod; + +import java.util.List; +import java.util.Map; + +/** + * View and Presenter Contract for Updating Transfer Method + */ +public interface UpdateTransferMethodContract { + + interface View { + + void notifyTransferMethodUpdated(@NonNull final TransferMethod transferMethod); + + void showErrorUpdateTransferMethod(@NonNull final List errors); + + void showErrorLoadTransferMethodConfigurationFields(@NonNull final List errors); + + void showTransferMethodFields(@NonNull final HyperwalletTransferMethodConfigurationField field); + + void showTransactionInformation(@NonNull final List fees, + @Nullable final ProcessingTime processingTime); + + void showUpdateButtonProgressBar(); + + void hideUpdateButtonProgressBar(); + + void showProgressBar(); + + void hideProgressBar(); + + void showInputErrors(@NonNull final List errors); + + /** + * Check the state of a View + * + * @return true when View is added to Container + */ + boolean isActive(); + + void retryUpdateTransferMethod(); + + void reloadTransferMethodConfigurationFields(); + } + + interface Presenter { + + void updateTransferMethod(@NonNull TransferMethod transferMethod); + + void loadTransferMethodConfigurationFields(boolean forceUpdate, @NonNull final String transferMethodType, @NonNull final String transferMethodToken); + + void handleUnmappedFieldError(@NonNull final Map fieldSet, + @NonNull final List errors); + } +} diff --git a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/UpdateTransferMethodFragment.java b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/UpdateTransferMethodFragment.java new file mode 100644 index 000000000..49ad03ac6 --- /dev/null +++ b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/UpdateTransferMethodFragment.java @@ -0,0 +1,689 @@ +package com.hyperwallet.android.ui.transfermethod.view; + +import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodFields.TYPE; +import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.BANK_ACCOUNT; +import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.BANK_CARD; +import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.PAPER_CHECK; +import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.PAYPAL_ACCOUNT; +import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.VENMO_ACCOUNT; +import static com.hyperwallet.android.model.transfermethod.TransferMethod.TransferMethodTypes.WIRE_ACCOUNT; +import static com.hyperwallet.android.ui.common.intent.HyperwalletIntent.EXTRA_TRANSFER_METHOD_UPDATED; +import static com.hyperwallet.android.ui.transfermethod.TransferMethodLocalBroadcast.TransferMethodLocalBroadcastAction.ACTION_HYPERWALLET_TRANSFER_METHOD_UPDATED; +import static com.hyperwallet.android.ui.transfermethod.view.FeeFormatter.isFeeAvailable; +import static com.hyperwallet.android.ui.transfermethod.view.FeeFormatter.isProcessingTimeAvailable; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.widget.NestedScrollView; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentTransaction; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.hyperwallet.android.exception.HyperwalletException; +import com.hyperwallet.android.model.Error; +import com.hyperwallet.android.model.graphql.Fee; +import com.hyperwallet.android.model.graphql.HyperwalletTransferMethodConfigurationField; +import com.hyperwallet.android.model.graphql.ProcessingTime; +import com.hyperwallet.android.model.graphql.field.Field; +import com.hyperwallet.android.model.graphql.field.FieldGroup; +import com.hyperwallet.android.model.graphql.field.TransferMethodConfiguration; +import com.hyperwallet.android.model.transfermethod.BankAccount; +import com.hyperwallet.android.model.transfermethod.BankCard; +import com.hyperwallet.android.model.transfermethod.PaperCheck; +import com.hyperwallet.android.model.transfermethod.PayPalAccount; +import com.hyperwallet.android.model.transfermethod.TransferMethod; +import com.hyperwallet.android.model.transfermethod.VenmoAccount; +import com.hyperwallet.android.ui.R; +import com.hyperwallet.android.ui.common.insight.HyperwalletInsight; +import com.hyperwallet.android.ui.common.util.ErrorTypes; +import com.hyperwallet.android.ui.common.util.PageGroups; +import com.hyperwallet.android.ui.common.view.TransferMethodUtils; +import com.hyperwallet.android.ui.transfermethod.TransferMethodLocalBroadcast; +import com.hyperwallet.android.ui.transfermethod.repository.TransferMethodRepositoryFactory; +import com.hyperwallet.android.ui.transfermethod.view.widget.AbstractWidget; +import com.hyperwallet.android.ui.transfermethod.view.widget.DateChangedListener; +import com.hyperwallet.android.ui.transfermethod.view.widget.DateWidget; +import com.hyperwallet.android.ui.transfermethod.view.widget.WidgetEventListener; +import com.hyperwallet.android.ui.transfermethod.view.widget.WidgetFactory; +import com.hyperwallet.android.ui.transfermethod.view.widget.WidgetInputState; + +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.TreeMap; + +public class UpdateTransferMethodFragment extends Fragment implements WidgetEventListener, + UpdateTransferMethodContract.View { + + public static final String TAG = UpdateTransferMethodActivity.TAG; + + private static final String ARGUMENT_TRANSFER_METHOD_TOKEN = "ARGUMENT_TRANSFER_METHOD_TOKEN"; + private static final String ARGUMENT_SHOW_UPDATE_PROGRESS_BAR = "ARGUMENT_SHOW_UPDATE_PROGRESS_BAR"; + private static final String ARGUMENT_WIDGET_STATE_MAP = "ARGUMENT_WIDGET_STATE_MAP"; + private static final boolean FORCE_UPDATE = false; + private View mUpdateButtonProgressBar; + private Button mUpdateTransferMethodButton; + private ViewGroup mDynamicContainer; + private NestedScrollView mNestedScrollView; + private OnUpdateTransferMethodNetworkErrorCallback mOnUpdateTransferMethodNetworkErrorCallback; + private OnLoadTransferMethodConfigurationFieldsNetworkErrorCallback + mOnLoadTransferMethodConfigurationFieldsNetworkErrorCallback; + private UpdateTransferMethodContract.Presenter mPresenter; + private View mProgressBar; + private boolean mUpdateProgressBar; + private String mTransferMethodType; + private TransferMethod mTransferMethod; + private String mTransferMethodToken; + private HashMap mWidgetInputStateHashMap; + private boolean isEdited; + + /** + * Please do not use this to have instance of UpdateTransferMethodFragment this is reserved for android framework + */ + public UpdateTransferMethodFragment() { + } + + /** + * Creates new instance of UpdateTransferMethodFragment this is the proper initialization of this class + * since the default constructor is reserved for android framework when lifecycle is triggered. + * The parameters in {@link UpdateTransferMethodFragment#newInstance(String)} is mandatory + * and should be supplied with correct data or this fragment will not initialize properly. + * + * @param transferMethodToken the country selected when creating transfer method + */ + public static UpdateTransferMethodFragment newInstance(@NonNull String transferMethodToken) { + UpdateTransferMethodFragment updateTransferMethodFragment = new UpdateTransferMethodFragment(); + Bundle arguments = new Bundle(); + + updateTransferMethodFragment.mTransferMethodToken = transferMethodToken; + updateTransferMethodFragment.mWidgetInputStateHashMap = new HashMap<>(1); + updateTransferMethodFragment.mTransferMethod = null; + arguments.putString(ARGUMENT_TRANSFER_METHOD_TOKEN, transferMethodToken); + arguments.putSerializable(ARGUMENT_WIDGET_STATE_MAP, updateTransferMethodFragment.mWidgetInputStateHashMap); + updateTransferMethodFragment.setArguments(arguments); + + return updateTransferMethodFragment; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + + try { + mOnUpdateTransferMethodNetworkErrorCallback = (OnUpdateTransferMethodNetworkErrorCallback) context; + } catch (ClassCastException e) { + throw new ClassCastException(getActivity().toString() + " must implement " + + OnUpdateTransferMethodNetworkErrorCallback.class.getCanonicalName()); + } + + try { + mOnLoadTransferMethodConfigurationFieldsNetworkErrorCallback = + (OnLoadTransferMethodConfigurationFieldsNetworkErrorCallback) context; + } catch (ClassCastException e) { + throw new ClassCastException(getActivity().toString() + " must implement " + + OnLoadTransferMethodConfigurationFieldsNetworkErrorCallback.class.getCanonicalName()); + } + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + } + + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_update_transfer_method, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + mDynamicContainer = view.findViewById(R.id.update_transfer_method_dynamic_container); + mNestedScrollView = view.findViewById(R.id.update_transfer_method_scroll_view); + + mUpdateButtonProgressBar = view.findViewById(R.id.update_transfer_method_button_progress_bar); + mProgressBar = view.findViewById(R.id.update_transfer_method_progress_bar_layout); + mUpdateTransferMethodButton = view.findViewById(R.id.update_transfer_method_button); + + mUpdateTransferMethodButton.setBackgroundColor(getResources().getColor(R.color.colorPrimary)); + mUpdateTransferMethodButton.setTextColor(getResources().getColor(R.color.regularColorPrimary)); + mUpdateTransferMethodButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + HyperwalletInsight.getInstance().trackClick(requireContext(), + TAG, PageGroups.TRANSFER_METHOD, + HyperwalletInsight.LINK_SELECT_TRANSFER_METHOD_CREATE, + new HyperwalletInsight.TransferMethodParamsBuilder() + .type(mTransferMethodType) + .build()); + + triggerUpdate(); + } + }); + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + TransferMethodRepositoryFactory factory = TransferMethodRepositoryFactory.getInstance(); + mPresenter = new UpdateTransferMethodPresenter(this, + factory.getTransferMethodUpdateConfigurationRepository(), + factory.getTransferMethodRepository()); + } + + @Override + public void onViewStateRestored(@Nullable Bundle savedInstanceState) { + super.onViewStateRestored(savedInstanceState); + + if (savedInstanceState != null) { + mWidgetInputStateHashMap = (HashMap) savedInstanceState.getSerializable(ARGUMENT_WIDGET_STATE_MAP); + mTransferMethodToken = savedInstanceState.getString(ARGUMENT_TRANSFER_METHOD_TOKEN); + mUpdateProgressBar = savedInstanceState.getBoolean(ARGUMENT_SHOW_UPDATE_PROGRESS_BAR); + mTransferMethod = savedInstanceState.getParcelable(ARGUMENT_TRANSFER_METHOD_TOKEN); + } else { // same as UpdateTransferMethodFragment#newInstance + mWidgetInputStateHashMap = (HashMap) getArguments().getSerializable(ARGUMENT_WIDGET_STATE_MAP); + mTransferMethodToken = getArguments().getString(ARGUMENT_TRANSFER_METHOD_TOKEN); + } + } + + @Override + public void onResume() { + super.onResume(); + mPresenter.loadTransferMethodConfigurationFields(FORCE_UPDATE, mTransferMethodType, mTransferMethodToken); + } + + @Override + public void showErrorUpdateTransferMethod(@NonNull final List errors) { + mOnUpdateTransferMethodNetworkErrorCallback.showErrorsUpdateTransferMethod(errors); + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + outState.putSerializable(ARGUMENT_WIDGET_STATE_MAP, mWidgetInputStateHashMap); + outState.putString(ARGUMENT_TRANSFER_METHOD_TOKEN, mTransferMethodToken); + outState.putBoolean(ARGUMENT_SHOW_UPDATE_PROGRESS_BAR, mUpdateProgressBar); + super.onSaveInstanceState(outState); + } + + private void triggerUpdate() { + hideSoftKeys(); + if (performValidation()) { + switch (mTransferMethodType) { + case BANK_ACCOUNT: + mTransferMethod = new BankAccount.Builder() + .token(mTransferMethodToken) + .build(); + break; + case BANK_CARD: + mTransferMethod = new BankCard.Builder() + .token(mTransferMethodToken) + .build(); + break; + case PAYPAL_ACCOUNT: + mTransferMethod = new PayPalAccount.Builder() + .token(mTransferMethodToken) + .build(); + break; + case WIRE_ACCOUNT: + mTransferMethod = new BankAccount.Builder() + .token(mTransferMethodToken) + .transferMethodType(WIRE_ACCOUNT) + .build(); + break; + case VENMO_ACCOUNT: + mTransferMethod = new VenmoAccount.Builder() + .token(mTransferMethodToken) + .build(); + break; + case PAPER_CHECK: + mTransferMethod = new PaperCheck.Builder() + .token(mTransferMethodToken) + .build(); + break; + default: + mTransferMethod = new TransferMethod(); + mTransferMethod.setField(TYPE, mTransferMethodType); + } + + for (int i = 0; i < mDynamicContainer.getChildCount(); i++) { + View view = mDynamicContainer.getChildAt(i); + if (view.getTag() instanceof AbstractWidget) { + AbstractWidget widget = (AbstractWidget) view.getTag(); + if (widget.isEdited) { + isEdited = true; + mTransferMethod.setField(widget.getName(), widget.getValue()); + } + } + } + + if (isEdited) { + mPresenter.updateTransferMethod(mTransferMethod); + } else { + getActivity().finish(); + } + } + } + + private void hideSoftKeys() { + View view = requireActivity().getCurrentFocus(); + + if (view != null) { + view.clearFocus(); + InputMethodManager inputMethodManager = (InputMethodManager) view.getContext().getSystemService( + Activity.INPUT_METHOD_SERVICE); + inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + } + + /** + * Use this to perform validation on an entire form, typically used during form submission. + * + * @return true if the form is valid + */ + private boolean performValidation() { + boolean containsInvalidWidget = false; + + // this is added since some phones triggers the create button but the widgets are not yet initialized + boolean hasWidget = false; + Resources resources = requireContext().getResources(); + int pixels = (int) (resources.getDimension(R.dimen.negative_padding) * resources.getDisplayMetrics().density); + + for (int i = 0; i < mDynamicContainer.getChildCount(); i++) { + View currentView = mDynamicContainer.getChildAt(i); + if (currentView.getTag() instanceof AbstractWidget) { + hasWidget = true; + + AbstractWidget widget = (AbstractWidget) currentView.getTag(); + WidgetInputState widgetInputState = mWidgetInputStateHashMap.get(widget.getName()); + widgetInputState.setValue(widget.getValue()); + + if (!isWidgetItemValid(widget) && !containsInvalidWidget) { + containsInvalidWidget = true; + mNestedScrollView.smoothScrollTo(0, currentView.getTop() - pixels); + } + } + } + return hasWidget && !containsInvalidWidget; + } + + /** + * Use this to perform validation on a single widget item, typically used while the user is inputting data. + * + * @param widget the widget to validate + * @return true if the input is valid + */ + private boolean isWidgetItemValid(@NonNull final AbstractWidget widget) { + boolean valid = true; + Context context = requireContext(); + + WidgetInputState widgetInputState = mWidgetInputStateHashMap.get(widget.getName()); + widgetInputState.setValue(widget.getValue()); + if (widget.isValid()) { + if (!widgetInputState.hasApiError()) { + widgetInputState.setErrorMessage(null); + widget.showValidationError(null); + } + } else { + HyperwalletInsight.getInstance().trackError(context, + TAG, PageGroups.TRANSFER_METHOD, + new HyperwalletInsight.ErrorParamsBuilder() + .message(widget.getErrorMessage()) + .fieldName(widget.getName()) + .type(ErrorTypes.FORM_ERROR) + .addAll(new HyperwalletInsight.TransferMethodParamsBuilder() + .type(mTransferMethodType) + .build()) + .build()); + + valid = false; + widget.showValidationError(null); + widgetInputState.setErrorMessage(null); + widget.showValidationError(widget.getErrorMessage()); + widgetInputState.setErrorMessage(widget.getErrorMessage()); + widgetInputState.setHasApiError(false); + } + return valid; + } + + @Override + public void notifyTransferMethodUpdated(@NonNull TransferMethod transferMethod) { + HyperwalletInsight.getInstance().trackImpression(requireContext(), + TAG, PageGroups.TRANSFER_METHOD, + new HyperwalletInsight.TransferMethodParamsBuilder() + .goal(HyperwalletInsight.TRANSFER_METHOD_GOAL) + .type(mTransferMethodType) + .build()); + + Intent intent = TransferMethodLocalBroadcast.createBroadcastIntentTransferMethodUpdated( + transferMethod); + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + + Intent activityResult = new Intent(); + activityResult.setAction(ACTION_HYPERWALLET_TRANSFER_METHOD_UPDATED); + activityResult.putExtra(EXTRA_TRANSFER_METHOD_UPDATED, transferMethod); + getActivity().setResult(Activity.RESULT_OK, activityResult); + getActivity().finish(); + } + + @Override + public void showErrorLoadTransferMethodConfigurationFields(@NonNull List errors) { + mOnLoadTransferMethodConfigurationFieldsNetworkErrorCallback.showErrorsLoadTransferMethodConfigurationFields( + errors); + } + + private String getSectionHeaderText(@NonNull final FieldGroup group, @NonNull final Locale locale, + @NonNull final String currency) { + if (FieldGroup.GroupTypes.ACCOUNT_INFORMATION.equals(group.getGroupName())) { + return requireContext().getString(R.string.account_information, + locale.getDisplayName().toUpperCase(), currency); + } + + return requireContext().getString(requireContext().getResources() + .getIdentifier(group.getGroupName().toLowerCase(Locale.ROOT), "string", + requireContext().getPackageName())); + } + + @Override + public void showTransactionInformation(@NonNull List fees, + @Nullable ProcessingTime processingTime) { + View header = getView().findViewById(R.id.update_transfer_method_static_container_header); + View container = getView().findViewById(R.id.update_transfer_method_static_container); + TextView feeAndProcessingTime = getView().findViewById(R.id.update_transfer_method_information); + + if (isFeeAvailable(fees) && isProcessingTimeAvailable(processingTime)) { + String formattedFee = FeeFormatter.getFormattedFee(header.getContext(), fees); + feeAndProcessingTime.setVisibility(View.VISIBLE); + feeAndProcessingTime.setText( + feeAndProcessingTime.getContext().getString(R.string.feeAndProcessingTimeInformation, formattedFee, + processingTime.getValue())); + } else if (isFeeAvailable(fees) && !isProcessingTimeAvailable(processingTime)) { + String formattedFee = FeeFormatter.getFormattedFee(header.getContext(), fees); + feeAndProcessingTime.setVisibility(View.VISIBLE); + feeAndProcessingTime.setText( + feeAndProcessingTime.getContext().getString(R.string.feeInformation, formattedFee)); + } else if (isProcessingTimeAvailable(processingTime) && !isFeeAvailable(fees)) { + feeAndProcessingTime.setVisibility(View.VISIBLE); + feeAndProcessingTime.setText(processingTime.getValue()); + } else { + feeAndProcessingTime.setVisibility(View.GONE); + } + + if (feeAndProcessingTime.getVisibility() == View.VISIBLE) { + header.setVisibility(View.VISIBLE); + container.setVisibility(View.VISIBLE); + } else { + header.setVisibility(View.GONE); + container.setVisibility(View.GONE); + } + } + + @Override + public void showTransferMethodFields( + @NonNull HyperwalletTransferMethodConfigurationField hyperwalletTransferMethodConfigurationField) { + mDynamicContainer.removeAllViews(); + mUpdateTransferMethodButton.setVisibility(View.VISIBLE); + + try { + TransferMethodConfiguration fields = hyperwalletTransferMethodConfigurationField.getFields(); + Locale locale = new Locale.Builder().setRegion(fields.getCountry()).build(); + mTransferMethodType = fields.getTransferMethodType(); + String transferMethod = TransferMethodUtils.getTransferMethodName(getContext(), mTransferMethodType); + ((UpdateTransferMethodActivity) getActivity()).getSupportActionBar().setTitle(transferMethod); + // group + for (FieldGroup group : fields.getFieldGroups()) { + View sectionHeader = LayoutInflater.from(mDynamicContainer.getContext()) + .inflate(R.layout.item_widget_section_header, mDynamicContainer, false); + TextView sectionTitle = sectionHeader.findViewById(R.id.section_header_title); + sectionTitle.setText(getSectionHeaderText(group, locale, fields.getCurrency())); + sectionHeader.setId(View.generateViewId()); + mDynamicContainer.addView(sectionHeader); + + // group fields + for (final Field field : group.getFields()) { + AbstractWidget widget = WidgetFactory + .newWidget(field, this, mWidgetInputStateHashMap.containsKey(field.getName()) ? + mWidgetInputStateHashMap.get(field.getName()).getValue() : field.getValue(), + mUpdateTransferMethodButton); + if (mWidgetInputStateHashMap.isEmpty() || !mWidgetInputStateHashMap.containsKey(widget.getName())) { + mWidgetInputStateHashMap.put(widget.getName(), widget.getWidgetInputState()); + } + + View widgetView = widget.getView(mDynamicContainer); + widgetView.setTag(widget); + widgetView.setId(View.generateViewId()); + final String error = mWidgetInputStateHashMap.get(widget.getName()).getErrorMessage(); + widget.showValidationError(error); + mDynamicContainer.addView(widgetView); + } + } + + HyperwalletInsight.getInstance().trackImpression(requireContext(), + TAG, PageGroups.TRANSFER_METHOD, + new HyperwalletInsight.TransferMethodParamsBuilder() + .type(mTransferMethodType) + .build()); + + if (mUpdateProgressBar) { + setVisibleAndDisableFields(); + } + } catch (HyperwalletException e) { + throw new IllegalStateException("Widget initialization error: " + e.getMessage()); + } + } + + @Override + public void showUpdateButtonProgressBar() { + mUpdateProgressBar = true; + setVisibleAndDisableFields(); + } + + private void setVisibleAndDisableFields() { + getActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, + WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); + getActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); + mUpdateButtonProgressBar.setVisibility(View.VISIBLE); + mUpdateTransferMethodButton.setBackgroundColor(getResources().getColor(R.color.colorSecondaryDark)); + } + + @Override + public void hideUpdateButtonProgressBar() { + mUpdateProgressBar = false; + getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); + getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); + mUpdateButtonProgressBar.setVisibility(View.GONE); + mUpdateTransferMethodButton.setBackgroundColor(getResources().getColor(R.color.colorPrimary)); + mUpdateTransferMethodButton.setTextColor(getResources().getColor(R.color.regularColorPrimary)); + } + + @Override + public void showProgressBar() { + mProgressBar.setVisibility(View.VISIBLE); + } + + @Override + public void hideProgressBar() { + mProgressBar.setVisibility(View.GONE); + mUpdateTransferMethodButton.setVisibility(View.VISIBLE); + } + + @Override + public void showInputErrors(@NonNull List errors) { + boolean focusSet = false; + Context context = requireContext(); + Resources resources = context.getResources(); + int pixels = (int) (resources.getDimension(R.dimen.negative_padding) * resources.getDisplayMetrics().density); + + for (Error error : errors) { + for (int i = 0; i < mDynamicContainer.getChildCount(); i++) { + View view = mDynamicContainer.getChildAt(i); + if (view.getTag() instanceof AbstractWidget) { + AbstractWidget widget = (AbstractWidget) view.getTag(); + WidgetInputState widgetInputState = mWidgetInputStateHashMap.get(widget.getName()); + if (widget.getName().equals(error.getFieldName())) { + if (!focusSet) { + mNestedScrollView.smoothScrollTo(0, view.getTop() - pixels); + focusSet = true; + } + HyperwalletInsight.getInstance().trackError(context, + TAG, PageGroups.TRANSFER_METHOD, + new HyperwalletInsight.ErrorParamsBuilder() + .code(error.getCode()) + .message(error.getMessage()) + .fieldName(error.getFieldName()) + .type(ErrorTypes.API_ERROR) + .build()); + + widget.showValidationError(null); + widgetInputState.setErrorMessage(null); + widget.showValidationError(error.getMessage()); + widgetInputState.setErrorMessage(error.getMessage()); + widgetInputState.setHasApiError(true); + } else { + widget.showValidationError(null); + widgetInputState.setErrorMessage(null); + } + } + } + } + + mPresenter.handleUnmappedFieldError(mWidgetInputStateHashMap, errors); + } + + @Override + public boolean isActive() { + return isAdded(); + } + + @Override + public void retryUpdateTransferMethod() { + mPresenter.updateTransferMethod(mTransferMethod); + } + + @Override + public void reloadTransferMethodConfigurationFields() { + mPresenter.loadTransferMethodConfigurationFields(FORCE_UPDATE, mTransferMethodType, mTransferMethodToken); + } + + @Override + public void valueChanged(@NonNull AbstractWidget widget) { + isWidgetItemValid(widget); + } + + @Override + public boolean isWidgetSelectionFragmentDialogOpen() { + return getFragmentManager().findFragmentByTag(WidgetSelectionDialogFragment.TAG) != null; + } + + @Override + public void openWidgetSelectionFragmentDialog(@NonNull TreeMap nameValueMap, + @NonNull String selectedName, @NonNull String fieldLabel, @NonNull String fieldName) { + String selectedLabel = selectedName; + if (TextUtils.isEmpty(selectedLabel)) { + selectedLabel = mWidgetInputStateHashMap.get(fieldName).getSelectedName(); + } else { + mWidgetInputStateHashMap.get(fieldName).setSelectedName(selectedLabel); + } + + if (!isWidgetSelectionFragmentDialogOpen()) { + WidgetSelectionDialogFragment widgetSelectionDialogFragment = WidgetSelectionDialogFragment + .newInstance(nameValueMap, selectedLabel, fieldLabel, fieldName); + + FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); + fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); + fragmentTransaction.replace(android.R.id.content, widgetSelectionDialogFragment, + WidgetSelectionDialogFragment.TAG); + fragmentTransaction.addToBackStack(WidgetSelectionDialogFragment.TAG); + fragmentTransaction.commit(); + } + } + + @Override + public void widgetFocused(@NonNull String fieldName) { + WidgetInputState widgetInputState = mWidgetInputStateHashMap.get(fieldName); + widgetInputState.setHasFocused(true); + } + + @Override + public void saveTextChanged(@NonNull String fieldName, @NonNull String value) { + WidgetInputState inputState = mWidgetInputStateHashMap.get(fieldName); + if (inputState.hasApiError()) { + String oldValue = inputState.getValue(); + if (!TextUtils.isEmpty(oldValue) && !oldValue.equals(value)) { + inputState.setHasApiError(false); + } + } + inputState.setValue(value); + } + + void onWidgetSelectionItemClicked(@NonNull final String selectedValue, @NonNull final String fieldName) { + for (int i = 0; i < mDynamicContainer.getChildCount(); i++) { + View view = mDynamicContainer.getChildAt(i); + if (view.getTag() instanceof WidgetSelectionDialogFragment.WidgetSelectionItemType) { + AbstractWidget widget = (AbstractWidget) view.getTag(); + if (fieldName.equals(widget.getName())) { + ((WidgetSelectionDialogFragment.WidgetSelectionItemType) view.getTag()) + .onWidgetSelectionItemClicked(selectedValue); + return; + } + } + } + } + + @Override + public void openWidgetDateDialog(@Nullable String date, @NonNull String fieldName) { + if (getFragmentManager() != null) { + WidgetDateDialogFragment dateDialogFragment = (WidgetDateDialogFragment) + getFragmentManager().findFragmentByTag(WidgetDateDialogFragment.TAG); + + if (dateDialogFragment == null) { + dateDialogFragment = WidgetDateDialogFragment.newInstance(date, fieldName); + } + + if (!dateDialogFragment.isAdded()) { + dateDialogFragment.show(getFragmentManager()); + } + } + } + + void onDateSelected(@NonNull final String selectedValue, @NonNull final String fieldName) { + for (int i = 0; i < mDynamicContainer.getChildCount(); i++) { + View view = mDynamicContainer.getChildAt(i); + if (view.getTag() instanceof DateWidget) { + AbstractWidget widget = (AbstractWidget) view.getTag(); + if (fieldName.equals(widget.getName()) && widget instanceof DateChangedListener) { + ((DateChangedListener) view.getTag()).onUpdate(selectedValue); + return; + } + } + } + } + + interface OnLoadTransferMethodConfigurationFieldsNetworkErrorCallback { + void showErrorsLoadTransferMethodConfigurationFields(@NonNull final List errors); + } + + interface OnUpdateTransferMethodNetworkErrorCallback { + void showErrorsUpdateTransferMethod(@NonNull final List errors); + } +} diff --git a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/UpdateTransferMethodPresenter.java b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/UpdateTransferMethodPresenter.java new file mode 100644 index 000000000..3194f6055 --- /dev/null +++ b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/UpdateTransferMethodPresenter.java @@ -0,0 +1,109 @@ +package com.hyperwallet.android.ui.transfermethod.view; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.hyperwallet.android.model.Error; +import com.hyperwallet.android.model.Errors; +import com.hyperwallet.android.model.graphql.HyperwalletTransferMethodConfigurationField; +import com.hyperwallet.android.model.transfermethod.TransferMethod; +import com.hyperwallet.android.ui.R; +import com.hyperwallet.android.ui.transfermethod.repository.TransferMethodRepository; +import com.hyperwallet.android.ui.transfermethod.repository.TransferMethodUpdateConfigurationRepository; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class UpdateTransferMethodPresenter implements UpdateTransferMethodContract.Presenter { + private static final String ERROR_UNMAPPED_FIELD = "ERROR_UNMAPPED_FIELD"; + private final TransferMethodUpdateConfigurationRepository mTransferMethodUpdateConfigurationRepository; + private final TransferMethodRepository mTransferMethodRepository; + private final UpdateTransferMethodContract.View mView; + + public UpdateTransferMethodPresenter(UpdateTransferMethodContract.View view, + TransferMethodUpdateConfigurationRepository transferMethodUpdateConfigurationRepository, + TransferMethodRepository transferMethodRepository) { + mView = view; + mTransferMethodUpdateConfigurationRepository = transferMethodUpdateConfigurationRepository; + mTransferMethodRepository = transferMethodRepository; + } + + @Override + public void loadTransferMethodConfigurationFields(boolean forceUpdate, @NonNull String transferMethodType, + @NonNull final String transferMethodToken) { + mView.showProgressBar(); + + if (forceUpdate) { + mTransferMethodUpdateConfigurationRepository.refreshFields(); + } + + mTransferMethodUpdateConfigurationRepository.getFields(transferMethodToken, + new TransferMethodUpdateConfigurationRepository.LoadFieldsCallback() { + @Override + public void onFieldsLoaded(@Nullable HyperwalletTransferMethodConfigurationField field) { + if (!mView.isActive()) { + return; + } + + mView.hideProgressBar(); + mView.showTransferMethodFields(field); + // there can be multiple fees when we have flat fee + percentage fees + mView.showTransactionInformation(field.getFees(), field.getProcessingTime()); + } + + @Override + public void onError(@NonNull Errors errors) { + if (!mView.isActive()) { + return; + } + mView.hideProgressBar(); + mView.showErrorLoadTransferMethodConfigurationFields(errors.getErrors()); + } + }); + } + + @Override + public void updateTransferMethod(@NonNull TransferMethod transferMethod) { + mView.showUpdateButtonProgressBar(); + mTransferMethodRepository.updateTransferMethod(transferMethod, + new TransferMethodRepository.LoadTransferMethodCallback() { + @Override + public void onTransferMethodLoaded(TransferMethod transferMethod) { + + if (!mView.isActive()) { + return; + } + mView.hideUpdateButtonProgressBar(); + mView.notifyTransferMethodUpdated(transferMethod); + } + + @Override + public void onError(Errors errors) { + if (!mView.isActive()) { + return; + } + + mView.hideUpdateButtonProgressBar(); + if (errors.containsInputError()) { + mView.showInputErrors(errors.getErrors()); + } else { + mView.showErrorUpdateTransferMethod(errors.getErrors()); + } + } + }); + } + + @Override + public void handleUnmappedFieldError(@NonNull Map fieldSet, @NonNull List errors) { + for (Error error : errors) { + if (fieldSet.get(error.getFieldName()) == null) { + List errorList = new ArrayList() {{ + add(new Error(R.string.error_unmapped_field, ERROR_UNMAPPED_FIELD)); + }}; + mView.showErrorUpdateTransferMethod(errorList); + return; + } + } + } +} diff --git a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/AbstractMaskedInputWidget.java b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/AbstractMaskedInputWidget.java index 72cf67afa..87218b628 100644 --- a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/AbstractMaskedInputWidget.java +++ b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/AbstractMaskedInputWidget.java @@ -69,7 +69,7 @@ String formatToApi(@NonNull final String displayValue) { * @return a String formatted to the specification in the mask pattern */ String formatToDisplay(@NonNull final String apiValue) { - if (mField != null && mField.getMask() != null) { + if (mField != null && mField.getMask() != null && !mField.isFieldValueMasked()) { // format String pattern = mField.getMask().getPattern(apiValue); if (!TextUtils.isEmpty(pattern)) { diff --git a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/AbstractWidget.java b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/AbstractWidget.java index 3db0c5618..0e9cb8311 100644 --- a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/AbstractWidget.java +++ b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/AbstractWidget.java @@ -36,6 +36,7 @@ public abstract class AbstractWidget { protected final WidgetEventListener mListener; protected int mBottomViewId = 0; protected WidgetInputState mWidgetInputState; + public boolean isEdited = false; public AbstractWidget(@Nullable Field field, @NonNull WidgetEventListener listener, @Nullable String defaultValue, @NonNull View defaultFocusView) { @@ -64,6 +65,9 @@ public boolean isValid() { if (mField == null) { return true; } + else if(!isEdited && mField.isFieldValueMasked()) { + return true; + } return !isInvalidEmptyValue() && !isInvalidLength() && !isInvalidRegex(); } @@ -163,6 +167,7 @@ public DefaultKeyListener(View focusView, View clearFocusView) { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { + isEdited = true; if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (keyCode) { case KeyEvent.KEYCODE_DPAD_CENTER: diff --git a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/ExpireDateUtils.java b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/ExpireDateUtils.java index d26dbd81b..697c890de 100644 --- a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/ExpireDateUtils.java +++ b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/ExpireDateUtils.java @@ -154,8 +154,11 @@ private Calendar getInputDate(@NonNull final String input) throws ParseException //get month from server month part private String getMonthFromServer(String[] splitDate) { - return splitDate.length != 2 ? "" : - splitDate[1].length() == 1 && Integer.parseInt(splitDate[1]) > 1 ? - ZERO.concat(splitDate[1]) : splitDate[1]; + if (splitDate.length >= 2) { + return splitDate[1].length() == 1 && Integer.parseInt(splitDate[1]) > 1 ? ZERO.concat(splitDate[1]) + : splitDate[1]; + } else { + return ""; + } } } \ No newline at end of file diff --git a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/NumberWidget.java b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/NumberWidget.java index 2b1e5a70e..690347109 100644 --- a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/NumberWidget.java +++ b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/NumberWidget.java @@ -57,9 +57,9 @@ public View getView(@NonNull final ViewGroup viewGroup) { editText.setTextColor(viewGroup.getContext().getResources().getColor(R.color.regularColorSecondary)); editText.setEnabled(mField.isEditable()); + editText.setSelectAllOnFocus(mField.isFieldValueMasked()); setIdFromFieldLabel(mTextInputLayout); setIdFromFieldName(editText); - editText.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { diff --git a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/SelectionWidget.java b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/SelectionWidget.java index 2fe33c069..93510d78f 100644 --- a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/SelectionWidget.java +++ b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/SelectionWidget.java @@ -144,6 +144,7 @@ public void onWidgetSelectionItemClicked(@NonNull String selectedValue) { mListener.valueChanged(SelectionWidget.this); mEditText.setText(getKeyFromValue(selectedValue)); mEditText.requestFocus(); + isEdited = true; } private void hideSoftKey(@NonNull View focusedView) { diff --git a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/TextWidget.java b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/TextWidget.java index 4e8994718..7b99fbacb 100644 --- a/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/TextWidget.java +++ b/transfermethodui/src/main/java/com/hyperwallet/android/ui/transfermethod/view/widget/TextWidget.java @@ -54,7 +54,7 @@ public View getView(@NonNull final ViewGroup viewGroup) { new ContextThemeWrapper(viewGroup.getContext(), R.style.Widget_Hyperwallet_TextInputEditText)); editText.setTextColor(viewGroup.getContext().getResources().getColor(R.color.regularColorSecondary)); editText.setEnabled(mField.isEditable()); - + editText.setSelectAllOnFocus(mField.isFieldValueMasked()); mTextInputLayout.setHint(mField.getLabel()); setIdFromFieldLabel(mTextInputLayout); setIdFromFieldName(editText); diff --git a/transfermethodui/src/main/res/layout/activity_add_transfer_method.xml b/transfermethodui/src/main/res/layout/activity_add_transfer_method.xml index 7661e177a..038b0ace1 100644 --- a/transfermethodui/src/main/res/layout/activity_add_transfer_method.xml +++ b/transfermethodui/src/main/res/layout/activity_add_transfer_method.xml @@ -19,6 +19,7 @@ android:theme="@style/AppTheme.AppBarOverlay"> + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/transfermethodui/src/main/res/layout/fragment_update_transfer_method.xml b/transfermethodui/src/main/res/layout/fragment_update_transfer_method.xml new file mode 100644 index 000000000..47ee191f6 --- /dev/null +++ b/transfermethodui/src/main/res/layout/fragment_update_transfer_method.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +