diff --git a/CHANGELOG.md b/CHANGELOG.md index 99079d069..2fa2f661c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +Changes: + +- Option to override language + ## v2.2.3 (2021-08-13) Changes: diff --git a/app/build.gradle b/app/build.gradle index f80978117..ba0c7d0e4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -37,6 +37,12 @@ android { } } + bundle { + language { + enableSplit = false + } + } + compileOptions { encoding "UTF-8" diff --git a/app/src/main/java/protect/card_locker/AboutActivity.java b/app/src/main/java/protect/card_locker/AboutActivity.java index e9dd0bd5e..5b6b19e53 100644 --- a/app/src/main/java/protect/card_locker/AboutActivity.java +++ b/app/src/main/java/protect/card_locker/AboutActivity.java @@ -1,5 +1,6 @@ package protect.card_locker; +import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Bundle; @@ -21,10 +22,16 @@ public class AboutActivity extends AppCompatActivity { private static final String TAG = "Catima"; + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(Utils.updateBaseContextLocale(base)); + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setTitle(R.string.about); setContentView(R.layout.about_activity); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); diff --git a/app/src/main/java/protect/card_locker/BarcodeSelectorActivity.java b/app/src/main/java/protect/card_locker/BarcodeSelectorActivity.java index 24f898edc..8715638c3 100644 --- a/app/src/main/java/protect/card_locker/BarcodeSelectorActivity.java +++ b/app/src/main/java/protect/card_locker/BarcodeSelectorActivity.java @@ -1,6 +1,7 @@ package protect.card_locker; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; @@ -63,11 +64,16 @@ public class BarcodeSelectorActivity extends AppCompatActivity private Map> barcodeViewMap; private LinkedList barcodeGeneratorTasks = new LinkedList<>(); + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(Utils.updateBaseContextLocale(base)); + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - + setTitle(R.string.selectBarcodeTitle); setContentView(R.layout.barcode_selector_activity); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); diff --git a/app/src/main/java/protect/card_locker/ImportExportActivity.java b/app/src/main/java/protect/card_locker/ImportExportActivity.java index 144454e44..23c56b695 100644 --- a/app/src/main/java/protect/card_locker/ImportExportActivity.java +++ b/app/src/main/java/protect/card_locker/ImportExportActivity.java @@ -2,6 +2,7 @@ package protect.card_locker; import android.Manifest; import android.content.ActivityNotFoundException; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; @@ -48,10 +49,16 @@ public class ImportExportActivity extends AppCompatActivity private String importAlertMessage; private DataFormat importDataFormat; + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(Utils.updateBaseContextLocale(base)); + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setTitle(R.string.importExport); setContentView(R.layout.import_export_activity); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java index b51e273e4..10696b195 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java @@ -141,6 +141,11 @@ public class LoyaltyCardEditActivity extends AppCompatActivity LoyaltyCard tempLoyaltyCard; + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(Utils.updateBaseContextLocale(base)); + } + private static LoyaltyCard updateTempState(LoyaltyCard loyaltyCard, LoyaltyCardField fieldName, Object value) { return new LoyaltyCard( (int) (fieldName == LoyaltyCardField.id ? value : loyaltyCard.id), diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java index e34745f0e..8e8b65efe 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java @@ -1,5 +1,6 @@ package protect.card_locker; +import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; @@ -104,6 +105,11 @@ public class LoyaltyCardViewActivity extends AppCompatActivity IMAGE_BACK } + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(Utils.updateBaseContextLocale(base)); + } + private void extractIntentFields(Intent intent) { final Bundle b = intent.getExtras(); diff --git a/app/src/main/java/protect/card_locker/MainActivity.java b/app/src/main/java/protect/card_locker/MainActivity.java index 3a3cab1f2..647353238 100644 --- a/app/src/main/java/protect/card_locker/MainActivity.java +++ b/app/src/main/java/protect/card_locker/MainActivity.java @@ -50,6 +50,11 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor private View mHelpText; private View mNoMatchingCardsText; + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(Utils.updateBaseContextLocale(base)); + } + private ActionMode.Callback mCurrentActionModeCallback = new ActionMode.Callback() { @Override @@ -179,6 +184,7 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor { setTheme(R.style.AppTheme_NoActionBar); super.onCreate(inputSavedInstanceState); + setTitle(R.string.app_name); setContentView(R.layout.main_activity); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); diff --git a/app/src/main/java/protect/card_locker/ManageGroupsActivity.java b/app/src/main/java/protect/card_locker/ManageGroupsActivity.java index 72f61ad7d..97cd880ce 100644 --- a/app/src/main/java/protect/card_locker/ManageGroupsActivity.java +++ b/app/src/main/java/protect/card_locker/ManageGroupsActivity.java @@ -31,10 +31,16 @@ public class ManageGroupsActivity extends AppCompatActivity implements GroupCurs private RecyclerView mGroupList; GroupCursorAdapter mAdapter; + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(Utils.updateBaseContextLocale(base)); + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setTitle(R.string.groups); setContentView(R.layout.manage_groups_activity); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); diff --git a/app/src/main/java/protect/card_locker/ScanActivity.java b/app/src/main/java/protect/card_locker/ScanActivity.java index 305d73a67..ddf33f8ca 100644 --- a/app/src/main/java/protect/card_locker/ScanActivity.java +++ b/app/src/main/java/protect/card_locker/ScanActivity.java @@ -1,6 +1,7 @@ package protect.card_locker; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; @@ -39,6 +40,11 @@ public class ScanActivity extends AppCompatActivity { private String addGroup; private boolean torch = false; + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(Utils.updateBaseContextLocale(base)); + } + private void extractIntentFields(Intent intent) { final Bundle b = intent.getExtras(); cardId = b != null ? b.getString(LoyaltyCardEditActivity.BUNDLE_CARDID) : null; @@ -49,6 +55,7 @@ public class ScanActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setTitle(R.string.scanCardBarcode); setContentView(R.layout.scan_activity); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); diff --git a/app/src/main/java/protect/card_locker/Utils.java b/app/src/main/java/protect/card_locker/Utils.java index 93e61613e..c2930f89a 100644 --- a/app/src/main/java/protect/card_locker/Utils.java +++ b/app/src/main/java/protect/card_locker/Utils.java @@ -3,10 +3,14 @@ package protect.card_locker; import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.Matrix; +import android.os.Build; +import android.os.LocaleList; import android.provider.MediaStore; import android.util.Log; import android.widget.Toast; @@ -31,8 +35,10 @@ import java.util.Currency; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; +import java.util.Locale; import androidx.core.graphics.ColorUtils; +import protect.card_locker.preferences.Settings; import androidx.exifinterface.media.ExifInterface; public class Utils { @@ -343,4 +349,35 @@ public class Utils { static public Object hashmapGetOrDefault(HashMap hashMap, String key, Object defaultValue) { return hashmapGetOrDefault(hashMap, key, defaultValue, String.class); } + + static public Locale stringToLocale(String localeString) { + String[] localeParts = localeString.split("-"); + if (localeParts.length == 1) { + return new Locale(localeParts[0]); + } + + if (localeParts[1].startsWith("r")) { + localeParts[1] = localeParts[1].substring(1); + } + return new Locale(localeParts[0], localeParts[1]); + } + + static public Context updateBaseContextLocale(Context context) { + Settings settings = new Settings(context); + + Locale chosenLocale = settings.getLocale(); + + Resources res = context.getResources(); + Configuration configuration = res.getConfiguration(); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + configuration.locale = chosenLocale != null ? chosenLocale : Locale.getDefault(); + res.updateConfiguration(configuration, res.getDisplayMetrics()); + return context; + } + + LocaleList localeList = chosenLocale != null ? new LocaleList(chosenLocale) : LocaleList.getDefault(); + LocaleList.setDefault(localeList); + configuration.setLocales(localeList); + return context.createConfigurationContext(configuration); + } } 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 ede946634..a28ac5f8a 100644 --- a/app/src/main/java/protect/card_locker/preferences/Settings.java +++ b/app/src/main/java/protect/card_locker/preferences/Settings.java @@ -3,11 +3,14 @@ package protect.card_locker.preferences; import android.content.Context; import android.content.SharedPreferences; +import java.util.Locale; + import androidx.annotation.IntegerRes; import androidx.annotation.StringRes; import androidx.appcompat.app.AppCompatDelegate; import androidx.preference.PreferenceManager; import protect.card_locker.R; +import protect.card_locker.Utils; public class Settings { @@ -45,6 +48,17 @@ public class Settings return settings.getBoolean(getResString(keyId), defaultValue); } + public Locale getLocale() + { + String value = getString(R.string.settings_key_locale, ""); + + if (value.length() == 0) { + return null; + } + + return Utils.stringToLocale(value); + } + public int getTheme() { String value = getString(R.string.settings_key_theme, getResString(R.string.settings_key_system_theme)); diff --git a/app/src/main/java/protect/card_locker/preferences/SettingsActivity.java b/app/src/main/java/protect/card_locker/preferences/SettingsActivity.java index b9198cb2c..edbc3dbe0 100644 --- a/app/src/main/java/protect/card_locker/preferences/SettingsActivity.java +++ b/app/src/main/java/protect/card_locker/preferences/SettingsActivity.java @@ -1,26 +1,47 @@ package protect.card_locker.preferences; +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Build; import android.os.Bundle; +import android.util.Log; import android.view.MenuItem; +import com.journeyapps.barcodescanner.Util; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatDelegate; import androidx.core.app.ActivityCompat; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.FragmentActivity; +import androidx.preference.ListPreference; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import nl.invissvenska.numberpickerpreference.NumberDialogPreference; import nl.invissvenska.numberpickerpreference.NumberPickerPreferenceDialogFragment; +import protect.card_locker.MainActivity; import protect.card_locker.R; +import protect.card_locker.Utils; public class SettingsActivity extends AppCompatActivity { + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(Utils.updateBaseContextLocale(base)); + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setTitle(R.string.settings); setContentView(R.layout.settings_activity); ActionBar actionBar = getSupportActionBar(); @@ -30,8 +51,10 @@ public class SettingsActivity extends AppCompatActivity } // Display the fragment as the main content. + SettingsFragment fragment = new SettingsFragment(); + fragment.setParentReference(this); getSupportFragmentManager().beginTransaction() - .replace(R.id.settings_container, new SettingsFragment()) + .replace(R.id.settings_container, fragment) .commit(); } @@ -52,46 +75,63 @@ public class SettingsActivity extends AppCompatActivity public static class SettingsFragment extends PreferenceFragmentCompat { private static final String DIALOG_FRAGMENT_TAG = "SettingsFragment"; + private SettingsActivity parent; + + public void setParentReference(SettingsActivity settingsActivity) { + parent = settingsActivity; + } + @Override - public void onCreatePreferences(Bundle savedInstanceState, String rootKey) - { + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { // Load the preferences from an XML resource addPreferencesFromResource(R.xml.preferences); + // Show pretty names + ListPreference localePreference = findPreference(getResources().getString(R.string.settings_key_locale)); + assert localePreference != null; + CharSequence[] entryValues = localePreference.getEntryValues(); + List entries = new ArrayList<>(); + for (CharSequence entry : entryValues) { + if (entry.length() == 0) { + entries.add(getResources().getString(R.string.settings_system_locale)); + } else { + Locale entryLocale = Utils.stringToLocale(entry.toString()); + entries.add(entryLocale.getDisplayName(entryLocale)); + } + } + + localePreference.setEntries(entries.toArray(new CharSequence[entryValues.length])); + Preference themePreference = findPreference(getResources().getString(R.string.settings_key_theme)); assert themePreference != null; - themePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() - { - @Override - public boolean onPreferenceChange(Preference preference, Object o) - { - if(o.toString().equals(getResources().getString(R.string.settings_key_light_theme))) - { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); - } - else if(o.toString().equals(getResources().getString(R.string.settings_key_dark_theme))) - { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); - } - else - { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); - } - - FragmentActivity activity = getActivity(); - if (activity != null) { - ActivityCompat.recreate(activity); - } - return true; + themePreference.setOnPreferenceChangeListener((preference, o) -> { + if (o.toString().equals(getResources().getString(R.string.settings_key_light_theme))) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); + } else if (o.toString().equals(getResources().getString(R.string.settings_key_dark_theme))) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); + } else { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); } + + FragmentActivity activity = getActivity(); + if (activity != null) { + ActivityCompat.recreate(activity); + } + return true; + }); + + localePreference.setOnPreferenceChangeListener((preference, newValue) -> { + // Refresh the activity + parent.finish(); + startActivity(parent.getIntent()); + + return true; }); } @Override - public void onDisplayPreferenceDialog(Preference preference) - { - if (preference instanceof NumberDialogPreference) - { + public void onDisplayPreferenceDialog(Preference preference) { + if (preference instanceof NumberDialogPreference) { NumberDialogPreference dialogPreference = (NumberDialogPreference) preference; DialogFragment dialogFragment = NumberPickerPreferenceDialogFragment .newInstance( @@ -103,9 +143,7 @@ public class SettingsActivity extends AppCompatActivity ); dialogFragment.setTargetFragment(this, 0); dialogFragment.show(getParentFragmentManager(), DIALOG_FRAGMENT_TAG); - } - else - { + } else { super.onDisplayPreferenceDialog(preference); } } diff --git a/app/src/main/res/values/settings.xml b/app/src/main/res/values/settings.xml index 6cde783a8..005c886eb 100644 --- a/app/src/main/res/values/settings.xml +++ b/app/src/main/res/values/settings.xml @@ -12,6 +12,34 @@ @string/settings_dark_theme + + + bg + cs + de + el-rGR + eo + es-rAR + es + fi + fr + he-rIL + it + ja + ko + lt + nb-rNO + nl + oc + pl + ro-rRO + ru + sk + sl + uk + zh-rCN + + #f16364 #f58559 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 81452284d..aca676ddc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -213,4 +213,7 @@ Could not generate sharable URL. Please report this. Turn flashlight on Turn flashlight off + Language + pref_locale + System diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 8b1bb4855..cbd9051cd 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -16,6 +16,15 @@ app:iconSpaceReserved="false" app:singleLineTitle="false" /> + +