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:
Alexander Bakker 2019-09-07 22:26:34 +02:00
parent 5e7697039a
commit 51a0a16afb
8 changed files with 123 additions and 24 deletions

View file

@ -19,7 +19,13 @@ public class Preferences {
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() {
// screen security should be enabled by default, but not for debug builds

View file

@ -102,6 +102,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
_entryListView.setListener(this);
_entryListView.setShowAccountName(getPreferences().isAccountNameVisible());
_entryListView.setSearchAccountName(getPreferences().isSearchAccountNameEnabled());
_entryListView.setHighlightEntry(getPreferences().isEntryHighlightEnabled());
_entryListView.setTapToReveal(getPreferences().isTapToRevealEnabled());
_entryListView.setTapToRevealTime(getPreferences().getTapToRevealTime());
_entryListView.setSortCategory(getPreferences().getCurrentSortCategory(), false);
@ -218,11 +219,13 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
} else if (data.getBooleanExtra("needsRefresh", false)) {
boolean showAccountName = getPreferences().isAccountNameVisible();
boolean searchAccountName = getPreferences().isSearchAccountNameEnabled();
boolean highlightEntry = getPreferences().isEntryHighlightEnabled();
boolean tapToReveal = getPreferences().isTapToRevealEnabled();
int tapToRevealTime = getPreferences().getTapToRevealTime();
ViewMode viewMode = getPreferences().getCurrentViewMode();
_entryListView.setShowAccountName(showAccountName);
_entryListView.setSearchAccountName(searchAccountName);
_entryListView.setHighlightEntry(highlightEntry);
_entryListView.setTapToReveal(tapToReveal);
_entryListView.setTapToRevealTime(tapToRevealTime);
_entryListView.setViewMode(viewMode);

View file

@ -207,6 +207,12 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
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");
tapToRevealPreference.setOnPreferenceChangeListener((preference, newValue) -> {
_result.putExtra("needsRefresh", true);
@ -348,7 +354,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
}
@Override
public void onActivityCreated (Bundle savedInstanceState) {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
updateEncryptionPreferences();
}

View file

@ -1,9 +1,12 @@
package com.beemdevelopment.aegis.ui.views;
import android.os.Handler;
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.SortCategory;
import com.beemdevelopment.aegis.ViewMode;
@ -19,15 +22,15 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import androidx.recyclerview.widget.RecyclerView;
public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements ItemTouchHelperAdapter {
private EntryListView _view;
private List<DatabaseEntry> _entries;
private List<DatabaseEntry> _shownEntries;
private DatabaseEntry _selectedEntry;
private DatabaseEntry _highlightedEntry;
private boolean _showAccountName;
private boolean _searchAccountName;
private boolean _highlightEntry;
private boolean _tapToReveal;
private int _tapToRevealTime;
private String _groupFilter;
@ -35,6 +38,7 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
private ViewMode _viewMode;
private String _searchFilter;
private boolean _isPeriodUniform = true;
private Handler _dimHandler;
// keeps track of the viewholders that are currently bound
private List<EntryHolder> _holders;
@ -43,6 +47,7 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
_entries = new ArrayList<>();
_shownEntries = new ArrayList<>();
_holders = new ArrayList<>();
_dimHandler = new Handler();
_view = view;
}
@ -65,7 +70,13 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
_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) {
return _shownEntries.get(position);
@ -293,19 +304,34 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
DatabaseEntry entry = _shownEntries.get(position);
holder.setFocused(entry == _selectedEntry);
boolean dimmed = _highlightedEntry != null && _highlightedEntry != entry;
boolean showProgress = !isPeriodUniform() && entry.getInfo() instanceof TotpInfo;
holder.setData(entry, _showAccountName, showProgress, _tapToReveal);
holder.setData(entry, _showAccountName, showProgress, _tapToReveal, dimmed);
holder.setTapToRevealTime(_tapToRevealTime);
holder.loadIcon(_view);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
if (_tapToReveal && holder.isCodeHidden() && _selectedEntry == null) {
holder.revealCode();
} else {
_view.onEntryClick(_shownEntries.get(position));
boolean handled = false;
if (_selectedEntry == null) {
if (_tapToReveal && holder.isCodeHidden()) {
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;
}
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) {
if (entry == null) {
notifyItemChanged(_shownEntries.indexOf(_selectedEntry));
} else if (_highlightEntry) {
resetHighlight();
}
_selectedEntry = entry;

View file

@ -6,6 +6,9 @@ import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import com.amulyakhare.textdrawable.TextDrawable;
import com.beemdevelopment.aegis.R;
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.load.engine.DiskCacheStrategy;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
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 _profileCode;
private TextView _profileIssuer;
@ -74,7 +77,7 @@ public class EntryHolder extends RecyclerView.ViewHolder {
_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;
_hidden = hidden;
@ -98,6 +101,12 @@ public class EntryHolder extends RecyclerView.ViewHolder {
} else {
refreshCode();
}
itemView.setAlpha(dimmed ? DIMMED_ALPHA : DEFAULT_ALPHA);
}
public DatabaseEntry getEntry() {
return _entry;
}
public void loadIcon(Fragment fragment) {
@ -191,6 +200,18 @@ public class EntryHolder extends RecyclerView.ViewHolder {
_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() {
_profileCode.setText(R.string.tap_to_reveal);
_hidden = true;

View file

@ -10,6 +10,15 @@ import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController;
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.SortCategory;
import com.beemdevelopment.aegis.ViewMode;
@ -27,15 +36,6 @@ import com.bumptech.glide.util.ViewPreloadSizeProvider;
import java.util.Collections;
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 {
private EntryAdapter _adapter;
private Listener _listener;
@ -215,6 +215,10 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
_adapter.setSearchAccountName(searchAccountName);
}
public void setHighlightEntry(boolean highlightEntry) {
_adapter.setHighlightEntry(highlightEntry);
}
public void setTapToReveal(boolean tapToReveal) {
_adapter.setTapToReveal(tapToReveal);
}

View file

@ -160,6 +160,8 @@
<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_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="selected">Selected</string>
<string name="dark_theme_title">Dark theme</string>

View file

@ -51,6 +51,12 @@
android:title="@string/pref_search_name_title"
android:summary="@string/pref_search_name_summary"
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