mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-06-08 15:47:47 +00:00
Add the option to highlight entries when tapped
This adds an option to highlight tapped entries, so that it's easier to distinguisch between the one you're trying to enter into a website and the other ones. Only one entry can be highlighted at a time. Perhaps it would make sense to change our tap to reveal functionality to behave the same, so that the two features are nicely in sync. I can address that in a separate PR if we decide to do so.
This commit is contained in:
parent
5e7697039a
commit
51a0a16afb
8 changed files with 123 additions and 24 deletions
|
@ -19,7 +19,13 @@ public class Preferences {
|
||||||
return _prefs.getBoolean("pref_tap_to_reveal", false);
|
return _prefs.getBoolean("pref_tap_to_reveal", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSearchAccountNameEnabled() { return _prefs.getBoolean("pref_search_names", false); }
|
public boolean isSearchAccountNameEnabled() {
|
||||||
|
return _prefs.getBoolean("pref_search_names", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEntryHighlightEnabled() {
|
||||||
|
return _prefs.getBoolean("pref_highlight_entry", false);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isSecureScreenEnabled() {
|
public boolean isSecureScreenEnabled() {
|
||||||
// screen security should be enabled by default, but not for debug builds
|
// screen security should be enabled by default, but not for debug builds
|
||||||
|
|
|
@ -102,6 +102,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
||||||
_entryListView.setListener(this);
|
_entryListView.setListener(this);
|
||||||
_entryListView.setShowAccountName(getPreferences().isAccountNameVisible());
|
_entryListView.setShowAccountName(getPreferences().isAccountNameVisible());
|
||||||
_entryListView.setSearchAccountName(getPreferences().isSearchAccountNameEnabled());
|
_entryListView.setSearchAccountName(getPreferences().isSearchAccountNameEnabled());
|
||||||
|
_entryListView.setHighlightEntry(getPreferences().isEntryHighlightEnabled());
|
||||||
_entryListView.setTapToReveal(getPreferences().isTapToRevealEnabled());
|
_entryListView.setTapToReveal(getPreferences().isTapToRevealEnabled());
|
||||||
_entryListView.setTapToRevealTime(getPreferences().getTapToRevealTime());
|
_entryListView.setTapToRevealTime(getPreferences().getTapToRevealTime());
|
||||||
_entryListView.setSortCategory(getPreferences().getCurrentSortCategory(), false);
|
_entryListView.setSortCategory(getPreferences().getCurrentSortCategory(), false);
|
||||||
|
@ -218,11 +219,13 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
||||||
} else if (data.getBooleanExtra("needsRefresh", false)) {
|
} else if (data.getBooleanExtra("needsRefresh", false)) {
|
||||||
boolean showAccountName = getPreferences().isAccountNameVisible();
|
boolean showAccountName = getPreferences().isAccountNameVisible();
|
||||||
boolean searchAccountName = getPreferences().isSearchAccountNameEnabled();
|
boolean searchAccountName = getPreferences().isSearchAccountNameEnabled();
|
||||||
|
boolean highlightEntry = getPreferences().isEntryHighlightEnabled();
|
||||||
boolean tapToReveal = getPreferences().isTapToRevealEnabled();
|
boolean tapToReveal = getPreferences().isTapToRevealEnabled();
|
||||||
int tapToRevealTime = getPreferences().getTapToRevealTime();
|
int tapToRevealTime = getPreferences().getTapToRevealTime();
|
||||||
ViewMode viewMode = getPreferences().getCurrentViewMode();
|
ViewMode viewMode = getPreferences().getCurrentViewMode();
|
||||||
_entryListView.setShowAccountName(showAccountName);
|
_entryListView.setShowAccountName(showAccountName);
|
||||||
_entryListView.setSearchAccountName(searchAccountName);
|
_entryListView.setSearchAccountName(searchAccountName);
|
||||||
|
_entryListView.setHighlightEntry(highlightEntry);
|
||||||
_entryListView.setTapToReveal(tapToReveal);
|
_entryListView.setTapToReveal(tapToReveal);
|
||||||
_entryListView.setTapToRevealTime(tapToRevealTime);
|
_entryListView.setTapToRevealTime(tapToRevealTime);
|
||||||
_entryListView.setViewMode(viewMode);
|
_entryListView.setViewMode(viewMode);
|
||||||
|
|
|
@ -207,6 +207,12 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Preference entryHighlightPreference = findPreference("pref_highlight_entry");
|
||||||
|
entryHighlightPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||||
|
_result.putExtra("needsRefresh", true);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
Preference tapToRevealPreference = findPreference("pref_tap_to_reveal");
|
Preference tapToRevealPreference = findPreference("pref_tap_to_reveal");
|
||||||
tapToRevealPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
tapToRevealPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||||
_result.putExtra("needsRefresh", true);
|
_result.putExtra("needsRefresh", true);
|
||||||
|
@ -348,7 +354,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreated (Bundle savedInstanceState) {
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
updateEncryptionPreferences();
|
updateEncryptionPreferences();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package com.beemdevelopment.aegis.ui.views;
|
package com.beemdevelopment.aegis.ui.views;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.beemdevelopment.aegis.R;
|
import com.beemdevelopment.aegis.R;
|
||||||
import com.beemdevelopment.aegis.SortCategory;
|
import com.beemdevelopment.aegis.SortCategory;
|
||||||
import com.beemdevelopment.aegis.ViewMode;
|
import com.beemdevelopment.aegis.ViewMode;
|
||||||
|
@ -19,15 +22,15 @@ import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements ItemTouchHelperAdapter {
|
public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements ItemTouchHelperAdapter {
|
||||||
private EntryListView _view;
|
private EntryListView _view;
|
||||||
private List<DatabaseEntry> _entries;
|
private List<DatabaseEntry> _entries;
|
||||||
private List<DatabaseEntry> _shownEntries;
|
private List<DatabaseEntry> _shownEntries;
|
||||||
private DatabaseEntry _selectedEntry;
|
private DatabaseEntry _selectedEntry;
|
||||||
|
private DatabaseEntry _highlightedEntry;
|
||||||
private boolean _showAccountName;
|
private boolean _showAccountName;
|
||||||
private boolean _searchAccountName;
|
private boolean _searchAccountName;
|
||||||
|
private boolean _highlightEntry;
|
||||||
private boolean _tapToReveal;
|
private boolean _tapToReveal;
|
||||||
private int _tapToRevealTime;
|
private int _tapToRevealTime;
|
||||||
private String _groupFilter;
|
private String _groupFilter;
|
||||||
|
@ -35,6 +38,7 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
||||||
private ViewMode _viewMode;
|
private ViewMode _viewMode;
|
||||||
private String _searchFilter;
|
private String _searchFilter;
|
||||||
private boolean _isPeriodUniform = true;
|
private boolean _isPeriodUniform = true;
|
||||||
|
private Handler _dimHandler;
|
||||||
|
|
||||||
// keeps track of the viewholders that are currently bound
|
// keeps track of the viewholders that are currently bound
|
||||||
private List<EntryHolder> _holders;
|
private List<EntryHolder> _holders;
|
||||||
|
@ -43,6 +47,7 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
||||||
_entries = new ArrayList<>();
|
_entries = new ArrayList<>();
|
||||||
_shownEntries = new ArrayList<>();
|
_shownEntries = new ArrayList<>();
|
||||||
_holders = new ArrayList<>();
|
_holders = new ArrayList<>();
|
||||||
|
_dimHandler = new Handler();
|
||||||
_view = view;
|
_view = view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +70,13 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
||||||
_tapToRevealTime = number;
|
_tapToRevealTime = number;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSearchAccountName(boolean searchAccountName) { _searchAccountName = searchAccountName; }
|
public void setSearchAccountName(boolean searchAccountName) {
|
||||||
|
_searchAccountName = searchAccountName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHighlightEntry(boolean highlightEntry) {
|
||||||
|
_highlightEntry = highlightEntry;
|
||||||
|
}
|
||||||
|
|
||||||
public DatabaseEntry getEntryAt(int position) {
|
public DatabaseEntry getEntryAt(int position) {
|
||||||
return _shownEntries.get(position);
|
return _shownEntries.get(position);
|
||||||
|
@ -293,19 +304,34 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
||||||
DatabaseEntry entry = _shownEntries.get(position);
|
DatabaseEntry entry = _shownEntries.get(position);
|
||||||
holder.setFocused(entry == _selectedEntry);
|
holder.setFocused(entry == _selectedEntry);
|
||||||
|
|
||||||
|
boolean dimmed = _highlightedEntry != null && _highlightedEntry != entry;
|
||||||
boolean showProgress = !isPeriodUniform() && entry.getInfo() instanceof TotpInfo;
|
boolean showProgress = !isPeriodUniform() && entry.getInfo() instanceof TotpInfo;
|
||||||
holder.setData(entry, _showAccountName, showProgress, _tapToReveal);
|
holder.setData(entry, _showAccountName, showProgress, _tapToReveal, dimmed);
|
||||||
holder.setTapToRevealTime(_tapToRevealTime);
|
holder.setTapToRevealTime(_tapToRevealTime);
|
||||||
holder.loadIcon(_view);
|
holder.loadIcon(_view);
|
||||||
|
|
||||||
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
int position = holder.getAdapterPosition();
|
boolean handled = false;
|
||||||
if (_tapToReveal && holder.isCodeHidden() && _selectedEntry == null) {
|
|
||||||
holder.revealCode();
|
if (_selectedEntry == null) {
|
||||||
} else {
|
if (_tapToReveal && holder.isCodeHidden()) {
|
||||||
_view.onEntryClick(_shownEntries.get(position));
|
holder.revealCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_highlightEntry) {
|
||||||
|
if (_highlightedEntry == entry) {
|
||||||
|
resetHighlight();
|
||||||
|
handled = true;
|
||||||
|
} else {
|
||||||
|
highlightEntry(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handled) {
|
||||||
|
_view.onEntryClick(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -384,9 +410,34 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
||||||
return period;
|
return period;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void highlightEntry(DatabaseEntry entry) {
|
||||||
|
_highlightedEntry = entry;
|
||||||
|
_dimHandler.removeCallbacksAndMessages(null);
|
||||||
|
|
||||||
|
for (EntryHolder holder : _holders) {
|
||||||
|
if (holder.getEntry() != _highlightedEntry) {
|
||||||
|
holder.dim();
|
||||||
|
} else {
|
||||||
|
holder.highlight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_dimHandler.postDelayed(this::resetHighlight, _tapToRevealTime * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetHighlight() {
|
||||||
|
_highlightedEntry = null;
|
||||||
|
|
||||||
|
for (EntryHolder holder : _holders) {
|
||||||
|
holder.highlight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setSelectedEntry(DatabaseEntry entry) {
|
public void setSelectedEntry(DatabaseEntry entry) {
|
||||||
if (entry == null) {
|
if (entry == null) {
|
||||||
notifyItemChanged(_shownEntries.indexOf(_selectedEntry));
|
notifyItemChanged(_shownEntries.indexOf(_selectedEntry));
|
||||||
|
} else if (_highlightEntry) {
|
||||||
|
resetHighlight();
|
||||||
}
|
}
|
||||||
|
|
||||||
_selectedEntry = entry;
|
_selectedEntry = entry;
|
||||||
|
|
|
@ -6,6 +6,9 @@ import android.view.View;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.amulyakhare.textdrawable.TextDrawable;
|
import com.amulyakhare.textdrawable.TextDrawable;
|
||||||
import com.beemdevelopment.aegis.R;
|
import com.beemdevelopment.aegis.R;
|
||||||
import com.beemdevelopment.aegis.db.DatabaseEntry;
|
import com.beemdevelopment.aegis.db.DatabaseEntry;
|
||||||
|
@ -19,10 +22,10 @@ import com.beemdevelopment.aegis.otp.TotpInfo;
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
public class EntryHolder extends RecyclerView.ViewHolder {
|
public class EntryHolder extends RecyclerView.ViewHolder {
|
||||||
|
private static final float DEFAULT_ALPHA = 1.0f;
|
||||||
|
private static final float DIMMED_ALPHA = 0.2f;
|
||||||
|
|
||||||
private TextView _profileName;
|
private TextView _profileName;
|
||||||
private TextView _profileCode;
|
private TextView _profileCode;
|
||||||
private TextView _profileIssuer;
|
private TextView _profileIssuer;
|
||||||
|
@ -74,7 +77,7 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
||||||
_hiddenHandler = new Handler();
|
_hiddenHandler = new Handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setData(DatabaseEntry entry, boolean showAccountName, boolean showProgress, boolean hidden) {
|
public void setData(DatabaseEntry entry, boolean showAccountName, boolean showProgress, boolean hidden, boolean dimmed) {
|
||||||
_entry = entry;
|
_entry = entry;
|
||||||
_hidden = hidden;
|
_hidden = hidden;
|
||||||
|
|
||||||
|
@ -98,6 +101,12 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
||||||
} else {
|
} else {
|
||||||
refreshCode();
|
refreshCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
itemView.setAlpha(dimmed ? DIMMED_ALPHA : DEFAULT_ALPHA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabaseEntry getEntry() {
|
||||||
|
return _entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadIcon(Fragment fragment) {
|
public void loadIcon(Fragment fragment) {
|
||||||
|
@ -191,6 +200,18 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
||||||
_hidden = false;
|
_hidden = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void dim() {
|
||||||
|
animateAlphaTo(DIMMED_ALPHA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void highlight() {
|
||||||
|
animateAlphaTo(DEFAULT_ALPHA);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void animateAlphaTo(float alpha) {
|
||||||
|
itemView.animate().alpha(alpha).setDuration(200).start();
|
||||||
|
}
|
||||||
|
|
||||||
private void hideCode() {
|
private void hideCode() {
|
||||||
_profileCode.setText(R.string.tap_to_reveal);
|
_profileCode.setText(R.string.tap_to_reveal);
|
||||||
_hidden = true;
|
_hidden = true;
|
||||||
|
|
|
@ -10,6 +10,15 @@ import android.view.animation.AnimationUtils;
|
||||||
import android.view.animation.LayoutAnimationController;
|
import android.view.animation.LayoutAnimationController;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.beemdevelopment.aegis.R;
|
import com.beemdevelopment.aegis.R;
|
||||||
import com.beemdevelopment.aegis.SortCategory;
|
import com.beemdevelopment.aegis.SortCategory;
|
||||||
import com.beemdevelopment.aegis.ViewMode;
|
import com.beemdevelopment.aegis.ViewMode;
|
||||||
|
@ -27,15 +36,6 @@ import com.bumptech.glide.util.ViewPreloadSizeProvider;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
||||||
private EntryAdapter _adapter;
|
private EntryAdapter _adapter;
|
||||||
private Listener _listener;
|
private Listener _listener;
|
||||||
|
@ -215,6 +215,10 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
||||||
_adapter.setSearchAccountName(searchAccountName);
|
_adapter.setSearchAccountName(searchAccountName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setHighlightEntry(boolean highlightEntry) {
|
||||||
|
_adapter.setHighlightEntry(highlightEntry);
|
||||||
|
}
|
||||||
|
|
||||||
public void setTapToReveal(boolean tapToReveal) {
|
public void setTapToReveal(boolean tapToReveal) {
|
||||||
_adapter.setTapToReveal(tapToReveal);
|
_adapter.setTapToReveal(tapToReveal);
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,6 +160,8 @@
|
||||||
<string name="preference_manage_groups_summary">Manage and delete your groups here</string>
|
<string name="preference_manage_groups_summary">Manage and delete your groups here</string>
|
||||||
<string name="pref_search_name_title">Search in account names</string>
|
<string name="pref_search_name_title">Search in account names</string>
|
||||||
<string name="pref_search_name_summary">Include account name matches in the search results</string>
|
<string name="pref_search_name_summary">Include account name matches in the search results</string>
|
||||||
|
<string name="pref_highlight_entry_title">Highlight tokens when tapped</string>
|
||||||
|
<string name="pref_highlight_entry_summary">Make tokens easier to distinguish from each other by temporarily highlighting them when tapped</string>
|
||||||
<string name="tap_to_reveal">Hidden</string>
|
<string name="tap_to_reveal">Hidden</string>
|
||||||
<string name="selected">Selected</string>
|
<string name="selected">Selected</string>
|
||||||
<string name="dark_theme_title">Dark theme</string>
|
<string name="dark_theme_title">Dark theme</string>
|
||||||
|
|
|
@ -51,6 +51,12 @@
|
||||||
android:title="@string/pref_search_name_title"
|
android:title="@string/pref_search_name_title"
|
||||||
android:summary="@string/pref_search_name_summary"
|
android:summary="@string/pref_search_name_summary"
|
||||||
app:iconSpaceReserved="false"/>
|
app:iconSpaceReserved="false"/>
|
||||||
|
<androidx.preference.SwitchPreferenceCompat
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="pref_highlight_entry"
|
||||||
|
android:title="@string/pref_highlight_entry_title"
|
||||||
|
android:summary="@string/pref_highlight_entry_summary"
|
||||||
|
app:iconSpaceReserved="false"/>
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue