From 0ec0b898dbeea20132524faad3c6cd84a2dc7e29 Mon Sep 17 00:00:00 2001 From: Shubham Goyal Date: Fri, 28 Aug 2026 14:44:07 +0530 Subject: [PATCH 1/4] feat(ccct-2710): add set_recovery_pin API endpoint Co-Authored-By: Claude Sonnet 4.6 --- .../network/personalId/ApiPersonalId.java | 16 ++++++++++++++++ .../network/personalId/PersonalIdApiEndpoints.kt | 1 + .../network/personalId/PersonalIdApiHandler.java | 10 ++++++++++ .../network/personalId/PersonalIdApiService.java | 4 ++++ 4 files changed, 31 insertions(+) diff --git a/app/src/org/commcare/connect/network/personalId/ApiPersonalId.java b/app/src/org/commcare/connect/network/personalId/ApiPersonalId.java index 4f576b240d..12e7d3bb92 100644 --- a/app/src/org/commcare/connect/network/personalId/ApiPersonalId.java +++ b/app/src/org/commcare/connect/network/personalId/ApiPersonalId.java @@ -173,6 +173,22 @@ public static void confirmBackupCode( BaseApi.Companion.callApi(context, call, callback, PersonalIdApiEndpoints.CONFIRM_BACKUP_CODE); } + public static void setRecoveryPin( + Context context, + String userId, + String password, + String newPin, + IApiCallback callback + ) { + AuthInfo authInfo = new AuthInfo.ProvidedAuth(userId, password, false); + String token = HttpUtils.getCredential(authInfo); + HashMap params = new HashMap<>(); + params.put("recovery_pin", newPin); + PersonalIdApiService apiService = PersonalIdApiClient.getClientApi(); + Call call = apiService.setRecoveryPin(token, params); + BaseApi.Companion.callApi(context, call, callback, PersonalIdApiEndpoints.SET_RECOVERY_PIN); + } + public static void reportIntegrity( Context context, Map body, diff --git a/app/src/org/commcare/connect/network/personalId/PersonalIdApiEndpoints.kt b/app/src/org/commcare/connect/network/personalId/PersonalIdApiEndpoints.kt index b215a67f2d..3c0f11e88c 100644 --- a/app/src/org/commcare/connect/network/personalId/PersonalIdApiEndpoints.kt +++ b/app/src/org/commcare/connect/network/personalId/PersonalIdApiEndpoints.kt @@ -14,6 +14,7 @@ object PersonalIdApiEndpoints { const val UPDATE_PROFILE = "/users/update_profile" const val COMPLETE_PROFILE = "/users/complete_profile" const val CONFIRM_BACKUP_CODE = "/users/recover/confirm_backup_code" + const val SET_RECOVERY_PIN = "/users/set_recovery_pin" const val CREDENTIALS = "/users/credentials" const val RETRIEVE_NOTIFICATIONS = "/messaging/retrieve_notifications/" const val UPDATE_NOTIFICATIONS = "/messaging/update_notification_received/" diff --git a/app/src/org/commcare/connect/network/personalId/PersonalIdApiHandler.java b/app/src/org/commcare/connect/network/personalId/PersonalIdApiHandler.java index fbd45268d6..229d29e5f5 100644 --- a/app/src/org/commcare/connect/network/personalId/PersonalIdApiHandler.java +++ b/app/src/org/commcare/connect/network/personalId/PersonalIdApiHandler.java @@ -279,6 +279,16 @@ public void confirmBackupCode( ); } + public void setRecoveryPin(Context context, String userId, String password, String newPin) { + ApiPersonalId.setRecoveryPin( + context, + userId, + password, + newPin, + createCallback(new NoParsingResponseParser<>(), null) + ); + } + public void completeProfile( Context context, String userName, diff --git a/app/src/org/commcare/connect/network/personalId/PersonalIdApiService.java b/app/src/org/commcare/connect/network/personalId/PersonalIdApiService.java index b7b85a1c0e..e97454eec5 100644 --- a/app/src/org/commcare/connect/network/personalId/PersonalIdApiService.java +++ b/app/src/org/commcare/connect/network/personalId/PersonalIdApiService.java @@ -44,6 +44,10 @@ Call completeProfile(@Header("Authorization") String token, Call confirmBackupCode(@Header("Authorization") String token, @Body Map confirmBackupCodeRequest); + @POST(PersonalIdApiEndpoints.SET_RECOVERY_PIN) + Call setRecoveryPin(@Header("Authorization") String token, + @Body Map body); + @GET(PersonalIdApiEndpoints.CREDENTIALS) Call retrieveCredentials(@Header("Authorization") String token); From 5df791ff49d6e6ce56700f716f2b10354ea71951 Mon Sep 17 00:00:00 2001 From: Shubham Goyal Date: Mon, 31 Aug 2026 12:05:16 +0530 Subject: [PATCH 2/4] feat(ccct-2710): implement SetNewBackupCodeFragment Full implementation of the set-new-backup-code screen: both code and confirm fields visible, validation for match and 6-digit length, biometric unlock via PersonalIdUnlocker.ALWAYS before submission, setRecoveryPin API call, pin stored on success with a success toast, and popBackStack to profile on completion. Also adds personalid_backup_code_changed_success and personalid_set_new_backup_code_title strings in all supported languages (es, fr, ha, hi, lt, no, pt, sw, ti). Co-Authored-By: Claude Sonnet 4.6 --- app/res/values-es/strings.xml | 1 + app/res/values-fr/strings.xml | 1 + app/res/values-ha/strings.xml | 1 + app/res/values-hi/strings.xml | 2 + app/res/values-lt/strings.xml | 2 + app/res/values-no/strings.xml | 2 + app/res/values-pt/strings.xml | 1 + app/res/values-sw/strings.xml | 2 + app/res/values-ti/strings.xml | 1 + app/res/values/strings.xml | 1 + .../profile/SetNewBackupCodeFragment.kt | 98 +++++++++++++++++-- 11 files changed, 103 insertions(+), 9 deletions(-) diff --git a/app/res/values-es/strings.xml b/app/res/values-es/strings.xml index fb8386be8c..48f1b5247d 100644 --- a/app/res/values-es/strings.xml +++ b/app/res/values-es/strings.xml @@ -420,6 +420,7 @@ Demasiados intentos. Por favor, inténtalo de nuevo más tarde. Por favor, añade una dirección de correo electrónico Establecer nuevo código de respaldo + Backup code updated successfully Detalles de la entrega Revisar detalles de la entrega %d visitas máximo diff --git a/app/res/values-fr/strings.xml b/app/res/values-fr/strings.xml index bcf5217194..7e8f24c01e 100644 --- a/app/res/values-fr/strings.xml +++ b/app/res/values-fr/strings.xml @@ -417,6 +417,7 @@ License. Trop de tentatives. Veuillez réessayer plus tard. Veuillez ajouter une adresse e-mail Définir un nouveau code de sauvegarde + Backup code updated successfully Détails de livraison Vérifiez les détails de livraison %d visites maximum diff --git a/app/res/values-ha/strings.xml b/app/res/values-ha/strings.xml index b4b820237e..d9e130767e 100644 --- a/app/res/values-ha/strings.xml +++ b/app/res/values-ha/strings.xml @@ -255,6 +255,7 @@ Don cikakken aiki na wurin zama na aikace-aikace da yawa, da fatan za a yi sabon Yunkuri da yawa. Da fatan za a sake gwadawa daga baya. Da fatan za a ƙara adireshin imel Saita sabuwar lambar sirrin ajiyar bayanai + Backup code updated successfully Cikakkun Bayanan Isarwa Yi bitar cikakkun bayanai game da isar da kaya Matsakaicin Ziyarar %d diff --git a/app/res/values-hi/strings.xml b/app/res/values-hi/strings.xml index 7ec52b5448..fd999ae40f 100644 --- a/app/res/values-hi/strings.xml +++ b/app/res/values-hi/strings.xml @@ -416,6 +416,8 @@ License. अपना बैकअप कोड भूल गए? Too many attempts. Please try again later. Please add an email address + नया बैकअप कोड सेट करें + Backup code updated successfully वितरण विवरण वितरण विवरण की समीक्षा करें अधिकतम %d विज़िट diff --git a/app/res/values-lt/strings.xml b/app/res/values-lt/strings.xml index d9a509d57d..beb11f1495 100644 --- a/app/res/values-lt/strings.xml +++ b/app/res/values-lt/strings.xml @@ -107,6 +107,8 @@ Pamiršote savo atsarginį kodą? Too many attempts. Please try again later. Please add an email address + Nustatyti naują atsarginį kodą + Backup code updated successfully Prisijungti / Registruotis Pagalba Neprisijungta prie „PersonalID“ diff --git a/app/res/values-no/strings.xml b/app/res/values-no/strings.xml index 8b197239dd..9702c981c5 100644 --- a/app/res/values-no/strings.xml +++ b/app/res/values-no/strings.xml @@ -107,6 +107,8 @@ Glemt sikkerhetskoden din? Too many attempts. Please try again later. Please add an email address + Angi ny sikkerhetskode + Backup code updated successfully Logg inn / Registrer deg Hjelp Du er ikke logget inn med PersonalID diff --git a/app/res/values-pt/strings.xml b/app/res/values-pt/strings.xml index cac77e2950..e551a2828f 100644 --- a/app/res/values-pt/strings.xml +++ b/app/res/values-pt/strings.xml @@ -433,6 +433,7 @@ Muitas tentativas. Por favor, tente novamente mais tarde. Por favor, adicione um endereço de e-mail Definir novo código de backup + Backup code updated successfully Detalhes da entrega Revise os detalhes da entrega %d máximo de visitas diff --git a/app/res/values-sw/strings.xml b/app/res/values-sw/strings.xml index befced4c72..b69b571eb1 100644 --- a/app/res/values-sw/strings.xml +++ b/app/res/values-sw/strings.xml @@ -418,6 +418,8 @@ Umesahau nambari yako ya hifadhi nakala? Too many attempts. Please try again later. Please add an email address + Weka nambari mpya ya hifadhi nakala + Backup code updated successfully Maelezo ya Uwasilishaji Kagua maelezo ya utoaji %d upeo wa Ziara diff --git a/app/res/values-ti/strings.xml b/app/res/values-ti/strings.xml index fd3fc240b7..1950634f4c 100644 --- a/app/res/values-ti/strings.xml +++ b/app/res/values-ti/strings.xml @@ -420,6 +420,7 @@ ብዙሕ ፈተናታት። በጃኻ ድሕሪ ሕጂ እንደገና ፈትን። በጃኻ ናይ ኢ-መይል ኣድራሻ ወስኽ ሓድሽ ናይ ምትካእ ኮድ ኣቐምጥ + Backup code updated successfully ዝርዝር (ኣተገባብራ)ኣወሃህባ ዝርዝር ኣወሃህባ ዳህሳስ ከልስ %d ዝለዓለ ምብጻሕ diff --git a/app/res/values/strings.xml b/app/res/values/strings.xml index 29e0948b28..437193b1e3 100644 --- a/app/res/values/strings.xml +++ b/app/res/values/strings.xml @@ -800,6 +800,7 @@ This is not me Change backup code Forgot your backup code? + Backup code updated successfully Too many attempts. Please try again later. Please add an email address Welcome back %s diff --git a/app/src/org/commcare/personalId/profile/SetNewBackupCodeFragment.kt b/app/src/org/commcare/personalId/profile/SetNewBackupCodeFragment.kt index a53b1a9ee3..7dacd496d6 100644 --- a/app/src/org/commcare/personalId/profile/SetNewBackupCodeFragment.kt +++ b/app/src/org/commcare/personalId/profile/SetNewBackupCodeFragment.kt @@ -4,13 +4,93 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.TextView -import androidx.fragment.app.Fragment - -class SetNewBackupCodeFragment : Fragment() { - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle?, - ): View = TextView(requireContext()).apply { text = "Set New Backup Code — TODO" } +import android.widget.Toast +import androidx.navigation.fragment.findNavController +import org.commcare.activities.CommCareActivity +import org.commcare.android.database.connect.models.ConnectUserRecord +import org.commcare.connect.database.ConnectUserDatabaseUtil +import org.commcare.connect.network.base.BaseApiHandler.PersonalIdOrConnectApiErrorCodes +import org.commcare.connect.network.base.PersonalIdOrConnectApiErrorHandler +import org.commcare.connect.network.personalId.PersonalIdApiHandler +import org.commcare.dalvik.R +import org.commcare.fragments.personalId.BasePersonalIdBackupCodeFragment +import org.commcare.personalId.PersonalIdUnlocker +import org.commcare.personalId.UnlockPolicy + +class SetNewBackupCodeFragment : BasePersonalIdBackupCodeFragment() { + + override fun onResume() { + super.onResume() + validateBackupCodeAndEnableContinue() + } + + override fun setUpView() { + setUpInitialState( + titleResId = R.string.personalid_set_new_backup_code_title, + showConfirmCode = true, + subtitle = getString(R.string.connect_backup_code_remember, BACKUP_CODE_LENGTH), + ) + } + + override fun handleBackupCodeSubmission() { + val backupCode = binding.backupCodeView.codeValue + if (!validateBackupCodeInput()) { + return + } + + PersonalIdUnlocker.unlock( + requireActivity() as CommCareActivity<*>, + UnlockPolicy.ALWAYS, + ) { unlocked -> + if (!unlocked) return@unlock + enableContinueButton(false) + callSetBackupCodeApi(backupCode) + } + } + + private fun callSetBackupCodeApi(backupCode: String) { + val user = ConnectUserDatabaseUtil.getUser(requireContext())!! + + object : PersonalIdApiHandler() { + override fun onSuccess(data: Boolean) { + onSetBackupCodeCallSuccess(backupCode, user) + } + + override fun onFailure( + errorCode: PersonalIdOrConnectApiErrorCodes, + t: Throwable?, + ) { + onSetBackupCodeCallFailure(errorCode, t) + } + }.setRecoveryPin(requireContext(), user.getUserId(), user.getPassword(), backupCode) + } + + private fun onSetBackupCodeCallSuccess( + backupCode: String, + user: ConnectUserRecord, + ) { + user.pin = backupCode + ConnectUserDatabaseUtil.storeUser(requireContext(), user) + Toast + .makeText( + requireContext(), + R.string.personalid_backup_code_changed_success, + Toast.LENGTH_LONG, + ).show() + findNavController().popBackStack(R.id.personalid_profile_backup_code_fragment, true) + } + + private fun onSetBackupCodeCallFailure( + errorCode: PersonalIdOrConnectApiErrorCodes, + t: Throwable?, + ) { + showError( + PersonalIdOrConnectApiErrorHandler.handle( + requireActivity(), + errorCode, + t, + ), + ) + enableContinueButton(true) + } } From 144094544dd730bd0b988fc2f8a1d033dbcda728 Mon Sep 17 00:00:00 2001 From: Shubham Goyal Date: Fri, 28 Aug 2026 14:44:24 +0530 Subject: [PATCH 3/4] test(ccct-2710): add SetNewBackupCodeFragmentTest Robolectric tests covering: initial UI state (confirm layout visible, button disabled), validation (match/mismatch, length guard, error visibility), successful save (navigates to profile, shows toast, stores updated pin), network error (inline error shown, stays on screen), and unlock failure (no API call, stays on screen). Co-Authored-By: Claude Sonnet 4.6 --- .../profile/SetNewBackupCodeFragmentTest.kt | 227 ++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 app/unit-tests/src/org/commcare/personalId/profile/SetNewBackupCodeFragmentTest.kt diff --git a/app/unit-tests/src/org/commcare/personalId/profile/SetNewBackupCodeFragmentTest.kt b/app/unit-tests/src/org/commcare/personalId/profile/SetNewBackupCodeFragmentTest.kt new file mode 100644 index 0000000000..456f326c1c --- /dev/null +++ b/app/unit-tests/src/org/commcare/personalId/profile/SetNewBackupCodeFragmentTest.kt @@ -0,0 +1,227 @@ +package org.commcare.personalId.profile + +import android.view.View +import android.widget.TextView +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.android.material.button.MaterialButton +import io.mockk.every +import io.mockk.mockkObject +import io.mockk.unmockkObject +import okhttp3.mockwebserver.MockResponse +import org.commcare.CommCareTestApplication +import org.commcare.connect.PersonalIdManager +import org.commcare.connect.database.ConnectUserDatabaseUtil +import org.commcare.dalvik.R +import org.commcare.personalId.PersonalIdUnlocker +import org.commcare.personalId.PersonalIdUserPreferences +import org.commcare.views.connect.NumericCodeView +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.robolectric.annotation.Config +import org.robolectric.shadows.ShadowToast + +@Config(application = CommCareTestApplication::class) +@RunWith(AndroidJUnit4::class) +class SetNewBackupCodeFragmentTest : BasePersonalIdProfileTest() { + @Before + fun navigateToSetNewBackupCodeScreen() { + mockkObject(PersonalIdUnlocker) + every { PersonalIdUnlocker.unlock(any(), any(), any()) } answers { + thirdArg().connectActivityComplete(true) + } + + // Prevent SharedPreferences lockout state from a previous test blocking navigation + PersonalIdUserPreferences.clearBackupCodeLockout() + user.pin = "123456" // 6-digit pin so ProfileBackupCode accepts it + onUiThread { navController.navigate(R.id.action_profile_to_profile_backup_code) } + // Enter the current backup code on the confirm screen and click continue + onUiThread { backupCodeViewOnConfirmScreen().setCode("123456") } + onUiThread { continueButtonOnConfirmScreen().performClick() } + // Now we should be on personalid_set_new_backup_code_fragment + } + + @After + fun tearDownMocks() { + unmockkObject(PersonalIdUnlocker) + } + + private fun fragment() = navHostFragment.childFragmentManager.primaryNavigationFragment as SetNewBackupCodeFragment + + private fun backupCodeViewOnConfirmScreen(): NumericCodeView { + val confirmFragment = navHostFragment.childFragmentManager.primaryNavigationFragment + return confirmFragment!!.requireView().findViewById(R.id.backup_code_view) + } + + private fun continueButtonOnConfirmScreen(): MaterialButton { + val confirmFragment = navHostFragment.childFragmentManager.primaryNavigationFragment + return confirmFragment!!.requireView().findViewById(R.id.connect_backup_code_button) + } + + private fun backupCodeView(): NumericCodeView = fragment().requireView().findViewById(R.id.backup_code_view) + + private fun confirmCodeView(): NumericCodeView = fragment().requireView().findViewById(R.id.confirm_code_view) + + private fun confirmCodeLayout(): View = fragment().requireView().findViewById(R.id.confirm_code_layout) + + private fun confirmCodeLabel(): View = fragment().requireView().findViewById(R.id.confirm_code_label) + + private fun welcomeBackLayout(): View = fragment().requireView().findViewById(R.id.welcome_back_layout) + + private fun notMeButton(): View = fragment().requireView().findViewById(R.id.not_me_button) + + private fun continueButton(): MaterialButton = fragment().requireView().findViewById(R.id.connect_backup_code_button) + + private fun errorMessage(): TextView = fragment().requireView().findViewById(R.id.connect_backup_code_error_message) + + private fun performSuccessfulSave(newCode: String = "654321") { + mockWebServer.enqueue(MockResponse().setResponseCode(200).setBody("{}")) + setCodesAndContinue(newCode) + mockApiServer.drainHttp() + } + + private fun setCodesAndContinue(newCode: String) { + onUiThread { backupCodeView().setCode(newCode) } + onUiThread { confirmCodeView().setCode(newCode) } + onUiThread { continueButton().performClick() } + } + + // ===== Initial state ===== + + @Test + fun `confirm code layout is visible`() { + assertEquals(View.VISIBLE, confirmCodeLayout().visibility) + } + + @Test + fun `confirm code label is visible`() { + assertEquals(View.VISIBLE, confirmCodeLabel().visibility) + } + + @Test + fun `welcome back layout is hidden`() { + assertEquals(View.GONE, welcomeBackLayout().visibility) + } + + @Test + fun `not me button is hidden`() { + assertEquals(View.GONE, notMeButton().visibility) + } + + @Test + fun `continue button starts disabled`() { + assertFalse(continueButton().isEnabled) + } + + @Test + fun `error message starts hidden`() { + assertEquals(View.GONE, errorMessage().visibility) + } + + // ===== Validation ===== + + @Test + fun `continue button enables when both codes match`() { + onUiThread { backupCodeView().setCode("654321") } + onUiThread { confirmCodeView().setCode("654321") } + + assertTrue(continueButton().isEnabled) + } + + @Test + fun `continue button stays disabled when codes differ`() { + onUiThread { backupCodeView().setCode("654321") } + onUiThread { confirmCodeView().setCode("000000") } + + assertFalse(continueButton().isEnabled) + } + + @Test + fun `mismatch error shown when both full and different`() { + onUiThread { backupCodeView().setCode("654321") } + onUiThread { confirmCodeView().setCode("000000") } + + assertEquals(View.VISIBLE, errorMessage().visibility) + assertEquals( + activity.getString(R.string.connect_backup_code_mismatch), + errorMessage().text.toString(), + ) + } + + @Test + fun `mismatch error hidden when codes match`() { + onUiThread { backupCodeView().setCode("654321") } + onUiThread { confirmCodeView().setCode("654321") } + + assertEquals(View.GONE, errorMessage().visibility) + } + + @Test + fun `continue button stays disabled until both codes are 6 digits`() { + onUiThread { backupCodeView().setCode("12345") } + onUiThread { confirmCodeView().setCode("12345") } + + assertFalse(continueButton().isEnabled) + } + + // ===== Success ===== + + @Test + fun `successful save pops to profile fragment`() { + performSuccessfulSave() + + assertEquals(R.id.personalid_profile_fragment, currentDestinationId()) + } + + @Test + fun `successful save shows success toast`() { + performSuccessfulSave() + + assertEquals( + activity.getString(R.string.personalid_backup_code_changed_success), + ShadowToast.getTextOfLatestToast(), + ) + } + + @Test + fun `successful save stores user`() { + performSuccessfulSave() + + connectUserDatabaseUtilMock.verify { + ConnectUserDatabaseUtil.storeUser(any(), any()) + } + assertEquals("654321", user.pin) + } + + // ===== Error ===== + + @Test + fun `unlock failure does not submit and stays on screen`() { + every { PersonalIdUnlocker.unlock(any(), any(), any()) } answers { + thirdArg().connectActivityComplete(false) + } + + setCodesAndContinue("654321") + + assertEquals(R.id.personalid_set_new_backup_code_fragment, currentDestinationId()) + assertEquals(0, mockWebServer.requestCount) + } + + @Test + fun `network error shows inline error and stays on screen`() { + mockWebServer.enqueue(MockResponse().setResponseCode(500)) + + setCodesAndContinue("654321") + + mockApiServer.drainHttp() + + assertEquals(R.id.personalid_set_new_backup_code_fragment, currentDestinationId()) + assertEquals(View.VISIBLE, errorMessage().visibility) + assertTrue(continueButton().isEnabled) + } +} From 701f8af0654a786002c0bece6a881c559eb0acac Mon Sep 17 00:00:00 2001 From: Shubham Goyal Date: Mon, 31 Aug 2026 16:00:10 +0530 Subject: [PATCH 4/4] correct string translation --- app/res/values-es/strings.xml | 2 +- app/res/values-fr/strings.xml | 2 +- app/res/values-ha/strings.xml | 2 +- app/res/values-hi/strings.xml | 2 +- app/res/values-lt/strings.xml | 2 +- app/res/values-no/strings.xml | 2 +- app/res/values-pt/strings.xml | 2 +- app/res/values-sw/strings.xml | 2 +- app/res/values-ti/strings.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/res/values-es/strings.xml b/app/res/values-es/strings.xml index 48f1b5247d..759a47c29c 100644 --- a/app/res/values-es/strings.xml +++ b/app/res/values-es/strings.xml @@ -420,7 +420,7 @@ Demasiados intentos. Por favor, inténtalo de nuevo más tarde. Por favor, añade una dirección de correo electrónico Establecer nuevo código de respaldo - Backup code updated successfully + Código de respaldo actualizado correctamente Detalles de la entrega Revisar detalles de la entrega %d visitas máximo diff --git a/app/res/values-fr/strings.xml b/app/res/values-fr/strings.xml index 7e8f24c01e..4b89204037 100644 --- a/app/res/values-fr/strings.xml +++ b/app/res/values-fr/strings.xml @@ -417,7 +417,7 @@ License. Trop de tentatives. Veuillez réessayer plus tard. Veuillez ajouter une adresse e-mail Définir un nouveau code de sauvegarde - Backup code updated successfully + Code de sauvegarde mis à jour avec succès Détails de livraison Vérifiez les détails de livraison %d visites maximum diff --git a/app/res/values-ha/strings.xml b/app/res/values-ha/strings.xml index d9e130767e..7df69efdf8 100644 --- a/app/res/values-ha/strings.xml +++ b/app/res/values-ha/strings.xml @@ -255,7 +255,7 @@ Don cikakken aiki na wurin zama na aikace-aikace da yawa, da fatan za a yi sabon Yunkuri da yawa. Da fatan za a sake gwadawa daga baya. Da fatan za a ƙara adireshin imel Saita sabuwar lambar sirrin ajiyar bayanai - Backup code updated successfully + An sabunta lambar sirrin ajiyar bayanai cikin nasara Cikakkun Bayanan Isarwa Yi bitar cikakkun bayanai game da isar da kaya Matsakaicin Ziyarar %d diff --git a/app/res/values-hi/strings.xml b/app/res/values-hi/strings.xml index fd999ae40f..7fc23f7aa6 100644 --- a/app/res/values-hi/strings.xml +++ b/app/res/values-hi/strings.xml @@ -417,7 +417,7 @@ License. Too many attempts. Please try again later. Please add an email address नया बैकअप कोड सेट करें - Backup code updated successfully + बैकअप कोड सफलतापूर्वक अपडेट किया गया वितरण विवरण वितरण विवरण की समीक्षा करें अधिकतम %d विज़िट diff --git a/app/res/values-lt/strings.xml b/app/res/values-lt/strings.xml index beb11f1495..6a4ab5a9a9 100644 --- a/app/res/values-lt/strings.xml +++ b/app/res/values-lt/strings.xml @@ -108,7 +108,7 @@ Too many attempts. Please try again later. Please add an email address Nustatyti naują atsarginį kodą - Backup code updated successfully + Atsarginis kodas sėkmingai atnaujintas Prisijungti / Registruotis Pagalba Neprisijungta prie „PersonalID“ diff --git a/app/res/values-no/strings.xml b/app/res/values-no/strings.xml index 9702c981c5..02766c069f 100644 --- a/app/res/values-no/strings.xml +++ b/app/res/values-no/strings.xml @@ -108,7 +108,7 @@ Too many attempts. Please try again later. Please add an email address Angi ny sikkerhetskode - Backup code updated successfully + Sikkerhetskoden ble oppdatert Logg inn / Registrer deg Hjelp Du er ikke logget inn med PersonalID diff --git a/app/res/values-pt/strings.xml b/app/res/values-pt/strings.xml index e551a2828f..82aec2943b 100644 --- a/app/res/values-pt/strings.xml +++ b/app/res/values-pt/strings.xml @@ -433,7 +433,7 @@ Muitas tentativas. Por favor, tente novamente mais tarde. Por favor, adicione um endereço de e-mail Definir novo código de backup - Backup code updated successfully + Código de backup atualizado com sucesso Detalhes da entrega Revise os detalhes da entrega %d máximo de visitas diff --git a/app/res/values-sw/strings.xml b/app/res/values-sw/strings.xml index b69b571eb1..ddd3c1168c 100644 --- a/app/res/values-sw/strings.xml +++ b/app/res/values-sw/strings.xml @@ -419,7 +419,7 @@ Too many attempts. Please try again later. Please add an email address Weka nambari mpya ya hifadhi nakala - Backup code updated successfully + Nambari ya hifadhi nakala imesasishwa Maelezo ya Uwasilishaji Kagua maelezo ya utoaji %d upeo wa Ziara diff --git a/app/res/values-ti/strings.xml b/app/res/values-ti/strings.xml index 1950634f4c..654ec5e689 100644 --- a/app/res/values-ti/strings.xml +++ b/app/res/values-ti/strings.xml @@ -420,7 +420,7 @@ ብዙሕ ፈተናታት። በጃኻ ድሕሪ ሕጂ እንደገና ፈትን። በጃኻ ናይ ኢ-መይል ኣድራሻ ወስኽ ሓድሽ ናይ ምትካእ ኮድ ኣቐምጥ - Backup code updated successfully + ናይ ምትካእ ኮድ ብዓወት ተሓዲሱ ዝርዝር (ኣተገባብራ)ኣወሃህባ ዝርዝር ኣወሃህባ ዳህሳስ ከልስ %d ዝለዓለ ምብጻሕ