Material 3

Co-authored-by: Michael Schättgen <michael@schattgen.me>
This commit is contained in:
Alexander Bakker 2022-10-14 16:42:43 +02:00
parent f7bac4331e
commit fcde086ae3
205 changed files with 1935 additions and 1723 deletions

View File

@ -139,7 +139,6 @@ dependencies {
implementation "androidx.camera:camera-camera2:$cameraxVersion"
implementation "androidx.camera:camera-lifecycle:$cameraxVersion"
implementation "androidx.camera:camera-view:$cameraxVersion"
implementation 'androidx.cardview:cardview:1.0.0'
implementation "androidx.core:core:1.12.0"
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.documentfile:documentfile:1.0.1'
@ -162,8 +161,6 @@ dependencies {
implementation 'com.google.android.material:material:1.11.0'
implementation 'com.google.protobuf:protobuf-javalite:3.25.1'
implementation 'com.google.zxing:core:3.5.2'
implementation "com.mikepenz:iconics-core:3.2.5"
implementation 'com.mikepenz:material-design-iconic-typeface:2.2.0.5@aar'
implementation 'com.nulab-inc:zxcvbn:1.8.2'
implementation 'de.hdodenhof:circleimageview:3.1.0'
implementation 'de.psdev.licensesdialog:licensesdialog:2.2.0'

View File

@ -2,15 +2,19 @@ package com.beemdevelopment.aegis;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.espresso.matcher.BoundedMatcher;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.GrantPermissionRule;
import com.beemdevelopment.aegis.crypto.CryptoUtils;
import com.beemdevelopment.aegis.crypto.SCryptParameters;
import com.beemdevelopment.aegis.otp.OtpInfo;
import com.beemdevelopment.aegis.ui.views.EntryHolder;
import com.beemdevelopment.aegis.vault.VaultEntry;
import com.beemdevelopment.aegis.vault.VaultFileCredentials;
import com.beemdevelopment.aegis.vault.VaultManager;
@ -20,6 +24,7 @@ import com.beemdevelopment.aegis.vault.slots.PasswordSlot;
import com.beemdevelopment.aegis.vault.slots.SlotException;
import com.beemdevelopment.aegis.vectors.VaultEntries;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Rule;
@ -178,4 +183,21 @@ public abstract class AegisTest {
}
};
}
@NonNull
protected static Matcher<RecyclerView.ViewHolder> withOtpType(Class<? extends OtpInfo> otpClass) {
return new BoundedMatcher<RecyclerView.ViewHolder, EntryHolder>(EntryHolder.class) {
@Override
public boolean matchesSafely(EntryHolder holder) {
return holder != null
&& holder.getEntry() != null
&& holder.getEntry().getInfo().getClass().equals(otpClass);
}
@Override
public void describeTo(Description description) {
description.appendText(String.format("with otp type '%s'", otpClass.getSimpleName()));
}
};
}
}

View File

@ -1,7 +1,6 @@
package com.beemdevelopment.aegis;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
import static androidx.test.espresso.Espresso.openContextualActionModeOverflowMenu;
import static androidx.test.espresso.action.ViewActions.clearText;
import static androidx.test.espresso.action.ViewActions.click;
@ -18,19 +17,17 @@ import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.containsString;
import androidx.annotation.IdRes;
import androidx.test.core.app.ApplicationProvider;
import androidx.recyclerview.widget.RecyclerView;
import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.contrib.RecyclerViewActions;
import androidx.test.espresso.matcher.RootMatchers;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.beemdevelopment.aegis.encoding.Base32;
import com.beemdevelopment.aegis.encoding.Hex;
@ -41,6 +38,7 @@ import com.beemdevelopment.aegis.otp.TotpInfo;
import com.beemdevelopment.aegis.otp.YandexInfo;
import com.beemdevelopment.aegis.rules.ScreenshotTestRule;
import com.beemdevelopment.aegis.ui.MainActivity;
import com.beemdevelopment.aegis.ui.views.EntryAdapter;
import com.beemdevelopment.aegis.vault.VaultEntry;
import com.beemdevelopment.aegis.vault.VaultRepository;
import com.beemdevelopment.aegis.vault.slots.PasswordSlot;
@ -55,6 +53,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import dagger.hilt.android.testing.HiltAndroidTest;
@ -104,13 +103,19 @@ public class OverallTest extends AegisTest {
}
for (int i = 0; i < 10; i++) {
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(1, clickChildViewWithId(R.id.buttonRefresh)));
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnHolderItem(withOtpType(HotpInfo.class), clickChildViewWithId(R.id.buttonRefresh)));
}
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(0, longClick()));
AtomicBoolean isErrorCardShown = new AtomicBoolean(false);
_activityRule.getScenario().onActivity(activity -> {
isErrorCardShown.set(((EntryAdapter)((RecyclerView) activity.findViewById(R.id.rvKeyProfiles)).getAdapter()).isErrorCardShown());
});
int entryPosOffset = isErrorCardShown.get() ? 1 : 0;
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(entryPosOffset + 0, longClick()));
onView(withId(R.id.action_copy)).perform(click());
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(1, longClick()));
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(entryPosOffset + 1, longClick()));
onView(withId(R.id.action_edit)).perform(click());
onView(withId(R.id.text_name)).perform(clearText(), typeText("Bob"), closeSoftKeyboard());
onView(withId(R.id.dropdown_group)).perform(click());
@ -129,13 +134,13 @@ public class OverallTest extends AegisTest {
changeGroupFilter(_groupName);
changeGroupFilter(null);
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(2, longClick()));
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(3, click()));
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(4, click()));
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(entryPosOffset + 2, longClick()));
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(entryPosOffset + 3, click()));
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(entryPosOffset + 4, click()));
onView(withId(R.id.action_share_qr)).perform(click());
onView(withId(R.id.btnNext)).perform(click()).perform(click()).perform(click());
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(0, longClick()));
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(entryPosOffset + 0, longClick()));
onView(allOf(isDescendantOfA(withClassName(containsString("ActionBarContextView"))), withClassName(containsString("OverflowMenuButton")))).perform(click());
onView(withText(R.string.action_delete)).perform(click());
onView(withId(android.R.id.button1)).perform(click());

View File

@ -27,8 +27,6 @@
android:icon="@mipmap/${iconName}"
android:label="Aegis"
android:supportsRtl="true"
android:theme="@style/Theme.Aegis.Launch"
tools:replace="android:theme"
tools:targetApi="tiramisu">
<activity android:name=".ui.TransferEntriesActivity"
android:label="@string/title_activity_transfer" />

View File

@ -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);

View File

@ -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) {

View File

@ -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
);
}

View File

@ -44,7 +44,7 @@ public enum ViewMode {
return 4;
}
return 20;
return 8;
}
public int getColumnSpan() {

View File

@ -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);
}

View File

@ -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) -> {

View File

@ -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 -> {

View File

@ -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);

View File

@ -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.

View File

@ -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());

View File

@ -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());
}

View File

@ -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());

View File

@ -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) -> {

View File

@ -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

View File

@ -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)));
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);
})

View File

@ -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")

View File

@ -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();

View File

@ -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;
}

View File

@ -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();

View File

@ -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);

View File

@ -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)

View File

@ -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());

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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) {

View File

@ -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();
}

View File

@ -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());
}
}

View File

@ -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);
}

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#D7E3EF" android:state_checked="true" />
<item android:color="@color/background" />
</selector>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/colorPrimary" android:state_checked="true" />
<item android:color="@color/primary_text" />
</selector>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/colorSecondary" android:state_enabled="true"/>
<item android:alpha="0.38" android:color="@color/colorSecondary"/>
<item android:color="?attr/colorSecondary" android:state_enabled="true"/>
<item android:alpha="0.38" android:color="?attr/colorSecondary"/>
</selector>

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_enabled="false"
android:color="#78efefef"
/>
<item
android:state_enabled="true"
android:color="#efefef"
/>
</selector>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/iconColorPrimary" android:state_focused="true"/>
<item android:color="?attr/iconColorPrimary" android:state_activated="true"/>
<item android:color="?attr/iconColorPrimary"/>
</selector>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#fff" android:state_focused="true"/>
<item android:color="#fff" android:state_hovered="true"/>
<item android:color="#fff"/>
</selector>

View File

@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF">
<path
android:fillColor="#FF000000"
android:pathData="M3,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z"/>
</vector>

View File

@ -1,15 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF">
<group android:scaleX="1.1111112"
android:scaleY="1.1111112"
android:translateX="-1.3333334"
android:translateY="-1.3333334">
<path
android:fillColor="@android:color/white"
android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
</group>
</vector>

View File

@ -1,15 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF">
<group android:scaleX="1.1111112"
android:scaleY="1.1111112"
android:translateX="-1.3333334"
android:translateY="-1.3333334">
<path
android:fillColor="@android:color/white"
android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
</group>
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 B

View File

@ -1,26 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="137.93103"
android:viewportHeight="137.93103">
<group android:translateX="-11.034483"
android:translateY="-11.034483">
<path
android:pathData="M55.78,0h48.44C120.28,0 128.31,0 137,2.73A34,34 0,0 1,157.27 23C160,31.69 160,39.72 160,55.78v48.44c0,16.06 0,24.09 -2.73,32.74A34,34 0,0 1,137 157.27C128.31,160 120.28,160 104.22,160H55.78c-16.06,0 -24.09,0 -32.74,-2.73A34,34 0,0 1,2.73 137C0,128.31 0,120.28 0,104.22V55.78C0,39.72 0,31.69 2.73,23A34,34 0,0 1,23 2.73C31.69,0 39.72,0 55.78,0h0">
<aapt:attr name="android:fillColor">
<gradient
android:startY="28.48"
android:startX="51.92"
android:endY="189.51"
android:endX="139.67"
android:type="linear">
<item android:offset="0" android:color="#FFFFFFFF"/>
<item android:offset="0.37" android:color="#FFF5FAFC"/>
<item android:offset="1" android:color="#FFDBEBF5"/>
<item android:offset="1" android:color="#FFDBEBF5"/>
</gradient>
</aapt:attr>
</path>
</group>
</vector>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/white"/>
<corners android:topLeftRadius="16dp"
android:topRightRadius="16dp"/>
</shape>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 B

View File

@ -1,5 +1,11 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#000000" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M10,17l5,-5 -5,-5v10z"/>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M10,17l5,-5 -5,-5v10z" />
</vector>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#59000000" />
<size
android:width="50dp"
android:height="50dp" />
</shape>

View File

@ -1,9 +0,0 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/circular_button_background" />
<item
android:drawable="@drawable/ic_check_black_24dp"
android:bottom="16dp"
android:top="16dp"
android:left="16dp"
android:right="16dp" />
</layer-list>

View File

@ -1,9 +0,0 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/circular_button_background" />
<item
android:drawable="@drawable/ic_arrow_right_black_24dp"
android:bottom="16dp"
android:top="16dp"
android:left="16dp"
android:right="16dp" />
</layer-list>

View File

@ -1,9 +0,0 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/circular_button_background" />
<item
android:drawable="@drawable/ic_arrow_left_black_24dp"
android:bottom="16dp"
android:top="16dp"
android:left="16dp"
android:right="16dp" />
</layer-list>

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/divider2"/>
<size android:height=".1dp"/>
<padding android:left="1dp"
android:top="1dp"
android:right="1dp"
android:bottom="1dp" />
<corners android:radius="7dp" />
</shape>

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#B2000000"/>
<padding
android:left="16dp"
android:top="4dp"
android:right="16dp"
android:bottom="4dp"/>
<corners
android:radius="2dp"/>
</shape>

View File

@ -2,9 +2,8 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="@color/auth_button"/>
<stroke android:color="@color/auth_button" android:width="1dp" />
<!--corners allow us to make the rounded corners button-->
<solid />
<stroke android:width="1dp" />
<corners android:radius="4dp" />
</shape>
</item>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M19,7v2.99s-1.99,0.01 -2,0L17,7h-3s0.01,-1.99 0,-2h3L17,2h2v3h3v2h-3zM16,11L16,8h-3L13,5L5,5c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-8h-3zM5,19l3,-4 2,3 3,-4 4,5L5,19z"/>
</vector>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z" />
</vector>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M16.61 15.15C16.15 15.15 15.77 14.78 15.77 14.32S16.15 13.5 16.61 13.5H16.61C17.07 13.5 17.45 13.86 17.45 14.32C17.45 14.78 17.07 15.15 16.61 15.15M7.41 15.15C6.95 15.15 6.57 14.78 6.57 14.32C6.57 13.86 6.95 13.5 7.41 13.5H7.41C7.87 13.5 8.24 13.86 8.24 14.32C8.24 14.78 7.87 15.15 7.41 15.15M16.91 10.14L18.58 7.26C18.67 7.09 18.61 6.88 18.45 6.79C18.28 6.69 18.07 6.75 18 6.92L16.29 9.83C14.95 9.22 13.5 8.9 12 8.91C10.47 8.91 9 9.24 7.73 9.82L6.04 6.91C5.95 6.74 5.74 6.68 5.57 6.78C5.4 6.87 5.35 7.08 5.44 7.25L7.1 10.13C4.25 11.69 2.29 14.58 2 18H22C21.72 14.59 19.77 11.7 16.91 10.14H16.91Z" />
</vector>

View File

@ -1,9 +0,0 @@
<!-- drawable/ic_arrow_left_black_24dp.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:autoMirrored="true">
<path android:fillColor="#000" android:pathData="M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z" />
</vector>

View File

@ -1,9 +0,0 @@
<!-- drawable/ic_arrow_right_black_24dp.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:autoMirrored="true">
<path android:fillColor="#000" android:pathData="M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z" />
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M5.5,10.5h2v1h-2zM20,4L4,4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,6c0,-1.11 -0.89,-2 -2,-2zM9,11.5c0,0.85 -0.65,1.5 -1.5,1.5h-2v2L4,15L4,9h3.5c0.85,0 1.5,0.65 1.5,1.5v1zM12.5,15L11,15L11,9h1.5v6zM20,15h-1.2l-2.55,-3.5L16.25,15L15,15L15,9h1.25l2.5,3.5L18.75,9L20,9v6z"/>
</vector>

View File

@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFF"
android:pathData="M10,18h4v-2h-4v2zM3,6v2h18L21,6L3,6zM6,13h12v-2L6,11v2z"/>
</vector>

View File

@ -1,5 +0,0 @@
<vector android:height="32dp" android:tint="?attr/colorControlNormal"
android:viewportHeight="24" android:viewportWidth="24"
android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M3,18h18v-2L3,16v2zM3,13h18v-2L3,11v2zM3,6v2h18L21,6L3,6z"/>
</vector>

View File

@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M3,18h12v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h18v-2L3,11v2z"/>
</vector>

View File

@ -1,8 +0,0 @@
<!-- drawable/brush.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M20.71,4.63L19.37,3.29C19,2.9 18.35,2.9 17.96,3.29L9,12.25L11.75,15L20.71,6.04C21.1,5.65 21.1,5 20.71,4.63M7,14A3,3 0 0,0 4,17C4,18.31 2.84,19 2,19C2.92,20.22 4.5,21 6,21A4,4 0 0,0 10,17A3,3 0 0,0 7,14Z" />
</vector>

View File

@ -1,7 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M7,2H17V12.5C17,10.83 13.67,10 12,10C10.33,10 7,10.83 7,12.5M17,0H7A2,2 0 0,0 5,2V16A2,2 0 0,0 7,18H17A2,2 0 0,0 19,16V2A2,2 0 0,0 17,0M12,8A2,2 0 0,0 14,6A2,2 0 0,0 12,4A2,2 0 0,0 10,6A2,2 0 0,0 12,8M14,20V22H19V20M10,20H5V22H10V24L13,21L10,18V20Z" />
</vector>

View File

@ -1,7 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M12,6C10.89,6 10,5.1 10,4A2,2 0 0,1 12,2C13.09,2 14,2.9 14,4A2,2 0 0,1 12,6M17,0H7A2,2 0 0,0 5,2V16A2,2 0 0,0 7,18H17A2,2 0 0,0 19,16V2A2,2 0 0,0 17,0M14,20V22H19V20M10,20H5V22H10V24L13,21L10,18V20Z" />
</vector>

View File

@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#F7F7F7"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>

View File

@ -1,7 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z" />
</vector>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
<path
android:pathData="M0 0h24v24H0z" />
</vector>

View File

@ -1,8 +0,0 @@
<!-- drawable/cloud_upload_outline.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4C9.11,4 6.6,5.64 5.35,8.04C2.34,8.36 0,10.91 0,14A6,6 0 0,0 6,20H19A5,5 0 0,0 24,15C24,12.36 21.95,10.22 19.35,10.04M19,18H6A4,4 0 0,1 2,14C2,11.95 3.53,10.24 5.56,10.03L6.63,9.92L7.13,8.97C8.08,7.14 9.94,6 12,6C14.62,6 16.88,7.86 17.39,10.43L17.69,11.93L19.22,12.04C20.78,12.14 22,13.45 22,15A3,3 0 0,1 19,18M8,13H10.55V16H13.45V13H16L12,9L8,13Z" />
</vector>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#FFFFFF" android:pathData="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z" />
</vector>

View File

@ -2,6 +2,7 @@
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path android:fillColor="#000" android:pathData="M4,4H20A2,2 0 0,1 22,6V18A2,2 0 0,1 20,20H4A2,2 0 0,1 2,18V6A2,2 0 0,1 4,4M4,6V18H11V6H4M20,18V6H18.76C19,6.54 18.95,7.07 18.95,7.13C18.88,7.8 18.41,8.5 18.24,8.75L15.91,11.3L19.23,11.28L19.24,12.5L14.04,12.47L14,11.47C14,11.47 17.05,8.24 17.2,7.95C17.34,7.67 17.91,6 16.5,6C15.27,6.05 15.41,7.3 15.41,7.3L13.87,7.31C13.87,7.31 13.88,6.65 14.25,6H13V18H15.58L15.57,17.14L16.54,17.13C16.54,17.13 17.45,16.97 17.46,16.08C17.5,15.08 16.65,15.08 16.5,15.08C16.37,15.08 15.43,15.13 15.43,15.95H13.91C13.91,15.95 13.95,13.89 16.5,13.89C19.1,13.89 18.96,15.91 18.96,15.91C18.96,15.91 19,17.16 17.85,17.63L18.37,18H20M8.92,16H7.42V10.2L5.62,10.76V9.53L8.76,8.41H8.92V16Z" />
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
</vector>

View File

@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
</vector>

View File

@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#F7F7F7"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M18,7l-1.41,-1.41 -6.34,6.34 1.41,1.41L18,7zM22.24,5.59L11.66,16.17 7.48,12l-1.41,1.41L11.66,19l12,-12 -1.42,-1.41zM0.41,13.41L6,19l1.41,-1.41L1.83,12 0.41,13.41z"/>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
</vector>

View File

@ -1,8 +0,0 @@
<!-- drawable/dots_vertical.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z" />
</vector>

View File

@ -0,0 +1,8 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path android:fillColor="@android:color/white" android:pathData="M233,840L298,559L80,370L368,345L480,80L592,345L880,370L662,559L727,840L480,691L233,840Z"/>
</vector>

View File

@ -1,7 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M11.83,1.73C8.43,1.79 6.23,3.32 6.23,3.32C5.95,3.5 5.88,3.91 6.07,4.19C6.27,4.5 6.66,4.55 6.96,4.34C6.96,4.34 11.27,1.15 17.46,4.38C17.75,4.55 18.14,4.45 18.31,4.15C18.5,3.85 18.37,3.47 18.03,3.28C16.36,2.4 14.78,1.96 13.36,1.8C12.83,1.74 12.32,1.72 11.83,1.73M12.22,4.34C6.26,4.26 3.41,9.05 3.41,9.05C3.22,9.34 3.3,9.72 3.58,9.91C3.87,10.1 4.26,10 4.5,9.68C4.5,9.68 6.92,5.5 12.2,5.59C17.5,5.66 19.82,9.65 19.82,9.65C20,9.94 20.38,10.04 20.68,9.87C21,9.69 21.07,9.31 20.9,9C20.9,9 18.15,4.42 12.22,4.34M11.5,6.82C9.82,6.94 8.21,7.55 7,8.56C4.62,10.53 3.1,14.14 4.77,19C4.88,19.33 5.24,19.5 5.57,19.39C5.89,19.28 6.07,18.92 5.95,18.6V18.6C4.41,14.13 5.78,11.2 7.8,9.5C9.77,7.89 13.25,7.5 15.84,9.1C17.11,9.9 18.1,11.28 18.6,12.64C19.11,14 19.08,15.32 18.67,15.94C18.25,16.59 17.4,16.83 16.65,16.64C15.9,16.45 15.29,15.91 15.26,14.77C15.23,13.06 13.89,12 12.5,11.84C11.16,11.68 9.61,12.4 9.21,14C8.45,16.92 10.36,21.07 14.78,22.45C15.11,22.55 15.46,22.37 15.57,22.04C15.67,21.71 15.5,21.35 15.15,21.25C11.32,20.06 9.87,16.43 10.42,14.29C10.66,13.33 11.5,13 12.38,13.08C13.25,13.18 14,13.7 14,14.79C14.05,16.43 15.12,17.54 16.34,17.85C17.56,18.16 18.97,17.77 19.72,16.62C20.5,15.45 20.37,13.8 19.78,12.21C19.18,10.61 18.07,9.03 16.5,8.04C14.96,7.08 13.19,6.7 11.5,6.82M11.86,9.25V9.26C10.08,9.32 8.3,10.24 7.28,12.18C5.96,14.67 6.56,17.21 7.44,19.04C8.33,20.88 9.54,22.1 9.54,22.1C9.78,22.35 10.17,22.35 10.42,22.11C10.67,21.87 10.67,21.5 10.43,21.23C10.43,21.23 9.36,20.13 8.57,18.5C7.78,16.87 7.3,14.81 8.38,12.77C9.5,10.67 11.5,10.16 13.26,10.67C15.04,11.19 16.53,12.74 16.5,15.03C16.46,15.38 16.71,15.68 17.06,15.7C17.4,15.73 17.7,15.47 17.73,15.06C17.79,12.2 15.87,10.13 13.61,9.47C13.04,9.31 12.45,9.23 11.86,9.25M12.08,14.25C11.73,14.26 11.46,14.55 11.47,14.89C11.47,14.89 11.5,16.37 12.31,17.8C13.15,19.23 14.93,20.59 18.03,20.3C18.37,20.28 18.64,20 18.62,19.64C18.6,19.29 18.3,19.03 17.91,19.06C15.19,19.31 14.04,18.28 13.39,17.17C12.74,16.07 12.72,14.88 12.72,14.88C12.72,14.53 12.44,14.25 12.08,14.25Z" />
</vector>

View File

@ -1,8 +0,0 @@
<!-- drawable/gesture_tap.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M10,9A1,1 0 0,1 11,8A1,1 0 0,1 12,9V13.47L13.21,13.6L18.15,15.79C18.68,16.03 19,16.56 19,17.14V21.5C18.97,22.32 18.32,22.97 17.5,23H11C10.62,23 10.26,22.85 10,22.57L5.1,18.37L5.84,17.6C6.03,17.39 6.3,17.28 6.58,17.28H6.8L10,19V9M11,5A4,4 0 0,1 15,9C15,10.5 14.2,11.77 13,12.46V11.24C13.61,10.69 14,9.89 14,9A3,3 0 0,0 11,6A3,3 0 0,0 8,9C8,9.89 8.39,10.69 9,11.24V12.46C7.8,11.77 7,10.5 7,9A4,4 0 0,1 11,5Z" />
</vector>

File diff suppressed because one or more lines are too long

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M0 0h24v24H0z" />
<path
android:fillColor="#000000"
android:pathData="M11 17h2v-6h-2v6zm1-15C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zM11 9h2V7h-2v2z" />
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M7.41,7.84L12,12.42l4.59,-4.58L18,9.25l-6,6 -6,-6z"/>
</vector>

View File

@ -1,34 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M11.99,18.54l-7.37,-5.73L3,14.07l9,7 9,-7 -1.63,-1.27 -7.38,5.74zM12,16l7.36,-5.73L21,9l-9,-7 -9,7 1.63,1.27L12,16z"/>
</vector>

View File

@ -1,5 +0,0 @@
<!-- licensed under CC BY-SA 3.0 by WPZOOM (http://www.wpzoom.com/) -->
<vector android:height="24dp" android:viewportHeight="500.0"
android:viewportWidth="500.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M131.9,150.1v63.6h-27.3c-20.1,0 -36.3,16.3 -36.3,36.3v181.7c0,20.1 16.3,36.3 36.3,36.3h290.7c20.1,0 36.3,-16.3 36.3,-36.3V250c0,-20.1 -16.3,-36.3 -36.3,-36.3h-27.3v-63.6c0,-65.2 -52.9,-118.1 -118.1,-118.1S131.9,84.8 131.9,150.1zM177.3,213.7v-63.6c0,-40.2 32.5,-72.7 72.7,-72.7c40.2,0 72.7,32.5 72.7,72.7v63.6H177.3zM213.7,313.6c0,-20.1 16.3,-36.3 36.3,-36.3s36.3,16.3 36.3,36.3c0,12.8 -6.6,24.1 -16.6,30.5c0,0 3.5,21.4 7.5,46.7c0,7.5 -6.1,13.6 -13.6,13.6h-27.3c-7.5,0 -13.6,-6.1 -13.6,-13.6l7.5,-46.7C220.3,337.7 213.7,326.4 213.7,313.6z"/>
</vector>

View File

@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M440,520L200,520L200,440L440,440L440,200L520,200L520,440L760,440L760,520L520,520L520,760L440,760L440,520Z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L560,120L560,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760L760,760Q760,760 760,760Q760,760 760,760L760,400L840,400L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM680,360L680,280L600,280L600,200L680,200L680,120L760,120L760,200L840,200L840,280L760,280L760,360L680,360ZM240,680L720,680L570,480L450,640L360,520L240,680ZM200,200L200,320L200,320L200,400L200,400L200,760Q200,760 200,760Q200,760 200,760L200,760Q200,760 200,760Q200,760 200,760L200,200Q200,200 200,200Q200,200 200,200L200,200Z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M40,720Q49,613 105.5,523Q162,433 256,380L182,252Q176,243 179,233Q182,223 192,218Q200,213 210,216Q220,219 226,228L300,356Q386,320 480,320Q574,320 660,356L734,228Q740,219 750,216Q760,213 768,218Q778,223 781,233Q784,243 778,252L704,380Q798,433 854.5,523Q911,613 920,720L40,720ZM280,610Q301,610 315.5,595.5Q330,581 330,560Q330,539 315.5,524.5Q301,510 280,510Q259,510 244.5,524.5Q230,539 230,560Q230,581 244.5,595.5Q259,610 280,610ZM680,610Q701,610 715.5,595.5Q730,581 730,560Q730,539 715.5,524.5Q701,510 680,510Q659,510 644.5,524.5Q630,539 630,560Q630,581 644.5,595.5Q659,610 680,610Z" />
</vector>

View File

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M400,720L160,480L400,240L456,298L314,440L800,440L800,520L314,520L456,662L400,720Z" />
</vector>

Some files were not shown because too many files have changed in this diff Show More