mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-05-14 14:02:49 +00:00
Merge pull request #1172 from michaelschattgen/feature/assign-icons
Add ability to automatically assign icons to (imported) entries
This commit is contained in:
commit
9414b5c420
20 changed files with 717 additions and 15 deletions
|
@ -0,0 +1,278 @@
|
|||
package com.beemdevelopment.aegis;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.beemdevelopment.aegis.helpers.MetricsHelper;
|
||||
import com.beemdevelopment.aegis.icons.IconPack;
|
||||
import com.beemdevelopment.aegis.ui.AegisActivity;
|
||||
import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
|
||||
import com.beemdevelopment.aegis.ui.dialogs.IconPickerDialog;
|
||||
import com.beemdevelopment.aegis.ui.glide.IconLoader;
|
||||
import com.beemdevelopment.aegis.ui.models.AssignIconEntry;
|
||||
import com.beemdevelopment.aegis.ui.views.AssignIconAdapter;
|
||||
import com.beemdevelopment.aegis.ui.views.IconAdapter;
|
||||
import com.beemdevelopment.aegis.util.IOUtils;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.ListPreloader;
|
||||
import com.bumptech.glide.RequestBuilder;
|
||||
import com.bumptech.glide.integration.recyclerview.RecyclerViewPreloader;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.util.ViewPreloadSizeProvider;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class AssignIconsActivity extends AegisActivity implements AssignIconAdapter.Listener {
|
||||
private AssignIconAdapter _adapter;
|
||||
private ArrayList<AssignIconEntry> _entries = new ArrayList<>();
|
||||
private RecyclerView _entriesView;
|
||||
private AssignIconsActivity.BackPressHandler _backPressHandler;
|
||||
private ViewPreloadSizeProvider<AssignIconEntry> _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<UUID> assignIconEntriesIds = (ArrayList<UUID>) 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<IconPack.Icon> preloader1 = new RecyclerViewPreloader(this, modelProvider1, _preloadSizeProvider, 10);
|
||||
RecyclerViewPreloader<VaultEntry> 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<IconPack> 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<IconPack.Icon> suggestedIcons = _favoriteIconPack.getSuggestedIcons(entry.getEntry().getIssuer());
|
||||
if (suggestedIcons.size() > 0) {
|
||||
return suggestedIcons.get(0);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void saveAndFinish() throws IOException {
|
||||
ArrayList<UUID> 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<VaultEntry> {
|
||||
@NonNull
|
||||
@Override
|
||||
public List<VaultEntry> getPreloadItems(int position) {
|
||||
VaultEntry entry = _entries.get(position).getEntry();
|
||||
if (entry.hasIcon()) {
|
||||
return Collections.singletonList(entry);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public RequestBuilder<Drawable> 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<IconPack.Icon> {
|
||||
@NonNull
|
||||
@Override
|
||||
public List<IconPack.Icon> getPreloadItems(int position) {
|
||||
AssignIconEntry entry = _entries.get(position);
|
||||
if (entry.getNewIcon() != null) {
|
||||
return Collections.singletonList(entry.getNewIcon());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public RequestBuilder<Drawable> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -42,6 +42,10 @@ public class IconPackManager {
|
|||
return packs.get(0);
|
||||
}
|
||||
|
||||
public boolean hasIconPack() {
|
||||
return _iconPacks.size() > 0;
|
||||
}
|
||||
|
||||
public List<IconPack> getIconPacks() {
|
||||
return new ArrayList<>(_iconPacks);
|
||||
}
|
||||
|
|
|
@ -216,7 +216,7 @@ public class EditEntryActivity extends AegisActivity {
|
|||
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 @@ public class EditEntryActivity extends AegisActivity {
|
|||
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);
|
||||
|
|
|
@ -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.appcompat.app.AlertDialog;
|
|||
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 @@ public class ImportEntriesActivity extends AegisActivity {
|
|||
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<UUID> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ import androidx.appcompat.app.AlertDialog;
|
|||
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 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
break;
|
||||
case CODE_SCAN_IMAGE:
|
||||
onScanImageResult(data);
|
||||
break;
|
||||
case CODE_ASSIGN_ICONS:
|
||||
onAssignEntriesResult(data);
|
||||
break;
|
||||
}
|
||||
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
@ -317,6 +324,17 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
startActivityForResult(intent, requestCode);
|
||||
}
|
||||
|
||||
private void startAssignIconsActivity(int requestCode, List<VaultEntry> entries) {
|
||||
ArrayList<UUID> 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 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
}
|
||||
}
|
||||
|
||||
private void onAssignEntriesResult(Intent data) {
|
||||
if (_loaded) {
|
||||
ArrayList<UUID> entryUUIDs = (ArrayList<UUID>) 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 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
_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 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
_actionMode = startSupportActionMode(_actionModeCallbacks);
|
||||
_actionModeBackPressHandler.setEnabled(true);
|
||||
setFavoriteMenuItemVisiblity();
|
||||
setAssignIconsMenuItemVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1142,6 +1177,12 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
mode.finish();
|
||||
});
|
||||
return true;
|
||||
|
||||
case R.id.action_assign_icons:
|
||||
startAssignIconsActivity(CODE_ASSIGN_ICONS, _selectedEntries);
|
||||
mode.finish();
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ public class IconPickerDialog {
|
|||
|
||||
}
|
||||
|
||||
public static BottomSheetDialog create(Activity activity, List<IconPack> iconPacks, String issuer, IconAdapter.Listener listener) {
|
||||
public static BottomSheetDialog create(Activity activity, List<IconPack> 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 class IconPickerDialog {
|
|||
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 class IconPickerDialog {
|
|||
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()) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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<AssignIconHolder> {
|
||||
private AssignIconAdapter.Listener _listener;
|
||||
private ArrayList<AssignIconEntry> _entries;
|
||||
|
||||
public AssignIconAdapter(AssignIconAdapter.Listener listener) {
|
||||
_listener = listener;
|
||||
_entries = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void addEntries(Collection<AssignIconEntry> 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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -200,7 +200,7 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
|||
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)
|
||||
|
|
|
@ -40,7 +40,7 @@ public class IconAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
|||
/**
|
||||
* 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 class IconAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
|||
.count();
|
||||
|
||||
List<IconPack.Icon> 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 class IconAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
|||
_query = query;
|
||||
|
||||
if (_query == null) {
|
||||
loadIcons(_pack);
|
||||
loadIcons(_pack, false);
|
||||
} else {
|
||||
_icons = _pack.getIcons().stream()
|
||||
.filter(i -> i.isSuggestedFor(query))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue