From 9656480217353181c8ea7c6add286866d6aab702 Mon Sep 17 00:00:00 2001 From: Selinaliu1030 <77717012+Selinaliu1030@users.noreply.github.com> Date: Mon, 2 Mar 2026 11:01:39 +0100 Subject: [PATCH] Added Default Currency Choice in the Setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Key changes summary: 1. Added a new section "Card creation" in the Setting page 2. Added an entry "Default currency type" with a dropdown menu to choose from 3. Added a get method in Setting.java to get the value of selected default currency type 4. Set the currency type value to selected default in LoyaltyCardEditActivity::onCreate() 5. Added Unit tests and integration tests for the feature Note: The PR has passed all the existing tests and newly added tests --------- Co-authored-by: Rasmus Sjöberg Co-authored-by: filippaco Co-authored-by: Jacob F Co-authored-by: etisell Co-authored-by: Emma Tisell <89631117+Emmex03@users.noreply.github.com> --- ...tyCardEditActivityDefaultCurrencyTest.java | 83 ++++++++++++++++++ .../card_locker/LoyaltyCardEditActivity.java | 8 +- .../card_locker/preferences/Settings.java | 18 ++++ .../preferences/SettingsActivity.kt | 33 +++++++ app/src/main/res/values/strings.xml | 3 + app/src/main/res/xml/preferences.xml | 13 +++ .../LoyaltyCardEditActivityTest.java | 85 +++++++++++++++++++ .../LoyaltyCardViewActivityTest.java | 24 ++++++ 8 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 app/src/androidTest/java/protect/card_locker/LoyaltyCardEditActivityDefaultCurrencyTest.java create mode 100644 app/src/test/java/protect/card_locker/LoyaltyCardEditActivityTest.java diff --git a/app/src/androidTest/java/protect/card_locker/LoyaltyCardEditActivityDefaultCurrencyTest.java b/app/src/androidTest/java/protect/card_locker/LoyaltyCardEditActivityDefaultCurrencyTest.java new file mode 100644 index 0000000000..0f1df6d038 --- /dev/null +++ b/app/src/androidTest/java/protect/card_locker/LoyaltyCardEditActivityDefaultCurrencyTest.java @@ -0,0 +1,83 @@ +package protect.card_locker; + +import android.content.Context; +import android.content.SharedPreferences; +import androidx.preference.PreferenceManager; +import androidx.test.core.app.ActivityScenario; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.GrantPermissionRule; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.Rule; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static androidx.test.espresso.matcher.ViewMatchers.withClassName; +import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard; +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 org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.endsWith; + +@RunWith(AndroidJUnit4.class) +public class LoyaltyCardEditActivityDefaultCurrencyTest { + + @Rule + public GrantPermissionRule permissionRule = GrantPermissionRule.grant(android.Manifest.permission.CAMERA); + + @Test + public void defaultCurrencyPreselected() { + Context context = ApplicationProvider.getApplicationContext(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + String key = context.getString(R.string.settings_key_default_currency); + prefs.edit().putString(key, "USD").commit(); + + try (ActivityScenario scenario = ActivityScenario.launch(LoyaltyCardEditActivity.class)) { + onView(withText(R.string.options)).perform(click()); + onView(withId(R.id.balanceCurrencyField)) + .check(matches(withText("$"))); + } + } + + @Test + public void manualCurrencyPersistsAfterSave() { + Context context = ApplicationProvider.getApplicationContext(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + String key = context.getString(R.string.settings_key_default_currency); + prefs.edit().putString(key, "USD").commit(); + + try (ActivityScenario scenario = ActivityScenario.launch(MainActivity.class)) { + + onView(withId(R.id.fabAdd)).perform(click()); + + // handle if camera option shows up + try { + onView(withText("Got It")).perform(click()); + } catch (Exception e) { /* already clicked or never showed */ } + + onView(withText("More options")).perform(click()); + onView(withText("Add a card with no barcode")).perform(click()); + onView(withClassName(endsWith("EditText"))).perform(typeText("123"), closeSoftKeyboard()); + onView(withText("OK")).perform(click()); + onView(withId(R.id.storeNameEdit)).perform(typeText("card name 123"), closeSoftKeyboard()); + onView(withText(R.string.options)).perform(click()); + onView(withId(R.id.balanceCurrencyField)) + .check(matches(withText("$"))); + onView(withId(R.id.balanceCurrencyField)).perform(replaceText("£"), closeSoftKeyboard()); + onView(withId(R.id.fabSave)).perform(click()); + onView(withText("mr no hands")).perform(click()); + onView(withId(R.id.fabEdit)).perform(click()); + onView(withText(R.string.options)).perform(click()); + onView(withId(R.id.balanceCurrencyField)) + .check(matches(withText("£"))); + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java index 83fc94972c..09b981cde4 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java @@ -92,6 +92,7 @@ import protect.card_locker.databinding.LayoutChipChoiceBinding; import protect.card_locker.databinding.LoyaltyCardEditActivityBinding; import protect.card_locker.viewmodels.LoyaltyCardEditActivityViewModel; +import protect.card_locker.preferences.Settings; public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements BarcodeImageWriterResultCallback, ColorPickerDialogListener { private static final String TAG = "Catima"; @@ -144,6 +145,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements Toolbar toolbar; SQLiteDatabase mDatabase; + Settings settings; String tempStoredOldBarcodeValue = null; boolean initDone = false; @@ -320,8 +322,12 @@ protected void onCreate(Bundle savedInstanceState) { enableToolbarBackButton(); mDatabase = new DBHelper(this).getWritableDatabase(); - + if (settings == null) { settings = new Settings(this); } if (!viewModel.getInitialized()) { + var prefCurrency = settings.getPreferredCurrency(); + if(prefCurrency != null) { + setLoyaltyCardBalanceType(prefCurrency); + } if (!extractIntentFields(getIntent())) { return; } diff --git a/app/src/main/java/protect/card_locker/preferences/Settings.java b/app/src/main/java/protect/card_locker/preferences/Settings.java index 9c546f734c..1f1f170b5c 100644 --- a/app/src/main/java/protect/card_locker/preferences/Settings.java +++ b/app/src/main/java/protect/card_locker/preferences/Settings.java @@ -13,6 +13,7 @@ import androidx.preference.PreferenceManager; import java.util.Locale; +import java.util.Currency; import protect.card_locker.R; import protect.card_locker.Utils; @@ -105,6 +106,23 @@ public int getPreferredColumnCount() { } } + @Nullable + public Currency getPreferredCurrency() { + String points = getResString(R.string.points); + String stored = getString(R.string.settings_key_default_currency, points); + + if (points.equals(stored)) { + return null; + } + + // Guard against invalid or legacy stored currencies + try { + return Currency.getInstance(stored); + } catch (IllegalArgumentException ignored) { + return null; + } + } + public boolean useVolumeKeysForNavigation() { return getBoolean(R.string.settings_key_use_volume_keys_navigation, false); } diff --git a/app/src/main/java/protect/card_locker/preferences/SettingsActivity.kt b/app/src/main/java/protect/card_locker/preferences/SettingsActivity.kt index 8d48320d22..cafb69fbda 100644 --- a/app/src/main/java/protect/card_locker/preferences/SettingsActivity.kt +++ b/app/src/main/java/protect/card_locker/preferences/SettingsActivity.kt @@ -17,6 +17,7 @@ import protect.card_locker.MainActivity import protect.card_locker.R import protect.card_locker.Utils import protect.card_locker.databinding.SettingsActivityBinding +import java.util.Currency class SettingsActivity : CatimaAppCompatActivity() { @@ -160,6 +161,38 @@ class SettingsActivity : CatimaAppCompatActivity() { // Hide crash reporter settings on builds it's not enabled on val crashReporterPreference = findPreference("acra.enable") crashReporterPreference!!.isVisible = BuildConfig.useAcraCrashReporter + + // Set entries for preferred currency + val currencyPreference = findPreference(getString(R.string.settings_key_default_currency))!! + currencyPreference.let { pref -> + val currencies = Currency.getAvailableCurrencies() + .associateBy { it.symbol } // Deduplicates currencies by symbol to match behaviour in LoyaltyCardEditActivity + .values + .sortedWith { c1, c2 -> + val s1 = c1.symbol + val s2 = c2.symbol + + val s1Ascii = s1.matches("^[^a-zA-Z]*$".toRegex()) + val s2Ascii = s2.matches("^[^a-zA-Z]*$".toRegex()) + + when { + !s1Ascii && s2Ascii -> 1 + s1Ascii && !s2Ascii -> -1 + else -> s1.compareTo(s2) + } + } + + val symbols = currencies.map { it.symbol }.toMutableList() + val codes = currencies.map { it.currencyCode }.toMutableList() + + // Add points as an option + val points = getString(R.string.points) + symbols.add(0, points) + codes.add(0, points) + + pref.entries = symbols.toTypedArray() + pref.entryValues = codes.toTypedArray() + } } private fun refreshActivity(reloadMain: Boolean) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d6f8d93c5a..cc24793fb5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -284,8 +284,11 @@ sharedpreference_card_details_show_archived_cards Card view Cards overview + Card creation Columns in portrait mode Columns in landscape mode + Default currency type + Preferred currency Automatic default 1 diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index edcb29fe90..e2135db433 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -111,6 +111,19 @@ app:singleLineTitle="false" /> + + + + + diff --git a/app/src/test/java/protect/card_locker/LoyaltyCardEditActivityTest.java b/app/src/test/java/protect/card_locker/LoyaltyCardEditActivityTest.java new file mode 100644 index 0000000000..b0e0eca4a9 --- /dev/null +++ b/app/src/test/java/protect/card_locker/LoyaltyCardEditActivityTest.java @@ -0,0 +1,85 @@ +package protect.card_locker; + +import android.widget.TextView; +import android.content.SharedPreferences; +import androidx.preference.PreferenceManager; + +import org.junit.Before; +import org.junit.Test; +import org.junit.After; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.android.controller.ActivityController; +import org.robolectric.RobolectricTestRunner; + +import java.util.Currency; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import protect.card_locker.preferences.Settings; + +@RunWith(RobolectricTestRunner.class) +public class LoyaltyCardEditActivityTest { + + private ActivityController controller; + private LoyaltyCardEditActivity activity; + + @Before + public void setUp() { + controller = Robolectric.buildActivity(LoyaltyCardEditActivity.class); + activity = controller.get(); + } + + @After + public void tearDown() { + if (activity != null && activity.mDatabase != null && activity.mDatabase.isOpen()) { + activity.mDatabase.close(); + } + } + + @Test + public void onCreate_setsBalanceTypeFromSettings_SEK() { + Currency currency = Currency.getInstance("SEK"); + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); + prefs.edit() + .putString( + activity.getString(R.string.settings_key_default_currency), + currency.getSymbol() + ) + .commit(); + + controller.create(); + + assertEquals(currency, activity.viewModel.getLoyaltyCard().balanceType); + + controller.resume(); + + TextView field = activity.findViewById(R.id.balanceCurrencyField); + assertNotNull(field); + assertEquals( + currency.getSymbol(), + field.getText().toString() + ); + } + + @Test + public void onCreate_setsBalanceTypeWhenSettingsNull_points() { + controller.create(); + + assertEquals( + null, + activity.viewModel.getLoyaltyCard().balanceType + ); + + controller.resume(); + + TextView field = activity.findViewById(R.id.balanceCurrencyField); + assertNotNull(field); + assertEquals( + activity.getString(R.string.points), + field.getText().toString() + ); + } +} \ No newline at end of file diff --git a/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java b/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java index 0b7cbf9a8d..b5e3dffede 100644 --- a/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java +++ b/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java @@ -18,6 +18,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -41,6 +42,7 @@ import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.preference.PreferenceManager; import androidx.test.core.app.ApplicationProvider; import com.google.android.material.bottomappbar.BottomAppBar; @@ -1400,4 +1402,26 @@ public void importCardOldFormat() { checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", context.getString(R.string.anyDate), context.getString(R.string.never), "0", context.getString(R.string.points), "123456", context.getString(R.string.sameAsCardId), "Aztec", "ISO-8859-1", null, null); assertEquals(-416706, ((ColorDrawable) activity.findViewById(R.id.thumbnail).getBackground()).getColor()); } + + + @Test + public void verifyDefaultCurrencyPreferenceInitiallySetToPoints(){ + final Context context = ApplicationProvider.getApplicationContext(); + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + prefs.edit().clear().commit(); + + ActivityController activityController = Robolectric.buildActivity(LoyaltyCardEditActivity.class).create(); + + activityController.start(); + activityController.visible(); + activityController.resume(); + + shadowOf(getMainLooper()).idle(); + + Activity activity = (Activity) activityController.get(); + + checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.anyDate), context.getString(R.string.never), "0", context.getString(R.string.points), "", context.getString(R.string.sameAsCardId), context.getString(R.string.noBarcode), "ISO-8859-1", null, null); + } + }