mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-05-14 05:52:52 +00:00
Material 3
Co-authored-by: Michael Schättgen <michael@schattgen.me>
This commit is contained in:
parent
f7bac4331e
commit
fcde086ae3
205 changed files with 1935 additions and 1723 deletions
|
@ -22,8 +22,6 @@ import com.beemdevelopment.aegis.receivers.VaultLockReceiver;
|
|||
import com.beemdevelopment.aegis.ui.MainActivity;
|
||||
import com.beemdevelopment.aegis.util.IOUtils;
|
||||
import com.beemdevelopment.aegis.vault.VaultManager;
|
||||
import com.mikepenz.iconics.Iconics;
|
||||
import com.mikepenz.material_design_iconic_typeface_library.MaterialDesignIconic;
|
||||
import com.topjohnwu.superuser.Shell;
|
||||
|
||||
import java.util.Collections;
|
||||
|
@ -48,9 +46,6 @@ public abstract class AegisApplicationBase extends Application {
|
|||
super.onCreate();
|
||||
_vaultManager = EarlyEntryPoints.get(this, EntryPoint.class).getVaultManager();
|
||||
|
||||
Iconics.init(this);
|
||||
Iconics.registerFont(new MaterialDesignIconic());
|
||||
|
||||
VaultLockReceiver lockReceiver = new VaultLockReceiver();
|
||||
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
|
||||
ContextCompat.registerReceiver(this, lockReceiver, intentFilter, ContextCompat.RECEIVER_NOT_EXPORTED);
|
||||
|
|
|
@ -145,6 +145,10 @@ public class Preferences {
|
|||
return CodeGrouping.valueOf(value);
|
||||
}
|
||||
|
||||
public void setCodeGroupSize(CodeGrouping codeGroupSize) {
|
||||
_prefs.edit().putString("pref_code_group_size_string", codeGroupSize.name()).apply();
|
||||
}
|
||||
|
||||
public boolean isIntroDone() {
|
||||
return _prefs.getBoolean("pref_intro", false);
|
||||
}
|
||||
|
@ -198,6 +202,10 @@ public class Preferences {
|
|||
_prefs.edit().putInt("pref_current_theme", theme.ordinal()).apply();
|
||||
}
|
||||
|
||||
public boolean isDynamicColorsEnabled() {
|
||||
return _prefs.getBoolean("pref_dynamic_colors", false);
|
||||
}
|
||||
|
||||
public ViewMode getCurrentViewMode() {
|
||||
return ViewMode.fromInteger(_prefs.getInt("pref_current_view_mode", 0));
|
||||
}
|
||||
|
@ -266,8 +274,16 @@ public class Preferences {
|
|||
return _prefs.getInt("pref_timeout", -1);
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
return _prefs.getString("pref_lang", "system");
|
||||
}
|
||||
|
||||
public void setLanguage(String lang) {
|
||||
_prefs.edit().putString("pref_lang", lang).apply();
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
String lang = _prefs.getString("pref_lang", "system");
|
||||
String lang = getLanguage();
|
||||
|
||||
if (lang.equals("system")) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
|
|
|
@ -10,20 +10,14 @@ public class ThemeMap {
|
|||
}
|
||||
|
||||
public static final Map<Theme, Integer> DEFAULT = ImmutableMap.of(
|
||||
Theme.LIGHT, R.style.Theme_Aegis_Light_Default,
|
||||
Theme.DARK, R.style.Theme_Aegis_Dark_Default,
|
||||
Theme.AMOLED, R.style.Theme_Aegis_TrueDark_Default
|
||||
);
|
||||
|
||||
public static final Map<Theme, Integer> NO_ACTION_BAR = ImmutableMap.of(
|
||||
Theme.LIGHT, R.style.Theme_Aegis_Light_NoActionBar,
|
||||
Theme.DARK, R.style.Theme_Aegis_Dark_NoActionBar,
|
||||
Theme.AMOLED, R.style.Theme_Aegis_TrueDark_NoActionBar
|
||||
Theme.LIGHT, R.style.Theme_Aegis_Light,
|
||||
Theme.DARK, R.style.Theme_Aegis_Dark,
|
||||
Theme.AMOLED, R.style.Theme_Aegis_Amoled
|
||||
);
|
||||
|
||||
public static final Map<Theme, Integer> FULLSCREEN = ImmutableMap.of(
|
||||
Theme.LIGHT, R.style.Theme_Aegis_Light_Fullscreen,
|
||||
Theme.DARK, R.style.Theme_Aegis_Dark_Fullscreen,
|
||||
Theme.AMOLED, R.style.Theme_Aegis_TrueDark_Fullscreen
|
||||
Theme.AMOLED, R.style.Theme_Aegis_Amoled_Fullscreen
|
||||
);
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ public enum ViewMode {
|
|||
return 4;
|
||||
}
|
||||
|
||||
return 20;
|
||||
return 8;
|
||||
}
|
||||
|
||||
public int getColumnSpan() {
|
||||
|
|
|
@ -56,16 +56,20 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
|||
// It's not clear when this can happen, but sometimes the ViewHolder
|
||||
// that's passed to this function has a position of -1, leading
|
||||
// to a crash down the line.
|
||||
int position = viewHolder.getAdapterPosition();
|
||||
int position = viewHolder.getBindingAdapterPosition();
|
||||
if (position == NO_POSITION) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int swipeFlags = 0;
|
||||
|
||||
EntryAdapter adapter = (EntryAdapter) recyclerView.getAdapter();
|
||||
if (adapter == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int swipeFlags = 0;
|
||||
if (adapter.isPositionFooter(position)
|
||||
|| adapter.getEntryAt(position) != _selectedEntry
|
||||
|| adapter.isPositionErrorCard(position)
|
||||
|| adapter.getEntryAtPos(position) != _selectedEntry
|
||||
|| !isLongPressDragEnabled()) {
|
||||
return makeMovementFlags(0, swipeFlags);
|
||||
}
|
||||
|
@ -76,12 +80,13 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
|||
@Override
|
||||
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
|
||||
RecyclerView.ViewHolder target) {
|
||||
if (target.getAdapterPosition() < _adapter.getShownFavoritesCount()){
|
||||
int targetIndex = _adapter.translateEntryPosToIndex(target.getBindingAdapterPosition());
|
||||
if (targetIndex < _adapter.getShownFavoritesCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int firstPosition = viewHolder.getLayoutPosition();
|
||||
int secondPosition = target.getAdapterPosition();
|
||||
int secondPosition = target.getBindingAdapterPosition();
|
||||
|
||||
_adapter.onItemMove(firstPosition, secondPosition);
|
||||
_positionChanged = true;
|
||||
|
@ -90,7 +95,7 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
|||
|
||||
@Override
|
||||
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
|
||||
_adapter.onItemDismiss(viewHolder.getAdapterPosition());
|
||||
_adapter.onItemDismiss(viewHolder.getBindingAdapterPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -98,7 +103,7 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
|||
super.clearView(recyclerView, viewHolder);
|
||||
|
||||
if (_positionChanged) {
|
||||
_adapter.onItemDrop(viewHolder.getAdapterPosition());
|
||||
_adapter.onItemDrop(viewHolder.getBindingAdapterPosition());
|
||||
_positionChanged = false;
|
||||
_adapter.refresh(false);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
|
|||
import com.beemdevelopment.aegis.ui.tasks.PBKDFTask;
|
||||
import com.beemdevelopment.aegis.util.IOUtils;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.topjohnwu.superuser.io.SuFile;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
@ -182,7 +183,7 @@ public class AndOtpImporter extends DatabaseImporter {
|
|||
context.getResources().getString(R.string.andotp_old_format)
|
||||
};
|
||||
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(context)
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.choose_andotp_importer)
|
||||
.setSingleChoiceItems(choices, 0, null)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
|
|
|
@ -4,8 +4,6 @@ import android.content.Context;
|
|||
import android.content.pm.PackageManager;
|
||||
import android.util.Xml;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.crypto.CryptoUtils;
|
||||
import com.beemdevelopment.aegis.encoding.Base32;
|
||||
|
@ -18,6 +16,7 @@ import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
|
|||
import com.beemdevelopment.aegis.util.IOUtils;
|
||||
import com.beemdevelopment.aegis.util.PreferenceParser;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.topjohnwu.superuser.io.SuFile;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
@ -154,7 +153,7 @@ public class TotpAuthenticatorImporter extends DatabaseImporter {
|
|||
|
||||
@Override
|
||||
public void decrypt(Context context, DecryptListener listener) {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(context)
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(context)
|
||||
.setMessage(R.string.choose_totpauth_importer)
|
||||
.setPositiveButton(R.string.yes, (dialog, which) -> {
|
||||
Dialogs.showPasswordInputDialog(context, password -> {
|
||||
|
|
|
@ -13,17 +13,14 @@ import android.widget.Toast;
|
|||
|
||||
import androidx.annotation.AttrRes;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.view.LayoutInflaterCompat;
|
||||
|
||||
import com.beemdevelopment.aegis.BuildConfig;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.Theme;
|
||||
import com.beemdevelopment.aegis.helpers.ThemeHelper;
|
||||
import com.beemdevelopment.aegis.licenses.GlideLicense;
|
||||
import com.beemdevelopment.aegis.licenses.ProtobufLicense;
|
||||
import com.beemdevelopment.aegis.ui.dialogs.ChangelogDialog;
|
||||
import com.beemdevelopment.aegis.ui.dialogs.LicenseDialog;
|
||||
import com.mikepenz.iconics.context.IconicsLayoutInflater2;
|
||||
|
||||
import de.psdev.licensesdialog.LicenseResolver;
|
||||
import de.psdev.licensesdialog.LicensesDialog;
|
||||
|
@ -40,7 +37,6 @@ public class AboutActivity extends AegisActivity {
|
|||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
LayoutInflaterCompat.setFactory2(getLayoutInflater(), new IconicsLayoutInflater2(getDelegate()));
|
||||
super.onCreate(savedInstanceState);
|
||||
if (abortIfOrphan(savedInstanceState)) {
|
||||
return;
|
||||
|
@ -132,11 +128,10 @@ public class AboutActivity extends AegisActivity {
|
|||
|
||||
private void showThirdPartyLicenseDialog() {
|
||||
String stylesheet = getString(R.string.custom_notices_format_style);
|
||||
int backgroundColorResource = getConfiguredTheme() == Theme.AMOLED ? R.attr.cardBackgroundFocused : R.attr.cardBackground;
|
||||
String backgroundColor = getThemeColorAsHex(backgroundColorResource);
|
||||
String textColor = getThemeColorAsHex(R.attr.primaryText);
|
||||
String licenseColor = getThemeColorAsHex(R.attr.cardBackgroundFocused);
|
||||
String linkColor = getThemeColorAsHex(androidx.appcompat.R.attr.colorAccent);
|
||||
String backgroundColor = getThemeColorAsHex(com.google.android.material.R.attr.colorSurfaceContainerLow);
|
||||
String textColor = getThemeColorAsHex(com.google.android.material.R.attr.colorOnSurface);
|
||||
String licenseColor = getThemeColorAsHex(com.google.android.material.R.attr.colorSurfaceContainerLow);
|
||||
String linkColor = getThemeColorAsHex(com.google.android.material.R.attr.colorAccent);
|
||||
|
||||
stylesheet = String.format(stylesheet, backgroundColor, textColor, licenseColor, linkColor);
|
||||
|
||||
|
|
|
@ -4,21 +4,31 @@ import android.annotation.SuppressLint;
|
|||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.core.view.ViewPropertyAnimatorCompat;
|
||||
|
||||
import com.beemdevelopment.aegis.Preferences;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.Theme;
|
||||
import com.beemdevelopment.aegis.ThemeMap;
|
||||
import com.beemdevelopment.aegis.helpers.ThemeHelper;
|
||||
import com.beemdevelopment.aegis.icons.IconPackManager;
|
||||
import com.beemdevelopment.aegis.vault.VaultManager;
|
||||
import com.beemdevelopment.aegis.vault.VaultRepositoryException;
|
||||
import com.google.android.material.color.DynamicColors;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Locale;
|
||||
|
@ -42,6 +52,8 @@ public abstract class AegisActivity extends AppCompatActivity implements VaultMa
|
|||
@Inject
|
||||
protected IconPackManager _iconPackManager;
|
||||
|
||||
private ActionModeStatusGuardHack _statusGuardHack;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
// set the theme and locale before creating the activity
|
||||
|
@ -50,6 +62,8 @@ public abstract class AegisActivity extends AppCompatActivity implements VaultMa
|
|||
setLocale(_prefs.getLocale());
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
_statusGuardHack = new ActionModeStatusGuardHack();
|
||||
|
||||
// set FLAG_SECURE on the window of every AegisActivity
|
||||
if (_prefs.isSecureScreenEnabled()) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||
|
@ -106,6 +120,10 @@ public abstract class AegisActivity extends AppCompatActivity implements VaultMa
|
|||
protected void setTheme(Map<Theme, Integer> themeMap) {
|
||||
int theme = themeMap.get(getConfiguredTheme());
|
||||
setTheme(theme);
|
||||
|
||||
if (_prefs.isDynamicColorsEnabled()) {
|
||||
DynamicColors.applyToActivityIfAvailable(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected Theme getConfiguredTheme() {
|
||||
|
@ -169,6 +187,77 @@ public abstract class AegisActivity extends AppCompatActivity implements VaultMa
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSupportActionModeStarted(@NonNull ActionMode mode) {
|
||||
super.onSupportActionModeStarted(mode);
|
||||
_statusGuardHack.apply(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSupportActionModeFinished(@NonNull ActionMode mode) {
|
||||
super.onSupportActionModeFinished(mode);
|
||||
_statusGuardHack.apply(View.GONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* When starting/finishing an action mode, forcefully cancel the fade in/out animation and
|
||||
* set the status bar color. This requires the abc_decor_view_status_guard colors to be set
|
||||
* to transparent.
|
||||
*
|
||||
* This should fix any inconsistencies between the color of the action bar and the status bar
|
||||
* when an action mode is active.
|
||||
*/
|
||||
private class ActionModeStatusGuardHack {
|
||||
private Field _fadeAnimField;
|
||||
private Field _actionModeViewField;
|
||||
|
||||
@ColorInt
|
||||
private final int _statusBarColor;
|
||||
|
||||
private ActionModeStatusGuardHack() {
|
||||
_statusBarColor = getWindow().getStatusBarColor();
|
||||
|
||||
try {
|
||||
_fadeAnimField = getDelegate().getClass().getDeclaredField("mFadeAnim");
|
||||
_fadeAnimField.setAccessible(true);
|
||||
_actionModeViewField = getDelegate().getClass().getDeclaredField("mActionModeView");
|
||||
_actionModeViewField.setAccessible(true);
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private void apply(int visibility) {
|
||||
if (_fadeAnimField == null || _actionModeViewField == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewPropertyAnimatorCompat fadeAnim;
|
||||
ViewGroup actionModeView;
|
||||
try {
|
||||
fadeAnim = (ViewPropertyAnimatorCompat) _fadeAnimField.get(getDelegate());
|
||||
actionModeView = (ViewGroup) _actionModeViewField.get(getDelegate());
|
||||
} catch (IllegalAccessException e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fadeAnim == null || actionModeView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
fadeAnim.cancel();
|
||||
|
||||
actionModeView.setVisibility(visibility);
|
||||
actionModeView.setAlpha(visibility == View.VISIBLE ? 1f : 0f);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
int statusBarColor = visibility == View.VISIBLE
|
||||
? ThemeHelper.getThemeColor(com.google.android.material.R.attr.colorSurfaceContainer, getTheme())
|
||||
: _statusBarColor;
|
||||
getWindow().setStatusBarColor(statusBarColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports whether this Activity instance has become an orphan. This can happen if
|
||||
* the vault was killed/locked by an external trigger while the Activity was still open.
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.beemdevelopment.aegis.ui;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.view.KeyEvent;
|
||||
|
@ -20,11 +19,9 @@ import android.widget.Toast;
|
|||
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.biometric.BiometricPrompt;
|
||||
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.ThemeMap;
|
||||
import com.beemdevelopment.aegis.crypto.KeyStoreHandle;
|
||||
import com.beemdevelopment.aegis.crypto.KeyStoreHandleException;
|
||||
import com.beemdevelopment.aegis.crypto.MasterKey;
|
||||
|
@ -43,6 +40,7 @@ import com.beemdevelopment.aegis.vault.slots.Slot;
|
|||
import com.beemdevelopment.aegis.vault.slots.SlotException;
|
||||
import com.beemdevelopment.aegis.vault.slots.SlotIntegrityException;
|
||||
import com.beemdevelopment.aegis.vault.slots.SlotList;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -163,10 +161,11 @@ public class AuthActivity extends AegisActivity {
|
|||
|
||||
biometricsButton.setOnClickListener(v -> {
|
||||
if (_prefs.isPasswordReminderNeeded()) {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(this)
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Aegis_AlertDialog_Warning)
|
||||
.setTitle(getString(R.string.password_reminder_dialog_title))
|
||||
.setMessage(getString(R.string.password_reminder_dialog_message))
|
||||
.setCancelable(false)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(android.R.string.ok, (dialog1, which) -> {
|
||||
showBiometricPrompt();
|
||||
})
|
||||
|
@ -183,11 +182,6 @@ public class AuthActivity extends AegisActivity {
|
|||
outState.putBoolean("inhibitBioPrompt", _inhibitBioPrompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetTheme() {
|
||||
setTheme(ThemeMap.NO_ACTION_BAR);
|
||||
}
|
||||
|
||||
private void selectPassword() {
|
||||
_textPassword.selectAll();
|
||||
|
||||
|
@ -240,9 +234,6 @@ public class AuthActivity extends AegisActivity {
|
|||
PopupWindow popup = new PopupWindow(popupLayout, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
popup.setFocusable(false);
|
||||
popup.setOutsideTouchable(true);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
|
||||
popup.setElevation(5.0f);
|
||||
}
|
||||
_textPassword.post(() -> {
|
||||
if (isFinishing()) {
|
||||
return;
|
||||
|
@ -302,10 +293,11 @@ public class AuthActivity extends AegisActivity {
|
|||
}
|
||||
|
||||
private void onInvalidPassword() {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(AuthActivity.this)
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(AuthActivity.this, R.style.ThemeOverlay_Aegis_AlertDialog_Error)
|
||||
.setTitle(getString(R.string.unlock_vault_error))
|
||||
.setMessage(getString(R.string.unlock_vault_error_description))
|
||||
.setCancelable(false)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> selectPassword())
|
||||
.create());
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ import androidx.activity.result.contract.ActivityResultContracts.StartActivityFo
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.amulyakhare.textdrawable.TextDrawable;
|
||||
import com.avito.android.krop.KropView;
|
||||
|
@ -68,6 +67,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
|||
import com.bumptech.glide.request.target.CustomTarget;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
|
||||
|
@ -164,7 +164,7 @@ public class EditEntryActivity extends AegisActivity {
|
|||
|
||||
ActionBar bar = getSupportActionBar();
|
||||
if (bar != null) {
|
||||
bar.setHomeAsUpIndicator(R.drawable.ic_close);
|
||||
bar.setHomeAsUpIndicator(R.drawable.ic_outline_close_24);
|
||||
bar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
|
@ -473,7 +473,7 @@ public class EditEntryActivity extends AegisActivity {
|
|||
} else if (itemId == R.id.action_edit_icon) {
|
||||
startIconSelection();
|
||||
} else if (itemId == R.id.action_reset_usage_count) {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(this)
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.action_reset_usage_count)
|
||||
.setMessage(R.string.action_reset_usage_count_dialog)
|
||||
.setPositiveButton(android.R.string.yes, (dialog, which) -> resetUsageCount())
|
||||
|
@ -754,9 +754,10 @@ public class EditEntryActivity extends AegisActivity {
|
|||
}
|
||||
|
||||
private void onSaveError(String msg) {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(this)
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Aegis_AlertDialog_Error)
|
||||
.setTitle(getString(R.string.saving_profile_error))
|
||||
.setMessage(msg)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.create());
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import android.view.View;
|
|||
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
|
@ -15,6 +14,7 @@ import com.beemdevelopment.aegis.R;
|
|||
import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
|
||||
import com.beemdevelopment.aegis.ui.views.GroupAdapter;
|
||||
import com.beemdevelopment.aegis.vault.VaultGroup;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
@ -84,9 +84,10 @@ public class GroupManagerActivity extends AegisActivity implements GroupAdapter.
|
|||
|
||||
@Override
|
||||
public void onRemoveGroup(VaultGroup group) {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(this)
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Aegis_AlertDialog_Warning)
|
||||
.setTitle(R.string.remove_group)
|
||||
.setMessage(R.string.remove_group_description)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(android.R.string.yes, (dialog, whichButton) -> {
|
||||
_removedGroups.add(group.getUUID());
|
||||
_adapter.removeGroup(group);
|
||||
|
@ -98,9 +99,10 @@ public class GroupManagerActivity extends AegisActivity implements GroupAdapter.
|
|||
}
|
||||
|
||||
public void onRemoveUnusedGroups() {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(this)
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Aegis_AlertDialog_Warning)
|
||||
.setTitle(R.string.remove_unused_groups)
|
||||
.setMessage(R.string.remove_unused_groups_description)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(android.R.string.yes, (dialog, whichButton) -> {
|
||||
Set<VaultGroup> unusedGroups = new HashSet<>(_vaultManager.getVault().getGroups());
|
||||
unusedGroups.removeAll(_vaultManager.getVault().getUsedGroups());
|
||||
|
|
|
@ -11,7 +11,6 @@ import android.widget.Toast;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
|
@ -28,6 +27,7 @@ import com.beemdevelopment.aegis.util.UUIDMap;
|
|||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
import com.beemdevelopment.aegis.vault.VaultGroup;
|
||||
import com.beemdevelopment.aegis.vault.VaultRepository;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
|
@ -62,7 +62,7 @@ public class ImportEntriesActivity extends AegisActivity {
|
|||
_view = findViewById(R.id.importEntriesRootView);
|
||||
|
||||
ActionBar bar = getSupportActionBar();
|
||||
bar.setHomeAsUpIndicator(R.drawable.ic_close);
|
||||
bar.setHomeAsUpIndicator(R.drawable.ic_outline_close_24);
|
||||
bar.setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
_adapter = new ImportEntriesAdapter();
|
||||
|
@ -101,10 +101,11 @@ public class ImportEntriesActivity extends AegisActivity {
|
|||
if (importer.isInstalledAppVersionSupported()) {
|
||||
startImportApp(importer);
|
||||
} else {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(this)
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Aegis_AlertDialog_Warning)
|
||||
.setTitle(R.string.warning)
|
||||
.setMessage(getString(R.string.app_version_error, importerDef.getName()))
|
||||
.setCancelable(false)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(R.string.yes, (dialog1, which) -> {
|
||||
startImportApp(importer);
|
||||
})
|
||||
|
@ -215,7 +216,7 @@ public class ImportEntriesActivity extends AegisActivity {
|
|||
List<DatabaseImporterEntryException> errors = result.getErrors();
|
||||
if (errors.size() > 0) {
|
||||
String message = getResources().getQuantityString(R.plurals.import_error_dialog, errors.size(), errors.size());
|
||||
Dialogs.showMultiErrorDialog(this, R.string.import_error_title, message, errors, null);
|
||||
Dialogs.showMultiExceptionDialog(this, R.string.import_error_title, message, errors, null);
|
||||
}
|
||||
|
||||
findDuplicates(importEntries);
|
||||
|
@ -296,7 +297,7 @@ public class ImportEntriesActivity extends AegisActivity {
|
|||
|
||||
assignIconIntent.putExtra("entries", assignIconEntriesIds);
|
||||
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(this)
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.import_assign_icons_dialog_title)
|
||||
.setMessage(R.string.import_assign_icons_dialog_text)
|
||||
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
|
||||
|
|
|
@ -13,7 +13,6 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.ThemeMap;
|
||||
import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
|
||||
import com.beemdevelopment.aegis.ui.intro.IntroBaseActivity;
|
||||
import com.beemdevelopment.aegis.ui.intro.SlideFragment;
|
||||
|
@ -40,11 +39,6 @@ public class IntroActivity extends IntroBaseActivity {
|
|||
addSlide(DoneSlide.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetTheme() {
|
||||
setTheme(ThemeMap.NO_ACTION_BAR);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBeforeSlideChanged(Class<? extends SlideFragment> oldSlide, @NonNull Class<? extends SlideFragment> newSlide) {
|
||||
// hide the keyboard before every slide change
|
||||
|
|
|
@ -25,8 +25,6 @@ import android.view.MenuItem;
|
|||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
|
@ -37,8 +35,8 @@ import androidx.appcompat.app.AlertDialog;
|
|||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
|
||||
import com.beemdevelopment.aegis.CopyBehavior;
|
||||
import com.beemdevelopment.aegis.AccountNamePosition;
|
||||
import com.beemdevelopment.aegis.CopyBehavior;
|
||||
import com.beemdevelopment.aegis.Preferences;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.SortCategory;
|
||||
|
@ -51,11 +49,13 @@ import com.beemdevelopment.aegis.otp.OtpInfoException;
|
|||
import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
|
||||
import com.beemdevelopment.aegis.ui.fragments.preferences.BackupsPreferencesFragment;
|
||||
import com.beemdevelopment.aegis.ui.fragments.preferences.PreferencesFragment;
|
||||
import com.beemdevelopment.aegis.ui.models.ErrorCardInfo;
|
||||
import com.beemdevelopment.aegis.ui.tasks.QrDecodeTask;
|
||||
import com.beemdevelopment.aegis.ui.views.EntryListView;
|
||||
import com.beemdevelopment.aegis.util.TimeUtils;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
|
@ -87,8 +87,6 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
private Menu _menu;
|
||||
private SearchView _searchView;
|
||||
private EntryListView _entryListView;
|
||||
private LinearLayout _btnErrorBar;
|
||||
private TextView _textErrorBar;
|
||||
|
||||
private FabScrollHelper _fabScrollHelper;
|
||||
|
||||
|
@ -223,9 +221,6 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
Dialogs.showSecureDialog(dialog);
|
||||
});
|
||||
|
||||
_btnErrorBar = findViewById(R.id.btn_error_bar);
|
||||
_textErrorBar = findViewById(R.id.text_error_bar);
|
||||
|
||||
_fabScrollHelper = new FabScrollHelper(fab);
|
||||
_selectedEntries = new ArrayList<>();
|
||||
}
|
||||
|
@ -458,7 +453,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
boolean isSingleBatch = GoogleAuthInfo.Export.isSingleBatch(googleAuthExports);
|
||||
if (!isSingleBatch && errors.size() > 0) {
|
||||
errors.add(getString(R.string.unrelated_google_auth_batches_error));
|
||||
Dialogs.showMultiMessageDialog(this, R.string.import_error_title, getString(R.string.no_tokens_can_be_imported), errors, null);
|
||||
Dialogs.showMultiErrorDialog(this, R.string.import_error_title, getString(R.string.no_tokens_can_be_imported), errors, null);
|
||||
return;
|
||||
} else if (!isSingleBatch) {
|
||||
Dialogs.showErrorDialog(this, R.string.import_google_auth_failure, getString(R.string.unrelated_google_auth_batches_error));
|
||||
|
@ -473,7 +468,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
}
|
||||
|
||||
if ((errors.size() > 0 && results.size() > 1) || errors.size() > 1) {
|
||||
Dialogs.showMultiMessageDialog(this, R.string.import_error_title, getString(R.string.unable_to_read_qrcode_files, uris.size() - errors.size(), uris.size()), errors, dialogDismissHandler);
|
||||
Dialogs.showMultiErrorDialog(this, R.string.import_error_title, getString(R.string.unable_to_read_qrcode_files, uris.size() - errors.size(), uris.size()), errors, dialogDismissHandler);
|
||||
} else if (errors.size() > 0) {
|
||||
Dialogs.showErrorDialog(this, getString(R.string.unable_to_read_qrcode_file, results.get(0).getFileName()), errors.get(0), dialogDismissHandler);
|
||||
} else {
|
||||
|
@ -691,7 +686,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
handleIncomingIntent();
|
||||
updateLockIcon();
|
||||
doShortcutActions();
|
||||
updateErrorBar();
|
||||
updateErrorCard();
|
||||
}
|
||||
|
||||
private void deleteEntries(List<VaultEntry> entries) {
|
||||
|
@ -851,49 +846,49 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
}
|
||||
}
|
||||
|
||||
private void updateErrorBar() {
|
||||
Preferences.BackupResult backupRes = _prefs.getErroredBackupResult();
|
||||
if (backupRes != null) {
|
||||
_textErrorBar.setText(R.string.backup_error_bar_message);
|
||||
_btnErrorBar.setOnClickListener(view -> {
|
||||
Dialogs.showBackupErrorDialog(this, backupRes, (dialog, which) -> {
|
||||
startPreferencesActivity(BackupsPreferencesFragment.class, "pref_backups");
|
||||
});
|
||||
});
|
||||
_btnErrorBar.setVisibility(View.VISIBLE);
|
||||
} else if (_prefs.isBackupsReminderNeeded() && _prefs.isBackupReminderEnabled()) {
|
||||
Date date = _prefs.getLatestBackupOrExportTime();
|
||||
if (date != null) {
|
||||
_textErrorBar.setText(getString(R.string.backup_reminder_bar_message_with_latest, TimeUtils.getElapsedSince(this, date)));
|
||||
} else {
|
||||
_textErrorBar.setText(R.string.backup_reminder_bar_message);
|
||||
}
|
||||
_btnErrorBar.setOnClickListener(view -> {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.backup_reminder_bar_dialog_title)
|
||||
.setMessage(R.string.backup_reminder_bar_dialog_summary)
|
||||
.setPositiveButton(R.string.backup_reminder_bar_dialog_accept, (dialog, whichButton) -> {
|
||||
startPreferencesActivity(BackupsPreferencesFragment.class, "pref_backups");
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create());
|
||||
});
|
||||
_btnErrorBar.setVisibility(View.VISIBLE);
|
||||
} else if (_prefs.isPlaintextBackupWarningNeeded()) {
|
||||
_textErrorBar.setText(R.string.backup_plaintext_export_warning);
|
||||
_btnErrorBar.setOnClickListener(view -> showPlaintextExportWarningOptions());
|
||||
_btnErrorBar.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
_btnErrorBar.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
private void updateErrorCard() {
|
||||
ErrorCardInfo info = null;
|
||||
|
||||
Preferences.BackupResult backupRes = _prefs.getErroredBackupResult();
|
||||
if (backupRes != null) {
|
||||
info = new ErrorCardInfo(getString(R.string.backup_error_bar_message), view -> {
|
||||
Dialogs.showBackupErrorDialog(this, backupRes, (dialog, which) -> {
|
||||
startPreferencesActivity(BackupsPreferencesFragment.class, "pref_backups");
|
||||
});
|
||||
});
|
||||
} else if (_prefs.isBackupsReminderNeeded() && _prefs.isBackupReminderEnabled()) {
|
||||
String text;
|
||||
Date date = _prefs.getLatestBackupOrExportTime();
|
||||
if (date != null) {
|
||||
text = getString(R.string.backup_reminder_bar_message_with_latest, TimeUtils.getElapsedSince(this, date));
|
||||
} else {
|
||||
text = getString(R.string.backup_reminder_bar_message);
|
||||
}
|
||||
info = new ErrorCardInfo(text, view -> {
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Aegis_AlertDialog_Error)
|
||||
.setTitle(R.string.backup_reminder_bar_dialog_title)
|
||||
.setMessage(R.string.backup_reminder_bar_dialog_summary)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(R.string.backup_reminder_bar_dialog_accept, (dialog, whichButton) -> {
|
||||
startPreferencesActivity(BackupsPreferencesFragment.class, "pref_backups");
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create());
|
||||
});
|
||||
} else if (_prefs.isPlaintextBackupWarningNeeded()) {
|
||||
info = new ErrorCardInfo(getString(R.string.backup_plaintext_export_warning), view -> showPlaintextExportWarningOptions());
|
||||
}
|
||||
|
||||
_entryListView.setErrorCardInfo(info);
|
||||
}
|
||||
|
||||
private void showPlaintextExportWarningOptions() {
|
||||
View view = LayoutInflater.from(this).inflate(R.layout.dialog_plaintext_warning, null);
|
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(this)
|
||||
AlertDialog dialog = new MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Aegis_AlertDialog_Warning)
|
||||
.setTitle(R.string.backup_plaintext_export_warning)
|
||||
.setView(view)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create();
|
||||
|
@ -910,7 +905,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
_prefs.setIsPlaintextBackupWarningDisabled(checkBox.isChecked());
|
||||
_prefs.setIsPlaintextBackupWarningNeeded(false);
|
||||
|
||||
updateErrorBar();
|
||||
updateErrorCard();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -926,8 +921,6 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
setFavoriteMenuItemVisiblity();
|
||||
setIsMultipleSelected(_selectedEntries.size() > 1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -957,14 +950,14 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
|
||||
if (_selectedEntries.size() == 1){
|
||||
if (_selectedEntries.get(0).isFavorite()) {
|
||||
toggleFavoriteMenuItem.setIcon(R.drawable.ic_set_favorite);
|
||||
toggleFavoriteMenuItem.setIcon(R.drawable.ic_filled_star_24);
|
||||
toggleFavoriteMenuItem.setTitle(R.string.unfavorite);
|
||||
} else {
|
||||
toggleFavoriteMenuItem.setIcon(R.drawable.ic_unset_favorite);
|
||||
toggleFavoriteMenuItem.setIcon(R.drawable.ic_outline_star_24);
|
||||
toggleFavoriteMenuItem.setTitle(R.string.favorite);
|
||||
}
|
||||
} else {
|
||||
toggleFavoriteMenuItem.setIcon(R.drawable.ic_unset_favorite);
|
||||
toggleFavoriteMenuItem.setIcon(R.drawable.ic_outline_star_24);
|
||||
toggleFavoriteMenuItem.setTitle(String.format("%s / %s", getString(R.string.favorite), getString(R.string.unfavorite)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import com.beemdevelopment.aegis.ui.fragments.preferences.PreferencesFragment;
|
|||
public class PreferencesActivity extends AegisActivity implements
|
||||
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
|
||||
private Fragment _fragment;
|
||||
private CharSequence _prefTitle;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -48,6 +49,10 @@ public class PreferencesActivity extends AegisActivity implements
|
|||
}
|
||||
} else {
|
||||
_fragment = getSupportFragmentManager().findFragmentById(R.id.content);
|
||||
_prefTitle = savedInstanceState.getCharSequence("prefTitle");
|
||||
if (_prefTitle != null) {
|
||||
setTitle(_prefTitle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,6 +74,7 @@ public class PreferencesActivity extends AegisActivity implements
|
|||
// this is done so we don't lose anything if the fragment calls recreate on this activity
|
||||
outState.putParcelable("result", ((PreferencesFragment) _fragment).getResult());
|
||||
}
|
||||
outState.putCharSequence("prefTitle", _prefTitle);
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
|
@ -90,7 +96,8 @@ public class PreferencesActivity extends AegisActivity implements
|
|||
_fragment.setTargetFragment(caller, 0);
|
||||
showFragment(_fragment);
|
||||
|
||||
setTitle(pref.getTitle());
|
||||
_prefTitle = pref.getTitle();
|
||||
setTitle(_prefTitle);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -87,6 +87,11 @@ public class ScannerActivity extends AegisActivity implements QrCodeAnalyzer.Lis
|
|||
}, ContextCompat.getMainExecutor(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetTheme() {
|
||||
setTheme(ThemeMap.FULLSCREEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
if (_executor != null) {
|
||||
|
@ -95,11 +100,6 @@ public class ScannerActivity extends AegisActivity implements QrCodeAnalyzer.Lis
|
|||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetTheme() {
|
||||
setTheme(ThemeMap.FULLSCREEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
_menu = menu;
|
||||
|
@ -142,10 +142,10 @@ public class ScannerActivity extends AegisActivity implements QrCodeAnalyzer.Lis
|
|||
if (dual) {
|
||||
switch (_currentLens) {
|
||||
case CameraSelector.LENS_FACING_BACK:
|
||||
item.setIcon(R.drawable.ic_camera_front_24dp);
|
||||
item.setIcon(R.drawable.ic_outline_camera_front_24);
|
||||
break;
|
||||
case CameraSelector.LENS_FACING_FRONT:
|
||||
item.setIcon(R.drawable.ic_camera_rear_24dp);
|
||||
item.setIcon(R.drawable.ic_outline_camera_rear_24);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ import com.beemdevelopment.aegis.vault.VaultEntry;
|
|||
import com.beemdevelopment.aegis.vault.slots.PasswordSlot;
|
||||
import com.beemdevelopment.aegis.vault.slots.Slot;
|
||||
import com.beemdevelopment.aegis.vault.slots.SlotException;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.nulabinc.zxcvbn.Strength;
|
||||
|
@ -87,9 +88,10 @@ public class Dialogs {
|
|||
}
|
||||
textMessage.setText(message);
|
||||
|
||||
showSecureDialog(new AlertDialog.Builder(context)
|
||||
showSecureDialog(new MaterialAlertDialogBuilder(context, R.style.ThemeOverlay_Aegis_AlertDialog_Warning)
|
||||
.setTitle(title)
|
||||
.setView(view)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(android.R.string.yes, onDelete)
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.create());
|
||||
|
@ -108,9 +110,10 @@ public class Dialogs {
|
|||
}
|
||||
|
||||
public static void showDiscardDialog(Context context, DialogInterface.OnClickListener onSave, DialogInterface.OnClickListener onDiscard) {
|
||||
showSecureDialog(new AlertDialog.Builder(context)
|
||||
showSecureDialog(new MaterialAlertDialogBuilder(context, R.style.ThemeOverlay_Aegis_AlertDialog_Warning)
|
||||
.setTitle(context.getString(R.string.discard_changes))
|
||||
.setMessage(context.getString(R.string.discard_changes_description))
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(R.string.save, onSave)
|
||||
.setNegativeButton(R.string.discard, onDiscard)
|
||||
.create());
|
||||
|
@ -138,7 +141,7 @@ public class Dialogs {
|
|||
}
|
||||
});
|
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(activity)
|
||||
AlertDialog dialog = new MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.set_password)
|
||||
.setView(view)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
|
@ -209,7 +212,7 @@ public class Dialogs {
|
|||
}
|
||||
inputLayout.setHint(hintId);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context)
|
||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context)
|
||||
.setTitle(titleId)
|
||||
.setView(view)
|
||||
.setPositiveButton(android.R.string.ok, null);
|
||||
|
@ -272,7 +275,7 @@ public class Dialogs {
|
|||
CheckBox checkBox = view.findViewById(R.id.checkbox);
|
||||
checkBox.setText(checkboxMessageId);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context)
|
||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context)
|
||||
.setTitle(titleId)
|
||||
.setView(view)
|
||||
.setNegativeButton(R.string.no, (dialog1, which) ->
|
||||
|
@ -306,7 +309,7 @@ public class Dialogs {
|
|||
numberPicker.setValue(currentValue);
|
||||
numberPicker.setWrapSelectorWheel(true);
|
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(context)
|
||||
AlertDialog dialog = new MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.set_number)
|
||||
.setView(view)
|
||||
.setPositiveButton(android.R.string.ok, (dialog1, which) ->
|
||||
|
@ -331,7 +334,7 @@ public class Dialogs {
|
|||
numberPicker.setValue(currentVersionCount / 5 - 1);
|
||||
numberPicker.setWrapSelectorWheel(false);
|
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(context)
|
||||
AlertDialog dialog = new MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.set_number)
|
||||
.setView(view)
|
||||
.setPositiveButton(android.R.string.ok, (dialog1, which) ->
|
||||
|
@ -372,10 +375,11 @@ public class Dialogs {
|
|||
TextView textMessage = view.findViewById(R.id.error_message);
|
||||
textMessage.setText(message);
|
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(context)
|
||||
AlertDialog dialog = new MaterialAlertDialogBuilder(context, R.style.ThemeOverlay_Aegis_AlertDialog_Error)
|
||||
.setTitle(R.string.error_occurred)
|
||||
.setView(view)
|
||||
.setCancelable(false)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(android.R.string.ok, (dialog1, which) -> {
|
||||
if (listener != null) {
|
||||
listener.onClick(dialog1, which);
|
||||
|
@ -412,34 +416,35 @@ public class Dialogs {
|
|||
Dialogs.showErrorDialog(context, message, backupRes.getError(), listener);
|
||||
}
|
||||
|
||||
public static void showMultiMessageDialog(
|
||||
public static void showMultiErrorDialog(
|
||||
Context context, @StringRes int title, String message, List<CharSequence> messages, DialogInterface.OnClickListener listener) {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(context)
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(context, R.style.ThemeOverlay_Aegis_AlertDialog_Error)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setCancelable(false)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
if (listener != null) {
|
||||
listener.onClick(dialog, which);
|
||||
}
|
||||
})
|
||||
.setNeutralButton(context.getString(R.string.details), (dialog, which) -> {
|
||||
showDetailedMultiMessageDialog(context, title, messages, listener);
|
||||
showDetailedMultiErrorDialog(context, title, messages, listener);
|
||||
})
|
||||
.create());
|
||||
}
|
||||
|
||||
public static <T extends Throwable> void showMultiErrorDialog(
|
||||
public static <T extends Throwable> void showMultiExceptionDialog(
|
||||
Context context, @StringRes int title, String message, List<T> errors, DialogInterface.OnClickListener listener) {
|
||||
List<CharSequence> messages = new ArrayList<>();
|
||||
for (Throwable e : errors) {
|
||||
messages.add(e.toString());
|
||||
}
|
||||
|
||||
showMultiMessageDialog(context, title, message, messages, listener);
|
||||
showMultiErrorDialog(context, title, message, messages, listener);
|
||||
}
|
||||
|
||||
private static void showDetailedMultiMessageDialog(
|
||||
private static void showDetailedMultiErrorDialog(
|
||||
Context context, @StringRes int title, List<CharSequence> messages, DialogInterface.OnClickListener listener) {
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder();
|
||||
for (CharSequence message : messages) {
|
||||
|
@ -447,10 +452,11 @@ public class Dialogs {
|
|||
builder.append("\n\n");
|
||||
}
|
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(context)
|
||||
AlertDialog dialog = new MaterialAlertDialogBuilder(context, R.style.ThemeOverlay_Aegis_AlertDialog_Error)
|
||||
.setTitle(title)
|
||||
.setMessage(builder)
|
||||
.setCancelable(false)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(android.R.string.ok, (dialog1, which) -> {
|
||||
if (listener != null) {
|
||||
listener.onClick(dialog1, which);
|
||||
|
@ -477,10 +483,11 @@ public class Dialogs {
|
|||
View view = LayoutInflater.from(context).inflate(R.layout.dialog_time_sync, null);
|
||||
CheckBox checkBox = view.findViewById(R.id.check_warning_disable);
|
||||
|
||||
showSecureDialog(new AlertDialog.Builder(context)
|
||||
showSecureDialog(new MaterialAlertDialogBuilder(context, R.style.ThemeOverlay_Aegis_AlertDialog_Warning)
|
||||
.setTitle(R.string.time_sync_warning_title)
|
||||
.setView(view)
|
||||
.setCancelable(false)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(R.string.yes, (dialog, which) -> {
|
||||
if (checkBox.isChecked()) {
|
||||
prefs.setIsTimeSyncWarningEnabled(false);
|
||||
|
@ -515,7 +522,7 @@ public class Dialogs {
|
|||
setImporterHelpText(helpText, importers.get(position), isDirect);
|
||||
});
|
||||
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(context)
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.choose_application)
|
||||
.setView(view)
|
||||
.setPositiveButton(android.R.string.ok, (dialog1, which) -> {
|
||||
|
@ -536,11 +543,12 @@ public class Dialogs {
|
|||
errorDetails.append("\n\n");
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context)
|
||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context, R.style.ThemeOverlay_Aegis_AlertDialog_Warning)
|
||||
.setTitle(R.string.partial_google_auth_import)
|
||||
.setMessage(context.getString(R.string.partial_google_auth_import_warning, missingIndexesAsString))
|
||||
.setView(view)
|
||||
.setCancelable(false)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(context.getString(R.string.import_partial_export_anyway, entries), (dialog, which) -> {
|
||||
dismissHandler.onClick(dialog, which);
|
||||
})
|
||||
|
|
|
@ -18,6 +18,7 @@ import androidx.fragment.app.DialogFragment;
|
|||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.Theme;
|
||||
import com.beemdevelopment.aegis.helpers.ThemeHelper;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.common.io.CharStreams;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -44,14 +45,14 @@ public abstract class SimpleWebViewDialog extends DialogFragment {
|
|||
view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_web_view, null);
|
||||
} catch (InflateException e) {
|
||||
e.printStackTrace();
|
||||
return new AlertDialog.Builder(requireContext())
|
||||
return new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(android.R.string.dialog_alert_title)
|
||||
.setMessage(getString(R.string.webview_error))
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(requireContext())
|
||||
AlertDialog dialog = new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(_title)
|
||||
.setView(view)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
|
@ -69,12 +70,11 @@ public abstract class SimpleWebViewDialog extends DialogFragment {
|
|||
}
|
||||
|
||||
protected String getBackgroundColor() {
|
||||
int backgroundColorResource = _theme == Theme.AMOLED ? R.attr.cardBackgroundFocused : R.attr.cardBackground;
|
||||
return colorToCSS(ThemeHelper.getThemeColor(backgroundColorResource, requireContext().getTheme()));
|
||||
return colorToCSS(ThemeHelper.getThemeColor(com.google.android.material.R.attr.colorSurfaceContainerHigh, requireContext().getTheme()));
|
||||
}
|
||||
|
||||
protected String getTextColor() {
|
||||
return colorToCSS(0xFFFFFF & ThemeHelper.getThemeColor(R.attr.primaryText, requireContext().getTheme()));
|
||||
return colorToCSS(0xFFFFFF & ThemeHelper.getThemeColor(com.google.android.material.R.attr.colorOnSurface, requireContext().getTheme()));
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
|
|
|
@ -8,11 +8,17 @@ import androidx.appcompat.app.AlertDialog;
|
|||
import androidx.preference.Preference;
|
||||
|
||||
import com.beemdevelopment.aegis.AccountNamePosition;
|
||||
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.GroupManagerActivity;
|
||||
import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
|
||||
import com.google.android.material.color.DynamicColors;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class AppearancePreferencesFragment extends PreferencesFragment {
|
||||
private Preference _groupsPreference;
|
||||
|
@ -33,7 +39,7 @@ public class AppearancePreferencesFragment extends PreferencesFragment {
|
|||
|
||||
_resetUsageCountPreference = requirePreference("pref_reset_usage_count");
|
||||
_resetUsageCountPreference.setOnPreferenceClickListener(preference -> {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(requireContext())
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.preference_reset_usage_count)
|
||||
.setMessage(R.string.preference_reset_usage_count_dialog)
|
||||
.setPositiveButton(android.R.string.yes, (dialog, which) -> _prefs.clearUsageCount())
|
||||
|
@ -48,7 +54,7 @@ public class AppearancePreferencesFragment extends PreferencesFragment {
|
|||
darkModePreference.setOnPreferenceClickListener(preference -> {
|
||||
int currentTheme1 = _prefs.getCurrentTheme().ordinal();
|
||||
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(requireContext())
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.choose_theme)
|
||||
.setSingleChoiceItems(R.array.theme_titles, currentTheme1, (dialog, which) -> {
|
||||
int i = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
|
@ -65,11 +71,36 @@ public class AppearancePreferencesFragment extends PreferencesFragment {
|
|||
return true;
|
||||
});
|
||||
|
||||
Preference dynamicColorsPreference = requirePreference("pref_dynamic_colors");
|
||||
dynamicColorsPreference.setEnabled(DynamicColors.isDynamicColorAvailable());
|
||||
dynamicColorsPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
getResult().putExtra("needsRecreate", true);
|
||||
requireActivity().recreate();
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference langPreference = requirePreference("pref_lang");
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
langPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
getResult().putExtra("needsRecreate", true);
|
||||
requireActivity().recreate();
|
||||
String[] langs = getResources().getStringArray(R.array.pref_lang_values);
|
||||
String[] langNames = getResources().getStringArray(R.array.pref_lang_entries);
|
||||
List<String> langList = Arrays.asList(langs);
|
||||
int curLangIndex = langList.contains(_prefs.getLanguage()) ? langList.indexOf(_prefs.getLanguage()) : 0;
|
||||
langPreference.setSummary(langNames[curLangIndex]);
|
||||
langPreference.setOnPreferenceClickListener(preference -> {
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.pref_lang_title)
|
||||
.setSingleChoiceItems(langNames, curLangIndex, (dialog, which) -> {
|
||||
int newLangIndex = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
_prefs.setLanguage(langs[newLangIndex]);
|
||||
langPreference.setSummary(langNames[newLangIndex]);
|
||||
|
||||
dialog.dismiss();
|
||||
|
||||
getResult().putExtra("needsRecreate", true);
|
||||
requireActivity().recreate();
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create());
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
|
@ -83,7 +114,7 @@ public class AppearancePreferencesFragment extends PreferencesFragment {
|
|||
viewModePreference.setOnPreferenceClickListener(preference -> {
|
||||
int currentViewMode1 = _prefs.getCurrentViewMode().ordinal();
|
||||
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(requireContext())
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.choose_view_mode)
|
||||
.setSingleChoiceItems(R.array.view_mode_titles, currentViewMode1, (dialog, which) -> {
|
||||
int i = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
|
@ -99,9 +130,22 @@ public class AppearancePreferencesFragment extends PreferencesFragment {
|
|||
return true;
|
||||
});
|
||||
|
||||
String[] codeGroupings = getResources().getStringArray(R.array.pref_code_groupings_values);
|
||||
String[] codeGroupingNames = getResources().getStringArray(R.array.pref_code_groupings);
|
||||
int currentCodeGroupingIndex = Arrays.asList(codeGroupings).indexOf(_prefs.getCodeGroupSize().name());
|
||||
Preference codeDigitGroupingPreference = requirePreference("pref_code_group_size_string");
|
||||
codeDigitGroupingPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
getResult().putExtra("needsRefresh", true);
|
||||
codeDigitGroupingPreference.setOnPreferenceClickListener(preference -> {
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.pref_code_group_size_title)
|
||||
.setSingleChoiceItems(codeGroupingNames, currentCodeGroupingIndex, (dialog, which) -> {
|
||||
int newCodeGroupingIndex = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
_prefs.setCodeGroupSize(Preferences.CodeGrouping.valueOf(codeGroupings[newCodeGroupingIndex]));
|
||||
|
||||
dialog.dismiss();
|
||||
getResult().putExtra("needsRefresh", true);
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create());
|
||||
return true;
|
||||
});
|
||||
|
||||
|
@ -117,7 +161,7 @@ public class AppearancePreferencesFragment extends PreferencesFragment {
|
|||
_currentAccountNamePositionPreference.setOnPreferenceClickListener(preference -> {
|
||||
int currentAccountNamePosition1 = _prefs.getAccountNamePosition().ordinal();
|
||||
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(requireContext())
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(getString(R.string.choose_account_name_position))
|
||||
.setSingleChoiceItems(R.array.account_name_position_titles, currentAccountNamePosition1, (dialog, which) -> {
|
||||
int i = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
|
|
|
@ -19,6 +19,7 @@ import androidx.preference.SwitchPreferenceCompat;
|
|||
|
||||
import com.beemdevelopment.aegis.Preferences;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.helpers.ThemeHelper;
|
||||
import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
|
||||
import com.beemdevelopment.aegis.vault.VaultRepositoryException;
|
||||
|
||||
|
@ -195,9 +196,9 @@ public class BackupsPreferencesFragment extends PreferencesFragment {
|
|||
|
||||
// TODO: Find out why setting the tint of the icon doesn't work
|
||||
if (backupFailed) {
|
||||
pref.setIcon(R.drawable.ic_info_outline_black_24dp);
|
||||
pref.setIcon(R.drawable.ic_outline_error_24);
|
||||
} else if (res != null) {
|
||||
pref.setIcon(R.drawable.ic_check_black_24dp);
|
||||
pref.setIcon(R.drawable.ic_outline_check_24);
|
||||
} else {
|
||||
pref.setIcon(null);
|
||||
}
|
||||
|
@ -205,21 +206,20 @@ public class BackupsPreferencesFragment extends PreferencesFragment {
|
|||
|
||||
private CharSequence getBackupStatusMessage(@Nullable Preferences.BackupResult res) {
|
||||
String message;
|
||||
int color = R.color.warning_color;
|
||||
int colorAttr = com.google.android.material.R.attr.colorError;
|
||||
if (res == null) {
|
||||
message = getString(R.string.backup_status_none);
|
||||
} else if (res.isSuccessful()) {
|
||||
color = R.color.success_color;
|
||||
colorAttr = R.attr.colorSuccess;
|
||||
message = getString(R.string.backup_status_success, res.getElapsedSince(requireContext()));
|
||||
} else {
|
||||
message = getString(R.string.backup_status_failed, res.getElapsedSince(requireContext()));
|
||||
}
|
||||
|
||||
int color = ThemeHelper.getThemeColor(colorAttr, requireContext().getTheme());
|
||||
Spannable spannable = new SpannableString(message);
|
||||
spannable.setSpan(new ForegroundColorSpan(getResources().getColor(color)), 0, message.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
if (color == R.color.warning_color) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.BOLD), 0, message.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
spannable.setSpan(new ForegroundColorSpan(color), 0, message.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
spannable.setSpan(new StyleSpan(Typeface.BOLD), 0, message.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return spannable;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import androidx.preference.Preference;
|
|||
import com.beemdevelopment.aegis.CopyBehavior;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
public class BehaviorPreferencesFragment extends PreferencesFragment {
|
||||
private Preference _entryPausePreference;
|
||||
|
@ -23,7 +24,7 @@ public class BehaviorPreferencesFragment extends PreferencesFragment {
|
|||
copyBehaviorPreference.setOnPreferenceClickListener(preference -> {
|
||||
int currentCopyBehavior1 = _prefs.getCopyBehavior().ordinal();
|
||||
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(requireContext())
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(getString(R.string.choose_copy_behavior))
|
||||
.setSingleChoiceItems(R.array.copy_behavior_titles, currentCopyBehavior1, (dialog, which) -> {
|
||||
int i = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
|
|
|
@ -14,7 +14,6 @@ import androidx.activity.result.ActivityResultLauncher;
|
|||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
@ -30,6 +29,7 @@ import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
|
|||
import com.beemdevelopment.aegis.ui.tasks.ImportIconPackTask;
|
||||
import com.beemdevelopment.aegis.ui.views.IconPackAdapter;
|
||||
import com.beemdevelopment.aegis.vault.VaultManager;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
@ -102,9 +102,10 @@ public class IconPacksManagerFragment extends Fragment implements IconPackAdapte
|
|||
|
||||
@Override
|
||||
public void onRemoveIconPack(IconPack pack) {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(requireContext())
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(requireContext(), R.style.ThemeOverlay_Aegis_AlertDialog_Warning)
|
||||
.setTitle(R.string.remove_icon_pack)
|
||||
.setMessage(R.string.remove_icon_pack_description)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(android.R.string.yes, (dialog, whichButton) -> {
|
||||
try {
|
||||
_iconPackManager.removeIconPack(pack);
|
||||
|
@ -124,9 +125,10 @@ public class IconPacksManagerFragment extends Fragment implements IconPackAdapte
|
|||
ImportIconPackTask task = new ImportIconPackTask(requireContext(), result -> {
|
||||
Exception e = result.getException();
|
||||
if (e instanceof IconPackExistsException) {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(requireContext())
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(requireContext(), R.style.ThemeOverlay_Aegis_AlertDialog_Error)
|
||||
.setTitle(R.string.error_occurred)
|
||||
.setMessage(R.string.icon_pack_import_exists_error)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(R.string.yes, (dialog, which) -> {
|
||||
if (removeIconPack(((IconPackExistsException) e).getIconPack())) {
|
||||
importIconPack(uri);
|
||||
|
|
|
@ -45,6 +45,7 @@ import com.beemdevelopment.aegis.vault.VaultRepository;
|
|||
import com.beemdevelopment.aegis.vault.VaultRepositoryException;
|
||||
import com.beemdevelopment.aegis.vault.slots.PasswordSlot;
|
||||
import com.beemdevelopment.aegis.vault.slots.SlotException;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -202,7 +203,7 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
|
|||
groupsSelection.addItems(groupsArray, false);
|
||||
}
|
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(requireContext())
|
||||
AlertDialog dialog = new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.pref_export_summary)
|
||||
.setView(view)
|
||||
.setNeutralButton(R.string.share, null)
|
||||
|
|
|
@ -30,6 +30,7 @@ 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 com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
@ -91,9 +92,10 @@ public class SecurityPreferencesFragment extends PreferencesFragment {
|
|||
if (!_vaultManager.getVault().isEncryptionEnabled()) {
|
||||
Dialogs.showSetPasswordDialog(requireActivity(), new EnableEncryptionListener());
|
||||
} else {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(requireContext())
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(requireContext(), R.style.ThemeOverlay_Aegis_AlertDialog_Warning)
|
||||
.setTitle(R.string.disable_encryption)
|
||||
.setMessage(getText(R.string.disable_encryption_description))
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
|
||||
try {
|
||||
_vaultManager.disableEncryption();
|
||||
|
@ -169,9 +171,10 @@ public class SecurityPreferencesFragment extends PreferencesFragment {
|
|||
task.execute(getLifecycle(), params);
|
||||
} else {
|
||||
_pinKeyboardPreference.setChecked(false);
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(requireContext())
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(requireContext(), R.style.ThemeOverlay_Aegis_AlertDialog_Error)
|
||||
.setTitle(R.string.pin_keyboard_error)
|
||||
.setMessage(R.string.pin_keyboard_error_description)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.create());
|
||||
|
@ -192,7 +195,7 @@ public class SecurityPreferencesFragment extends PreferencesFragment {
|
|||
checkedItems[i] = _prefs.isAutoLockTypeEnabled(items[i]);
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext())
|
||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.pref_auto_lock_prompt)
|
||||
.setMultiChoiceItems(textItems, checkedItems, (dialog, index, isChecked) -> checkedItems[index] = isChecked)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
|
@ -221,7 +224,7 @@ public class SecurityPreferencesFragment extends PreferencesFragment {
|
|||
.map(f -> getString(f.getStringRes()))
|
||||
.toArray(String[]::new);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext())
|
||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.pref_password_reminder_title)
|
||||
.setSingleChoiceItems(textItems, currFreq.ordinal(), (dialog, which) -> {
|
||||
int i = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
|
@ -459,9 +462,10 @@ public class SecurityPreferencesFragment extends PreferencesFragment {
|
|||
if (result != null) {
|
||||
_pinKeyboardPreference.setChecked(true);
|
||||
} else {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(requireContext())
|
||||
Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(requireContext(), R.style.ThemeOverlay_Aegis_AlertDialog_Error)
|
||||
.setTitle(R.string.pin_keyboard_error)
|
||||
.setMessage(R.string.invalid_password)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.create());
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.beemdevelopment.aegis.ui.intro;
|
|||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -16,6 +15,7 @@ import androidx.viewpager2.widget.ViewPager2;
|
|||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.helpers.AnimationsHelper;
|
||||
import com.beemdevelopment.aegis.ui.AegisActivity;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
|
@ -28,8 +28,8 @@ public abstract class IntroBaseActivity extends AegisActivity implements IntroAc
|
|||
private List<Class<? extends SlideFragment>> _slides;
|
||||
private WeakReference<SlideFragment> _currentSlide;
|
||||
|
||||
private ImageButton _btnPrevious;
|
||||
private ImageButton _btnNext;
|
||||
private MaterialButton _btnPrevious;
|
||||
private MaterialButton _btnNext;
|
||||
private SlideIndicator _slideIndicator;
|
||||
|
||||
@Override
|
||||
|
@ -158,7 +158,7 @@ public abstract class IntroBaseActivity extends AegisActivity implements IntroAc
|
|||
? View.VISIBLE
|
||||
: View.INVISIBLE);
|
||||
if (pos == _slides.size() - 1) {
|
||||
_btnNext.setImageResource(R.drawable.circular_button_done);
|
||||
_btnNext.setIconResource(R.drawable.ic_outline_check_24);
|
||||
}
|
||||
_slideIndicator.setSlideCount(_slides.size());
|
||||
_slideIndicator.setCurrentSlide(pos);
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package com.beemdevelopment.aegis.ui.models;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
public class ErrorCardInfo {
|
||||
private final String _message;
|
||||
private final View.OnClickListener _listener;
|
||||
|
||||
public ErrorCardInfo(String message, View.OnClickListener listener) {
|
||||
_message = message;
|
||||
_listener = listener;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return _message;
|
||||
}
|
||||
|
||||
public View.OnClickListener getListener() {
|
||||
return _listener;
|
||||
}
|
||||
}
|
|
@ -1,26 +1,38 @@
|
|||
package com.beemdevelopment.aegis.ui.tasks;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Process;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.lifecycle.LifecycleObserver;
|
||||
import androidx.lifecycle.OnLifecycleEvent;
|
||||
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
public abstract class ProgressDialogTask<Params, Result> extends AsyncTask<Params, String, Result> {
|
||||
private ProgressDialog _dialog;
|
||||
private final AlertDialog _dialog;
|
||||
private final TextView _textProgress;
|
||||
|
||||
public ProgressDialogTask(Context context, String message) {
|
||||
_dialog = new ProgressDialog(context);
|
||||
_dialog.setCancelable(false);
|
||||
_dialog.setMessage(message);
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.dialog_progress, null);
|
||||
_textProgress = view.findViewById(R.id.text_progress);
|
||||
_textProgress.setText(message);
|
||||
|
||||
_dialog = new MaterialAlertDialogBuilder(context)
|
||||
.setView(view)
|
||||
.setCancelable(false)
|
||||
.create();
|
||||
|
||||
Dialogs.secureDialog(_dialog);
|
||||
}
|
||||
|
||||
|
@ -41,7 +53,7 @@ public abstract class ProgressDialogTask<Params, Result> extends AsyncTask<Param
|
|||
@Override
|
||||
protected void onProgressUpdate(String... values) {
|
||||
if (values.length == 1) {
|
||||
_dialog.setMessage(values[0]);
|
||||
_textProgress.setText(values[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,7 +61,7 @@ public abstract class ProgressDialogTask<Params, Result> extends AsyncTask<Param
|
|||
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND + Process.THREAD_PRIORITY_MORE_FAVORABLE);
|
||||
}
|
||||
|
||||
protected final ProgressDialog getDialog() {
|
||||
protected final AlertDialog getDialog() {
|
||||
return _dialog;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ public class AssignIconHolder extends RecyclerView.ViewHolder implements AssignI
|
|||
if (_entry.getNewIcon() != null) {
|
||||
GlideHelper.loadIcon(Glide.with(_view.getContext()), _entry.getNewIcon(), _newIcon);
|
||||
} else {
|
||||
GlideHelper.loadResource(Glide.with(_view.getContext()), R.drawable.ic_icon_unselected, _newIcon);
|
||||
GlideHelper.loadResource(Glide.with(_view.getContext()), R.drawable.ic_unselected, _newIcon);
|
||||
}
|
||||
|
||||
_btnReset.setVisibility(_entry.getNewIcon() != null ? View.VISIBLE : View.INVISIBLE);
|
||||
|
|
|
@ -17,10 +17,10 @@ import android.widget.TextView;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.beemdevelopment.aegis.CopyBehavior;
|
||||
import com.beemdevelopment.aegis.AccountNamePosition;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.CopyBehavior;
|
||||
import com.beemdevelopment.aegis.Preferences;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.SortCategory;
|
||||
import com.beemdevelopment.aegis.ViewMode;
|
||||
import com.beemdevelopment.aegis.helpers.ItemTouchHelperAdapter;
|
||||
|
@ -29,6 +29,7 @@ import com.beemdevelopment.aegis.otp.HotpInfo;
|
|||
import com.beemdevelopment.aegis.otp.OtpInfo;
|
||||
import com.beemdevelopment.aegis.otp.OtpInfoException;
|
||||
import com.beemdevelopment.aegis.otp.TotpInfo;
|
||||
import com.beemdevelopment.aegis.ui.models.ErrorCardInfo;
|
||||
import com.beemdevelopment.aegis.util.CollectionUtils;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
|
||||
|
@ -70,6 +71,7 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
private Handler _dimHandler;
|
||||
private Handler _doubleTapHandler;
|
||||
private boolean _pauseFocused;
|
||||
private ErrorCardInfo _errorCardInfo;
|
||||
|
||||
// keeps track of the EntryHolders that are currently bound
|
||||
private List<EntryHolder> _holders;
|
||||
|
@ -130,8 +132,21 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
_pauseFocused = pauseFocused;
|
||||
}
|
||||
|
||||
public VaultEntry getEntryAt(int position) {
|
||||
return _shownEntries.get(position);
|
||||
public void setErrorCardInfo(ErrorCardInfo info) {
|
||||
ErrorCardInfo oldInfo = _errorCardInfo;
|
||||
_errorCardInfo = info;
|
||||
|
||||
if (oldInfo == null && info != null) {
|
||||
notifyItemInserted(0);
|
||||
} else if (oldInfo != null && info == null) {
|
||||
notifyItemRemoved(0);
|
||||
} else {
|
||||
notifyItemChanged(0);
|
||||
}
|
||||
}
|
||||
|
||||
public VaultEntry getEntryAtPos(int position) {
|
||||
return _shownEntries.get(translateEntryPosToIndex(position));
|
||||
}
|
||||
|
||||
public int addEntry(VaultEntry entry) {
|
||||
|
@ -148,17 +163,17 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
for (int i = getShownFavoritesCount(); i < _shownEntries.size(); i++) {
|
||||
if (comparator.compare(_shownEntries.get(i), entry) > 0) {
|
||||
_shownEntries.add(i, entry);
|
||||
notifyItemInserted(i);
|
||||
position = i;
|
||||
position = translateEntryIndexToPos(i);
|
||||
notifyItemInserted(position);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (position < 0){
|
||||
if (position < 0) {
|
||||
_shownEntries.add(entry);
|
||||
|
||||
position = getItemCount() - 1;
|
||||
position = translateEntryIndexToPos(getShownEntriesCount() - 1);
|
||||
if (position == 0) {
|
||||
notifyDataSetChanged();
|
||||
} else {
|
||||
|
@ -186,9 +201,12 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
_entries.remove(entry);
|
||||
|
||||
if (_shownEntries.contains(entry)) {
|
||||
int position = _shownEntries.indexOf(entry);
|
||||
_shownEntries.remove(position);
|
||||
int index = _shownEntries.indexOf(entry);
|
||||
_shownEntries.remove(index);
|
||||
|
||||
int position = translateEntryIndexToPos(index);
|
||||
notifyItemRemoved(position);
|
||||
|
||||
updateFooter();
|
||||
}
|
||||
|
||||
|
@ -213,26 +231,32 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
_entries.set(_entries.indexOf(oldEntry), newEntry);
|
||||
|
||||
if (_shownEntries.contains(oldEntry)) {
|
||||
int position = _shownEntries.indexOf(oldEntry);
|
||||
int index = _shownEntries.indexOf(oldEntry);
|
||||
int position = translateEntryIndexToPos(index);
|
||||
if (isEntryFiltered(newEntry)) {
|
||||
_shownEntries.remove(position);
|
||||
_shownEntries.remove(index);
|
||||
notifyItemRemoved(position);
|
||||
} else {
|
||||
_shownEntries.set(position, newEntry);
|
||||
_shownEntries.set(index, newEntry);
|
||||
notifyItemChanged(position);
|
||||
}
|
||||
|
||||
sortShownEntries();
|
||||
int newPosition = _shownEntries.indexOf(newEntry);
|
||||
int newIndex = _shownEntries.indexOf(newEntry);
|
||||
int newPosition = translateEntryIndexToPos(newIndex);
|
||||
if (newPosition != NO_POSITION && position != newPosition) {
|
||||
notifyItemMoved(position, newPosition);
|
||||
}
|
||||
} else if (!isEntryFiltered(newEntry)) {
|
||||
// NOTE: This logic is wrong, because sorting is not taken into account. This code
|
||||
// path is currently never hit though, because it is not possible to edit an entry
|
||||
// that is not shown.
|
||||
_shownEntries.add(newEntry);
|
||||
|
||||
int position = getItemCount() - 1;
|
||||
notifyItemInserted(position);
|
||||
}
|
||||
|
||||
checkPeriodUniformity();
|
||||
updateFooter();
|
||||
}
|
||||
|
@ -247,6 +271,36 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the given entry position in the recycler view, to its index in the shown entries list.
|
||||
*/
|
||||
public int translateEntryPosToIndex(int position) {
|
||||
if (position == NO_POSITION) {
|
||||
return NO_POSITION;
|
||||
}
|
||||
|
||||
if (isErrorCardShown()) {
|
||||
position -= 1;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the given entry index in the shown entries list, to its position in the recycler view.
|
||||
*/
|
||||
private int translateEntryIndexToPos(int index) {
|
||||
if (index == NO_POSITION) {
|
||||
return NO_POSITION;
|
||||
}
|
||||
|
||||
if (isErrorCardShown()) {
|
||||
index += 1;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
private boolean isEntryFiltered(VaultEntry entry) {
|
||||
Set<UUID> groups = entry.getGroups();
|
||||
String issuer = entry.getIssuer().toLowerCase();
|
||||
|
@ -366,33 +420,42 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
public void onItemDrop(int position) {
|
||||
// moving entries is not allowed when a filter is applied
|
||||
// footer cant be moved, nor can items be moved below it
|
||||
if (!_groupFilter.isEmpty() || isPositionFooter(position)) {
|
||||
if (!_groupFilter.isEmpty() || isPositionFooter(position) || isPositionErrorCard(position)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_view.onEntryDrop(_shownEntries.get(position));
|
||||
int index = translateEntryPosToIndex(position);
|
||||
_view.onEntryDrop(_shownEntries.get(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemMove(int firstPosition, int secondPosition) {
|
||||
// moving entries is not allowed when a filter is applied
|
||||
// footer cant be moved, nor can items be moved below it
|
||||
if (!_groupFilter.isEmpty() || isPositionFooter(firstPosition) || isPositionFooter(secondPosition)) {
|
||||
if (!_groupFilter.isEmpty()
|
||||
|| isPositionFooter(firstPosition) || isPositionFooter(secondPosition)
|
||||
|| isPositionErrorCard(firstPosition) || isPositionErrorCard(secondPosition)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// notify the vault first
|
||||
_view.onEntryMove(_entries.get(firstPosition), _entries.get(secondPosition));
|
||||
int firstIndex = translateEntryPosToIndex(firstPosition);
|
||||
int secondIndex = translateEntryPosToIndex(secondPosition);
|
||||
_view.onEntryMove(_entries.get(firstIndex), _entries.get(secondIndex));
|
||||
|
||||
// then update our end
|
||||
CollectionUtils.move(_entries, firstPosition, secondPosition);
|
||||
CollectionUtils.move(_shownEntries, firstPosition, secondPosition);
|
||||
CollectionUtils.move(_entries, firstIndex, secondIndex);
|
||||
CollectionUtils.move(_shownEntries, firstIndex, secondIndex);
|
||||
|
||||
notifyItemMoved(firstPosition, secondPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (isPositionErrorCard(position)) {
|
||||
return R.layout.card_error;
|
||||
}
|
||||
|
||||
if (isPositionFooter(position)) {
|
||||
return R.layout.card_footer;
|
||||
}
|
||||
|
@ -406,7 +469,15 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
|
||||
RecyclerView.ViewHolder holder;
|
||||
View view = inflater.inflate(viewType, parent, false);
|
||||
holder = viewType == R.layout.card_footer ? new FooterView(view) : new EntryHolder(view);
|
||||
|
||||
if (viewType == R.layout.card_error) {
|
||||
holder = new ErrorCardHolder(view, _errorCardInfo);
|
||||
} else if (viewType == R.layout.card_footer) {
|
||||
holder = new FooterView(view);
|
||||
} else {
|
||||
holder = new EntryHolder(view);
|
||||
}
|
||||
|
||||
if (_showIcon && holder instanceof EntryHolder) {
|
||||
_view.setPreloadView(((EntryHolder) holder).getIconView());
|
||||
}
|
||||
|
@ -426,7 +497,8 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
|
||||
if (holder instanceof EntryHolder) {
|
||||
EntryHolder entryHolder = (EntryHolder) holder;
|
||||
VaultEntry entry = _shownEntries.get(position);
|
||||
int index = translateEntryPosToIndex(position);
|
||||
VaultEntry entry = _shownEntries.get(index);
|
||||
|
||||
boolean hidden = _tapToReveal && entry != _focusedEntry;
|
||||
boolean paused = _pauseFocused && entry == _focusedEntry;
|
||||
|
@ -508,12 +580,13 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
entryHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
int position = holder.getAdapterPosition();
|
||||
int position = holder.getBindingAdapterPosition();
|
||||
if (_selectedEntries.isEmpty()) {
|
||||
entryHolder.setFocusedAndAnimate(true);
|
||||
}
|
||||
|
||||
boolean returnVal = _view.onLongEntryClick(_shownEntries.get(position));
|
||||
int index = translateEntryPosToIndex(position);
|
||||
boolean returnVal = _view.onLongEntryClick(_shownEntries.get(index));
|
||||
if (_selectedEntries.size() == 0 || isEntryDraggable(entry)) {
|
||||
_view.startDrag(entryHolder);
|
||||
}
|
||||
|
@ -748,15 +821,30 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return getEntriesCount() + 1;
|
||||
// Always at least one item because of the footer
|
||||
// Two in case there's also an error card
|
||||
int baseCount = 1;
|
||||
if (isErrorCardShown()) {
|
||||
baseCount++;
|
||||
}
|
||||
|
||||
return baseCount + getShownEntriesCount();
|
||||
}
|
||||
|
||||
public int getEntriesCount() {
|
||||
public int getShownEntriesCount() {
|
||||
return _shownEntries.size();
|
||||
}
|
||||
|
||||
public boolean isPositionFooter(int position) {
|
||||
return position == getEntriesCount();
|
||||
return position == (getItemCount() - 1);
|
||||
}
|
||||
|
||||
public boolean isPositionErrorCard(int position) {
|
||||
return isErrorCardShown() && position == 0;
|
||||
}
|
||||
|
||||
public boolean isErrorCardShown() {
|
||||
return _errorCardInfo != null;
|
||||
}
|
||||
|
||||
private void updateFooter() {
|
||||
|
@ -772,7 +860,7 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
}
|
||||
|
||||
public void refresh() {
|
||||
int entriesShown = getEntriesCount();
|
||||
int entriesShown = getShownEntriesCount();
|
||||
SpannableString entriesShownSpannable = new SpannableString(_footerView.getResources().getQuantityString(R.plurals.entries_shown, entriesShown, entriesShown));
|
||||
|
||||
String entriesShownString = String.format("%d", entriesShown);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.beemdevelopment.aegis.ui.views;
|
||||
|
||||
import android.graphics.PorterDuff;
|
||||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
|
@ -17,7 +16,6 @@ import com.beemdevelopment.aegis.R;
|
|||
import com.beemdevelopment.aegis.ViewMode;
|
||||
import com.beemdevelopment.aegis.helpers.AnimationsHelper;
|
||||
import com.beemdevelopment.aegis.helpers.SimpleAnimationEndListener;
|
||||
import com.beemdevelopment.aegis.helpers.ThemeHelper;
|
||||
import com.beemdevelopment.aegis.helpers.UiRefresher;
|
||||
import com.beemdevelopment.aegis.otp.HotpInfo;
|
||||
import com.beemdevelopment.aegis.otp.OtpInfo;
|
||||
|
@ -28,6 +26,7 @@ import com.beemdevelopment.aegis.otp.YandexInfo;
|
|||
import com.beemdevelopment.aegis.ui.glide.GlideHelper;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
|
||||
public class EntryHolder extends RecyclerView.ViewHolder {
|
||||
private static final float DEFAULT_ALPHA = 1.0f;
|
||||
|
@ -56,7 +55,7 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
|||
private boolean _paused;
|
||||
|
||||
private TotpProgressBar _progressBar;
|
||||
private View _view;
|
||||
private MaterialCardView _view;
|
||||
|
||||
private UiRefresher _refresher;
|
||||
private Handler _animationHandler;
|
||||
|
@ -67,8 +66,7 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
|||
public EntryHolder(final View view) {
|
||||
super(view);
|
||||
|
||||
_view = view.findViewById(R.id.rlCardEntry);
|
||||
|
||||
_view = (MaterialCardView) view;
|
||||
_profileName = view.findViewById(R.id.profile_account_name);
|
||||
_profileCode = view.findViewById(R.id.profile_code);
|
||||
_profileIssuer = view.findViewById(R.id.profile_issuer);
|
||||
|
@ -84,9 +82,6 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
|||
_animationHandler = new Handler();
|
||||
|
||||
_progressBar = view.findViewById(R.id.progressBar);
|
||||
int primaryColorId = view.getContext().getResources().getColor(R.color.colorPrimary);
|
||||
_progressBar.getProgressDrawable().setColorFilter(primaryColorId, PorterDuff.Mode.SRC_IN);
|
||||
_view.setBackground(_view.getContext().getResources().getDrawable(R.color.card_background));
|
||||
|
||||
_scaleIn = AnimationsHelper.loadScaledAnimation(view.getContext(), R.anim.item_scale_in);
|
||||
_scaleOut = AnimationsHelper.loadScaledAnimation(view.getContext(), R.anim.item_scale_out);
|
||||
|
@ -229,11 +224,8 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
|||
public void setFocused(boolean focused) {
|
||||
if (focused) {
|
||||
_selected.setVisibility(View.VISIBLE);
|
||||
_view.setBackgroundColor(ThemeHelper.getThemeColor(R.attr.cardBackgroundFocused, _view.getContext().getTheme()));
|
||||
} else {
|
||||
_view.setBackgroundColor(ThemeHelper.getThemeColor(R.attr.cardBackground, _view.getContext().getTheme()));
|
||||
}
|
||||
_view.setSelected(focused);
|
||||
_view.setChecked(focused);
|
||||
}
|
||||
|
||||
public void setFocusedAndAnimate(boolean focused) {
|
||||
|
|
|
@ -37,6 +37,7 @@ import com.beemdevelopment.aegis.helpers.UiRefresher;
|
|||
import com.beemdevelopment.aegis.otp.TotpInfo;
|
||||
import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
|
||||
import com.beemdevelopment.aegis.ui.glide.GlideHelper;
|
||||
import com.beemdevelopment.aegis.ui.models.ErrorCardInfo;
|
||||
import com.beemdevelopment.aegis.ui.models.VaultGroupModel;
|
||||
import com.beemdevelopment.aegis.util.UUIDMap;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
|
@ -131,7 +132,9 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
|
||||
@Override
|
||||
public int getSpanSize(int position) {
|
||||
if (_viewMode == ViewMode.TILES && position == _adapter.getEntriesCount()) {
|
||||
if (_viewMode == ViewMode.TILES
|
||||
&& (_adapter.isPositionFooter(position)
|
||||
|| _adapter.isPositionErrorCard(position))) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
@ -376,6 +379,10 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
_adapter.setTapToRevealTime(number);
|
||||
}
|
||||
|
||||
public void setErrorCardInfo(ErrorCardInfo info) {
|
||||
_adapter.setErrorCardInfo(info);
|
||||
}
|
||||
|
||||
public void addEntry(VaultEntry entry) {
|
||||
addEntry(entry, false);
|
||||
}
|
||||
|
@ -472,7 +479,7 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
}
|
||||
|
||||
private void addChipTo(ChipGroup chipGroup, VaultGroupModel group) {
|
||||
Chip chip = (Chip) getLayoutInflater().inflate(R.layout.chip_material, null, false);
|
||||
Chip chip = (Chip) getLayoutInflater().inflate(R.layout.chip_group_filter, null, false);
|
||||
chip.setText(group.getName());
|
||||
chip.setCheckable(true);
|
||||
chip.setChecked(_groupFilter != null && _groupFilter.contains(group.getUUID()));
|
||||
|
@ -600,7 +607,7 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
}
|
||||
|
||||
private void updateEmptyState() {
|
||||
if (_adapter.getEntriesCount() > 0) {
|
||||
if (_adapter.getShownEntriesCount() > 0) {
|
||||
_recyclerView.setVisibility(View.VISIBLE);
|
||||
_emptyStateView.setVisibility(View.GONE);
|
||||
} else {
|
||||
|
@ -636,6 +643,11 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
|
||||
@Override
|
||||
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
|
||||
if (_adapter.isPositionErrorCard(parent.getChildAdapterPosition(view))) {
|
||||
outRect.top = MetricsHelper.convertDpToPixels(requireContext(), 4);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_adapter.isPositionFooter(parent.getChildAdapterPosition(view))) {
|
||||
int pixels = MetricsHelper.convertDpToPixels(requireContext(), 20);
|
||||
outRect.top = pixels;
|
||||
|
@ -662,41 +674,43 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
return;
|
||||
}
|
||||
|
||||
// The footer always has a top and bottom margin
|
||||
if (_adapter.isPositionFooter(adapterPosition)) {
|
||||
// The error card and the footer always have a top and bottom margin
|
||||
if (_adapter.isPositionErrorCard(adapterPosition)
|
||||
|| _adapter.isPositionFooter(adapterPosition)) {
|
||||
outRect.top = _height;
|
||||
outRect.bottom = _height;
|
||||
return;
|
||||
}
|
||||
|
||||
// The first entry should have a top margin, but only if the group chip is not shown
|
||||
if (adapterPosition == 0 && (_groups == null || _groups.isEmpty())) {
|
||||
int entryIndex = _adapter.translateEntryPosToIndex(adapterPosition);
|
||||
// The first entry should have a top margin, but only if the group chip is not shown and the error card is not shown
|
||||
if (entryIndex == 0 && (_groups == null || _groups.isEmpty()) && !_adapter.isErrorCardShown()) {
|
||||
outRect.top = _height;
|
||||
}
|
||||
|
||||
// Only non-favorite entries have a bottom margin, except for the final favorite entry
|
||||
int totalFavorites = _adapter.getShownFavoritesCount();
|
||||
if (totalFavorites == 0
|
||||
|| (adapterPosition < _adapter.getEntriesCount() && !_adapter.getEntryAt(adapterPosition).isFavorite())
|
||||
|| totalFavorites == adapterPosition + 1) {
|
||||
|| (entryIndex < _adapter.getShownEntriesCount() && !_adapter.getEntryAtPos(adapterPosition).isFavorite())
|
||||
|| totalFavorites == entryIndex + 1) {
|
||||
outRect.bottom = _height;
|
||||
}
|
||||
|
||||
if (totalFavorites > 0) {
|
||||
// If this entry is the last favorite entry in the list, it should always have
|
||||
// a bottom margin, regardless of the view mode
|
||||
if (adapterPosition == totalFavorites - 1) {
|
||||
if (entryIndex == totalFavorites - 1) {
|
||||
outRect.bottom = _height;
|
||||
}
|
||||
|
||||
// If this is the first non-favorite entry, it should have a top margin
|
||||
if (adapterPosition == totalFavorites) {
|
||||
if (entryIndex == totalFavorites) {
|
||||
outRect.top = _height;
|
||||
}
|
||||
}
|
||||
|
||||
// The last entry should never have a bottom margin
|
||||
if (_adapter.getEntriesCount() == adapterPosition + 1) {
|
||||
if (_adapter.getShownEntriesCount() == entryIndex + 1) {
|
||||
outRect.bottom = 0;
|
||||
}
|
||||
}
|
||||
|
@ -734,7 +748,11 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
VaultEntry entry = _adapter.getEntryAt(position);
|
||||
if (_adapter.getItemViewType(position) == R.layout.card_error) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
VaultEntry entry = _adapter.getEntryAtPos(position);
|
||||
if (!entry.hasIcon()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package com.beemdevelopment.aegis.ui.views;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.ui.models.ErrorCardInfo;
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
|
||||
public class ErrorCardHolder extends RecyclerView.ViewHolder {
|
||||
public ErrorCardHolder(@NonNull View itemView, ErrorCardInfo info) {
|
||||
super(itemView);
|
||||
|
||||
TextView errorTextView = itemView.findViewById(R.id.text_error_bar);
|
||||
errorTextView.setText(info.getMessage());
|
||||
|
||||
MaterialCardView errorCard = itemView.findViewById(R.id.card_error);
|
||||
errorCard.setOnClickListener(info.getListener());
|
||||
}
|
||||
}
|
|
@ -39,8 +39,8 @@ public class IconHolder extends RecyclerView.ViewHolder {
|
|||
|
||||
public void loadIcon(Context context) {
|
||||
if (_isCustom) {
|
||||
int tint = ThemeHelper.getThemeColor(R.attr.iconColorPrimary, context.getTheme());
|
||||
GlideHelper.loadResource(Glide.with(context), R.drawable.ic_plus_black_24dp, tint, _imageView);
|
||||
int tint = ThemeHelper.getThemeColor(com.google.android.material.R.attr.colorOnSurfaceVariant, context.getTheme());
|
||||
GlideHelper.loadResource(Glide.with(context), R.drawable.ic_outline_add_24, tint, _imageView);
|
||||
} else {
|
||||
GlideHelper.loadIconFile(Glide.with(context), _iconFile, _iconType, _imageView);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue