diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 724e935c52..215c62b0df 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -82,6 +82,8 @@ + _entries = new ArrayList<>(); + private RecyclerView _entriesView; + private AssignIconsActivity.BackPressHandler _backPressHandler; + private ViewPreloadSizeProvider _preloadSizeProvider; + private IconPack _favoriteIconPack; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (abortIfOrphan(savedInstanceState)) { + return; + } + + setContentView(R.layout.activity_assign_icons); + setSupportActionBar(findViewById(R.id.toolbar)); + if (getSupportActionBar() != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setDisplayShowHomeEnabled(true); + } + + ArrayList assignIconEntriesIds = (ArrayList) getIntent().getSerializableExtra("entries"); + for (UUID entryId: assignIconEntriesIds) { + VaultEntry vaultEntry = _vaultManager.getVault().getEntryByUUID(entryId); + _entries.add(new AssignIconEntry(vaultEntry)); + } + + _backPressHandler = new AssignIconsActivity.BackPressHandler(); + getOnBackPressedDispatcher().addCallback(this, _backPressHandler); + + IconPreloadProvider modelProvider1 = new IconPreloadProvider(); + EntryIconPreloadProvider modelProvider2 = new EntryIconPreloadProvider(); + _preloadSizeProvider = new ViewPreloadSizeProvider<>(); + RecyclerViewPreloader preloader1 = new RecyclerViewPreloader(this, modelProvider1, _preloadSizeProvider, 10); + RecyclerViewPreloader preloader2 = new RecyclerViewPreloader(this, modelProvider2, _preloadSizeProvider, 10); + + _adapter = new AssignIconAdapter(this); + _entriesView = findViewById(R.id.list_assign_icons); + LinearLayoutManager layoutManager = new LinearLayoutManager(this); + _entriesView.setLayoutManager(layoutManager); + _entriesView.setAdapter(_adapter); + _entriesView.setNestedScrollingEnabled(false); + _entriesView.addItemDecoration(new SpacesItemDecoration(8)); + _entriesView.addOnScrollListener(preloader1); + _entriesView.addOnScrollListener(preloader2); + + Optional favoriteIconPack = _iconPackManager.getIconPacks().stream() + .sorted(Comparator.comparing(IconPack::getName)) + .findFirst(); + + if (!favoriteIconPack.isPresent()) { + throw new RuntimeException(String.format("Started %s without any icon packs present", AssignIconsActivity.class.getName())); + } + + _favoriteIconPack = favoriteIconPack.get(); + + for (AssignIconEntry entry : _entries) { + IconPack.Icon suggestedIcon = findSuggestedIcon(entry); + if (suggestedIcon != null) { + entry.setNewIcon(suggestedIcon); + } + } + + _adapter.addEntries(_entries); + } + + private IconPack.Icon findSuggestedIcon(AssignIconEntry entry) { + List suggestedIcons = _favoriteIconPack.getSuggestedIcons(entry.getEntry().getIssuer()); + if (suggestedIcons.size() > 0) { + return suggestedIcons.get(0); + } + + return null; + } + + private void saveAndFinish() throws IOException { + ArrayList uuids = new ArrayList<>(); + for (AssignIconEntry selectedEntry : _entries) { + VaultEntry entry = selectedEntry.getEntry(); + if(selectedEntry.getNewIcon() != null) { + byte[] iconBytes; + try (FileInputStream inStream = new FileInputStream(selectedEntry.getNewIcon().getFile())){ + iconBytes = IOUtils.readFile(inStream); + } + + entry.setIcon(iconBytes, selectedEntry.getNewIcon().getIconType()); + uuids.add(entry.getUUID()); + + _vaultManager.getVault().replaceEntry(entry); + } + } + + Intent intent = new Intent(); + intent.putExtra("entryUUIDs", uuids); + + if (saveAndBackupVault()) { + setResult(RESULT_OK, intent); + finish(); + } + } + + private void discardAndFinish() { + Dialogs.showDiscardDialog(this, + (dialog, which) -> { + try { + saveAndFinish(); + } catch (IOException e) { + Toast.makeText(this, R.string.saving_assign_icons_error, Toast.LENGTH_SHORT).show(); + } + }, + (dialog, which) -> finish()); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_groups, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + discardAndFinish(); + break; + case R.id.action_save: + try { + saveAndFinish(); + } catch (IOException e) { + Toast.makeText(this, R.string.saving_assign_icons_error, Toast.LENGTH_SHORT).show(); + } + break; + default: + return super.onOptionsItemSelected(item); + } + + return true; + } + + @Override + public void onAssignIconEntryClick(AssignIconEntry entry) { + BottomSheetDialog dialog = IconPickerDialog.create(this, Collections.singletonList(_favoriteIconPack), entry.getEntry().getIssuer(), false, new IconAdapter.Listener() { + @Override + public void onIconSelected(IconPack.Icon icon) { + entry.setNewIcon(icon); + } + + @Override + public void onCustomSelected() { } + }); + Dialogs.showSecureDialog(dialog); + } + + @Override + public void onSetPreloadView(View view) { + _preloadSizeProvider.setView(view); + } + + private class BackPressHandler extends OnBackPressedCallback { + public BackPressHandler() { + super(false); + } + + @Override + public void handleOnBackPressed() { + discardAndFinish(); + } + } + + private class EntryIconPreloadProvider implements ListPreloader.PreloadModelProvider { + @NonNull + @Override + public List getPreloadItems(int position) { + VaultEntry entry = _entries.get(position).getEntry(); + if (entry.hasIcon()) { + return Collections.singletonList(entry); + } + return Collections.emptyList(); + } + + @Nullable + @Override + public RequestBuilder getPreloadRequestBuilder(@NonNull VaultEntry entry) { + return Glide.with(AssignIconsActivity.this) + .asDrawable() + .load(entry) + .set(IconLoader.ICON_TYPE, entry.getIconType()) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .skipMemoryCache(false); + } + } + + private class IconPreloadProvider implements ListPreloader.PreloadModelProvider { + @NonNull + @Override + public List getPreloadItems(int position) { + AssignIconEntry entry = _entries.get(position); + if (entry.getNewIcon() != null) { + return Collections.singletonList(entry.getNewIcon()); + } + return Collections.emptyList(); + } + + @Nullable + @Override + public RequestBuilder getPreloadRequestBuilder(@NonNull IconPack.Icon icon) { + return Glide.with(AssignIconsActivity.this) + .asDrawable() + .load(icon.getFile()) + .set(IconLoader.ICON_TYPE, icon.getIconType()) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .skipMemoryCache(false); + } + } + + private class SpacesItemDecoration extends RecyclerView.ItemDecoration { + private final int _space; + + public SpacesItemDecoration(int dpSpace) { + + this._space = MetricsHelper.convertDpToPixels(AssignIconsActivity.this, dpSpace); + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + outRect.left = _space; + outRect.right = _space; + outRect.bottom = _space; + + if (parent.getChildLayoutPosition(view) == 0) { + outRect.top = _space; + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/beemdevelopment/aegis/icons/IconPackManager.java b/app/src/main/java/com/beemdevelopment/aegis/icons/IconPackManager.java index 05b2cf29b0..7aa9a2925c 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/icons/IconPackManager.java +++ b/app/src/main/java/com/beemdevelopment/aegis/icons/IconPackManager.java @@ -42,6 +42,10 @@ private IconPack getIconPackByUUID(UUID uuid) { return packs.get(0); } + public boolean hasIconPack() { + return _iconPacks.size() > 0; + } + public List getIconPacks() { return new ArrayList<>(_iconPacks); } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/EditEntryActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/EditEntryActivity.java index 95de25e222..1c6a9b3f52 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/EditEntryActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/EditEntryActivity.java @@ -216,7 +216,7 @@ protected void onCreate(Bundle savedInstanceState) { IconViewHelper.setLayerType(_iconView, _origEntry.getIconType()); Glide.with(this) .asDrawable() - .load(_origEntry) + .load(_origEntry.getIcon()) .set(IconLoader.ICON_TYPE, _origEntry.getIconType()) .diskCacheStrategy(DiskCacheStrategy.NONE) .skipMemoryCache(false) @@ -500,7 +500,7 @@ private void startIconSelection() { return; } - BottomSheetDialog dialog = IconPickerDialog.create(this, iconPacks, _textIssuer.getText().toString(), new IconAdapter.Listener() { + BottomSheetDialog dialog = IconPickerDialog.create(this, iconPacks, _textIssuer.getText().toString(), true, new IconAdapter.Listener() { @Override public void onIconSelected(IconPack.Icon icon) { selectIcon(icon); diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/ImportEntriesActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/ImportEntriesActivity.java index 8e1261899d..570d6ce7ce 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/ImportEntriesActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/ImportEntriesActivity.java @@ -1,5 +1,6 @@ package com.beemdevelopment.aegis.ui; +import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.view.Menu; @@ -14,12 +15,14 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.beemdevelopment.aegis.AssignIconsActivity; import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.helpers.FabScrollHelper; import com.beemdevelopment.aegis.importers.DatabaseImporter; import com.beemdevelopment.aegis.importers.DatabaseImporterEntryException; import com.beemdevelopment.aegis.importers.DatabaseImporterException; import com.beemdevelopment.aegis.ui.dialogs.Dialogs; +import com.beemdevelopment.aegis.ui.models.AssignIconEntry; import com.beemdevelopment.aegis.ui.models.ImportEntry; import com.beemdevelopment.aegis.ui.tasks.RootShellTask; import com.beemdevelopment.aegis.ui.views.ImportEntriesAdapter; @@ -244,8 +247,30 @@ private void saveAndFinish(boolean wipeEntries) { String toastMessage = getResources().getQuantityString(R.plurals.imported_entries_count, selectedEntries.size(), selectedEntries.size()); Toast.makeText(this, toastMessage, Toast.LENGTH_SHORT).show(); + setResult(RESULT_OK, null); - finish(); + + if (_iconPackManager.hasIconPack()) { + ArrayList assignIconEntriesIds = new ArrayList<>(); + Intent assignIconIntent = new Intent(getBaseContext(), AssignIconsActivity.class); + for (ImportEntry entry : selectedEntries) { + assignIconEntriesIds.add(entry.getEntry().getUUID()); + } + + assignIconIntent.putExtra("entries", assignIconEntriesIds); + + Dialogs.showSecureDialog(new AlertDialog.Builder(this) + .setTitle(R.string.import_assign_icons_dialog_title) + .setMessage(R.string.import_assign_icons_dialog_text) + .setPositiveButton(android.R.string.yes, (dialog, which) -> { + startActivity(assignIconIntent); + finish(); + }) + .setNegativeButton(android.R.string.no, ((dialogInterface, i) -> finish())) + .create()); + } else { + finish(); + } } } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java index a43b164312..58face0c29 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java @@ -35,6 +35,7 @@ import androidx.appcompat.view.ActionMode; import androidx.appcompat.widget.SearchView; +import com.beemdevelopment.aegis.AssignIconsActivity; import com.beemdevelopment.aegis.CopyBehavior; import com.beemdevelopment.aegis.AccountNamePosition; import com.beemdevelopment.aegis.Preferences; @@ -76,6 +77,8 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene private static final int CODE_DECRYPT = 4; private static final int CODE_PREFERENCES = 5; private static final int CODE_SCAN_IMAGE = 6; + private static final int CODE_ASSIGN_ICONS = 7; + // Permission request codes private static final int CODE_PERM_CAMERA = 0; @@ -232,6 +235,10 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { break; case CODE_SCAN_IMAGE: onScanImageResult(data); + break; + case CODE_ASSIGN_ICONS: + onAssignEntriesResult(data); + break; } super.onActivityResult(requestCode, resultCode, data); @@ -317,6 +324,17 @@ private void startEditEntryActivity(int requestCode, VaultEntry entry) { startActivityForResult(intent, requestCode); } + private void startAssignIconsActivity(int requestCode, List entries) { + ArrayList assignIconEntriesIds = new ArrayList<>(); + Intent assignIconIntent = new Intent(getBaseContext(), AssignIconsActivity.class); + for (VaultEntry entry : entries) { + assignIconEntriesIds.add(entry.getUUID()); + } + + assignIconIntent.putExtra("entries", assignIconEntriesIds); + startActivityForResult(assignIconIntent, requestCode); + } + private void startIntroActivity() { if (!_isDoingIntro) { Intent intro = new Intent(this, IntroActivity.class); @@ -353,6 +371,17 @@ private void onEditEntryResult(Intent data) { } } + private void onAssignEntriesResult(Intent data) { + if (_loaded) { + ArrayList entryUUIDs = (ArrayList) data.getSerializableExtra("entryUUIDs"); + + for (UUID entryUUID: entryUUIDs) { + VaultEntry entry = _vaultManager.getVault().getEntryByUUID(entryUUID); + _entryListView.replaceEntry(entryUUID, entry); + } + } + } + private void onScanImageResult(Intent intent) { if (intent.getData() != null) { startDecodeQrCodeImages(Collections.singletonList(intent.getData())); @@ -916,6 +945,11 @@ private void setIsMultipleSelected(boolean multipleSelected) { _actionMode.getMenu().findItem(R.id.action_copy).setVisible(!multipleSelected); } + private void setAssignIconsMenuItemVisibility() { + MenuItem assignIconsMenuItem = _actionMode.getMenu().findItem(R.id.action_assign_icons); + assignIconsMenuItem.setVisible(_iconPackManager.hasIconPack()); + } + private void setFavoriteMenuItemVisiblity() { MenuItem toggleFavoriteMenuItem = _actionMode.getMenu().findItem(R.id.action_toggle_favorite); @@ -948,6 +982,7 @@ private void startActionMode() { _actionMode = startSupportActionMode(_actionModeCallbacks); _actionModeBackPressHandler.setEnabled(true); setFavoriteMenuItemVisiblity(); + setAssignIconsMenuItemVisibility(); } @Override @@ -1142,6 +1177,12 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) { mode.finish(); }); return true; + + case R.id.action_assign_icons: + startAssignIconsActivity(CODE_ASSIGN_ICONS, _selectedEntries); + mode.finish(); + return true; + default: return false; } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/dialogs/IconPickerDialog.java b/app/src/main/java/com/beemdevelopment/aegis/ui/dialogs/IconPickerDialog.java index fbcd148d42..ae9ecaa28b 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/dialogs/IconPickerDialog.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/dialogs/IconPickerDialog.java @@ -40,7 +40,7 @@ private IconPickerDialog() { } - public static BottomSheetDialog create(Activity activity, List iconPacks, String issuer, IconAdapter.Listener listener) { + public static BottomSheetDialog create(Activity activity, List iconPacks, String issuer, boolean showAddCustom, IconAdapter.Listener listener) { View view = LayoutInflater.from(activity).inflate(R.layout.dialog_icon_picker, null); TextView textIconPack = view.findViewById(R.id.text_icon_pack); @@ -128,7 +128,7 @@ public int getSpanSize(int position) { recyclerView.setLayoutManager(layoutManager); recyclerView.setAdapter(adapter); recyclerView.addOnScrollListener(preloader); - adapter.loadIcons(iconPacks.get(0)); + adapter.loadIcons(iconPacks.get(0), showAddCustom); textIconPack.setText(iconPacks.get(0).getName()); view.findViewById(R.id.btn_icon_pack).setOnClickListener(v -> { @@ -139,7 +139,7 @@ public int getSpanSize(int position) { PopupMenu popupMenu = new PopupMenu(activity, v); popupMenu.setOnMenuItemClickListener(item -> { IconPack pack = iconPacks.get(iconPackNames.indexOf(item.getTitle().toString())); - adapter.loadIcons(pack); + adapter.loadIcons(pack, showAddCustom); String query = iconSearch.getText().toString(); if (!query.isEmpty()) { diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/models/AssignIconEntry.java b/app/src/main/java/com/beemdevelopment/aegis/ui/models/AssignIconEntry.java new file mode 100644 index 0000000000..04e74a4bd7 --- /dev/null +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/models/AssignIconEntry.java @@ -0,0 +1,40 @@ +package com.beemdevelopment.aegis.ui.models; + +import com.beemdevelopment.aegis.icons.IconPack; +import com.beemdevelopment.aegis.vault.VaultEntry; + +import java.io.Serializable; + +public class AssignIconEntry implements Serializable { + private final VaultEntry _entry; + + private IconPack.Icon _newIcon; + + private transient AssignIconEntry.Listener _listener; + + public void setOnResetListener(AssignIconEntry.Listener listener) { + _listener = listener; + } + + public AssignIconEntry(VaultEntry entry) { + _entry = entry; + } + + public VaultEntry getEntry() { + return _entry; + } + + public IconPack.Icon getNewIcon() { return _newIcon; } + + public void setNewIcon(IconPack.Icon icon) { + _newIcon = icon; + + if (_listener != null) { + _listener.onNewIconChanged(); + } + } + + public interface Listener { + void onNewIconChanged(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/views/AssignIconAdapter.java b/app/src/main/java/com/beemdevelopment/aegis/ui/views/AssignIconAdapter.java new file mode 100644 index 0000000000..40e935a11f --- /dev/null +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/views/AssignIconAdapter.java @@ -0,0 +1,55 @@ +package com.beemdevelopment.aegis.ui.views; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.recyclerview.widget.RecyclerView; + +import com.beemdevelopment.aegis.R; +import com.beemdevelopment.aegis.ui.models.AssignIconEntry; + +import java.util.ArrayList; +import java.util.Collection; + +public class AssignIconAdapter extends RecyclerView.Adapter { + private AssignIconAdapter.Listener _listener; + private ArrayList _entries; + + public AssignIconAdapter(AssignIconAdapter.Listener listener) { + _listener = listener; + _entries = new ArrayList<>(); + } + + public void addEntries(Collection entries) { + _entries.addAll(entries); + } + + @Override + public AssignIconHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_assign_icon_entry, parent, false); + AssignIconHolder holder = new AssignIconHolder(view); + // NOTE: This assumes that the old and new icon views are the same size + _listener.onSetPreloadView(holder.getOldIconView()); + return holder; + } + + @Override + public void onBindViewHolder(AssignIconHolder holder, int position) { + holder.setData(_entries.get(position)); + holder.itemView.setOnClickListener(view -> { + _listener.onAssignIconEntryClick(_entries.get(position)); + }); + _entries.get(position).setOnResetListener(holder); + } + + @Override + public int getItemCount() { + return _entries.size(); + } + + public interface Listener { + void onAssignIconEntryClick(AssignIconEntry entry); + void onSetPreloadView(View view); + } +} diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/views/AssignIconHolder.java b/app/src/main/java/com/beemdevelopment/aegis/ui/views/AssignIconHolder.java new file mode 100644 index 0000000000..658eb1528a --- /dev/null +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/views/AssignIconHolder.java @@ -0,0 +1,91 @@ +package com.beemdevelopment.aegis.ui.views; + +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.recyclerview.widget.RecyclerView; + +import com.amulyakhare.textdrawable.TextDrawable; +import com.beemdevelopment.aegis.R; +import com.beemdevelopment.aegis.helpers.TextDrawableHelper; +import com.beemdevelopment.aegis.icons.IconType; +import com.beemdevelopment.aegis.ui.glide.IconLoader; +import com.beemdevelopment.aegis.ui.models.AssignIconEntry; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; + +public class AssignIconHolder extends RecyclerView.ViewHolder implements AssignIconEntry.Listener { + private View _view; + + private AssignIconEntry _entry; + private TextView _issuer; + private TextView _accountName; + private ImageView _oldIcon; + private ImageView _newIcon; + private ImageView _btnReset; + + public AssignIconHolder(final View view) { + super(view); + + _view = view.findViewById(R.id.rlCardEntry); + + _issuer = view.findViewById(R.id.tvIssuer); + _accountName = view.findViewById(R.id.tvAccountName); + _oldIcon = view.findViewById(R.id.ivOldImage); + _newIcon = view.findViewById(R.id.ivNewImage); + _btnReset = view.findViewById(R.id.btnReset); + _btnReset.setOnClickListener(l -> _entry.setNewIcon(null)); + } + + public void setData(AssignIconEntry entry) { + _entry = entry; + _issuer.setText(entry.getEntry().getIssuer()); + _accountName.setText(entry.getEntry().getName()); + + if(!entry.getEntry().hasIcon()) { + TextDrawable drawable = TextDrawableHelper.generate(entry.getEntry().getIssuer(), entry.getEntry().getName(), _oldIcon); + _oldIcon.setImageDrawable(drawable); + } else { + Glide.with(_view.getContext()) + .asDrawable() + .load(entry.getEntry()) + .set(IconLoader.ICON_TYPE, entry.getEntry().getIconType()) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .into(_oldIcon); + } + + setNewIcon(); + } + + private void setNewIcon() { + if (_entry.getNewIcon() != null) { + Glide.with(_view.getContext()) + .asDrawable() + .load(_entry.getNewIcon().getFile()) + .set(IconLoader.ICON_TYPE, _entry.getNewIcon().getIconType()) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .skipMemoryCache(false) + .into(_newIcon); + } else { + Glide.with(_view.getContext()) + .asDrawable() + .load(R.drawable.ic_icon_unselected) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .skipMemoryCache(false) + .into(_newIcon); + } + + _btnReset.setVisibility(_entry.getNewIcon() != null ? View.VISIBLE : View.INVISIBLE); + } + + public View getOldIconView() { + return _oldIcon; + } + + @Override + public void onNewIconChanged() { + setNewIcon(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryHolder.java b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryHolder.java index 25bdf2ab9c..0bb2e457e1 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryHolder.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryHolder.java @@ -200,7 +200,7 @@ public void loadIcon(Fragment fragment) { IconViewHelper.setLayerType(_profileDrawable, _entry.getIconType()); Glide.with(fragment) .asDrawable() - .load(_entry) + .load(_entry.getIcon()) .set(IconLoader.ICON_TYPE, _entry.getIconType()) .diskCacheStrategy(DiskCacheStrategy.NONE) .skipMemoryCache(false) diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/views/IconAdapter.java b/app/src/main/java/com/beemdevelopment/aegis/ui/views/IconAdapter.java index a35e7a0112..671c4501c4 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/views/IconAdapter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/views/IconAdapter.java @@ -40,7 +40,7 @@ public IconAdapter(@NonNull Context context, String issuer, @NonNull Listener li /** * Loads all icons from the given icon pack into this adapter. Any icons added before this call will be overwritten. */ - public void loadIcons(IconPack pack) { + public void loadIcons(IconPack pack, boolean showAddCustom) { _pack = pack; _query = null; _icons = new ArrayList<>(_pack.getIcons()); @@ -60,7 +60,11 @@ public void loadIcons(IconPack pack) { .count(); List suggested = pack.getSuggestedIcons(_issuer); - suggested.add(0, new DummyIcon(_context.getString(R.string.icon_custom))); + + if (showAddCustom) { + suggested.add(0, new DummyIcon(_context.getString(R.string.icon_custom))); + } + if (suggested.size() > 0) { CategoryHeader category = new CategoryHeader(_context.getString(R.string.suggested)); category.setIsCollapsed(false); @@ -90,7 +94,7 @@ public void setQuery(@Nullable String query) { _query = query; if (_query == null) { - loadIcons(_pack); + loadIcons(_pack, false); } else { _icons = _pack.getIcons().stream() .filter(i -> i.isSuggestedFor(query)) diff --git a/app/src/main/res/drawable/baseline_arrow_right_24.xml b/app/src/main/res/drawable/baseline_arrow_right_24.xml new file mode 100644 index 0000000000..e3b8f37b2c --- /dev/null +++ b/app/src/main/res/drawable/baseline_arrow_right_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_icon_unselected.xml b/app/src/main/res/drawable/ic_icon_unselected.xml new file mode 100644 index 0000000000..d60f168018 --- /dev/null +++ b/app/src/main/res/drawable/ic_icon_unselected.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_reset_image.xml b/app/src/main/res/drawable/ic_reset_image.xml new file mode 100644 index 0000000000..c42849c40f --- /dev/null +++ b/app/src/main/res/drawable/ic_reset_image.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/rounded_background.xml b/app/src/main/res/drawable/rounded_background.xml new file mode 100644 index 0000000000..e2a884d876 --- /dev/null +++ b/app/src/main/res/drawable/rounded_background.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/layout/activity_assign_icons.xml b/app/src/main/res/layout/activity_assign_icons.xml new file mode 100644 index 0000000000..3cef926821 --- /dev/null +++ b/app/src/main/res/layout/activity_assign_icons.xml @@ -0,0 +1,27 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/card_assign_icon_entry.xml b/app/src/main/res/layout/card_assign_icon_entry.xml new file mode 100644 index 0000000000..a9232419fd --- /dev/null +++ b/app/src/main/res/layout/card_assign_icon_entry.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/menu_action_mode.xml b/app/src/main/res/menu/menu_action_mode.xml index bea4f31b32..057988ab15 100644 --- a/app/src/main/res/menu/menu_action_mode.xml +++ b/app/src/main/res/menu/menu_action_mode.xml @@ -22,18 +22,25 @@ app:showAsAction="always" /> + app:showAsAction="never"/> + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 65204e976c..86b71839df 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -171,6 +171,7 @@ Set up biometric unlock Copy Edit + Assign icons Favorite Unfavorite ERROR @@ -205,6 +206,7 @@ Discard changes? Your changes have not been saved Error saving profile + Error assigning icons Welcome Aegis is a free, secure and open source 2FA app Setup completed @@ -461,6 +463,7 @@ Scan a QR code Manage key slots Import entries + Assign icons Wipe entries Your vault already contains entries. Do you want to remove these entries before importing this file?\n\nIn doing so, you will permanently lose access to the existing entries in the vault. Wipe vault contents @@ -491,6 +494,8 @@ Steam v3.0 and newer are not supported. Supply a copy of /data/data/com.valvesoftware.android.steam.community/files/Steamguard-*.json, located in the internal storage directory of Steam. Supply a TOTP Authenticator export file. Supply a WinAuth export file. + Assign icons + Do you want to assign icons to the imported entries? Encrypted entry was skipped: %s