mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-04-24 15:56:07 +00:00
Merge pull request #666 from alexbakker/separate-prefs
Split preferences into separate fragments
This commit is contained in:
commit
07c768893a
53 changed files with 1706 additions and 1406 deletions
1
app/proguard-rules.pro
vendored
1
app/proguard-rules.pro
vendored
|
@ -18,6 +18,7 @@
|
|||
|
||||
-keepclasseswithmembers public class androidx.recyclerview.widget.RecyclerView { *; }
|
||||
|
||||
-keep class com.beemdevelopment.aegis.ui.fragments.*
|
||||
-keep class com.beemdevelopment.aegis.importers.** { *; }
|
||||
|
||||
-keep class * extends com.google.protobuf.GeneratedMessageLite { *; }
|
||||
|
|
|
@ -127,6 +127,7 @@ public class OverallTest extends AegisTest {
|
|||
|
||||
openContextualActionModeOverflowMenu();
|
||||
onView(withText(R.string.action_settings)).perform(click());
|
||||
onView(withId(androidx.preference.R.id.recycler_view)).perform(RecyclerViewActions.actionOnItem(hasDescendant(withText(R.string.pref_security_group_title)), click()));
|
||||
onView(withId(androidx.preference.R.id.recycler_view)).perform(RecyclerViewActions.actionOnItem(hasDescendant(withText(R.string.pref_encryption_title)), click()));
|
||||
onView(withId(android.R.id.button1)).perform(click());
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ import com.beemdevelopment.aegis.helpers.FabScrollHelper;
|
|||
import com.beemdevelopment.aegis.helpers.PermissionHelper;
|
||||
import com.beemdevelopment.aegis.otp.GoogleAuthInfo;
|
||||
import com.beemdevelopment.aegis.otp.GoogleAuthInfoException;
|
||||
import com.beemdevelopment.aegis.ui.fragments.BackupsPreferencesFragment;
|
||||
import com.beemdevelopment.aegis.ui.fragments.PreferencesFragment;
|
||||
import com.beemdevelopment.aegis.ui.views.EntryListView;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
import com.beemdevelopment.aegis.vault.VaultFile;
|
||||
|
@ -141,7 +143,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
|
||||
_btnBackupError = findViewById(R.id.btn_backup_error);
|
||||
_btnBackupError.setOnClickListener(view -> {
|
||||
startPreferencesActivity("pref_backups");
|
||||
startPreferencesActivity(BackupsPreferencesFragment.class, "pref_backups");
|
||||
});
|
||||
|
||||
_fabScrollHelper = new FabScrollHelper(fab);
|
||||
|
@ -402,8 +404,13 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
startActivityForResult(chooserIntent, CODE_SCAN_IMAGE);
|
||||
}
|
||||
|
||||
private void startPreferencesActivity(String preference) {
|
||||
private void startPreferencesActivity() {
|
||||
startPreferencesActivity(null, null);
|
||||
}
|
||||
|
||||
private void startPreferencesActivity(Class<? extends PreferencesFragment> fragmentType, String preference) {
|
||||
Intent intent = new Intent(this, PreferencesActivity.class);
|
||||
intent.putExtra("fragment", fragmentType);
|
||||
intent.putExtra("pref", preference);
|
||||
startActivityForResult(intent, CODE_PREFERENCES);
|
||||
}
|
||||
|
@ -577,7 +584,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_settings: {
|
||||
startPreferencesActivity(null);
|
||||
startPreferencesActivity();
|
||||
return true;
|
||||
}
|
||||
case R.id.action_about: {
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
package com.beemdevelopment.aegis.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
|
||||
public class PreferencesActivity extends AegisActivity {
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.ui.fragments.MainPreferencesFragment;
|
||||
import com.beemdevelopment.aegis.ui.fragments.PreferencesFragment;
|
||||
|
||||
public class PreferencesActivity extends AegisActivity implements
|
||||
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
|
||||
private PreferencesFragment _fragment;
|
||||
|
||||
@Override
|
||||
|
@ -17,28 +25,31 @@ public class PreferencesActivity extends AegisActivity {
|
|||
}
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
_fragment = new PreferencesFragment();
|
||||
_fragment = new MainPreferencesFragment();
|
||||
_fragment.setArguments(getIntent().getExtras());
|
||||
getSupportFragmentManager().beginTransaction().replace(android.R.id.content, _fragment).commit();
|
||||
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(android.R.id.content, _fragment)
|
||||
.commit();
|
||||
|
||||
PreferencesFragment requestedFragment = getRequestedFragment();
|
||||
if (requestedFragment != null) {
|
||||
_fragment = requestedFragment;
|
||||
showFragment(_fragment);
|
||||
}
|
||||
} else {
|
||||
_fragment = (PreferencesFragment) getSupportFragmentManager().findFragmentById(android.R.id.content);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
Intent intent = getIntent();
|
||||
String preference = intent.getStringExtra("pref");
|
||||
if (preference != null) {
|
||||
_fragment.scrollToPreference(preference);
|
||||
intent.removeExtra("pref");
|
||||
}
|
||||
public void onBackPressed() {
|
||||
super.onBackPressed();
|
||||
setTitle(R.string.action_settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
// pass permission request results to the fragment
|
||||
_fragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
|
@ -62,14 +73,45 @@ public class PreferencesActivity extends AegisActivity {
|
|||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
onBackPressed();
|
||||
break;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
onBackPressed();
|
||||
} else {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref) {
|
||||
_fragment = (PreferencesFragment) getSupportFragmentManager().getFragmentFactory().instantiate(getClassLoader(), pref.getFragment());
|
||||
_fragment.setArguments(pref.getExtras());
|
||||
_fragment.setTargetFragment(caller, 0);
|
||||
showFragment(_fragment);
|
||||
|
||||
setTitle(pref.getTitle());
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showFragment(PreferencesFragment fragment) {
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right)
|
||||
.replace(android.R.id.content, fragment)
|
||||
.addToBackStack(null)
|
||||
.commit();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private PreferencesFragment getRequestedFragment() {
|
||||
Class<? extends PreferencesFragment> fragmentType = (Class<? extends PreferencesFragment>) getIntent().getSerializableExtra("fragment");
|
||||
if (fragmentType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return fragmentType.newInstance();
|
||||
} catch (IllegalAccessException | InstantiationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,132 @@
|
|||
package com.beemdevelopment.aegis.ui.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.beemdevelopment.aegis.Preferences;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.Theme;
|
||||
import com.beemdevelopment.aegis.ViewMode;
|
||||
import com.beemdevelopment.aegis.ui.Dialogs;
|
||||
import com.beemdevelopment.aegis.ui.GroupManagerActivity;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class AppearancePreferencesFragment extends PreferencesFragment {
|
||||
private Preference _groupsPreference;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
super.onCreatePreferences(savedInstanceState, rootKey);
|
||||
addPreferencesFromResource(R.xml.preferences_appearance);
|
||||
Preferences prefs = getPreferences();
|
||||
|
||||
_groupsPreference = findPreference("pref_groups");
|
||||
_groupsPreference.setOnPreferenceClickListener(preference -> {
|
||||
Intent intent = new Intent(getActivity(), GroupManagerActivity.class);
|
||||
intent.putExtra("groups", new ArrayList<>(getVault().getGroups()));
|
||||
startActivityForResult(intent, CODE_GROUPS);
|
||||
return true;
|
||||
});
|
||||
|
||||
int currentTheme = prefs.getCurrentTheme().ordinal();
|
||||
Preference darkModePreference = findPreference("pref_dark_mode");
|
||||
darkModePreference.setSummary(String.format("%s: %s", getString(R.string.selected), getResources().getStringArray(R.array.theme_titles)[currentTheme]));
|
||||
darkModePreference.setOnPreferenceClickListener(preference -> {
|
||||
int currentTheme1 = prefs.getCurrentTheme().ordinal();
|
||||
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.choose_theme)
|
||||
.setSingleChoiceItems(R.array.theme_titles, currentTheme1, (dialog, which) -> {
|
||||
int i = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
prefs.setCurrentTheme(Theme.fromInteger(i));
|
||||
|
||||
dialog.dismiss();
|
||||
|
||||
getResult().putExtra("needsRecreate", true);
|
||||
getActivity().recreate();
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create());
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference langPreference = findPreference("pref_lang");
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
langPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
getResult().putExtra("needsRecreate", true);
|
||||
getActivity().recreate();
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
// Setting locale doesn't work on Marshmallow or below
|
||||
langPreference.setVisible(false);
|
||||
}
|
||||
|
||||
int currentViewMode = prefs.getCurrentViewMode().ordinal();
|
||||
Preference viewModePreference = findPreference("pref_view_mode");
|
||||
viewModePreference.setSummary(String.format("%s: %s", getString(R.string.selected), getResources().getStringArray(R.array.view_mode_titles)[currentViewMode]));
|
||||
viewModePreference.setOnPreferenceClickListener(preference -> {
|
||||
int currentViewMode1 = prefs.getCurrentViewMode().ordinal();
|
||||
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.choose_view_mode)
|
||||
.setSingleChoiceItems(R.array.view_mode_titles, currentViewMode1, (dialog, which) -> {
|
||||
int i = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
prefs.setCurrentViewMode(ViewMode.fromInteger(i));
|
||||
viewModePreference.setSummary(String.format("%s: %s", getString(R.string.selected), getResources().getStringArray(R.array.view_mode_titles)[i]));
|
||||
getResult().putExtra("needsRefresh", true);
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create());
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference codeDigitGroupingPreference = findPreference("pref_code_group_size");
|
||||
codeDigitGroupingPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
getResult().putExtra("needsRefresh", true);
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference issuerPreference = findPreference("pref_account_name");
|
||||
issuerPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
getResult().putExtra("needsRefresh", true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (data != null && requestCode == CODE_GROUPS) {
|
||||
onGroupManagerResult(resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
private void onGroupManagerResult(int resultCode, Intent data) {
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
HashSet<String> groups = new HashSet<>(data.getStringArrayListExtra("groups"));
|
||||
|
||||
for (VaultEntry entry : getVault().getEntries()) {
|
||||
if (!groups.contains(entry.getGroup())) {
|
||||
entry.setGroup(null);
|
||||
}
|
||||
}
|
||||
|
||||
saveVault();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
package com.beemdevelopment.aegis.ui.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
import com.beemdevelopment.aegis.Preferences;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.ui.Dialogs;
|
||||
import com.beemdevelopment.aegis.vault.VaultManagerException;
|
||||
|
||||
public class BackupsPreferencesFragment extends PreferencesFragment {
|
||||
private SwitchPreferenceCompat _backupsPreference;
|
||||
private Preference _backupsLocationPreference;
|
||||
private Preference _backupsTriggerPreference;
|
||||
private Preference _backupsVersionsPreference;
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateBackupPreference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
super.onCreatePreferences(savedInstanceState, rootKey);
|
||||
addPreferencesFromResource(R.xml.preferences_backups);
|
||||
Preferences prefs = getPreferences();
|
||||
|
||||
_backupsPreference = findPreference("pref_backups");
|
||||
_backupsPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if ((boolean) newValue) {
|
||||
selectBackupsLocation();
|
||||
} else {
|
||||
prefs.setIsBackupsEnabled(false);
|
||||
updateBackupPreference();
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
Uri backupLocation = prefs.getBackupsLocation();
|
||||
_backupsLocationPreference = findPreference("pref_backups_location");
|
||||
if (backupLocation != null) {
|
||||
_backupsLocationPreference.setSummary(String.format("%s: %s", getString(R.string.pref_backups_location_summary), Uri.decode(backupLocation.toString())));
|
||||
}
|
||||
_backupsLocationPreference.setOnPreferenceClickListener(preference -> {
|
||||
selectBackupsLocation();
|
||||
return false;
|
||||
});
|
||||
|
||||
_backupsTriggerPreference = findPreference("pref_backups_trigger");
|
||||
_backupsTriggerPreference.setOnPreferenceClickListener(preference -> {
|
||||
if (prefs.isBackupsEnabled()) {
|
||||
try {
|
||||
getVault().backup();
|
||||
Toast.makeText(getActivity(), R.string.backup_successful, Toast.LENGTH_LONG).show();
|
||||
} catch (VaultManagerException e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(getContext(), R.string.backup_error, e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
_backupsVersionsPreference = findPreference("pref_backups_versions");
|
||||
_backupsVersionsPreference.setSummary(getResources().getQuantityString(R.plurals.pref_backups_versions_summary, prefs.getBackupsVersionCount(), prefs.getBackupsVersionCount()));
|
||||
_backupsVersionsPreference.setOnPreferenceClickListener(preference -> {
|
||||
Dialogs.showBackupVersionsPickerDialog(getActivity(), number -> {
|
||||
number = number * 5 + 5;
|
||||
prefs.setBackupsVersionCount(number);
|
||||
_backupsVersionsPreference.setSummary(getResources().getQuantityString(R.plurals.pref_backups_versions_summary, prefs.getBackupsVersionCount(), prefs.getBackupsVersionCount()));
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (data != null && requestCode == CODE_BACKUPS) {
|
||||
onSelectBackupsLocationResult(resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
private void onSelectBackupsLocationResult(int resultCode, Intent data) {
|
||||
Uri uri = data.getData();
|
||||
if (resultCode != Activity.RESULT_OK || uri == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int flags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
getContext().getContentResolver().takePersistableUriPermission(data.getData(), flags);
|
||||
|
||||
Preferences prefs = getPreferences();
|
||||
prefs.setBackupsLocation(uri);
|
||||
prefs.setIsBackupsEnabled(true);
|
||||
prefs.setBackupsError(null);
|
||||
_backupsLocationPreference.setSummary(String.format("%s: %s", getString(R.string.pref_backups_location_summary), Uri.decode(uri.toString())));
|
||||
updateBackupPreference();
|
||||
}
|
||||
|
||||
private void updateBackupPreference() {
|
||||
boolean encrypted = getVault().isEncryptionEnabled();
|
||||
boolean backupEnabled = getPreferences().isBackupsEnabled() && encrypted;
|
||||
_backupsPreference.setChecked(backupEnabled);
|
||||
_backupsPreference.setEnabled(encrypted);
|
||||
_backupsLocationPreference.setVisible(backupEnabled);
|
||||
_backupsTriggerPreference.setVisible(backupEnabled);
|
||||
_backupsVersionsPreference.setVisible(backupEnabled);
|
||||
}
|
||||
|
||||
private void selectBackupsLocation() {
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
|
||||
|
||||
getApp().setBlockAutoLock(true);
|
||||
startActivityForResult(intent, CODE_BACKUPS);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.beemdevelopment.aegis.ui.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
|
||||
public class BehaviorPreferencesFragment extends PreferencesFragment {
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
super.onCreatePreferences(savedInstanceState, rootKey);
|
||||
addPreferencesFromResource(R.xml.preferences_behavior);
|
||||
|
||||
Preference copyOnTapPreference = findPreference("pref_copy_on_tap");
|
||||
copyOnTapPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
getResult().putExtra("needsRefresh", true);
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference entryHighlightPreference = findPreference("pref_highlight_entry");
|
||||
entryHighlightPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
getResult().putExtra("needsRefresh", true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.beemdevelopment.aegis.ui.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.beemdevelopment.aegis.R;
|
||||
|
||||
public class MainPreferencesFragment extends PreferencesFragment {
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
super.onCreatePreferences(savedInstanceState, rootKey);
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package com.beemdevelopment.aegis.ui.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
import com.beemdevelopment.aegis.AegisApplication;
|
||||
import com.beemdevelopment.aegis.Preferences;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.ui.Dialogs;
|
||||
import com.beemdevelopment.aegis.vault.VaultManager;
|
||||
import com.beemdevelopment.aegis.vault.VaultManagerException;
|
||||
|
||||
public abstract class PreferencesFragment extends PreferenceFragmentCompat {
|
||||
// activity request codes
|
||||
public static final int CODE_IMPORT = 0;
|
||||
public static final int CODE_IMPORT_DECRYPT = 1;
|
||||
public static final int CODE_SLOTS = 2;
|
||||
public static final int CODE_GROUPS = 3;
|
||||
public static final int CODE_SELECT_ENTRIES = 4;
|
||||
public static final int CODE_EXPORT = 5;
|
||||
public static final int CODE_EXPORT_PLAIN = 6;
|
||||
public static final int CODE_EXPORT_GOOGLE_URI = 7;
|
||||
public static final int CODE_BACKUPS = 8;
|
||||
|
||||
private AegisApplication _app;
|
||||
private Intent _result;
|
||||
private Preferences _prefs;
|
||||
private VaultManager _vault;
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
_app = (AegisApplication) getActivity().getApplication();
|
||||
_prefs = _app.getPreferences();
|
||||
_vault = _app.getVaultManager();
|
||||
|
||||
setResult(new Intent());
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
Intent intent = getActivity().getIntent();
|
||||
String preference = intent.getStringExtra("pref");
|
||||
if (preference != null) {
|
||||
scrollToPreference(preference);
|
||||
intent.removeExtra("pref");
|
||||
}
|
||||
}
|
||||
|
||||
public Intent getResult() {
|
||||
return _result;
|
||||
}
|
||||
|
||||
public void setResult(Intent result) {
|
||||
_result = result;
|
||||
getActivity().setResult(Activity.RESULT_OK, _result);
|
||||
}
|
||||
|
||||
protected AegisApplication getApp() {
|
||||
return _app;
|
||||
}
|
||||
|
||||
protected Preferences getPreferences() {
|
||||
return _prefs;
|
||||
}
|
||||
|
||||
protected VaultManager getVault() {
|
||||
return _vault;
|
||||
}
|
||||
|
||||
protected boolean saveVault() {
|
||||
try {
|
||||
_vault.save(true);
|
||||
} catch (VaultManagerException e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(getContext(), R.string.saving_error, e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,410 @@
|
|||
package com.beemdevelopment.aegis.ui.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.biometric.BiometricPrompt;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
import com.beemdevelopment.aegis.BuildConfig;
|
||||
import com.beemdevelopment.aegis.Preferences;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.crypto.KeyStoreHandle;
|
||||
import com.beemdevelopment.aegis.crypto.KeyStoreHandleException;
|
||||
import com.beemdevelopment.aegis.helpers.BiometricSlotInitializer;
|
||||
import com.beemdevelopment.aegis.helpers.BiometricsHelper;
|
||||
import com.beemdevelopment.aegis.services.NotificationService;
|
||||
import com.beemdevelopment.aegis.ui.Dialogs;
|
||||
import com.beemdevelopment.aegis.ui.SlotManagerActivity;
|
||||
import com.beemdevelopment.aegis.ui.preferences.SwitchPreference;
|
||||
import com.beemdevelopment.aegis.ui.tasks.PasswordSlotDecryptTask;
|
||||
import com.beemdevelopment.aegis.vault.VaultFileCredentials;
|
||||
import com.beemdevelopment.aegis.vault.VaultManagerException;
|
||||
import com.beemdevelopment.aegis.vault.slots.BiometricSlot;
|
||||
import com.beemdevelopment.aegis.vault.slots.PasswordSlot;
|
||||
import com.beemdevelopment.aegis.vault.slots.Slot;
|
||||
import com.beemdevelopment.aegis.vault.slots.SlotException;
|
||||
import com.beemdevelopment.aegis.vault.slots.SlotList;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
import static android.text.TextUtils.isDigitsOnly;
|
||||
|
||||
public class SecurityPreferencesFragment extends PreferencesFragment {
|
||||
private SwitchPreference _encryptionPreference;
|
||||
private SwitchPreference _biometricsPreference;
|
||||
private Preference _autoLockPreference;
|
||||
private Preference _setPasswordPreference;
|
||||
private Preference _slotsPreference;
|
||||
private Preference _passwordReminderPreference;
|
||||
private SwitchPreferenceCompat _pinKeyboardPreference;
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateEncryptionPreferences();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
super.onCreatePreferences(savedInstanceState, rootKey);
|
||||
addPreferencesFromResource(R.xml.preferences_security);
|
||||
|
||||
Preference tapToRevealPreference = findPreference("pref_tap_to_reveal");
|
||||
tapToRevealPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
getResult().putExtra("needsRefresh", true);
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference screenPreference = findPreference("pref_secure_screen");
|
||||
screenPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
getResult().putExtra("needsRecreate", true);
|
||||
Window window = getActivity().getWindow();
|
||||
if ((boolean) newValue) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||
} else {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference tapToRevealTimePreference = findPreference("pref_tap_to_reveal_time");
|
||||
tapToRevealTimePreference.setSummary(getPreferences().getTapToRevealTime() + " seconds");
|
||||
tapToRevealTimePreference.setOnPreferenceClickListener(preference -> {
|
||||
Dialogs.showNumberPickerDialog(getActivity(), number -> {
|
||||
getPreferences().setTapToRevealTime(number);
|
||||
tapToRevealTimePreference.setSummary(number + " seconds");
|
||||
getResult().putExtra("needsRefresh", true);
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
_encryptionPreference = findPreference("pref_encryption");
|
||||
_encryptionPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if (!getVault().isEncryptionEnabled()) {
|
||||
Dialogs.showSetPasswordDialog(getActivity(), new EnableEncryptionListener());
|
||||
} else {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.disable_encryption)
|
||||
.setMessage(getString(R.string.disable_encryption_description))
|
||||
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
|
||||
try {
|
||||
getVault().disableEncryption();
|
||||
} catch (VaultManagerException e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(getContext(), R.string.disable_encryption_error, e);
|
||||
return;
|
||||
}
|
||||
|
||||
// clear the KeyStore
|
||||
try {
|
||||
KeyStoreHandle handle = new KeyStoreHandle();
|
||||
handle.clear();
|
||||
} catch (KeyStoreHandleException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
getActivity().stopService(new Intent(getActivity(), NotificationService.class));
|
||||
updateEncryptionPreferences();
|
||||
})
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.create());
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
_biometricsPreference = findPreference("pref_biometrics");
|
||||
_biometricsPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
VaultFileCredentials creds = getVault().getCredentials();
|
||||
SlotList slots = creds.getSlots();
|
||||
|
||||
if (!slots.has(BiometricSlot.class)) {
|
||||
if (BiometricsHelper.isAvailable(getContext())) {
|
||||
BiometricSlotInitializer initializer = new BiometricSlotInitializer(SecurityPreferencesFragment.this, new RegisterBiometricsListener());
|
||||
BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder()
|
||||
.setTitle(getString(R.string.set_up_biometric))
|
||||
.setNegativeButtonText(getString(android.R.string.cancel))
|
||||
.build();
|
||||
initializer.authenticate(info);
|
||||
}
|
||||
} else {
|
||||
// remove the biometric slot
|
||||
BiometricSlot slot = slots.find(BiometricSlot.class);
|
||||
slots.remove(slot);
|
||||
getVault().setCredentials(creds);
|
||||
|
||||
// remove the KeyStore key
|
||||
try {
|
||||
KeyStoreHandle handle = new KeyStoreHandle();
|
||||
handle.deleteKey(slot.getUUID().toString());
|
||||
} catch (KeyStoreHandleException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
saveVault();
|
||||
updateEncryptionPreferences();
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
_setPasswordPreference = findPreference("pref_password");
|
||||
_setPasswordPreference.setOnPreferenceClickListener(preference -> {
|
||||
Dialogs.showSetPasswordDialog(getActivity(), new SetPasswordListener());
|
||||
return false;
|
||||
});
|
||||
|
||||
_slotsPreference = findPreference("pref_slots");
|
||||
_slotsPreference.setOnPreferenceClickListener(preference -> {
|
||||
Intent intent = new Intent(getActivity(), SlotManagerActivity.class);
|
||||
intent.putExtra("creds", getVault().getCredentials());
|
||||
startActivityForResult(intent, CODE_SLOTS);
|
||||
return true;
|
||||
});
|
||||
|
||||
_pinKeyboardPreference = findPreference("pref_pin_keyboard");
|
||||
_pinKeyboardPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if (!(boolean) newValue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Dialogs.showPasswordInputDialog(getActivity(), R.string.set_password_confirm, R.string.pin_keyboard_description, password -> {
|
||||
if (isDigitsOnly(new String(password))) {
|
||||
List<PasswordSlot> slots = getVault().getCredentials().getSlots().findAll(PasswordSlot.class);
|
||||
PasswordSlotDecryptTask.Params params = new PasswordSlotDecryptTask.Params(slots, password);
|
||||
PasswordSlotDecryptTask task = new PasswordSlotDecryptTask(getActivity(), new PasswordConfirmationListener());
|
||||
task.execute(getLifecycle(), params);
|
||||
} else {
|
||||
_pinKeyboardPreference.setChecked(false);
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.pin_keyboard_error)
|
||||
.setMessage(R.string.pin_keyboard_error_description)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.create());
|
||||
}
|
||||
}, dialog -> {
|
||||
_pinKeyboardPreference.setChecked(false);
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
_autoLockPreference = findPreference("pref_auto_lock");
|
||||
_autoLockPreference.setSummary(getAutoLockSummary());
|
||||
_autoLockPreference.setOnPreferenceClickListener((preference) -> {
|
||||
final int[] items = Preferences.AUTO_LOCK_SETTINGS;
|
||||
final String[] textItems = getResources().getStringArray(R.array.pref_auto_lock_types);
|
||||
final boolean[] checkedItems = new boolean[items.length];
|
||||
for (int i = 0; i < items.length; i++) {
|
||||
checkedItems[i] = getPreferences().isAutoLockTypeEnabled(items[i]);
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.pref_auto_lock_prompt)
|
||||
.setMultiChoiceItems(textItems, checkedItems, (dialog, index, isChecked) -> checkedItems[index] = isChecked)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
int autoLock = Preferences.AUTO_LOCK_OFF;
|
||||
for (int i = 0; i < checkedItems.length; i++) {
|
||||
if (checkedItems[i]) {
|
||||
autoLock |= items[i];
|
||||
}
|
||||
}
|
||||
|
||||
getPreferences().setAutoLockMask(autoLock);
|
||||
_autoLockPreference.setSummary(getAutoLockSummary());
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null);
|
||||
Dialogs.showSecureDialog(builder.create());
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
_passwordReminderPreference = findPreference("pref_password_reminder");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (data != null && requestCode == CODE_SLOTS) {
|
||||
onSlotManagerResult(resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
private void onSlotManagerResult(int resultCode, Intent data) {
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
VaultFileCredentials creds = (VaultFileCredentials) data.getSerializableExtra("creds");
|
||||
getVault().setCredentials(creds);
|
||||
saveVault();
|
||||
updateEncryptionPreferences();
|
||||
}
|
||||
|
||||
private void updateEncryptionPreferences() {
|
||||
boolean encrypted = getVault().isEncryptionEnabled();
|
||||
_encryptionPreference.setChecked(encrypted, true);
|
||||
_setPasswordPreference.setVisible(encrypted);
|
||||
_biometricsPreference.setVisible(encrypted);
|
||||
_slotsPreference.setEnabled(encrypted);
|
||||
_autoLockPreference.setVisible(encrypted);
|
||||
_pinKeyboardPreference.setVisible(encrypted);
|
||||
|
||||
if (encrypted) {
|
||||
SlotList slots = getVault().getCredentials().getSlots();
|
||||
boolean multiPassword = slots.findAll(PasswordSlot.class).size() > 1;
|
||||
boolean multiBio = slots.findAll(BiometricSlot.class).size() > 1;
|
||||
boolean showSlots = BuildConfig.DEBUG || multiPassword || multiBio;
|
||||
boolean canUseBio = BiometricsHelper.isAvailable(getContext());
|
||||
_setPasswordPreference.setEnabled(!multiPassword);
|
||||
_biometricsPreference.setEnabled(canUseBio && !multiBio);
|
||||
_biometricsPreference.setChecked(slots.has(BiometricSlot.class), true);
|
||||
_slotsPreference.setVisible(showSlots);
|
||||
_passwordReminderPreference.setVisible(slots.has(BiometricSlot.class));
|
||||
} else {
|
||||
_setPasswordPreference.setEnabled(false);
|
||||
_biometricsPreference.setEnabled(false);
|
||||
_biometricsPreference.setChecked(false, true);
|
||||
_slotsPreference.setVisible(false);
|
||||
_passwordReminderPreference.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
private String getAutoLockSummary() {
|
||||
final int[] settings = Preferences.AUTO_LOCK_SETTINGS;
|
||||
final String[] descriptions = getResources().getStringArray(R.array.pref_auto_lock_types);
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < settings.length; i++) {
|
||||
if (getPreferences().isAutoLockTypeEnabled(settings[i])) {
|
||||
if (builder.length() != 0) {
|
||||
builder.append(", ");
|
||||
}
|
||||
|
||||
builder.append(descriptions[i].toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
if (builder.length() == 0) {
|
||||
return getString(R.string.pref_auto_lock_summary_disabled);
|
||||
}
|
||||
|
||||
return getString(R.string.pref_auto_lock_summary, builder.toString());
|
||||
}
|
||||
|
||||
private class SetPasswordListener implements Dialogs.SlotListener {
|
||||
@Override
|
||||
public void onSlotResult(Slot slot, Cipher cipher) {
|
||||
VaultFileCredentials creds = getVault().getCredentials();
|
||||
SlotList slots = creds.getSlots();
|
||||
|
||||
try {
|
||||
// encrypt the master key for this slot
|
||||
slot.setKey(creds.getKey(), cipher);
|
||||
|
||||
// remove the old master password slot
|
||||
PasswordSlot oldSlot = creds.getSlots().find(PasswordSlot.class);
|
||||
if (oldSlot != null) {
|
||||
slots.remove(oldSlot);
|
||||
}
|
||||
|
||||
// add the new master password slot
|
||||
slots.add(slot);
|
||||
} catch (SlotException e) {
|
||||
onException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
getVault().setCredentials(creds);
|
||||
saveVault();
|
||||
|
||||
if (getPreferences().isPinKeyboardEnabled()) {
|
||||
_pinKeyboardPreference.setChecked(false);
|
||||
Toast.makeText(getContext(), R.string.pin_keyboard_disabled, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onException(Exception e) {
|
||||
e.printStackTrace();
|
||||
updateEncryptionPreferences();
|
||||
Dialogs.showErrorDialog(getContext(), R.string.encryption_set_password_error, e);
|
||||
}
|
||||
}
|
||||
|
||||
private class RegisterBiometricsListener implements BiometricSlotInitializer.Listener {
|
||||
@Override
|
||||
public void onInitializeSlot(BiometricSlot slot, Cipher cipher) {
|
||||
VaultFileCredentials creds = getVault().getCredentials();
|
||||
try {
|
||||
slot.setKey(creds.getKey(), cipher);
|
||||
} catch (SlotException e) {
|
||||
e.printStackTrace();
|
||||
onSlotInitializationFailed(0, e.toString());
|
||||
return;
|
||||
}
|
||||
creds.getSlots().add(slot);
|
||||
getVault().setCredentials(creds);
|
||||
|
||||
saveVault();
|
||||
updateEncryptionPreferences();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSlotInitializationFailed(int errorCode, @NonNull CharSequence errString) {
|
||||
if (!BiometricsHelper.isCanceled(errorCode)) {
|
||||
Dialogs.showErrorDialog(getContext(), R.string.encryption_enable_biometrics_error, errString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class EnableEncryptionListener implements Dialogs.SlotListener {
|
||||
@Override
|
||||
public void onSlotResult(Slot slot, Cipher cipher) {
|
||||
VaultFileCredentials creds = new VaultFileCredentials();
|
||||
|
||||
try {
|
||||
slot.setKey(creds.getKey(), cipher);
|
||||
creds.getSlots().add(slot);
|
||||
getVault().enableEncryption(creds);
|
||||
} catch (VaultManagerException | SlotException e) {
|
||||
onException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
getActivity().startService(new Intent(getActivity(), NotificationService.class));
|
||||
updateEncryptionPreferences();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onException(Exception e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(getContext(), R.string.encryption_set_password_error, e);
|
||||
}
|
||||
}
|
||||
|
||||
private class PasswordConfirmationListener implements PasswordSlotDecryptTask.Callback {
|
||||
@Override
|
||||
public void onTaskFinished(PasswordSlotDecryptTask.Result result) {
|
||||
if (result != null) {
|
||||
_pinKeyboardPreference.setChecked(true);
|
||||
} else {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.pin_keyboard_error)
|
||||
.setMessage(R.string.invalid_password)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.create());
|
||||
_pinKeyboardPreference.setChecked(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,485 @@
|
|||
package com.beemdevelopment.aegis.ui.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.beemdevelopment.aegis.BuildConfig;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.helpers.SpinnerHelper;
|
||||
import com.beemdevelopment.aegis.importers.AegisImporter;
|
||||
import com.beemdevelopment.aegis.importers.DatabaseImporter;
|
||||
import com.beemdevelopment.aegis.importers.DatabaseImporterEntryException;
|
||||
import com.beemdevelopment.aegis.importers.DatabaseImporterException;
|
||||
import com.beemdevelopment.aegis.ui.AuthActivity;
|
||||
import com.beemdevelopment.aegis.ui.Dialogs;
|
||||
import com.beemdevelopment.aegis.ui.SelectEntriesActivity;
|
||||
import com.beemdevelopment.aegis.ui.models.ImportEntry;
|
||||
import com.beemdevelopment.aegis.ui.tasks.ExportTask;
|
||||
import com.beemdevelopment.aegis.util.UUIDMap;
|
||||
import com.beemdevelopment.aegis.vault.VaultBackupManager;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
import com.beemdevelopment.aegis.vault.VaultFileCredentials;
|
||||
import com.beemdevelopment.aegis.vault.VaultManager;
|
||||
import com.beemdevelopment.aegis.vault.VaultManagerException;
|
||||
import com.beemdevelopment.aegis.vault.slots.Slot;
|
||||
import com.beemdevelopment.aegis.vault.slots.SlotException;
|
||||
import com.topjohnwu.superuser.Shell;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
public class ToolsPreferencesFragment extends PreferencesFragment {
|
||||
// keep a reference to the type of database converter the user selected
|
||||
private Class<? extends DatabaseImporter> _importerType;
|
||||
private AegisImporter.State _importerState;
|
||||
private UUIDMap<VaultEntry> _importerEntries;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
super.onCreatePreferences(savedInstanceState, rootKey);
|
||||
addPreferencesFromResource(R.xml.preferences_tools);
|
||||
|
||||
Preference importPreference = findPreference("pref_import");
|
||||
importPreference.setOnPreferenceClickListener(preference -> {
|
||||
Dialogs.showImportersDialog(getContext(), false, definition -> {
|
||||
_importerType = definition.getType();
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.setType("*/*");
|
||||
startActivityForResult(intent, CODE_IMPORT);
|
||||
});
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference importAppPreference = findPreference("pref_import_app");
|
||||
importAppPreference.setOnPreferenceClickListener(preference -> {
|
||||
Dialogs.showImportersDialog(getContext(), true, definition -> {
|
||||
DatabaseImporter importer = DatabaseImporter.create(getContext(), definition.getType());
|
||||
importApp(importer);
|
||||
});
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference exportPreference = findPreference("pref_export");
|
||||
exportPreference.setOnPreferenceClickListener(preference -> {
|
||||
startExport();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (data != null) {
|
||||
switch (requestCode) {
|
||||
case CODE_IMPORT:
|
||||
onImportResult(resultCode, data);
|
||||
break;
|
||||
case CODE_IMPORT_DECRYPT:
|
||||
onImportDecryptResult(resultCode, data);
|
||||
break;
|
||||
case CODE_SELECT_ENTRIES:
|
||||
onSelectEntriesResult(resultCode, data);
|
||||
break;
|
||||
case CODE_EXPORT:
|
||||
// intentional fallthrough
|
||||
case CODE_EXPORT_PLAIN:
|
||||
// intentional fallthrough
|
||||
case CODE_EXPORT_GOOGLE_URI:
|
||||
onExportResult(requestCode, resultCode, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void importApp(DatabaseImporter importer) {
|
||||
// obtain the global root shell and close it immediately after we're done
|
||||
// TODO: find a way to use SuFileInputStream with Shell.newInstance()
|
||||
try (Shell shell = Shell.getShell()) {
|
||||
if (!shell.isRoot()) {
|
||||
Toast.makeText(getActivity(), R.string.root_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
DatabaseImporter.State state = importer.readFromApp();
|
||||
processImporterState(state);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(getActivity(), R.string.app_lookup_error, Toast.LENGTH_SHORT).show();
|
||||
} catch (IOException | DatabaseImporterException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(getActivity(), R.string.reading_file_error, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void processImporterState(DatabaseImporter.State state) {
|
||||
try {
|
||||
if (state.isEncrypted()) {
|
||||
// temporary special case for encrypted Aegis vaults
|
||||
if (state instanceof AegisImporter.EncryptedState) {
|
||||
_importerState = state;
|
||||
|
||||
Intent intent = new Intent(getActivity(), AuthActivity.class);
|
||||
intent.putExtra("slots", ((AegisImporter.EncryptedState) state).getSlots());
|
||||
startActivityForResult(intent, CODE_IMPORT_DECRYPT);
|
||||
} else {
|
||||
state.decrypt(getActivity(), new DatabaseImporter.DecryptListener() {
|
||||
@Override
|
||||
public void onStateDecrypted(DatabaseImporter.State state) {
|
||||
importDatabase(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(getContext(), R.string.decryption_error, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
importDatabase(state);
|
||||
}
|
||||
} catch (DatabaseImporterException e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(getContext(), R.string.parsing_file_error, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void onImportDecryptResult(int resultCode, Intent data) {
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
_importerState = null;
|
||||
return;
|
||||
}
|
||||
|
||||
VaultFileCredentials creds = (VaultFileCredentials) data.getSerializableExtra("creds");
|
||||
DatabaseImporter.State state;
|
||||
try {
|
||||
state = ((AegisImporter.EncryptedState) _importerState).decrypt(creds);
|
||||
} catch (DatabaseImporterException e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(getContext(), R.string.decryption_error, e);
|
||||
return;
|
||||
}
|
||||
|
||||
importDatabase(state);
|
||||
_importerState = null;
|
||||
}
|
||||
|
||||
private void onImportResult(int resultCode, Intent data) {
|
||||
Uri uri = data.getData();
|
||||
if (resultCode != Activity.RESULT_OK || uri == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (InputStream stream = getContext().getContentResolver().openInputStream(uri)) {
|
||||
DatabaseImporter importer = DatabaseImporter.create(getContext(), _importerType);
|
||||
DatabaseImporter.State state = importer.read(stream);
|
||||
processImporterState(state);
|
||||
} catch (FileNotFoundException e) {
|
||||
Toast.makeText(getActivity(), R.string.file_not_found, Toast.LENGTH_SHORT).show();
|
||||
} catch (DatabaseImporterException | IOException e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(getContext(), R.string.reading_file_error, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void importDatabase(DatabaseImporter.State state) {
|
||||
DatabaseImporter.Result result;
|
||||
try {
|
||||
result = state.convert();
|
||||
} catch (DatabaseImporterException e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(getContext(), R.string.parsing_file_error, e);
|
||||
return;
|
||||
}
|
||||
|
||||
_importerEntries = result.getEntries();
|
||||
List<ImportEntry> entries = new ArrayList<>();
|
||||
for (VaultEntry entry : _importerEntries) {
|
||||
entries.add(new ImportEntry(entry));
|
||||
}
|
||||
|
||||
Intent intent = new Intent(getActivity(), SelectEntriesActivity.class);
|
||||
intent.putExtra("entries", (ArrayList<ImportEntry>) entries);
|
||||
intent.putExtra("errors", (ArrayList<DatabaseImporterEntryException>) result.getErrors());
|
||||
intent.putExtra("vaultContainsEntries", getVault().getEntries().size() > 0);
|
||||
startActivityForResult(intent, CODE_SELECT_ENTRIES);
|
||||
}
|
||||
|
||||
private void startExport() {
|
||||
View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_export, null);
|
||||
TextView warningText = view.findViewById(R.id.text_export_warning);
|
||||
CheckBox checkBoxEncrypt = view.findViewById(R.id.checkbox_export_encrypt);
|
||||
CheckBox checkBoxAccept = view.findViewById(R.id.checkbox_accept);
|
||||
Spinner spinner = view.findViewById(R.id.spinner_export_format);
|
||||
SpinnerHelper.fillSpinner(getContext(), spinner, R.array.export_formats);
|
||||
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
checkBoxEncrypt.setChecked(position == 0);
|
||||
checkBoxEncrypt.setEnabled(position == 0);
|
||||
warningText.setVisibility(checkBoxEncrypt.isChecked() ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(getContext())
|
||||
.setTitle(R.string.pref_export_summary)
|
||||
.setView(view)
|
||||
.setNeutralButton(R.string.share, null)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create();
|
||||
|
||||
dialog.setOnShowListener(d -> {
|
||||
Button btnPos = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
Button btnNeutral = dialog.getButton(AlertDialog.BUTTON_NEUTRAL);
|
||||
|
||||
checkBoxEncrypt.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
warningText.setVisibility(isChecked ? View.GONE : View.VISIBLE);
|
||||
checkBoxAccept.setVisibility(isChecked ? View.GONE : View.VISIBLE);
|
||||
checkBoxAccept.setChecked(false);
|
||||
btnPos.setEnabled(isChecked);
|
||||
btnNeutral.setEnabled(isChecked);
|
||||
});
|
||||
|
||||
checkBoxAccept.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
btnPos.setEnabled(isChecked);
|
||||
btnNeutral.setEnabled(isChecked);
|
||||
});
|
||||
|
||||
btnPos.setOnClickListener(v -> {
|
||||
dialog.dismiss();
|
||||
|
||||
if (!checkBoxEncrypt.isChecked() && !checkBoxAccept.isChecked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int requestCode = getExportRequestCode(spinner.getSelectedItemPosition(), checkBoxEncrypt.isChecked());
|
||||
VaultBackupManager.FileInfo fileInfo = getExportFileInfo(spinner.getSelectedItemPosition(), checkBoxEncrypt.isChecked());
|
||||
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
.setType(getExportMimeType(requestCode))
|
||||
.putExtra(Intent.EXTRA_TITLE, fileInfo.toString());
|
||||
startActivityForResult(intent, requestCode);
|
||||
});
|
||||
|
||||
btnNeutral.setOnClickListener(v -> {
|
||||
dialog.dismiss();
|
||||
|
||||
if (!checkBoxEncrypt.isChecked() && !checkBoxAccept.isChecked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
File file;
|
||||
try {
|
||||
VaultBackupManager.FileInfo fileInfo = getExportFileInfo(spinner.getSelectedItemPosition(), checkBoxEncrypt.isChecked());
|
||||
file = File.createTempFile(fileInfo.getFilename() + "-", "." + fileInfo.getExtension(), getExportCacheDir());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(getContext(), R.string.exporting_vault_error, e);
|
||||
return;
|
||||
}
|
||||
|
||||
int requestCode = getExportRequestCode(spinner.getSelectedItemPosition(), checkBoxEncrypt.isChecked());
|
||||
startExportVault(requestCode, cb -> {
|
||||
try (OutputStream stream = new FileOutputStream(file)) {
|
||||
cb.exportVault(stream);
|
||||
} catch (IOException | VaultManagerException e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(getContext(), R.string.exporting_vault_error, e);
|
||||
return;
|
||||
}
|
||||
|
||||
Uri uri = FileProvider.getUriForFile(getContext(), BuildConfig.FILE_PROVIDER_AUTHORITY, file);
|
||||
Intent intent = new Intent(Intent.ACTION_SEND)
|
||||
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
.setType(getExportMimeType(requestCode))
|
||||
.putExtra(Intent.EXTRA_STREAM, uri);
|
||||
Intent chooser = Intent.createChooser(intent, getString(R.string.pref_export_summary));
|
||||
startActivity(chooser);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Dialogs.showSecureDialog(dialog);
|
||||
}
|
||||
|
||||
private void onSelectEntriesResult(int resultCode, Intent data) {
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean wipeEntries = data.getBooleanExtra("wipeEntries", false);
|
||||
if (wipeEntries) {
|
||||
getVault().wipeEntries();
|
||||
}
|
||||
|
||||
List<ImportEntry> selectedEntries = (ArrayList<ImportEntry>) data.getSerializableExtra("entries");
|
||||
for (ImportEntry selectedEntry : selectedEntries) {
|
||||
VaultEntry savedEntry = _importerEntries.getByUUID(selectedEntry.getUUID());
|
||||
|
||||
// temporary: randomize the UUID of duplicate entries and add them anyway
|
||||
if (getVault().isEntryDuplicate(savedEntry)) {
|
||||
savedEntry.resetUUID();
|
||||
}
|
||||
|
||||
getVault().addEntry(savedEntry);
|
||||
}
|
||||
|
||||
_importerEntries = null;
|
||||
if (!saveVault()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String toastMessage = getResources().getQuantityString(R.plurals.imported_entries_count, selectedEntries.size(), selectedEntries.size());
|
||||
Toast.makeText(getContext(), toastMessage, Toast.LENGTH_SHORT).show();
|
||||
|
||||
getResult().putExtra("needsRecreate", true);
|
||||
}
|
||||
|
||||
private static int getExportRequestCode(int spinnerPos, boolean encrypt) {
|
||||
if (spinnerPos == 0) {
|
||||
return encrypt ? CODE_EXPORT : CODE_EXPORT_PLAIN;
|
||||
}
|
||||
|
||||
return CODE_EXPORT_GOOGLE_URI;
|
||||
}
|
||||
|
||||
private static VaultBackupManager.FileInfo getExportFileInfo(int spinnerPos, boolean encrypt) {
|
||||
if (spinnerPos == 0) {
|
||||
String filename = encrypt ? VaultManager.FILENAME_PREFIX_EXPORT : VaultManager.FILENAME_PREFIX_EXPORT_PLAIN;
|
||||
return new VaultBackupManager.FileInfo(filename);
|
||||
}
|
||||
|
||||
return new VaultBackupManager.FileInfo(VaultManager.FILENAME_PREFIX_EXPORT_URI, "txt");
|
||||
}
|
||||
|
||||
private static String getExportMimeType(int requestCode) {
|
||||
return requestCode == CODE_EXPORT_GOOGLE_URI ? "text/plain" : "application/json";
|
||||
}
|
||||
|
||||
private File getExportCacheDir() throws IOException {
|
||||
File dir = new File(getContext().getCacheDir(), "export");
|
||||
if (!dir.exists() && !dir.mkdir()) {
|
||||
throw new IOException(String.format("Unable to create directory %s", dir));
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
private void startExportVault(int requestCode, StartExportCallback cb) {
|
||||
switch (requestCode) {
|
||||
case CODE_EXPORT:
|
||||
if (getVault().isEncryptionEnabled()) {
|
||||
cb.exportVault(stream -> getVault().export(stream));
|
||||
} else {
|
||||
Dialogs.showSetPasswordDialog(getActivity(), new Dialogs.SlotListener() {
|
||||
@Override
|
||||
public void onSlotResult(Slot slot, Cipher cipher) {
|
||||
VaultFileCredentials creds = new VaultFileCredentials();
|
||||
|
||||
try {
|
||||
slot.setKey(creds.getKey(), cipher);
|
||||
creds.getSlots().add(slot);
|
||||
} catch (SlotException e) {
|
||||
onException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
cb.exportVault(stream -> getVault().export(stream, creds));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onException(Exception e) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
case CODE_EXPORT_PLAIN:
|
||||
cb.exportVault((stream) -> getVault().export(stream, null));
|
||||
break;
|
||||
case CODE_EXPORT_GOOGLE_URI:
|
||||
cb.exportVault((stream) -> getVault().exportGoogleUris(stream));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void onExportResult(int requestCode, int resultCode, Intent data) {
|
||||
Uri uri = data.getData();
|
||||
if (resultCode != Activity.RESULT_OK || uri == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
startExportVault(requestCode, cb -> {
|
||||
File file;
|
||||
OutputStream outStream = null;
|
||||
try {
|
||||
file = File.createTempFile(VaultManager.FILENAME_PREFIX_EXPORT + "-", ".json", getExportCacheDir());
|
||||
outStream = new FileOutputStream(file);
|
||||
cb.exportVault(outStream);
|
||||
|
||||
new ExportTask(getContext(), new ExportResultListener()).execute(getLifecycle(), new ExportTask.Params(file, uri));
|
||||
} catch (VaultManagerException | IOException e) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(getContext(), R.string.exporting_vault_error, e);
|
||||
} finally {
|
||||
try {
|
||||
if (outStream != null) {
|
||||
outStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class ExportResultListener implements ExportTask.Callback {
|
||||
@Override
|
||||
public void onTaskFinished(Exception e) {
|
||||
if (e != null) {
|
||||
e.printStackTrace();
|
||||
Dialogs.showErrorDialog(getContext(), R.string.exporting_vault_error, e);
|
||||
} else {
|
||||
Toast.makeText(getContext(), getString(R.string.exported_vault), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private interface FinishExportCallback {
|
||||
void exportVault(OutputStream stream) throws IOException, VaultManagerException;
|
||||
}
|
||||
|
||||
private interface StartExportCallback {
|
||||
void exportVault(FinishExportCallback exportCb);
|
||||
}
|
||||
}
|
25
app/src/main/res/anim/slide_in_left.xml
Normal file
25
app/src/main/res/anim/slide_in_left.xml
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/* //device/apps/common/res/anim/slide_in_left.xml
|
||||
**
|
||||
** Copyright 2007, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
-->
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate android:fromXDelta="-50%p" android:toXDelta="0"
|
||||
android:duration="@android:integer/config_shortAnimTime"/>
|
||||
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
|
||||
android:duration="@android:integer/config_shortAnimTime" />
|
||||
</set>
|
25
app/src/main/res/anim/slide_in_right.xml
Normal file
25
app/src/main/res/anim/slide_in_right.xml
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/* //device/apps/common/res/anim/slide_in_right.xml
|
||||
**
|
||||
** Copyright 2007, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
-->
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate android:fromXDelta="50%p" android:toXDelta="0"
|
||||
android:duration="@android:integer/config_shortAnimTime"/>
|
||||
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
|
||||
android:duration="@android:integer/config_shortAnimTime" />
|
||||
</set>
|
25
app/src/main/res/anim/slide_out_left.xml
Normal file
25
app/src/main/res/anim/slide_out_left.xml
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/* //device/apps/common/res/anim/slide_out_left.xml
|
||||
**
|
||||
** Copyright 2007, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
-->
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate android:fromXDelta="0" android:toXDelta="-50%p"
|
||||
android:duration="@android:integer/config_shortAnimTime"/>
|
||||
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
|
||||
android:duration="@android:integer/config_shortAnimTime" />
|
||||
</set>
|
25
app/src/main/res/anim/slide_out_right.xml
Normal file
25
app/src/main/res/anim/slide_out_right.xml
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/* //device/apps/common/res/anim/slide_out_right.xml
|
||||
**
|
||||
** Copyright 2007, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
-->
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate android:fromXDelta="0" android:toXDelta="50%p"
|
||||
android:duration="@android:integer/config_shortAnimTime"/>
|
||||
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
|
||||
android:duration="@android:integer/config_shortAnimTime" />
|
||||
</set>
|
8
app/src/main/res/drawable/ic_brush_black_24dp.xml
Normal file
8
app/src/main/res/drawable/ic_brush_black_24dp.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<!-- drawable/brush.xml -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#000" android:pathData="M20.71,4.63L19.37,3.29C19,2.9 18.35,2.9 17.96,3.29L9,12.25L11.75,15L20.71,6.04C21.1,5.65 21.1,5 20.71,4.63M7,14A3,3 0 0,0 4,17C4,18.31 2.84,19 2,19C2.92,20.22 4.5,21 6,21A4,4 0 0,0 10,17A3,3 0 0,0 7,14Z" />
|
||||
</vector>
|
|
@ -0,0 +1,8 @@
|
|||
<!-- drawable/cloud_upload_outline.xml -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#000" android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4C9.11,4 6.6,5.64 5.35,8.04C2.34,8.36 0,10.91 0,14A6,6 0 0,0 6,20H19A5,5 0 0,0 24,15C24,12.36 21.95,10.22 19.35,10.04M19,18H6A4,4 0 0,1 2,14C2,11.95 3.53,10.24 5.56,10.03L6.63,9.92L7.13,8.97C8.08,7.14 9.94,6 12,6C14.62,6 16.88,7.86 17.39,10.43L17.69,11.93L19.22,12.04C20.78,12.14 22,13.45 22,15A3,3 0 0,1 19,18M8,13H10.55V16H13.45V13H16L12,9L8,13Z" />
|
||||
</vector>
|
8
app/src/main/res/drawable/ic_gesture_tap_24dp.xml
Normal file
8
app/src/main/res/drawable/ic_gesture_tap_24dp.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<!-- drawable/gesture_tap.xml -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#000" android:pathData="M10,9A1,1 0 0,1 11,8A1,1 0 0,1 12,9V13.47L13.21,13.6L18.15,15.79C18.68,16.03 19,16.56 19,17.14V21.5C18.97,22.32 18.32,22.97 17.5,23H11C10.62,23 10.26,22.85 10,22.57L5.1,18.37L5.84,17.6C6.03,17.39 6.3,17.28 6.58,17.28H6.8L10,19V9M11,5A4,4 0 0,1 15,9C15,10.5 14.2,11.77 13,12.46V11.24C13.61,10.69 14,9.89 14,9A3,3 0 0,0 11,6A3,3 0 0,0 8,9C8,9.89 8.39,10.69 9,11.24V12.46C7.8,11.77 7,10.5 7,9A4,4 0 0,1 11,5Z" />
|
||||
</vector>
|
8
app/src/main/res/drawable/ic_tools_black_24dp.xml
Normal file
8
app/src/main/res/drawable/ic_tools_black_24dp.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<!-- drawable/tools.xml -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#000" android:pathData="M21.71 20.29L20.29 21.71A1 1 0 0 1 18.88 21.71L7 9.85A3.81 3.81 0 0 1 6 10A4 4 0 0 1 2.22 4.7L4.76 7.24L5.29 6.71L6.71 5.29L7.24 4.76L4.7 2.22A4 4 0 0 1 10 6A3.81 3.81 0 0 1 9.85 7L21.71 18.88A1 1 0 0 1 21.71 20.29M2.29 18.88A1 1 0 0 0 2.29 20.29L3.71 21.71A1 1 0 0 0 5.12 21.71L10.59 16.25L7.76 13.42M20 2L16 4V6L13.83 8.17L15.83 10.17L18 8H20L22 4Z" />
|
||||
</vector>
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">المصدِّر</string>
|
||||
<string name="settings">التفضيلات</string>
|
||||
<string name="pref_appearance_group_title">المظهر</string>
|
||||
<string name="pref_general_group_title">العامة</string>
|
||||
<string name="pref_security_group_title">الأمان</string>
|
||||
<string name="pref_tools_group_title">الأدوات</string>
|
||||
<string name="pref_backups_group_title">النسخ الاحتياطي</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">Poskytovatel</string>
|
||||
<string name="settings">Předvolby</string>
|
||||
<string name="pref_appearance_group_title">Vzhled</string>
|
||||
<string name="pref_general_group_title">Obecné</string>
|
||||
<string name="pref_security_group_title">Zabezpečení</string>
|
||||
<string name="pref_tools_group_title">Nástroje</string>
|
||||
<string name="pref_backups_group_title">Zálohy</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">Herausgeber</string>
|
||||
<string name="settings">Einstellungen</string>
|
||||
<string name="pref_appearance_group_title">Erscheinungsbild</string>
|
||||
<string name="pref_general_group_title">Allgemein</string>
|
||||
<string name="pref_security_group_title">Sicherheit</string>
|
||||
<string name="pref_tools_group_title">Werkzeuge</string>
|
||||
<string name="pref_backups_group_title">Sicherungskopien</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">Εκδότης</string>
|
||||
<string name="settings">Προτιμήσεις</string>
|
||||
<string name="pref_appearance_group_title">Εμφάνιση</string>
|
||||
<string name="pref_general_group_title">Γενικά</string>
|
||||
<string name="pref_security_group_title">Ασφάλεια</string>
|
||||
<string name="pref_tools_group_title">Εργαλεία</string>
|
||||
<string name="pref_backups_group_title">Αντίγραφα Ασφαλείας</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">Emisor</string>
|
||||
<string name="settings">Preferencias</string>
|
||||
<string name="pref_appearance_group_title">Apariencia</string>
|
||||
<string name="pref_general_group_title">General</string>
|
||||
<string name="pref_security_group_title">Seguridad</string>
|
||||
<string name="pref_tools_group_title">Herramientas</string>
|
||||
<string name="pref_backups_group_title">Copias de seguridad</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">Igorlea</string>
|
||||
<string name="settings">Hobespenak</string>
|
||||
<string name="pref_appearance_group_title">Itxura</string>
|
||||
<string name="pref_general_group_title">Orokorra</string>
|
||||
<string name="pref_security_group_title">Segurtasuna</string>
|
||||
<string name="pref_tools_group_title">Tresnak</string>
|
||||
<string name="pref_backups_group_title">Segurtasun-kopiak</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">صادر کننده</string>
|
||||
<string name="settings">تنظیمات</string>
|
||||
<string name="pref_appearance_group_title">ظاهر</string>
|
||||
<string name="pref_general_group_title">عمومی</string>
|
||||
<string name="pref_security_group_title">امنیت</string>
|
||||
<string name="pref_tools_group_title">ابزارها</string>
|
||||
<string name="pref_backups_group_title">پشتیبان گیری</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">Myöntäjä</string>
|
||||
<string name="settings">Asetukset</string>
|
||||
<string name="pref_appearance_group_title">Ulkoasu</string>
|
||||
<string name="pref_general_group_title">Yleiset</string>
|
||||
<string name="pref_security_group_title">Turvallisuus</string>
|
||||
<string name="pref_tools_group_title">Työkalut</string>
|
||||
<string name="pref_backups_group_title">Varmuuskopiot</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">Émetteur</string>
|
||||
<string name="settings">Préférences</string>
|
||||
<string name="pref_appearance_group_title">Apparence</string>
|
||||
<string name="pref_general_group_title">Général</string>
|
||||
<string name="pref_security_group_title">Sécurité</string>
|
||||
<string name="pref_tools_group_title">Outils</string>
|
||||
<string name="pref_backups_group_title">Sauvegardes</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">ज़ारीकर्ता</string>
|
||||
<string name="settings">वरीयताएँ</string>
|
||||
<string name="pref_appearance_group_title">रंगरूप</string>
|
||||
<string name="pref_general_group_title">सामान्य</string>
|
||||
<string name="pref_security_group_title">सुरक्षा</string>
|
||||
<string name="pref_tools_group_title">टूल्स</string>
|
||||
<string name="pref_backups_group_title">बैकअप्स</string>
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
<string name="issuer">Kibocsátó</string>
|
||||
<string name="settings">Beállítások</string>
|
||||
<string name="pref_appearance_group_title">Megjelenés</string>
|
||||
<string name="pref_general_group_title">Általános</string>
|
||||
<string name="pref_security_group_title">Biztonság</string>
|
||||
<string name="pref_tools_group_title">Eszközök</string>
|
||||
<string name="pref_backups_group_title">Biztonsági mentések</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">Penerbit</string>
|
||||
<string name="settings">Pilihan</string>
|
||||
<string name="pref_appearance_group_title">Tampilan</string>
|
||||
<string name="pref_general_group_title">Umum</string>
|
||||
<string name="pref_security_group_title">Keamanan</string>
|
||||
<string name="pref_tools_group_title">Alat</string>
|
||||
<string name="pref_backups_group_title">Cadangan</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">Emittente</string>
|
||||
<string name="settings">Impostazioni</string>
|
||||
<string name="pref_appearance_group_title">Aspetto</string>
|
||||
<string name="pref_general_group_title">Generale</string>
|
||||
<string name="pref_security_group_title">Sicurezza</string>
|
||||
<string name="pref_tools_group_title">Strumenti</string>
|
||||
<string name="pref_backups_group_title">Backup</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">発行者</string>
|
||||
<string name="settings">設定</string>
|
||||
<string name="pref_appearance_group_title">外観</string>
|
||||
<string name="pref_general_group_title">全般</string>
|
||||
<string name="pref_security_group_title">セキュリティ</string>
|
||||
<string name="pref_tools_group_title">ツール</string>
|
||||
<string name="pref_backups_group_title">バックアップ</string>
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
<string name="issuer">ನೀಡುವವರು</string>
|
||||
<string name="settings">ಸಿದ್ಢತೆಗಳು</string>
|
||||
<string name="pref_appearance_group_title">ನೋಟ</string>
|
||||
<string name="pref_general_group_title">ಸಾಮಾನ್ಯ</string>
|
||||
<string name="pref_security_group_title">ಭದ್ರತೆ</string>
|
||||
<string name="pref_tools_group_title">ಉಪಕರಣಗಳು</string>
|
||||
<string name="pref_select_theme_title">ಥೀಮ್</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">Uitgever</string>
|
||||
<string name="settings">Voorkeuren</string>
|
||||
<string name="pref_appearance_group_title">Uiterlijk</string>
|
||||
<string name="pref_general_group_title">Algemeen</string>
|
||||
<string name="pref_security_group_title">Beveiliging</string>
|
||||
<string name="pref_tools_group_title">Hulpmiddelen</string>
|
||||
<string name="pref_backups_group_title">Back-ups</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">Wydawca</string>
|
||||
<string name="settings">Ustawienia</string>
|
||||
<string name="pref_appearance_group_title">Wygląd</string>
|
||||
<string name="pref_general_group_title">Ogólne</string>
|
||||
<string name="pref_security_group_title">Bezpieczeństwo</string>
|
||||
<string name="pref_tools_group_title">Narzędzia</string>
|
||||
<string name="pref_backups_group_title">Kopie zapasowe</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">Nome do serviço</string>
|
||||
<string name="settings">Preferências</string>
|
||||
<string name="pref_appearance_group_title">Aparência</string>
|
||||
<string name="pref_general_group_title">Geral</string>
|
||||
<string name="pref_security_group_title">Segurança</string>
|
||||
<string name="pref_tools_group_title">Ferramentas</string>
|
||||
<string name="pref_backups_group_title">Backups</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">Emissor</string>
|
||||
<string name="settings">Preferências</string>
|
||||
<string name="pref_appearance_group_title">Aparência</string>
|
||||
<string name="pref_general_group_title">Geral</string>
|
||||
<string name="pref_security_group_title">Segurança</string>
|
||||
<string name="pref_tools_group_title">Ferramentas</string>
|
||||
<string name="pref_backups_group_title">Backups</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">Emitent</string>
|
||||
<string name="settings">Preferințe</string>
|
||||
<string name="pref_appearance_group_title">Aspect</string>
|
||||
<string name="pref_general_group_title">General</string>
|
||||
<string name="pref_security_group_title">Securitate</string>
|
||||
<string name="pref_tools_group_title">Unelte</string>
|
||||
<string name="pref_backups_group_title">Copii de rezervă</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">Эмитент</string>
|
||||
<string name="settings">Предпочтения</string>
|
||||
<string name="pref_appearance_group_title">Внешний вид</string>
|
||||
<string name="pref_general_group_title">Общее</string>
|
||||
<string name="pref_security_group_title">Безопасность</string>
|
||||
<string name="pref_tools_group_title">Инструменты</string>
|
||||
<string name="pref_backups_group_title">Резервные копии</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">Vydavateľ</string>
|
||||
<string name="settings">Predvoľby</string>
|
||||
<string name="pref_appearance_group_title">Vzhľad</string>
|
||||
<string name="pref_general_group_title">Všeobecné</string>
|
||||
<string name="pref_security_group_title">Zabezpečenie</string>
|
||||
<string name="pref_tools_group_title">Nástroje</string>
|
||||
<string name="pref_backups_group_title">Zálohy</string>
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
<string name="issuer">Sağlayıcı</string>
|
||||
<string name="settings">Ayarlar</string>
|
||||
<string name="pref_appearance_group_title">Görünüm</string>
|
||||
<string name="pref_general_group_title">Genel</string>
|
||||
<string name="pref_security_group_title">Güvenlik</string>
|
||||
<string name="pref_tools_group_title">Araçlar</string>
|
||||
<string name="pref_backups_group_title">Yedekler</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">服务商</string>
|
||||
<string name="settings">偏好设置</string>
|
||||
<string name="pref_appearance_group_title">外观</string>
|
||||
<string name="pref_general_group_title">常规</string>
|
||||
<string name="pref_security_group_title">安全</string>
|
||||
<string name="pref_tools_group_title">工具</string>
|
||||
<string name="pref_backups_group_title">备份</string>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<string name="issuer">服務商</string>
|
||||
<string name="settings">偏好設定</string>
|
||||
<string name="pref_appearance_group_title">外觀</string>
|
||||
<string name="pref_general_group_title">一般</string>
|
||||
<string name="pref_security_group_title">安全</string>
|
||||
<string name="pref_tools_group_title">工具</string>
|
||||
<string name="pref_backups_group_title">備份</string>
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
<string name="issuer">Issuer</string>
|
||||
|
||||
<string name="settings">Preferences</string>
|
||||
<string name="pref_behavior_group_title">Behavior</string>
|
||||
<string name="pref_appearance_group_title">Appearance</string>
|
||||
<string name="pref_general_group_title">General</string>
|
||||
<string name="pref_security_group_title">Security</string>
|
||||
<string name="pref_tools_group_title">Tools</string>
|
||||
<string name="pref_backups_group_title">Backups</string>
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
<item name="codePrimaryText">@color/code_primary_text</item>
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="preferenceTheme">@style/PreferenceTheme</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="background">@color/background</item>
|
||||
<item name="divider">@color/divider</item>
|
||||
|
@ -30,7 +31,7 @@
|
|||
<item name="iconColorInverted">@color/icon_primary_inverted</item>
|
||||
|
||||
<item name="actionModeStyle">@style/ActionModeStyle</item>
|
||||
<item name="actionBarTheme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item>
|
||||
<item name="actionBarTheme">@style/ActionBarTheme</item>
|
||||
<item name="alertDialogTheme">@style/DialogStyle</item>
|
||||
|
||||
<item name="dot_color">@color/indicator_dot</item>
|
||||
|
@ -158,6 +159,7 @@
|
|||
<item name="codePrimaryText">@color/code_primary_text_dark</item>
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="preferenceTheme">@style/PreferenceTheme</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="background">@color/background_dark</item>
|
||||
<item name="divider">@color/divider_dark</item>
|
||||
|
@ -181,7 +183,7 @@
|
|||
<item name="background">@color/background_true_dark</item>
|
||||
<item name="cardBackgroundFocused">@color/card_background_focused_true_dark</item>
|
||||
<item name="colorPrimaryDark">@color/background_true_dark</item>
|
||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
|
||||
<item name="preferenceTheme">@style/PreferenceTheme</item>
|
||||
<item name="actionModeStyle">@style/ActionModeStyle.TrueBlack</item>
|
||||
<item name="alertDialogTheme">@style/DialogStyle.TrueDark</item>
|
||||
<item name="divider">@color/divider_true_dark</item>
|
||||
|
@ -230,4 +232,11 @@
|
|||
|
||||
<style name="AppTheme.NoActionBar.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||
|
||||
<style name="PreferenceTheme" parent="@style/PreferenceThemeOverlay.v14.Material">
|
||||
<item name="android:tint">?attr/iconColorPrimary</item>
|
||||
</style>
|
||||
|
||||
<style name="ActionBarTheme" parent="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
|
||||
<item name="android:tint">?attr/iconColorInverted</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
|
@ -2,199 +2,31 @@
|
|||
<androidx.preference.PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:title="@string/settings">
|
||||
android:title="@string/action_settings">
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/pref_appearance_group_title"
|
||||
app:iconSpaceReserved="false">
|
||||
<Preference
|
||||
android:defaultValue="false"
|
||||
android:key="pref_dark_mode"
|
||||
android:title="@string/pref_select_theme_title"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
android:fragment="com.beemdevelopment.aegis.ui.fragments.BehaviorPreferencesFragment"
|
||||
app:icon="@drawable/ic_gesture_tap_24dp"
|
||||
app:title="@string/pref_behavior_group_title" />
|
||||
|
||||
<Preference
|
||||
android:defaultValue="false"
|
||||
android:key="pref_view_mode"
|
||||
android:title="@string/pref_view_mode_title"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
android:fragment="com.beemdevelopment.aegis.ui.fragments.AppearancePreferencesFragment"
|
||||
app:icon="@drawable/ic_brush_black_24dp"
|
||||
app:title="@string/pref_appearance_group_title" />
|
||||
|
||||
<ListPreference
|
||||
android:key="pref_lang"
|
||||
android:title="@string/pref_lang_title"
|
||||
android:summary="%s"
|
||||
android:entries="@array/pref_lang_entries"
|
||||
android:entryValues="@array/pref_lang_values"
|
||||
android:defaultValue="system"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
android:fragment="com.beemdevelopment.aegis.ui.fragments.SecurityPreferencesFragment"
|
||||
app:icon="@drawable/ic_vpn_key_black_24dp"
|
||||
app:title="@string/pref_security_group_title" />
|
||||
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="pref_code_group_size"
|
||||
android:title="@string/pref_code_group_size_title"
|
||||
android:summary="@string/pref_code_group_size_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
android:fragment="com.beemdevelopment.aegis.ui.fragments.BackupsPreferencesFragment"
|
||||
app:icon="@drawable/ic_cloud_upload_outline_black_24dp"
|
||||
app:title="@string/pref_backups_group_title" />
|
||||
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="pref_account_name"
|
||||
android:title="@string/pref_account_name_title"
|
||||
android:summary="@string/pref_account_name_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<Preference
|
||||
android:key="pref_groups"
|
||||
android:title="@string/preference_manage_groups"
|
||||
android:summary="@string/preference_manage_groups_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/pref_general_group_title"
|
||||
app:iconSpaceReserved="false">
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="pref_copy_on_tap"
|
||||
android:title="@string/pref_copy_on_tap_title"
|
||||
android:summary="@string/pref_copy_on_tap_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="pref_highlight_entry"
|
||||
android:title="@string/pref_highlight_entry_title"
|
||||
android:summary="@string/pref_highlight_entry_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="pref_panic_trigger"
|
||||
android:title="@string/pref_panic_trigger_title"
|
||||
android:summary="@string/pref_panic_trigger_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/pref_security_group_title"
|
||||
app:iconSpaceReserved="false">
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="@bool/pref_secure_screen_default"
|
||||
android:key="pref_secure_screen"
|
||||
android:title="@string/pref_secure_screen_title"
|
||||
android:summary="@string/pref_secure_screen_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="pref_tap_to_reveal"
|
||||
android:title="@string/pref_tap_to_reveal_title"
|
||||
android:summary="@string/pref_tap_to_reveal_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
android:key="pref_tap_to_reveal_time"
|
||||
android:title="@string/pref_tap_to_reveal_time_title"
|
||||
android:dependency="pref_tap_to_reveal"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<!--<EditTextPreference
|
||||
android:key="pref_timeout"
|
||||
android:title="@string/pref_timeout_title"
|
||||
android:summary="@string/pref_timeout_summary"
|
||||
android:inputType="number"
|
||||
android:defaultValue="30"
|
||||
android:dialogTitle="Set number of seconds of inactivity before Aegis locks the vault"
|
||||
app:iconSpaceReserved="false"/>-->
|
||||
<com.beemdevelopment.aegis.ui.preferences.SwitchPreference
|
||||
android:key="pref_encryption"
|
||||
android:title="@string/pref_encryption_title"
|
||||
android:summary="@string/pref_encryption_summary"
|
||||
android:persistent="false"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<Preference
|
||||
android:key="pref_password"
|
||||
android:title="@string/pref_set_password_title"
|
||||
android:summary="@string/pref_set_password_summary"
|
||||
android:dependency="pref_encryption"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<com.beemdevelopment.aegis.ui.preferences.SwitchPreference
|
||||
android:key="pref_biometrics"
|
||||
android:title="@string/pref_biometrics_title"
|
||||
android:summary="@string/pref_biometrics_summary"
|
||||
android:persistent="false"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="pref_password_reminder"
|
||||
android:title="@string/pref_password_reminder_title"
|
||||
android:summary="@string/pref_password_reminder_summary"
|
||||
android:dependency="pref_biometrics"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:key="pref_pin_keyboard"
|
||||
android:dependency="pref_encryption"
|
||||
android:title="@string/pref_pin_keyboard_title"
|
||||
android:summary="@string/pref_pin_keyboard_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<Preference
|
||||
android:key="pref_auto_lock"
|
||||
android:dependency="pref_encryption"
|
||||
android:persistent="false"
|
||||
android:title="@string/pref_auto_lock_title"
|
||||
android:summary="@string/pref_auto_lock_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<Preference
|
||||
android:key="pref_slots"
|
||||
android:title="@string/pref_slots_title"
|
||||
android:summary="@string/pref_slots_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/pref_backups_group_title"
|
||||
app:iconSpaceReserved="false">
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="pref_backups"
|
||||
android:title="@string/pref_backups_title"
|
||||
android:summary="@string/pref_backups_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
android:key="pref_backups_location"
|
||||
android:title="@string/pref_backups_location_title"
|
||||
android:summary="@string/pref_backups_location_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
android:key="pref_backups_trigger"
|
||||
android:title="@string/pref_backups_trigger_title"
|
||||
android:summary="@string/pref_backups_trigger_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
android:key="pref_backups_versions"
|
||||
android:title="@string/pref_backups_versions_title"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/pref_tools_group_title"
|
||||
app:iconSpaceReserved="false">
|
||||
<Preference
|
||||
android:key="pref_import"
|
||||
android:title="@string/pref_import_file_title"
|
||||
android:summary="@string/pref_import_file_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
android:key="pref_import_app"
|
||||
android:title="@string/pref_import_app_title"
|
||||
android:summary="@string/pref_import_app_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
android:key="pref_export"
|
||||
android:title="@string/pref_export_title"
|
||||
android:summary="@string/pref_export_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
<Preference
|
||||
android:fragment="com.beemdevelopment.aegis.ui.fragments.ToolsPreferencesFragment"
|
||||
app:icon="@drawable/ic_tools_black_24dp"
|
||||
app:title="@string/pref_tools_group_title" />
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
||||
|
|
45
app/src/main/res/xml/preferences_appearance.xml
Normal file
45
app/src/main/res/xml/preferences_appearance.xml
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:title="@string/pref_appearance_group_title">
|
||||
<Preference
|
||||
android:defaultValue="false"
|
||||
android:key="pref_dark_mode"
|
||||
android:title="@string/pref_select_theme_title"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<Preference
|
||||
android:defaultValue="false"
|
||||
android:key="pref_view_mode"
|
||||
android:title="@string/pref_view_mode_title"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<ListPreference
|
||||
android:key="pref_lang"
|
||||
android:title="@string/pref_lang_title"
|
||||
android:summary="%s"
|
||||
android:entries="@array/pref_lang_entries"
|
||||
android:entryValues="@array/pref_lang_values"
|
||||
android:defaultValue="system"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="pref_code_group_size"
|
||||
android:title="@string/pref_code_group_size_title"
|
||||
android:summary="@string/pref_code_group_size_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="pref_account_name"
|
||||
android:title="@string/pref_account_name_title"
|
||||
android:summary="@string/pref_account_name_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<Preference
|
||||
android:key="pref_groups"
|
||||
android:title="@string/preference_manage_groups"
|
||||
android:summary="@string/preference_manage_groups_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceScreen>
|
25
app/src/main/res/xml/preferences_backups.xml
Normal file
25
app/src/main/res/xml/preferences_backups.xml
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:title="@string/pref_backups_group_title">
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="pref_backups"
|
||||
android:title="@string/pref_backups_title"
|
||||
android:summary="@string/pref_backups_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
android:key="pref_backups_location"
|
||||
android:title="@string/pref_backups_location_title"
|
||||
android:summary="@string/pref_backups_location_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
android:key="pref_backups_trigger"
|
||||
android:title="@string/pref_backups_trigger_title"
|
||||
android:summary="@string/pref_backups_trigger_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
android:key="pref_backups_versions"
|
||||
android:title="@string/pref_backups_versions_title"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceScreen>
|
23
app/src/main/res/xml/preferences_behavior.xml
Normal file
23
app/src/main/res/xml/preferences_behavior.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:title="@string/pref_behavior_group_title">
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="pref_copy_on_tap"
|
||||
android:title="@string/pref_copy_on_tap_title"
|
||||
android:summary="@string/pref_copy_on_tap_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="pref_highlight_entry"
|
||||
android:title="@string/pref_highlight_entry_title"
|
||||
android:summary="@string/pref_highlight_entry_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="pref_panic_trigger"
|
||||
android:title="@string/pref_panic_trigger_title"
|
||||
android:summary="@string/pref_panic_trigger_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceScreen>
|
72
app/src/main/res/xml/preferences_security.xml
Normal file
72
app/src/main/res/xml/preferences_security.xml
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:title="@string/pref_security_group_title">
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="@bool/pref_secure_screen_default"
|
||||
android:key="pref_secure_screen"
|
||||
android:title="@string/pref_secure_screen_title"
|
||||
android:summary="@string/pref_secure_screen_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="pref_tap_to_reveal"
|
||||
android:title="@string/pref_tap_to_reveal_title"
|
||||
android:summary="@string/pref_tap_to_reveal_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
android:key="pref_tap_to_reveal_time"
|
||||
android:title="@string/pref_tap_to_reveal_time_title"
|
||||
android:dependency="pref_tap_to_reveal"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<com.beemdevelopment.aegis.ui.preferences.SwitchPreference
|
||||
android:key="pref_encryption"
|
||||
android:title="@string/pref_encryption_title"
|
||||
android:summary="@string/pref_encryption_summary"
|
||||
android:persistent="false"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<Preference
|
||||
android:key="pref_password"
|
||||
android:title="@string/pref_set_password_title"
|
||||
android:summary="@string/pref_set_password_summary"
|
||||
android:dependency="pref_encryption"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<com.beemdevelopment.aegis.ui.preferences.SwitchPreference
|
||||
android:key="pref_biometrics"
|
||||
android:title="@string/pref_biometrics_title"
|
||||
android:summary="@string/pref_biometrics_summary"
|
||||
android:persistent="false"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="pref_password_reminder"
|
||||
android:title="@string/pref_password_reminder_title"
|
||||
android:summary="@string/pref_password_reminder_summary"
|
||||
android:dependency="pref_biometrics"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:key="pref_pin_keyboard"
|
||||
android:dependency="pref_encryption"
|
||||
android:title="@string/pref_pin_keyboard_title"
|
||||
android:summary="@string/pref_pin_keyboard_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<Preference
|
||||
android:key="pref_auto_lock"
|
||||
android:dependency="pref_encryption"
|
||||
android:persistent="false"
|
||||
android:title="@string/pref_auto_lock_title"
|
||||
android:summary="@string/pref_auto_lock_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<Preference
|
||||
android:key="pref_slots"
|
||||
android:title="@string/pref_slots_title"
|
||||
android:summary="@string/pref_slots_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceScreen>
|
20
app/src/main/res/xml/preferences_tools.xml
Normal file
20
app/src/main/res/xml/preferences_tools.xml
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:title="@string/pref_tools_group_title">
|
||||
<Preference
|
||||
android:key="pref_import"
|
||||
android:title="@string/pref_import_file_title"
|
||||
android:summary="@string/pref_import_file_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
android:key="pref_import_app"
|
||||
android:title="@string/pref_import_app_title"
|
||||
android:summary="@string/pref_import_app_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
android:key="pref_export"
|
||||
android:title="@string/pref_export_title"
|
||||
android:summary="@string/pref_export_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceScreen>
|
Loading…
Add table
Reference in a new issue