diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Activities/BackupActivity.java b/app/src/main/java/org/shadowice/flocke/andotp/Activities/BackupActivity.java index e596f64a..627127c0 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Activities/BackupActivity.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Activities/BackupActivity.java @@ -40,6 +40,7 @@ import android.util.Log; import android.view.View; import android.view.ViewStub; import android.widget.LinearLayout; +import android.widget.Switch; import android.widget.TextView; import android.widget.Toast; @@ -92,6 +93,8 @@ public class BackupActivity extends BaseActivity { private Uri encryptTargetFile; private Uri decryptSourceFile; + private Switch replace; + private boolean reload = false; @Override @@ -193,6 +196,8 @@ public class BackupActivity extends BaseActivity { }); } + replace = v.findViewById(R.id.backup_replace); + } // End with a result @@ -361,10 +366,18 @@ public class BackupActivity extends BaseActivity { private void doRestorePlain(Uri uri) { if (Tools.isExternalStorageReadable()) { - boolean success = DatabaseHelper.importFromJSON(this, uri); + ArrayList entries = DatabaseHelper.importFromJSON(this, uri); - if (success) { + if (entries != null) { + if (! replace.isChecked()) { + ArrayList currentEntries = DatabaseHelper.loadDatabase(this); + entries.removeAll(currentEntries); + entries.addAll(currentEntries); + } + + DatabaseHelper.saveDatabase(this, entries); reload = true; + Toast.makeText(this, R.string.backup_toast_import_success, Toast.LENGTH_LONG).show(); } else { Toast.makeText(this, R.string.backup_toast_import_failed, Toast.LENGTH_LONG).show(); @@ -427,6 +440,13 @@ public class BackupActivity extends BaseActivity { byte[] decrypted = EncryptionHelper.decrypt(key, encrypted); ArrayList entries = DatabaseHelper.stringToEntries(new String(decrypted, StandardCharsets.UTF_8)); + + if (! replace.isChecked()) { + ArrayList currentEntries = DatabaseHelper.loadDatabase(this); + entries.removeAll(currentEntries); + entries.addAll(currentEntries); + } + DatabaseHelper.saveDatabase(this, entries); } catch (Exception e) { e.printStackTrace(); @@ -491,9 +511,15 @@ public class BackupActivity extends BaseActivity { ArrayList entries = DatabaseHelper.stringToEntries(content); if (entries.size() > 0) { - DatabaseHelper.saveDatabase(this, entries); + if (! replace.isChecked()) { + ArrayList currentEntries = DatabaseHelper.loadDatabase(this); + entries.removeAll(currentEntries); + entries.addAll(currentEntries); + } + DatabaseHelper.saveDatabase(this, entries); reload = true; + Toast.makeText(this, R.string.backup_toast_import_success, Toast.LENGTH_LONG).show(); } else { Toast.makeText(this, R.string.backup_toast_import_failed, Toast.LENGTH_LONG).show(); diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Activities/MainActivity.java b/app/src/main/java/org/shadowice/flocke/andotp/Activities/MainActivity.java index 2866fdb9..1eaab2a8 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Activities/MainActivity.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Activities/MainActivity.java @@ -428,9 +428,11 @@ public class MainActivity extends BaseActivity public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { if (key.equals(getString(R.string.settings_key_label_size)) || - key.equals(getString(R.string.settings_key_tap_to_reveal))) { + key.equals(getString(R.string.settings_key_tap_to_reveal)) || + key.equals(getString(R.string.settings_key_label_scroll))) { adapter.notifyDataSetChanged(); - } else if (key.equals(getString(R.string.settings_key_theme))) { + } else if (key.equals(getString(R.string.settings_key_theme)) || + key.equals(getString(R.string.settings_key_lang))) { recreate(); } } diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Activities/SettingsActivity.java b/app/src/main/java/org/shadowice/flocke/andotp/Activities/SettingsActivity.java index 0024b0e8..6f85ccad 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Activities/SettingsActivity.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Activities/SettingsActivity.java @@ -85,7 +85,8 @@ public class SettingsActivity extends BaseActivity } public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { - if (key.equals(getString(R.string.settings_key_theme))) { + if (key.equals(getString(R.string.settings_key_theme)) || + key.equals(getString(R.string.settings_key_lang))) { recreate(); } } diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Activities/ThemedActivity.java b/app/src/main/java/org/shadowice/flocke/andotp/Activities/ThemedActivity.java index 71cbe2a9..1824dbad 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Activities/ThemedActivity.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Activities/ThemedActivity.java @@ -22,12 +22,15 @@ package org.shadowice.flocke.andotp.Activities; +import android.content.res.Configuration; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import org.shadowice.flocke.andotp.R; import org.shadowice.flocke.andotp.Utilities.Settings; +import java.util.Locale; + public abstract class ThemedActivity extends AppCompatActivity { public Settings settings; @@ -35,6 +38,20 @@ public abstract class ThemedActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { settings = new Settings(this); + setTheme(); + setLocale(); + + super.onCreate(savedInstanceState); + } + + @Override + public void onResume() { + setLocale(); + + super.onResume(); + } + + public void setTheme() { String theme = settings.getTheme(); if (theme.equals("light")) { @@ -44,7 +61,15 @@ public abstract class ThemedActivity extends AppCompatActivity { } else if (theme.equals("black")) { setTheme(R.style.AppTheme_Black_NoActionBar); } + } - super.onCreate(savedInstanceState); + public void setLocale() { + Locale locale = settings.getLang(); + Locale.setDefault(locale); + + Configuration config = new Configuration(); + config.locale = locale; + + getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); } } diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Database/Entry.java b/app/src/main/java/org/shadowice/flocke/andotp/Database/Entry.java index da4402a1..52c20566 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Database/Entry.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Database/Entry.java @@ -132,7 +132,7 @@ public class Entry { } public Entry (JSONObject jsonObj) throws JSONException { - this.secret = new Base32().decode(jsonObj.getString(JSON_SECRET)); + this.secret = new Base32().decode(jsonObj.getString(JSON_SECRET).toUpperCase()); this.label = jsonObj.getString(JSON_LABEL); this.period = jsonObj.getInt(JSON_PERIOD); diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/DatabaseHelper.java b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/DatabaseHelper.java index 68c3c3e1..477e1c93 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/DatabaseHelper.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/DatabaseHelper.java @@ -113,19 +113,14 @@ public class DatabaseHelper { return FileHelper.writeStringToFile(context, file, entriesToString(entries)); } - public static boolean importFromJSON(Context context, Uri file) { - boolean success = false; - + public static ArrayList importFromJSON(Context context, Uri file) { String content = FileHelper.readFileToString(context, file); - if (! content.isEmpty()) { - ArrayList entries = stringToEntries(content); + ArrayList entries = null; - saveDatabase(context, entries); + if (! content.isEmpty()) + entries = stringToEntries(content); - success = true; - } - - return success; + return entries; } } \ No newline at end of file diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/Settings.java b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/Settings.java index b781d493..77e3d4bf 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/Settings.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/Settings.java @@ -37,6 +37,7 @@ import java.nio.charset.StandardCharsets; import java.security.KeyPair; import java.util.Collections; import java.util.HashSet; +import java.util.Locale; import java.util.Set; import static org.shadowice.flocke.andotp.Preferences.PasswordEncryptedPreference.KEY_ALIAS; @@ -213,10 +214,23 @@ public class Settings { return settings.getStringSet(getResString(R.string.settings_key_panic), Collections.emptySet()); } + public Locale getLang() { + String lang = getString(R.string.settings_key_lang, R.string.settings_default_lang); + + if (lang.equals("system")) + return Tools.getSystemLocale(); + else + return new Locale(lang); + } + public String getTheme() { return getString(R.string.settings_key_theme, R.string.settings_default_theme); } + public boolean getScrollLabel() { + return getBoolean(R.string.settings_key_label_scroll, false); + } + public boolean getFirstTimeWarningShown() { return getBoolean(R.string.settings_key_security_backup_warning, false); } diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/Tools.java b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/Tools.java index ef434d77..faefd08e 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/Tools.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/Tools.java @@ -29,9 +29,11 @@ import android.graphics.ColorFilter; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.net.Uri; +import android.os.Build; import android.os.Environment; import java.io.File; +import java.util.Locale; public class Tools { /* Checks if external storage is available for read and write */ @@ -70,4 +72,12 @@ public class Tools { File dir = new File(path); return dir.exists() || dir.mkdirs(); } + + public static Locale getSystemLocale() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return Resources.getSystem().getConfiguration().getLocales().get(0); + } else { + return Resources.getSystem().getConfiguration().locale; + } + } } diff --git a/app/src/main/java/org/shadowice/flocke/andotp/View/EntriesCardAdapter.java b/app/src/main/java/org/shadowice/flocke/andotp/View/EntriesCardAdapter.java index a4d3c3da..c01b89ad 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/View/EntriesCardAdapter.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/View/EntriesCardAdapter.java @@ -169,6 +169,8 @@ public class EntriesCardAdapter extends RecyclerView.Adapter int fontSize = sharedPrefs.getInt(context.getString(R.string.settings_key_label_size), context.getResources().getInteger(R.integer.settings_default_label_size)); entryViewHolder.setLabelSize(fontSize); + + entryViewHolder.setLabelScroll(sharedPrefs.getBoolean(context.getString(R.string.settings_key_label_scroll), false)); } @Override diff --git a/app/src/main/java/org/shadowice/flocke/andotp/View/EntryViewHolder.java b/app/src/main/java/org/shadowice/flocke/andotp/View/EntryViewHolder.java index 3997400f..9f9f4370 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/View/EntryViewHolder.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/View/EntryViewHolder.java @@ -26,6 +26,7 @@ import android.content.Context; import android.graphics.ColorFilter; import android.support.v7.widget.CardView; import android.support.v7.widget.RecyclerView; +import android.text.TextUtils; import android.util.TypedValue; import android.view.View; import android.widget.ImageButton; @@ -136,6 +137,18 @@ public class EntryViewHolder extends RecyclerView.ViewHolder tags.setTextSize(TypedValue.COMPLEX_UNIT_PT, size - 2); } + public void setLabelScroll(boolean active) { + if (active) { + label.setEllipsize(TextUtils.TruncateAt.MARQUEE); + label.setHorizontallyScrolling(true); + label.setSelected(true); + } else { + label.setEllipsize(TextUtils.TruncateAt.END); + label.setHorizontallyScrolling(false); + label.setSelected(false); + } + } + public void enableTapToReveal() { valueLayout.setVisibility(View.GONE); coverLayout.setVisibility(View.VISIBLE); diff --git a/app/src/main/res/layout/component_card.xml b/app/src/main/res/layout/component_card.xml index 490d74ec..5ef14874 100644 --- a/app/src/main/res/layout/component_card.xml +++ b/app/src/main/res/layout/component_card.xml @@ -66,7 +66,7 @@ android:textColor="?android:attr/textColorSecondary" android:textSize="28sp" android:textStyle="bold" - android:text="@string/label_tap_to_reveal" /> + android:text="@string/label_hidden" /> diff --git a/app/src/main/res/layout/content_about.xml b/app/src/main/res/layout/content_about.xml index 240601d4..35d1d7ab 100644 --- a/app/src/main/res/layout/content_about.xml +++ b/app/src/main/res/layout/content_about.xml @@ -486,7 +486,7 @@ android:id="@+id/about_layout_bugs" android:orientation="horizontal" android:layout_width="match_parent" - android:layout_height="?android:attr/listPreferredItemHeight" + android:layout_height="wrap_content" android:layout_marginTop="@dimen/activity_margin" android:paddingTop="@dimen/activity_margin_small" android:paddingBottom="@dimen/activity_margin_small" @@ -504,7 +504,7 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_manual_entry.xml b/app/src/main/res/layout/dialog_manual_entry.xml index 38cf0841..d22050c7 100644 --- a/app/src/main/res/layout/dialog_manual_entry.xml +++ b/app/src/main/res/layout/dialog_manual_entry.xml @@ -9,10 +9,11 @@ @@ -28,10 +29,11 @@ @@ -48,10 +50,11 @@ @@ -61,7 +64,7 @@ android:layout_weight="7" android:layout_width="0dp" android:layout_height="wrap_content" - android:lines="2" + android:lines="3" android:hint="@string/hint_secret" /> @@ -69,10 +72,11 @@ @@ -90,10 +94,11 @@ @@ -111,10 +116,11 @@ diff --git a/app/src/main/res/values/settings.xml b/app/src/main/res/values/settings.xml index 113ce137..da712e4b 100644 --- a/app/src/main/res/values/settings.xml +++ b/app/src/main/res/values/settings.xml @@ -11,8 +11,10 @@ pref_auth_pin_hash pref_panic + pref_lang pref_theme pref_label_size + pref_label_scroll pref_backup_ask pref_backup_directory @@ -32,6 +34,7 @@ none + system light 8 @@ -48,6 +51,19 @@ settings + + system + en + cs + de + es + fr + gl + nl + pl + ru + + light dark diff --git a/app/src/main/res/values/strings_backup.xml b/app/src/main/res/values/strings_backup.xml index 0d8d24c4..c15347d6 100644 --- a/app/src/main/res/values/strings_backup.xml +++ b/app/src/main/res/values/strings_backup.xml @@ -31,6 +31,8 @@ before you can create encrypted backups. + Replace already existing entries + Security warning diff --git a/app/src/main/res/values/strings_main.xml b/app/src/main/res/values/strings_main.xml index 4d27a5cc..dd40662c 100644 --- a/app/src/main/res/values/strings_main.xml +++ b/app/src/main/res/values/strings_main.xml @@ -20,7 +20,7 @@ OTP Token - Tap to reveal + Hidden Type Secret Period diff --git a/app/src/main/res/values/strings_settings.xml b/app/src/main/res/values/strings_settings.xml index b34cd8eb..aebf17e4 100644 --- a/app/src/main/res/values/strings_settings.xml +++ b/app/src/main/res/values/strings_settings.xml @@ -14,8 +14,10 @@ PIN Panic Trigger + Language Theme Label font size + Scroll label Ask for filename Backup directory @@ -30,6 +32,8 @@ revealed manually Decide what happens when a Panic Trigger is received + Scroll overlong labels instead of truncating them + Ask for the filename every time a backup is created or restored Directory for the backups (filenames will depend @@ -61,6 +65,19 @@ Reset app settings + + System default + English + Czech + German + Spanish + French + Galician + Dutch + Polish + Russian + + Light theme Dark theme diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index d8d5047e..8c32e7ea 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -37,6 +37,14 @@ + + + +