mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-05-04 20:30:36 +00:00
Fix the last couple of sorting bugs (#77)
This fixes the following bugs: - Sort category is forgotten after lock/unlock - The sort mode is not respected for new entries I got a little carried away while working on this patch and also included the following other enhancements: - Simplify the SortCategory, Theme and ViewMode enums - Simplify usage of string resources - Don't call notifyDataSetChanged and runLayoutAnimation unnecessarily
This commit is contained in:
parent
0a8dd56306
commit
6d26d1beb0
12 changed files with 264 additions and 309 deletions
|
@ -43,24 +43,24 @@ public class Preferences {
|
|||
_prefs.edit().putInt("pref_current_sort_category", category.ordinal()).apply();
|
||||
}
|
||||
|
||||
public int getCurrentSortCategory() {
|
||||
return _prefs.getInt("pref_current_sort_category", 0);
|
||||
public SortCategory getCurrentSortCategory() {
|
||||
return SortCategory.fromInteger(_prefs.getInt("pref_current_sort_category", 0));
|
||||
}
|
||||
|
||||
public int getTapToRevealTime() {
|
||||
return _prefs.getInt("pref_tap_to_reveal_time", 30);
|
||||
}
|
||||
|
||||
public int getCurrentTheme() {
|
||||
return _prefs.getInt("pref_current_theme", 0);
|
||||
public Theme getCurrentTheme() {
|
||||
return Theme.fromInteger(_prefs.getInt("pref_current_theme", 0));
|
||||
}
|
||||
|
||||
public void setCurrentTheme(Theme theme) {
|
||||
_prefs.edit().putInt("pref_current_theme", theme.ordinal()).apply();
|
||||
}
|
||||
|
||||
public int getCurrentViewMode() {
|
||||
return _prefs.getInt("pref_current_view_mode", 0);
|
||||
public ViewMode getCurrentViewMode() {
|
||||
return ViewMode.fromInteger(_prefs.getInt("pref_current_view_mode", 0));
|
||||
}
|
||||
|
||||
public void setCurrentViewMode(ViewMode viewMode) {
|
||||
|
|
|
@ -1,69 +1,61 @@
|
|||
package com.beemdevelopment.aegis;
|
||||
|
||||
import com.beemdevelopment.aegis.db.DatabaseEntry;
|
||||
import com.beemdevelopment.aegis.helpers.comparators.AccountNameComparator;
|
||||
import com.beemdevelopment.aegis.helpers.comparators.IssuerNameComparator;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
||||
public enum SortCategory {
|
||||
CUSTOM,
|
||||
ACCOUNT,
|
||||
ACCOUNTREVERSED,
|
||||
ACCOUNT_REVERSED,
|
||||
ISSUER,
|
||||
ISSUERREVERSED;
|
||||
ISSUER_REVERSED;
|
||||
|
||||
private static SortCategory[] _values;
|
||||
|
||||
static {
|
||||
_values = values();
|
||||
}
|
||||
|
||||
public static SortCategory fromInteger(int x) {
|
||||
switch (x) {
|
||||
case 0:
|
||||
return CUSTOM;
|
||||
case 1:
|
||||
return ACCOUNT;
|
||||
case 2:
|
||||
return ACCOUNTREVERSED;
|
||||
case 3:
|
||||
return ISSUER;
|
||||
case 4:
|
||||
return ISSUERREVERSED;
|
||||
}
|
||||
return null;
|
||||
return _values[x];
|
||||
}
|
||||
|
||||
public static Comparator getComparator(SortCategory sortCategory) {
|
||||
switch (sortCategory) {
|
||||
public Comparator<DatabaseEntry> getComparator() {
|
||||
Comparator<DatabaseEntry> comparator = null;
|
||||
|
||||
switch (this) {
|
||||
case ACCOUNT:
|
||||
case ACCOUNTREVERSED:
|
||||
return new AccountNameComparator();
|
||||
comparator = new AccountNameComparator();
|
||||
break;
|
||||
case ACCOUNT_REVERSED:
|
||||
comparator = Collections.reverseOrder(new AccountNameComparator());
|
||||
break;
|
||||
case ISSUER:
|
||||
case ISSUERREVERSED:
|
||||
return new IssuerNameComparator();
|
||||
case CUSTOM:
|
||||
return new IssuerNameComparator();
|
||||
comparator = new IssuerNameComparator();
|
||||
break;
|
||||
case ISSUER_REVERSED:
|
||||
comparator = Collections.reverseOrder(new IssuerNameComparator());
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
|
||||
return comparator;
|
||||
}
|
||||
|
||||
public static boolean isReversed(SortCategory sortCategory) {
|
||||
switch (sortCategory) {
|
||||
case ACCOUNTREVERSED:
|
||||
case ISSUERREVERSED:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getMenuItem(SortCategory sortCategory) {
|
||||
switch (sortCategory) {
|
||||
public int getMenuItem() {
|
||||
switch (this) {
|
||||
case CUSTOM:
|
||||
return R.id.menu_sort_custom;
|
||||
case ACCOUNT:
|
||||
return R.id.menu_sort_alphabetically_name;
|
||||
case ACCOUNTREVERSED:
|
||||
case ACCOUNT_REVERSED:
|
||||
return R.id.menu_sort_alphabetically_name_reverse;
|
||||
case ISSUER:
|
||||
return R.id.menu_sort_alphabetically;
|
||||
case ISSUERREVERSED:
|
||||
case ISSUER_REVERSED:
|
||||
return R.id.menu_sort_alphabetically_reverse;
|
||||
default:
|
||||
return R.id.menu_sort_custom;
|
||||
|
|
|
@ -5,43 +5,13 @@ public enum Theme {
|
|||
DARK,
|
||||
AMOLED;
|
||||
|
||||
private static Theme[] _values;
|
||||
|
||||
static {
|
||||
_values = values();
|
||||
}
|
||||
|
||||
public static Theme fromInteger(int x) {
|
||||
switch (x) {
|
||||
case 0:
|
||||
return LIGHT;
|
||||
case 1:
|
||||
return DARK;
|
||||
case 2:
|
||||
return AMOLED;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int getThemeNameResource(int x) {
|
||||
switch (x) {
|
||||
case 0:
|
||||
return R.string.light_theme_title;
|
||||
case 1:
|
||||
return R.string.dark_theme_title;
|
||||
case 2:
|
||||
return R.string.amoled_theme_title;
|
||||
}
|
||||
return R.string.light_theme_title;
|
||||
}
|
||||
|
||||
public static String[] getThemeNames() {
|
||||
return new String[]{
|
||||
"Light theme",
|
||||
"Dark theme",
|
||||
"Amoled theme"
|
||||
};
|
||||
}
|
||||
|
||||
public static int[] getThemeNameResources() {
|
||||
return new int[] {
|
||||
R.string.light_theme_title,
|
||||
R.string.dark_theme_title,
|
||||
R.string.amoled_theme_title
|
||||
};
|
||||
return _values[x];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,48 +1,33 @@
|
|||
package com.beemdevelopment.aegis;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
|
||||
public enum ViewMode {
|
||||
NORMAL,
|
||||
COMPACT,
|
||||
SMALL;
|
||||
|
||||
private static ViewMode[] _values;
|
||||
|
||||
static {
|
||||
_values = values();
|
||||
}
|
||||
|
||||
public static ViewMode fromInteger(int x) {
|
||||
switch (x) {
|
||||
case 0:
|
||||
return NORMAL;
|
||||
case 1:
|
||||
return COMPACT;
|
||||
case 2:
|
||||
return SMALL;
|
||||
return _values[x];
|
||||
}
|
||||
|
||||
@LayoutRes
|
||||
public int getLayoutId() {
|
||||
switch (this) {
|
||||
case NORMAL:
|
||||
return R.layout.card_entry;
|
||||
case COMPACT:
|
||||
return R.layout.card_entry_compact;
|
||||
case SMALL:
|
||||
return R.layout.card_entry_small;
|
||||
default:
|
||||
return R.layout.card_entry;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int getViewModeNameResource(int x) {
|
||||
switch (x) {
|
||||
case 0:
|
||||
return R.string.normal_viewmode_title;
|
||||
case 1:
|
||||
return R.string.compact_mode_title;
|
||||
case 2:
|
||||
return R.string.small_mode_title;
|
||||
}
|
||||
|
||||
return R.string.normal_viewmode_title;
|
||||
}
|
||||
|
||||
public static String[] getViewModeNames() {
|
||||
return new String[]{
|
||||
"Normal",
|
||||
"Compact",
|
||||
"Small"
|
||||
};
|
||||
}
|
||||
|
||||
public static int[] getViewModeNameResources() {
|
||||
return new int[] {
|
||||
R.string.normal_viewmode_title,
|
||||
R.string.compact_mode_title,
|
||||
R.string.small_mode_title
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ public abstract class AegisActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
// set the theme
|
||||
setPreferredTheme(Theme.fromInteger(getPreferences().getCurrentTheme()));
|
||||
setPreferredTheme(getPreferences().getCurrentTheme());
|
||||
|
||||
// apply a dirty hack to make progress bars work even if animations are disabled
|
||||
setGlobalAnimationDurationScale();
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package com.beemdevelopment.aegis.ui;
|
||||
|
||||
import android.Manifest;
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
|
@ -16,9 +14,6 @@ import android.view.Menu;
|
|||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SubMenu;
|
||||
import android.view.View;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
|
@ -48,15 +43,12 @@ import com.google.zxing.Reader;
|
|||
import com.google.zxing.Result;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
|
||||
public class MainActivity extends AegisActivity implements EntryListView.Listener {
|
||||
// activity request codes
|
||||
private static final int CODE_SCAN = 0;
|
||||
|
@ -98,7 +90,8 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
_entryListView.setShowAccountName(getPreferences().isAccountNameVisible());
|
||||
_entryListView.setTapToReveal(getPreferences().isTapToRevealEnabled());
|
||||
_entryListView.setTapToRevealTime(getPreferences().getTapToRevealTime());
|
||||
_entryListView.setViewMode(ViewMode.fromInteger(getPreferences().getCurrentViewMode()));
|
||||
_entryListView.setSortCategory(getPreferences().getCurrentSortCategory(), false);
|
||||
_entryListView.setViewMode(getPreferences().getCurrentViewMode());
|
||||
|
||||
// set up the floating action button
|
||||
_fabMenu = findViewById(R.id.fab);
|
||||
|
@ -203,9 +196,11 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
boolean showAccountName = getPreferences().isAccountNameVisible();
|
||||
boolean tapToReveal = getPreferences().isTapToRevealEnabled();
|
||||
int tapToRevealTime = getPreferences().getTapToRevealTime();
|
||||
ViewMode viewMode = getPreferences().getCurrentViewMode();
|
||||
_entryListView.setShowAccountName(showAccountName);
|
||||
_entryListView.setTapToReveal(tapToReveal);
|
||||
_entryListView.setTapToRevealTime(tapToRevealTime);
|
||||
_entryListView.setViewMode(viewMode);
|
||||
_entryListView.refresh(true);
|
||||
}
|
||||
}
|
||||
|
@ -317,23 +312,15 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
menu.setGroupCheckable(R.id.action_filter_group, true, true);
|
||||
}
|
||||
|
||||
private void updateSortCategoryMenu() {
|
||||
SortCategory category = getPreferences().getCurrentSortCategory();
|
||||
_menu.findItem(category.getMenuItem()).setChecked(true);
|
||||
}
|
||||
|
||||
private void setGroupFilter(String group) {
|
||||
getSupportActionBar().setSubtitle(group);
|
||||
_checkedGroup = group;
|
||||
_entryListView.setGroupFilter(group);
|
||||
}
|
||||
|
||||
private void setSortCategory(SortCategory sortCategory) {
|
||||
if (sortCategory == SortCategory.CUSTOM) {
|
||||
_entryListView.clearEntries();
|
||||
loadEntries();
|
||||
}
|
||||
|
||||
_entryListView.setSortCategory(sortCategory);
|
||||
}
|
||||
|
||||
private void updateSortCategoryMenu(SortCategory sortCategory) {
|
||||
_menu.findItem(SortCategory.getMenuItem(sortCategory)).setChecked(true);
|
||||
_entryListView.setGroupFilter(group, true);
|
||||
}
|
||||
|
||||
private void addEntry(DatabaseEntry entry) {
|
||||
|
@ -412,7 +399,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
}
|
||||
|
||||
// refresh all codes to prevent showing old ones
|
||||
_entryListView.refresh(true);
|
||||
_entryListView.refresh(false);
|
||||
} else {
|
||||
loadEntries();
|
||||
}
|
||||
|
@ -481,7 +468,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
updateLockIcon();
|
||||
if (_loaded) {
|
||||
updateGroupFilterMenu();
|
||||
updateSortCategoryMenu(SortCategory.fromInteger(getPreferences().getCurrentSortCategory()));
|
||||
updateSortCategoryMenu();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -517,13 +504,13 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
sortCategory = SortCategory.ISSUER;
|
||||
break;
|
||||
case R.id.menu_sort_alphabetically_reverse:
|
||||
sortCategory = SortCategory.ISSUERREVERSED;
|
||||
sortCategory = SortCategory.ISSUER_REVERSED;
|
||||
break;
|
||||
case R.id.menu_sort_alphabetically_name:
|
||||
sortCategory = SortCategory.ACCOUNT;
|
||||
break;
|
||||
case R.id.menu_sort_alphabetically_name_reverse:
|
||||
sortCategory = SortCategory.ACCOUNTREVERSED;
|
||||
sortCategory = SortCategory.ACCOUNT_REVERSED;
|
||||
break;
|
||||
case R.id.menu_sort_custom:
|
||||
default:
|
||||
|
@ -531,7 +518,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
break;
|
||||
}
|
||||
|
||||
setSortCategory(sortCategory);
|
||||
_entryListView.setSortCategory(sortCategory, true);
|
||||
getPreferences().setCurrentSortCategory(sortCategory);
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
|
@ -570,14 +557,13 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
}
|
||||
|
||||
loadEntries();
|
||||
SortCategory currentSortCategory = SortCategory.fromInteger(getPreferences().getCurrentSortCategory());
|
||||
setSortCategory(currentSortCategory);
|
||||
}
|
||||
|
||||
private void loadEntries() {
|
||||
// load all entries
|
||||
List<DatabaseEntry> entries = new ArrayList<DatabaseEntry>(_db.getEntries());
|
||||
_entryListView.addEntries(entries);
|
||||
_entryListView.runEntriesAnimation();
|
||||
_loaded = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -89,18 +89,17 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
// set the result intent in advance
|
||||
setResult(new Intent());
|
||||
|
||||
int currentTheme = app.getPreferences().getCurrentTheme();
|
||||
int currentTheme = app.getPreferences().getCurrentTheme().ordinal();
|
||||
Preference darkModePreference = findPreference("pref_dark_mode");
|
||||
darkModePreference.setSummary(String.format("%s: %s", getString(R.string.selected), getString(Theme.getThemeNameResource(currentTheme))));
|
||||
darkModePreference.setSummary(String.format("%s: %s", getString(R.string.selected), getResources().getStringArray(R.array.theme_titles)[currentTheme]));
|
||||
darkModePreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
String[] themeNames = getStringsFromResourceIds(Theme.getThemeNameResources());
|
||||
int checkedTheme = app.getPreferences().getCurrentTheme();
|
||||
int currentTheme = app.getPreferences().getCurrentTheme().ordinal();
|
||||
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())
|
||||
.setTitle(getString(R.string.choose_theme))
|
||||
.setSingleChoiceItems(themeNames, checkedTheme, (dialog, which) -> {
|
||||
.setTitle(R.string.choose_theme)
|
||||
.setSingleChoiceItems(R.array.theme_titles, currentTheme, (dialog, which) -> {
|
||||
int i = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
app.getPreferences().setCurrentTheme(Theme.fromInteger(i));
|
||||
|
||||
|
@ -116,34 +115,22 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
}
|
||||
});
|
||||
|
||||
darkModePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
_result.putExtra("needsRecreate", true);
|
||||
getActivity().recreate();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
int currentViewMode = app.getPreferences().getCurrentViewMode();
|
||||
int currentViewMode = app.getPreferences().getCurrentViewMode().ordinal();
|
||||
Preference viewModePreference = findPreference("pref_view_mode");
|
||||
viewModePreference.setSummary(String.format("%s: %s", getString(R.string.selected), getString(ViewMode.getViewModeNameResource(currentViewMode))));
|
||||
viewModePreference.setSummary(String.format("%s: %s", getString(R.string.selected), getResources().getStringArray(R.array.view_mode_titles)[currentViewMode]));
|
||||
viewModePreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
String[] viewModes = getStringsFromResourceIds(ViewMode.getViewModeNameResources());
|
||||
int checkedMode = app.getPreferences().getCurrentViewMode();
|
||||
int currentViewMode = app.getPreferences().getCurrentViewMode().ordinal();
|
||||
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())
|
||||
.setTitle(getString(R.string.choose_view_mode))
|
||||
.setSingleChoiceItems(viewModes, checkedMode, (dialog, which) -> {
|
||||
.setTitle(R.string.choose_view_mode)
|
||||
.setSingleChoiceItems(R.array.view_mode_titles, currentViewMode, (dialog, which) -> {
|
||||
int i = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
app.getPreferences().setCurrentViewMode(ViewMode.fromInteger(i));
|
||||
|
||||
viewModePreference.setSummary(String.format("%s: %s", getString(R.string.selected), getResources().getStringArray(R.array.view_mode_titles)[i]));
|
||||
_result.putExtra("needsRefresh", true);
|
||||
dialog.dismiss();
|
||||
|
||||
viewModePreference.setSummary(String.format("%s: %s", getString(R.string.selected), getString(ViewMode.getViewModeNameResource(i))));
|
||||
_result.putExtra("needsRecreate", true);
|
||||
})
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.create());
|
||||
|
@ -245,14 +232,14 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
Dialogs.showSetPasswordDialog(getActivity(), new EnableEncryptionListener());
|
||||
} else {
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())
|
||||
.setTitle(getString(R.string.disable_encryption))
|
||||
.setTitle(R.string.disable_encryption)
|
||||
.setMessage(getString(R.string.disable_encryption_description))
|
||||
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
try {
|
||||
_db.disableEncryption();
|
||||
} catch (DatabaseManagerException e) {
|
||||
Toast.makeText(getActivity(), getString(R.string.disable_encryption_error), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(getActivity(), R.string.disable_encryption_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -343,7 +330,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
if (!PermissionHelper.checkResults(grantResults)) {
|
||||
Toast.makeText(getActivity(), getString(R.string.permission_denied), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(getActivity(), R.string.permission_denied, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -400,7 +387,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
String[] names = importers.keySet().toArray(new String[0]);
|
||||
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())
|
||||
.setTitle(getString(R.string.choose_application))
|
||||
.setTitle(R.string.choose_application)
|
||||
.setSingleChoiceItems(names, 0, null)
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
|
@ -420,7 +407,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
String[] names = importers.keySet().toArray(new String[0]);
|
||||
|
||||
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())
|
||||
.setTitle(getString(R.string.choose_application))
|
||||
.setTitle(R.string.choose_application)
|
||||
.setSingleChoiceItems(names, 0, null)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
int i = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
|
@ -449,7 +436,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
Toast.makeText(getActivity(), R.string.app_lookup_error, Toast.LENGTH_SHORT).show();
|
||||
} catch (IOException | DatabaseImporterException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(getActivity(), getString(R.string.reading_file_error), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(getActivity(), R.string.reading_file_error, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -517,10 +504,10 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
DatabaseImporter importer = DatabaseImporter.create(getContext(), _importerType);
|
||||
importDatabase(importer, reader);
|
||||
} catch (FileNotFoundException e) {
|
||||
Toast.makeText(getActivity(), getString(R.string.file_not_found), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(getActivity(), R.string.file_not_found, Toast.LENGTH_SHORT).show();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(getActivity(), getString(R.string.reading_file_error), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(getActivity(), R.string.reading_file_error, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -558,7 +545,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
try {
|
||||
filename = _db.export(checked.get());
|
||||
} catch (DatabaseManagerException e) {
|
||||
Toast.makeText(getActivity(), getString(R.string.exporting_database_error), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(getActivity(), R.string.exporting_database_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -578,7 +565,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
builder.setMessage(getString(R.string.export_warning));
|
||||
builder.setMessage(R.string.export_warning);
|
||||
}
|
||||
Dialogs.showSecureDialog(builder.create());
|
||||
}
|
||||
|
@ -637,7 +624,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
try {
|
||||
_db.save();
|
||||
} catch (DatabaseManagerException e) {
|
||||
Toast.makeText(getActivity(), getString(R.string.saving_error), Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(getActivity(), R.string.saving_error, Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -668,15 +655,6 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
}
|
||||
}
|
||||
|
||||
private String[] getStringsFromResourceIds(int[] resourceIds) {
|
||||
String[] strings = new String[resourceIds.length];
|
||||
for(int i = 0; i < resourceIds.length; i++) {
|
||||
strings[i] = getString(resourceIds[i]);
|
||||
}
|
||||
|
||||
return strings;
|
||||
}
|
||||
|
||||
private class SetPasswordListener implements Dialogs.SlotListener {
|
||||
@Override
|
||||
public void onSlotResult(Slot slot, Cipher cipher) {
|
||||
|
|
|
@ -97,7 +97,7 @@ public class SelectEntriesActivity extends AegisActivity {
|
|||
ClipboardManager clipboard = (ClipboardManager) this.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clip = ClipData.newPlainText("text/plain", message);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
Toast.makeText(this, getString(R.string.errors_copied), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(this, R.string.errors_copied, Toast.LENGTH_SHORT).show();
|
||||
})
|
||||
.create());
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import android.view.LayoutInflater;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.SortCategory;
|
||||
import com.beemdevelopment.aegis.ViewMode;
|
||||
import com.beemdevelopment.aegis.db.DatabaseEntry;
|
||||
|
@ -16,6 +15,7 @@ import com.beemdevelopment.aegis.otp.TotpInfo;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -30,8 +30,8 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
|||
private int _tapToRevealTime;
|
||||
private String _groupFilter;
|
||||
private SortCategory _sortCategory;
|
||||
|
||||
private ViewMode _viewMode;
|
||||
private boolean _isPeriodUniform = true;
|
||||
|
||||
// keeps track of the viewholders that are currently bound
|
||||
private List<EntryHolder> _holders;
|
||||
|
@ -57,26 +57,43 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
|||
|
||||
public void addEntry(DatabaseEntry entry) {
|
||||
_entries.add(entry);
|
||||
if (!isEntryFiltered(entry)) {
|
||||
_shownEntries.add(entry);
|
||||
if (isEntryFiltered(entry)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int position = getItemCount() - 1;
|
||||
if (position == 0) {
|
||||
notifyDataSetChanged();
|
||||
} else {
|
||||
notifyItemInserted(position);
|
||||
boolean added = false;
|
||||
Comparator<DatabaseEntry> comparator = _sortCategory.getComparator();
|
||||
if (comparator != null) {
|
||||
// insert the entry in the correct order
|
||||
// note: this assumes that _shownEntries has already been sorted
|
||||
for (int i = 0; i < _shownEntries.size(); i++) {
|
||||
if (comparator.compare(_shownEntries.get(i), entry) > 0) {
|
||||
_shownEntries.add(i, entry);
|
||||
notifyItemInserted(i);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!added){
|
||||
_shownEntries.add(entry);
|
||||
|
||||
int position = getItemCount() - 1;
|
||||
if (position == 0) {
|
||||
notifyDataSetChanged();
|
||||
} else {
|
||||
notifyItemInserted(position);
|
||||
}
|
||||
}
|
||||
|
||||
checkPeriodUniformity();
|
||||
}
|
||||
|
||||
public void addEntries(List<DatabaseEntry> entries) {
|
||||
_entries.addAll(entries);
|
||||
for (DatabaseEntry entry : entries) {
|
||||
if (!isEntryFiltered(entry)) {
|
||||
_shownEntries.add(entry);
|
||||
}
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
updateShownEntries();
|
||||
checkPeriodUniformity();
|
||||
}
|
||||
|
||||
public void removeEntry(DatabaseEntry entry) {
|
||||
|
@ -88,6 +105,8 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
|||
_shownEntries.remove(position);
|
||||
notifyItemRemoved(position);
|
||||
}
|
||||
|
||||
checkPeriodUniformity();
|
||||
}
|
||||
|
||||
public void clearEntries() {
|
||||
|
@ -116,6 +135,8 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
|||
int position = getItemCount() - 1;
|
||||
notifyItemInserted(position);
|
||||
}
|
||||
|
||||
checkPeriodUniformity();
|
||||
}
|
||||
|
||||
private boolean isEntryFiltered(DatabaseEntry entry) {
|
||||
|
@ -145,36 +166,47 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
|||
}
|
||||
}
|
||||
|
||||
public void setGroupFilter(String group) {
|
||||
public void setGroupFilter(String group, boolean apply) {
|
||||
if (_groupFilter != null && _groupFilter.equals(group)) {
|
||||
return;
|
||||
}
|
||||
_groupFilter = group;
|
||||
if (apply) {
|
||||
updateShownEntries();
|
||||
checkPeriodUniformity();
|
||||
}
|
||||
}
|
||||
|
||||
public void setSortCategory(SortCategory category, boolean apply) {
|
||||
if (_sortCategory == category) {
|
||||
return;
|
||||
}
|
||||
_sortCategory = category;
|
||||
if (apply) {
|
||||
updateShownEntries();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateShownEntries() {
|
||||
// clear the list of shown entries first
|
||||
_shownEntries.clear();
|
||||
|
||||
// add entries back that are not filtered out
|
||||
for (DatabaseEntry entry : _entries) {
|
||||
if (!isEntryFiltered(entry)) {
|
||||
_shownEntries.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
sortList(_sortCategory);
|
||||
// sort the remaining list of entries
|
||||
Comparator<DatabaseEntry> comparator = _sortCategory.getComparator();
|
||||
if (comparator != null) {
|
||||
Collections.sort(_shownEntries, comparator);
|
||||
}
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setSortCategory(SortCategory sortCategory) {
|
||||
if (_sortCategory != sortCategory && sortCategory != SortCategory.CUSTOM) {
|
||||
sortList(sortCategory);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
_sortCategory = sortCategory;
|
||||
}
|
||||
|
||||
private void sortList(SortCategory sortCategory) {
|
||||
Collections.sort(_shownEntries, SortCategory.getComparator(sortCategory));
|
||||
|
||||
if (SortCategory.isReversed(sortCategory)) {
|
||||
Collections.reverse(_shownEntries);
|
||||
}
|
||||
}
|
||||
|
||||
public void setViewMode(ViewMode viewMode) {
|
||||
_viewMode = viewMode;
|
||||
}
|
||||
|
@ -211,16 +243,14 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
|||
}
|
||||
|
||||
@Override
|
||||
public EntryHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_entry, parent, false);
|
||||
if (_viewMode == ViewMode.NORMAL) {
|
||||
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_entry, parent, false);
|
||||
} else if (_viewMode == ViewMode.COMPACT) {
|
||||
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_entry_compact, parent, false);
|
||||
} else if (_viewMode == ViewMode.SMALL) {
|
||||
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_entry_small, parent, false);
|
||||
}
|
||||
public int getItemViewType(int position) {
|
||||
return _viewMode.getLayoutId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntryHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||
View view = inflater.inflate(_viewMode.getLayoutId(), parent, false);
|
||||
return new EntryHolder(view);
|
||||
}
|
||||
|
||||
|
@ -236,9 +266,6 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
|||
boolean showProgress = !isPeriodUniform() && entry.getInfo() instanceof TotpInfo;
|
||||
holder.setData(entry, _showAccountName, showProgress, _tapToReveal);
|
||||
holder.setTapToRevealTime(_tapToRevealTime);
|
||||
if (showProgress) {
|
||||
holder.startRefreshLoop();
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
|
@ -280,6 +307,20 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
|||
_holders.add(holder);
|
||||
}
|
||||
|
||||
private void checkPeriodUniformity() {
|
||||
boolean uniform = isPeriodUniform();
|
||||
if (uniform == _isPeriodUniform) {
|
||||
return;
|
||||
}
|
||||
_isPeriodUniform = uniform;
|
||||
|
||||
for (EntryHolder holder : _holders) {
|
||||
holder.setShowProgress(!_isPeriodUniform);
|
||||
}
|
||||
|
||||
_listener.onPeriodUniformityChanged(_isPeriodUniform);
|
||||
}
|
||||
|
||||
public int getUniformPeriod() {
|
||||
List<TotpInfo> infos = new ArrayList<>();
|
||||
for (DatabaseEntry entry : _shownEntries) {
|
||||
|
@ -318,5 +359,6 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
|||
void onEntryMove(DatabaseEntry entry1, DatabaseEntry entry2);
|
||||
void onEntryDrop(DatabaseEntry entry);
|
||||
void onEntryChange(DatabaseEntry entry);
|
||||
void onPeriodUniformityChanged(boolean uniform);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,11 +27,8 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
|||
private ImageView _profileDrawable;
|
||||
private DatabaseEntry _entry;
|
||||
private ImageView _buttonRefresh;
|
||||
|
||||
private View _entryDivider;
|
||||
|
||||
private View _currentView;
|
||||
|
||||
private boolean _hidden;
|
||||
private int _tapToRevealTime;
|
||||
|
||||
|
@ -42,7 +39,6 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
|||
|
||||
public EntryHolder(final View view) {
|
||||
super(view);
|
||||
_currentView = view;
|
||||
|
||||
_profileName = view.findViewById(R.id.profile_account_name);
|
||||
_profileCode = view.findViewById(R.id.profile_code);
|
||||
|
@ -80,14 +76,7 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
|||
_hidden = hidden;
|
||||
|
||||
// only show the progress bar if there is no uniform period and the entry type is TotpInfo
|
||||
_progressBar.setVisibility(showProgress ? View.VISIBLE : View.GONE);
|
||||
if (showProgress) {
|
||||
_progressBar.setPeriod(((TotpInfo)entry.getInfo()).getPeriod());
|
||||
|
||||
if (_entryDivider != null) {
|
||||
_entryDivider.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
setShowProgress(showProgress);
|
||||
|
||||
// only show the button if this entry is of type HotpInfo
|
||||
_buttonRefresh.setVisibility(entry.getInfo() instanceof HotpInfo ? View.VISIBLE : View.GONE);
|
||||
|
@ -125,6 +114,25 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
|||
_buttonRefresh.setOnClickListener(listener);
|
||||
}
|
||||
|
||||
public void setShowProgress(boolean showProgress) {
|
||||
if (_entry.getInfo() instanceof HotpInfo) {
|
||||
showProgress = false;
|
||||
}
|
||||
|
||||
_progressBar.setVisibility(showProgress ? View.VISIBLE : View.GONE);
|
||||
if (showProgress) {
|
||||
_progressBar.setPeriod(((TotpInfo) _entry.getInfo()).getPeriod());
|
||||
|
||||
if (_entryDivider != null) {
|
||||
_entryDivider.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
startRefreshLoop();
|
||||
} else {
|
||||
stopRefreshLoop();
|
||||
}
|
||||
}
|
||||
|
||||
public void startRefreshLoop() {
|
||||
_refresher.start();
|
||||
}
|
||||
|
@ -162,7 +170,7 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
|||
}
|
||||
|
||||
private void hideCode() {
|
||||
_profileCode.setText(_currentView.getContext().getResources().getString(R.string.tap_to_reveal));
|
||||
_profileCode.setText(R.string.tap_to_reveal);
|
||||
_hidden = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
private Listener _listener;
|
||||
private SimpleItemTouchHelperCallback _touchCallback;
|
||||
|
||||
private RecyclerView _rvKeyProfiles;
|
||||
private RecyclerView _recyclerView;
|
||||
private PeriodProgressBar _progressBar;
|
||||
private boolean _showProgress;
|
||||
|
||||
|
@ -44,12 +44,11 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_entry_list_view, container, false);
|
||||
|
||||
_progressBar = view.findViewById(R.id.progressBar);
|
||||
|
||||
// set up the recycler view
|
||||
_rvKeyProfiles = view.findViewById(R.id.rvKeyProfiles);
|
||||
_rvKeyProfiles.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
_recyclerView = view.findViewById(R.id.rvKeyProfiles);
|
||||
_recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
|
@ -58,15 +57,15 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
});
|
||||
|
||||
LinearLayoutManager mLayoutManager = new LinearLayoutManager(view.getContext());
|
||||
_rvKeyProfiles.setLayoutManager(mLayoutManager);
|
||||
_recyclerView.setLayoutManager(mLayoutManager);
|
||||
_touchCallback = new SimpleItemTouchHelperCallback(_adapter);
|
||||
ItemTouchHelper touchHelper = new ItemTouchHelper(_touchCallback);
|
||||
touchHelper.attachToRecyclerView(_rvKeyProfiles);
|
||||
_rvKeyProfiles.setAdapter(_adapter);
|
||||
touchHelper.attachToRecyclerView(_recyclerView);
|
||||
_recyclerView.setAdapter(_adapter);
|
||||
|
||||
int resId = R.anim.layout_animation_fall_down;
|
||||
LayoutAnimationController animation = AnimationUtils.loadLayoutAnimation(getContext(), resId);
|
||||
_rvKeyProfiles.setLayoutAnimation(animation);
|
||||
_recyclerView.setLayoutAnimation(animation);
|
||||
|
||||
_refresher = new UiRefresher(new UiRefresher.Listener() {
|
||||
@Override
|
||||
|
@ -83,23 +82,26 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
return view;
|
||||
}
|
||||
|
||||
public void setGroupFilter(String group) {
|
||||
_adapter.setGroupFilter(group);
|
||||
public void setGroupFilter(String group, boolean apply) {
|
||||
_touchCallback.setIsLongPressDragEnabled(group == null);
|
||||
checkPeriodUniformity();
|
||||
_adapter.setGroupFilter(group, apply);
|
||||
|
||||
runLayoutAnimation(_rvKeyProfiles);
|
||||
if (apply) {
|
||||
runEntriesAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
public void setSortCategory(SortCategory sortCategory) {
|
||||
public void setSortCategory(SortCategory sortCategory, boolean apply) {
|
||||
_touchCallback.setIsLongPressDragEnabled(sortCategory == SortCategory.CUSTOM);
|
||||
_adapter.setSortCategory(sortCategory, apply);
|
||||
|
||||
_adapter.setSortCategory(sortCategory);
|
||||
runLayoutAnimation(_rvKeyProfiles);
|
||||
if (apply) {
|
||||
runEntriesAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
public void setViewMode(ViewMode viewMode) {
|
||||
_adapter.setViewMode(viewMode);
|
||||
public void setViewMode(ViewMode mode) {
|
||||
_adapter.setViewMode(mode);
|
||||
}
|
||||
|
||||
public void refresh(boolean hard) {
|
||||
|
@ -109,33 +111,6 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
_adapter.refresh(hard);
|
||||
}
|
||||
|
||||
private void checkPeriodUniformity() {
|
||||
boolean uniform = _adapter.isPeriodUniform();
|
||||
if (uniform == _showProgress) {
|
||||
return;
|
||||
}
|
||||
_showProgress = uniform;
|
||||
|
||||
if (_showProgress) {
|
||||
_progressBar.setVisibility(View.VISIBLE);
|
||||
_progressBar.setPeriod(_adapter.getUniformPeriod());
|
||||
startRefreshLoop();
|
||||
} else {
|
||||
_progressBar.setVisibility(View.GONE);
|
||||
stopRefreshLoop();
|
||||
}
|
||||
}
|
||||
|
||||
private void startRefreshLoop() {
|
||||
refresh(true);
|
||||
_refresher.start();
|
||||
}
|
||||
|
||||
private void stopRefreshLoop() {
|
||||
refresh(true);
|
||||
_refresher.stop();
|
||||
}
|
||||
|
||||
public void setListener(Listener listener) {
|
||||
_listener = listener;
|
||||
}
|
||||
|
@ -165,6 +140,19 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
_listener.onEntryChange(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPeriodUniformityChanged(boolean isUniform) {
|
||||
_showProgress = isUniform;
|
||||
if (_showProgress) {
|
||||
_progressBar.setVisibility(View.VISIBLE);
|
||||
_progressBar.setPeriod(_adapter.getUniformPeriod());
|
||||
_refresher.start();
|
||||
} else {
|
||||
_progressBar.setVisibility(View.GONE);
|
||||
_refresher.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public void setShowAccountName(boolean showAccountName) {
|
||||
_adapter.setShowAccountName(showAccountName);
|
||||
}
|
||||
|
@ -179,37 +167,31 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
|
||||
public void addEntry(DatabaseEntry entry) {
|
||||
_adapter.addEntry(entry);
|
||||
checkPeriodUniformity();
|
||||
}
|
||||
|
||||
public void addEntries(List<DatabaseEntry> entries) {
|
||||
_adapter.addEntries(entries);
|
||||
checkPeriodUniformity();
|
||||
}
|
||||
|
||||
public void removeEntry(DatabaseEntry entry) {
|
||||
_adapter.removeEntry(entry);
|
||||
checkPeriodUniformity();
|
||||
}
|
||||
|
||||
public void clearEntries() {
|
||||
_adapter.clearEntries();
|
||||
checkPeriodUniformity();
|
||||
}
|
||||
|
||||
public void replaceEntry(DatabaseEntry entry) {
|
||||
_adapter.replaceEntry(entry);
|
||||
checkPeriodUniformity();
|
||||
}
|
||||
|
||||
private void runLayoutAnimation(final RecyclerView recyclerView) {
|
||||
final Context context = recyclerView.getContext();
|
||||
public void runEntriesAnimation() {
|
||||
final Context context = _recyclerView.getContext();
|
||||
final LayoutAnimationController controller =
|
||||
AnimationUtils.loadLayoutAnimation(context, R.anim.layout_animation_fall_down);
|
||||
|
||||
recyclerView.setLayoutAnimation(controller);
|
||||
recyclerView.getAdapter().notifyDataSetChanged();
|
||||
recyclerView.scheduleLayoutAnimation();
|
||||
_recyclerView.setLayoutAnimation(controller);
|
||||
_recyclerView.scheduleLayoutAnimation();
|
||||
}
|
||||
|
||||
public interface Listener {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue