/* * Copyright (C) 2015 Federico Iosue (federico.iosue@gmail.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package it.feio.android.omninotes; import android.Manifest; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.SuppressLint; import android.app.Activity; import android.app.SearchManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.drawable.AnimationDrawable; import android.os.*; import android.support.v4.util.Pair; import android.support.v4.view.GravityCompat; import android.support.v4.view.MenuItemCompat; import android.support.v7.view.ActionMode; import android.support.v7.widget.SearchView; import android.support.v7.widget.SearchView.OnQueryTextListener; import android.text.Html; import android.text.TextUtils; import android.util.Log; import android.view.*; import android.view.inputmethod.EditorInfo; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import butterknife.Bind; import butterknife.ButterKnife; import com.afollestad.materialdialogs.MaterialDialog; import com.neopixl.pixlui.components.textview.TextView; import com.nhaarman.listviewanimations.itemmanipulation.DynamicListView; import com.pnikosis.materialishprogress.ProgressWheel; import de.greenrobot.event.EventBus; import de.keyboardsurfer.android.widget.crouton.Crouton; import de.keyboardsurfer.android.widget.crouton.Style; import it.feio.android.omninotes.async.bus.CategoriesUpdatedEvent; import it.feio.android.omninotes.async.bus.NavigationUpdatedNavDrawerClosedEvent; import it.feio.android.omninotes.async.bus.NotesLoadedEvent; import it.feio.android.omninotes.async.bus.NotesMergeEvent; import it.feio.android.omninotes.async.notes.*; import it.feio.android.omninotes.db.DbHelper; import it.feio.android.omninotes.helpers.NotesHelper; import it.feio.android.omninotes.helpers.PermissionsHelper; import it.feio.android.omninotes.models.*; import it.feio.android.omninotes.models.adapters.NavDrawerCategoryAdapter; import it.feio.android.omninotes.models.adapters.NoteAdapter; import it.feio.android.omninotes.models.holders.NoteViewHolder; import it.feio.android.omninotes.models.listeners.OnViewTouchedListener; import it.feio.android.omninotes.models.views.Fab; import it.feio.android.omninotes.models.views.InterceptorLinearLayout; import it.feio.android.omninotes.utils.*; import it.feio.android.omninotes.utils.Display; import it.feio.android.pixlui.links.UrlCompleter; import it.feio.android.simplegallery.util.BitmapUtils; import org.apache.commons.lang.ObjectUtils; import java.util.*; import static android.support.v4.view.ViewCompat.animate; public class ListFragment extends BaseFragment implements OnViewTouchedListener, UndoBarController.UndoListener { private static final int REQUEST_CODE_CATEGORY = 1; private static final int REQUEST_CODE_CATEGORY_NOTES = 2; private static final int REQUEST_CODE_ADD_ALARMS = 3; @Bind(R.id.list_root) InterceptorLinearLayout listRoot; @Bind(R.id.list) DynamicListView list; @Bind(R.id.search_layout) View searchLayout; @Bind(R.id.search_query) android.widget.TextView searchQueryView; @Bind(R.id.search_cancel) ImageView searchCancel; @Bind(R.id.empty_list) TextView empyListItem; @Bind(R.id.expanded_image) ImageView expandedImageView; @Bind(R.id.fab) View fabView; @Bind(R.id.undobar) View undoBarView; @Bind(R.id.progress_wheel) ProgressWheel progress_wheel; @Bind(R.id.snackbar_placeholder) View snackBarPlaceholder; NoteViewHolder noteViewHolder; private List<Note> selectedNotes = new ArrayList<>(); private SearchView searchView; private MenuItem searchMenuItem; private Menu menu; private AnimationDrawable jinglesAnimation; private int listViewPosition; private int listViewPositionOffset = 16; private boolean sendToArchive; private SharedPreferences prefs; private ListFragment mFragment; private android.support.v7.view.ActionMode actionMode; private boolean keepActionMode = false; private TextView listFooter; // Undo archive/trash private boolean undoTrash = false; private boolean undoArchive = false; private boolean undoCategorize = false; private Category undoCategorizeCategory = null; private SortedMap<Integer, Note> undoNotesMap = new TreeMap<>(); // Used to remember removed categories from notes private Map<Note, Category> undoCategoryMap = new HashMap<>(); // Used to remember archived state from notes private Map<Note, Boolean> undoArchivedMap = new HashMap<>(); // Search variables private String searchQuery; private String searchQueryInstant; private String searchTags; private boolean goBackOnToggleSearchLabel = false; private boolean searchLabelActive = false; private NoteAdapter listAdapter; private UndoBarController ubc; private Fab fab; private MainActivity mainActivity; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mFragment = this; setHasOptionsMenu(true); setRetainInstance(false); } @Override public void onStart() { super.onStart(); EventBus.getDefault().register(this, 1); } @Override public void onStop() { super.onStop(); EventBus.getDefault().unregister(this); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (savedInstanceState != null) { if (savedInstanceState.containsKey("listViewPosition")) { listViewPosition = savedInstanceState.getInt("listViewPosition"); listViewPositionOffset = savedInstanceState.getInt("listViewPositionOffset"); searchQuery = savedInstanceState.getString("searchQuery"); searchTags = savedInstanceState.getString("searchTags"); } keepActionMode = false; } View view = inflater.inflate(R.layout.fragment_list, container, false); ButterKnife.bind(this, view); return view; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mainActivity = (MainActivity) getActivity(); prefs = mainActivity.prefs; if (savedInstanceState != null) { mainActivity.navigationTmp = savedInstanceState.getString("navigationTmp"); } init(); } private void init() { initEasterEgg(); initListView(); ubc = new UndoBarController(undoBarView, this); initNotesList(mainActivity.getIntent()); initFab(); initTitle(); // Restores again DefaultSharedPreferences too reload in case of data erased from Settings prefs = mainActivity.getSharedPreferences(Constants.PREFS_NAME, Context.MODE_MULTI_PROCESS); requestReadStoragePermission(); } private void requestReadStoragePermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { PermissionsHelper.requestPermission(mainActivity, Manifest.permission.READ_EXTERNAL_STORAGE, R .string.permission_external_storage, snackBarPlaceholder, () -> {}); } } private void initFab() { fab = new Fab(fabView, list, prefs.getBoolean(Constants.PREF_FAB_EXPANSION_BEHAVIOR, false)); fab.setOnFabItemClickedListener(id -> { View v = mainActivity.findViewById(id); switch (id) { case R.id.fab_expand_menu_button: editNote(new Note(), v); break; case R.id.fab_note: editNote(new Note(), v); break; case R.id.fab_camera: Intent i = mainActivity.getIntent(); i.setAction(Constants.ACTION_TAKE_PHOTO); mainActivity.setIntent(i); editNote(new Note(), v); break; case R.id.fab_checklist: Note note = new Note(); note.setChecklist(true); editNote(note, v); break; } }); fab.setOverlay(R.color.white_overlay); } boolean closeFab() { if (fab != null && fab.isExpanded()) { fab.performToggle(); return true; } return false; } /** * Activity title initialization based on navigation */ private void initTitle() { String[] navigationList = getResources().getStringArray(R.array.navigation_list); String[] navigationListCodes = getResources().getStringArray(R.array.navigation_list_codes); String navigation = mainActivity.navigationTmp != null ? mainActivity.navigationTmp : prefs.getString (Constants.PREF_NAVIGATION, navigationListCodes[0]); int index = Arrays.asList(navigationListCodes).indexOf(navigation); String title; // If is a traditional navigation item if (index >= 0 && index < navigationListCodes.length) { title = navigationList[index]; } else { Category category = DbHelper.getInstance().getCategory(Long.parseLong(navigation)); title = category != null ? category.getName() : ""; } title = title == null ? getString(R.string.title_activity_list) : title; mainActivity.setActionBarTitle(title); } /** * Starts a little animation on Mr.Jingles! */ private void initEasterEgg() { empyListItem.setOnClickListener(v -> { if (jinglesAnimation == null) { jinglesAnimation = (AnimationDrawable) empyListItem.getCompoundDrawables()[1]; empyListItem.post(() -> { if (jinglesAnimation != null) jinglesAnimation.start(); }); } else { stopJingles(); } }); } private void stopJingles() { if (jinglesAnimation != null) { jinglesAnimation.stop(); jinglesAnimation = null; empyListItem.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.jingles_animation, 0, 0); } } @Override public void onPause() { super.onPause(); searchQueryInstant = searchQuery; stopJingles(); Crouton.cancelAllCroutons(); closeFab(); if (!keepActionMode) { commitPending(); list.clearChoices(); if (getActionMode() != null) { getActionMode().finish(); } } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); refreshListScrollPosition(); outState.putInt("listViewPosition", listViewPosition); outState.putInt("listViewPositionOffset", listViewPositionOffset); outState.putString("searchQuery", searchQuery); outState.putString("searchTags", searchTags); } private void refreshListScrollPosition() { if (list != null) { listViewPosition = list.getFirstVisiblePosition(); View v = list.getChildAt(0); listViewPositionOffset = (v == null) ? (int) getResources().getDimension(R.dimen.vertical_margin) : v.getTop(); } } @SuppressWarnings("static-access") @Override public void onResume() { super.onResume(); if (Intent.ACTION_SEARCH.equals(mainActivity.getIntent().getAction())) { initNotesList(mainActivity.getIntent()); } } private final class ModeCallback implements android.support.v7.view.ActionMode.Callback { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { // Inflate the menu for the CAB MenuInflater inflater = mode.getMenuInflater(); inflater.inflate(R.menu.menu_list, menu); actionMode = mode; fab.setAllowed(isFabAllowed()); fab.hideFab(); return true; } @Override public void onDestroyActionMode(ActionMode mode) { // Here you can make any necessary updates to the activity when // the CAB is removed. By default, selected items are // deselected/unchecked. for (int i = 0; i < listAdapter.getSelectedItems().size(); i++) { int key = listAdapter.getSelectedItems().keyAt(i); View v = list.getChildAt(key - list.getFirstVisiblePosition()); if (listAdapter.getCount() > key && listAdapter.getItem(key) != null && v != null) { listAdapter.restoreDrawable(listAdapter.getItem(key), v.findViewById(R.id.card_layout)); } } // Clears data structures selectedNotes.clear(); listAdapter.clearSelectedItems(); list.clearChoices(); fab.setAllowed(isFabAllowed(true)); if (undoNotesMap.size() == 0) { fab.showFab(); } actionMode = null; Log.d(Constants.TAG, "Closed multiselection contextual menu"); } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { prepareActionModeMenu(); return true; } @Override public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) { Integer[] protectedActions = {R.id.menu_select_all, R.id.menu_merge}; if (!Arrays.asList(protectedActions).contains(item.getItemId())) { mainActivity.requestPassword(mainActivity, getSelectedNotes(), passwordConfirmed -> { if (passwordConfirmed) { performAction(item, mode); } }); } else { performAction(item, mode); } return true; } } public void finishActionMode() { if (getActionMode() != null) { getActionMode().finish(); } } /** * Manage check/uncheck of notes in list during multiple selection phase */ private void toggleListViewItem(View view, int position) { Note note = listAdapter.getItem(position); LinearLayout cardLayout = (LinearLayout) view.findViewById(R.id.card_layout); if (!getSelectedNotes().contains(note)) { getSelectedNotes().add(note); listAdapter.addSelectedItem(position); cardLayout.setBackgroundColor(getResources().getColor(R.color.list_bg_selected)); } else { getSelectedNotes().remove(note); listAdapter.removeSelectedItem(position); listAdapter.restoreDrawable(note, cardLayout); } prepareActionModeMenu(); // Close CAB if no items are selected if (getSelectedNotes().size() == 0) { finishActionMode(); } } /** * Notes list initialization. Data, actions and callback are defined here. */ private void initListView() { list.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); list.setItemsCanFocus(false); // If device runs KitKat a footer is added to list to avoid // navigation bar transparency covering items if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { int navBarHeight = Display.getNavigationBarHeightKitkat(mainActivity); listFooter = new TextView(mainActivity.getApplicationContext()); listFooter.setHeight(navBarHeight); // To avoid useless events on footer listFooter.setOnClickListener(null); list.addFooterView(listFooter); } // Note long click to start CAB mode list.setOnItemLongClickListener((arg0, view, position, arg3) -> { if (view.equals(listFooter)) return true; if (getActionMode() != null) { return false; } // Start the CAB using the ActionMode.Callback defined above mainActivity.startSupportActionMode(new ModeCallback()); toggleListViewItem(view, position); setCabTitle(); return true; }); // Note single click listener managed by the activity itself list.setOnItemClickListener((arg0, view, position, arg3) -> { if (view.equals(listFooter)) return; if (getActionMode() == null) { editNote(listAdapter.getItem(position), view); return; } // If in CAB mode toggleListViewItem(view, position); setCabTitle(); }); listRoot.setOnViewTouchedListener(this); } /** * Retrieves from the single listview note item the element to be zoomed when opening a note */ private ImageView getZoomListItemView(View view, Note note) { if (expandedImageView != null) { View targetView = null; if (note.getAttachmentsList().size() > 0) { targetView = view.findViewById(R.id.attachmentThumbnail); } if (targetView == null && note.getCategory() != null) { targetView = view.findViewById(R.id.category_marker); } if (targetView == null) { targetView = new ImageView(mainActivity); targetView.setBackgroundColor(Color.WHITE); } targetView.setDrawingCacheEnabled(true); targetView.buildDrawingCache(); Bitmap bmp = targetView.getDrawingCache(); expandedImageView.setBackgroundColor(BitmapUtils.getDominantColor(bmp)); } return expandedImageView; } /** * Listener that fires note opening once the zooming animation is finished */ private AnimatorListenerAdapter buildAnimatorListenerAdapter(final Note note) { return new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { editNote2(note); } }; } @Override public void onViewTouchOccurred(MotionEvent ev) { Log.v(Constants.TAG, "Notes list: onViewTouchOccurred " + ev.getAction()); commitPending(); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.menu_list, menu); super.onCreateOptionsMenu(menu, inflater); this.menu = menu; initSearchView(menu); } private void initSortingSubmenu() { final String[] arrayDb = getResources().getStringArray(R.array.sortable_columns); final String[] arrayDialog = getResources().getStringArray(R.array.sortable_columns_human_readable); int selected = Arrays.asList(arrayDb).indexOf(prefs.getString(Constants.PREF_SORTING_COLUMN, arrayDb[0])); SubMenu sortMenu = this.menu.findItem(R.id.menu_sort).getSubMenu(); for (int i = 0; i < arrayDialog.length; i++) { if (sortMenu.findItem(i) == null) { sortMenu.add(Constants.MENU_SORT_GROUP_ID, i, i, arrayDialog[i]); } if (i == selected) sortMenu.getItem(i).setChecked(true); } sortMenu.setGroupCheckable(Constants.MENU_SORT_GROUP_ID, true, true); } @Override public void onPrepareOptionsMenu(Menu menu) { setActionItemsVisibility(menu, false); } private void prepareActionModeMenu() { Menu menu = getActionMode().getMenu(); int navigation = Navigation.getNavigation(); boolean showArchive = navigation == Navigation.NOTES || navigation == Navigation.REMINDERS || navigation == Navigation.UNCATEGORIZED || navigation == Navigation.CATEGORY; boolean showUnarchive = navigation == Navigation.ARCHIVE || navigation == Navigation.UNCATEGORIZED || navigation == Navigation.CATEGORY; if (navigation == Navigation.TRASH) { menu.findItem(R.id.menu_untrash).setVisible(true); menu.findItem(R.id.menu_delete).setVisible(true); } else { if (getSelectedCount() == 1) { menu.findItem(R.id.menu_share).setVisible(true); menu.findItem(R.id.menu_merge).setVisible(false); menu.findItem(R.id.menu_archive) .setVisible(showArchive && !getSelectedNotes().get(0).isArchived ()); menu.findItem(R.id.menu_unarchive) .setVisible(showUnarchive && getSelectedNotes().get(0).isArchived ()); } else { menu.findItem(R.id.menu_share).setVisible(false); menu.findItem(R.id.menu_merge).setVisible(true); menu.findItem(R.id.menu_archive).setVisible(showArchive); menu.findItem(R.id.menu_unarchive).setVisible(showUnarchive); } menu.findItem(R.id.menu_add_reminder).setVisible(true); menu.findItem(R.id.menu_category).setVisible(true); menu.findItem(R.id.menu_tags).setVisible(true); menu.findItem(R.id.menu_trash).setVisible(true); } menu.findItem(R.id.menu_select_all).setVisible(true); setCabTitle(); } private int getSelectedCount() { return getSelectedNotes().size(); } private void setCabTitle() { if (getActionMode() != null) { int title = getSelectedCount(); getActionMode().setTitle(String.valueOf(title)); } } /** * SearchView initialization. It's a little complex because it's not using SearchManager but is implementing on its * own. */ @SuppressLint("NewApi") private void initSearchView(final Menu menu) { // Prevents some mysterious NullPointer on app fast-switching if (mainActivity == null) return; // Save item as class attribute to make it collapse on drawer opening searchMenuItem = menu.findItem(R.id.menu_search); // Associate searchable configuration with the SearchView SearchManager searchManager = (SearchManager) mainActivity.getSystemService(Context.SEARCH_SERVICE); searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.menu_search)); searchView.setSearchableInfo(searchManager.getSearchableInfo(mainActivity.getComponentName())); searchView.setImeOptions(EditorInfo.IME_ACTION_SEARCH); // Expands the widget hiding other actionbar icons searchView.setOnQueryTextFocusChangeListener((v, hasFocus) -> setActionItemsVisibility(menu, hasFocus)); MenuItemCompat.setOnActionExpandListener(searchMenuItem, new MenuItemCompat.OnActionExpandListener() { boolean searchPerformed = false; @Override public boolean onMenuItemActionCollapse(MenuItem item) { // Reinitialize notes list to all notes when search is collapsed searchQuery = null; if (searchLayout.getVisibility() == View.VISIBLE) { toggleSearchLabel(false); } mainActivity.getIntent().setAction(Intent.ACTION_MAIN); initNotesList(mainActivity.getIntent()); mainActivity.supportInvalidateOptionsMenu(); return true; } @Override public boolean onMenuItemActionExpand(MenuItem item) { searchView.setOnQueryTextListener(new OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String arg0) { return prefs.getBoolean("settings_instant_search", false); } @Override public boolean onQueryTextChange(String pattern) { if (prefs.getBoolean("settings_instant_search", false) && searchLayout != null && searchPerformed && mFragment.isAdded()) { searchTags = null; searchQuery = pattern; NoteLoaderTask.getInstance().execute("getNotesByPattern", pattern); return true; } else { searchPerformed = true; return false; } } }); return true; } }); } private void setActionItemsVisibility(Menu menu, boolean searchViewHasFocus) { boolean drawerOpen = mainActivity.getDrawerLayout() != null && mainActivity.getDrawerLayout().isDrawerOpen (GravityCompat.START); boolean expandedView = prefs.getBoolean(Constants.PREF_EXPANDED_VIEW, true); int navigation = Navigation.getNavigation(); boolean navigationReminders = navigation == Navigation.REMINDERS; boolean navigationArchive = navigation == Navigation.ARCHIVE; boolean navigationTrash = navigation == Navigation.TRASH; boolean navigationCategory = navigation == Navigation.CATEGORY; boolean filterPastReminders = prefs.getBoolean(Constants.PREF_FILTER_PAST_REMINDERS, true); boolean filterArchivedInCategory = navigationCategory && prefs.getBoolean(Constants .PREF_FILTER_ARCHIVED_IN_CATEGORIES + Navigation.getCategory(), false); if (isFabAllowed()) { fab.setAllowed(true); fab.showFab(); } else { fab.setAllowed(false); fab.hideFab(); } menu.findItem(R.id.menu_search).setVisible(!drawerOpen); menu.findItem(R.id.menu_filter).setVisible(!drawerOpen && !filterPastReminders && navigationReminders && !searchViewHasFocus); menu.findItem(R.id.menu_filter_remove).setVisible(!drawerOpen && filterPastReminders && navigationReminders && !searchViewHasFocus); menu.findItem(R.id.menu_filter_category).setVisible(!drawerOpen && !filterArchivedInCategory && navigationCategory && !searchViewHasFocus); menu.findItem(R.id.menu_filter_category_remove).setVisible(!drawerOpen && filterArchivedInCategory && navigationCategory && !searchViewHasFocus); menu.findItem(R.id.menu_sort).setVisible(!drawerOpen && !navigationReminders && !searchViewHasFocus); menu.findItem(R.id.menu_expanded_view).setVisible(!drawerOpen && !expandedView && !searchViewHasFocus); menu.findItem(R.id.menu_contracted_view).setVisible(!drawerOpen && expandedView && !searchViewHasFocus); menu.findItem(R.id.menu_empty_trash).setVisible(!drawerOpen && navigationTrash); menu.findItem(R.id.menu_tags).setVisible(searchViewHasFocus); } @Override public boolean onOptionsItemSelected(final MenuItem item) { Integer[] protectedActions = {R.id.menu_empty_trash}; if (Arrays.asList(protectedActions).contains(item.getItemId())) { mainActivity.requestPassword(mainActivity, getSelectedNotes(), passwordConfirmed -> { if (passwordConfirmed) { performAction(item, null); } }); } else { performAction(item, null); } return super.onOptionsItemSelected(item); } /** * Performs one of the ActionBar button's actions after checked notes protection */ public boolean performAction(MenuItem item, ActionMode actionMode) { if (actionMode == null) { switch (item.getItemId()) { case android.R.id.home: if (mainActivity.getDrawerLayout().isDrawerOpen(GravityCompat.START)) { mainActivity.getDrawerLayout().closeDrawer(GravityCompat.START); } else { mainActivity.getDrawerLayout().openDrawer(GravityCompat.START); } break; case R.id.menu_filter: filterReminders(true); break; case R.id.menu_filter_remove: filterReminders(false); break; case R.id.menu_filter_category: filterCategoryArchived(true); break; case R.id.menu_filter_category_remove: filterCategoryArchived(false); break; case R.id.menu_tags: filterByTags(); break; case R.id.menu_sort: initSortingSubmenu(); break; case R.id.menu_expanded_view: switchNotesView(); break; case R.id.menu_contracted_view: switchNotesView(); break; case R.id.menu_empty_trash: emptyTrash(); break; } } else { switch (item.getItemId()) { case R.id.menu_category: categorizeNotes(); break; case R.id.menu_tags: tagNotes(); break; case R.id.menu_share: share(); break; case R.id.menu_merge: merge(); break; case R.id.menu_archive: archiveNotes(true); break; case R.id.menu_unarchive: archiveNotes(false); break; case R.id.menu_trash: trashNotes(true); break; case R.id.menu_untrash: trashNotes(false); break; case R.id.menu_delete: deleteNotes(); break; case R.id.menu_select_all: selectAllNotes(); break; case R.id.menu_add_reminder: addReminders(); break; // case R.id.menu_synchronize: // synchronizeSelectedNotes(); // break; } } checkSortActionPerformed(item); return super.onOptionsItemSelected(item); } private void addReminders() { Intent intent = new Intent(OmniNotes.getAppContext(), SnoozeActivity.class); intent.setAction(Constants.ACTION_POSTPONE); intent.putExtra(Constants.INTENT_NOTE, selectedNotes.toArray(new Note[selectedNotes.size()])); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivityForResult(intent, REQUEST_CODE_ADD_ALARMS); } private void switchNotesView() { boolean expandedView = prefs.getBoolean(Constants.PREF_EXPANDED_VIEW, true); prefs.edit().putBoolean(Constants.PREF_EXPANDED_VIEW, !expandedView).commit(); // Change list view initNotesList(mainActivity.getIntent()); // Called to switch menu voices mainActivity.supportInvalidateOptionsMenu(); } void editNote(final Note note, final View view) { if (note.isLocked() && !prefs.getBoolean("settings_password_access", false)) { PasswordHelper.requestPassword(mainActivity, passwordConfirmed -> { if (passwordConfirmed) { note.setPasswordChecked(true); AnimationsHelper.zoomListItem(mainActivity, view, getZoomListItemView(view, note), listRoot, buildAnimatorListenerAdapter(note)); } }); } else { AnimationsHelper.zoomListItem(mainActivity, view, getZoomListItemView(view, note), listRoot, buildAnimatorListenerAdapter(note)); } } void editNote2(Note note) { if (note.get_id() == null) { Log.d(Constants.TAG, "Adding new note"); // if navigation is a category it will be set into note try { if (Navigation.checkNavigation(Navigation.CATEGORY) || !TextUtils.isEmpty(mainActivity.navigationTmp)) { String categoryId = (String) ObjectUtils.defaultIfNull(mainActivity.navigationTmp, Navigation.getCategory().toString()); note.setCategory(DbHelper.getInstance().getCategory(Long.parseLong(categoryId))); } } catch (NumberFormatException e) { Log.v(Constants.TAG, "Maybe was not a category!"); } } else { Log.d(Constants.TAG, "Editing note with id: " + note.get_id()); } // Current list scrolling position is saved to be restored later refreshListScrollPosition(); // Fragments replacing mainActivity.switchToDetail(note); } @Override public// Used to show a Crouton dialog after saved (or tried to) a note void onActivityResult(int requestCode, final int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); switch (requestCode) { case REQUEST_CODE_CATEGORY: // Dialog retarded to give time to activity's views of being completely initialized // The dialog style is choosen depending on result code switch (resultCode) { case Activity.RESULT_OK: mainActivity.showMessage(R.string.category_saved, ONStyle.CONFIRM); EventBus.getDefault().post(new CategoriesUpdatedEvent()); break; case Activity.RESULT_FIRST_USER: mainActivity.showMessage(R.string.category_deleted, ONStyle.ALERT); break; default: break; } break; case REQUEST_CODE_CATEGORY_NOTES: if (intent != null) { Category tag = intent.getParcelableExtra(Constants.INTENT_CATEGORY); categorizeNotesExecute(tag); } break; case REQUEST_CODE_ADD_ALARMS: list.clearChoices(); selectedNotes.clear(); finishActionMode(); list.invalidateViews(); break; default: break; } } private void checkSortActionPerformed(MenuItem item) { if (item.getGroupId() == Constants.MENU_SORT_GROUP_ID) { final String[] arrayDb = getResources().getStringArray(R.array.sortable_columns); prefs.edit().putString(Constants.PREF_SORTING_COLUMN, arrayDb[item.getOrder()]).apply(); initNotesList(mainActivity.getIntent()); // Resets list scrolling position listViewPositionOffset = 16; listViewPosition = 0; restoreListScrollPosition(); toggleSearchLabel(false); // Updates app widgets BaseActivity.notifyAppWidgets(mainActivity); } else { ((OmniNotes) getActivity().getApplication()).getAnalyticsHelper().trackActionFromResourceId(getActivity(), item.getItemId()); } } /** * Empties trash deleting all the notes */ private void emptyTrash() { new MaterialDialog.Builder(mainActivity) .content(R.string.empty_trash_confirmation) .positiveText(R.string.ok) .callback(new MaterialDialog.ButtonCallback() { @Override public void onPositive(MaterialDialog materialDialog) { boolean mustDeleteLockedNotes = false; for (int i = 0; i < listAdapter.getCount(); i++) { selectedNotes.add(listAdapter.getItem(i)); mustDeleteLockedNotes = mustDeleteLockedNotes || listAdapter.getItem(i).isLocked(); } if (mustDeleteLockedNotes) { mainActivity.requestPassword(mainActivity, getSelectedNotes(), passwordConfirmed -> { if (passwordConfirmed) { deleteNotesExecute(); } }); } else { deleteNotesExecute(); } } }).build().show(); } /** * Notes list adapter initialization and association to view */ void initNotesList(Intent intent) { Log.d(Constants.TAG, "initNotesList intent: " + intent.getAction()); progress_wheel.setAlpha(1); list.setAlpha(0); // Search for a tag // A workaround to simplify it's to simulate normal search if (Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getCategories() != null && intent.getCategories().contains(Intent.CATEGORY_BROWSABLE)) { searchTags = intent.getDataString().replace(UrlCompleter.HASHTAG_SCHEME, ""); goBackOnToggleSearchLabel = true; } // Searching searchQuery = searchQueryInstant; searchQueryInstant = null; if (searchTags != null || searchQuery != null || Intent.ACTION_SEARCH.equals(intent.getAction())) { // Using tags if (searchTags != null && intent.getStringExtra(SearchManager.QUERY) == null) { searchQuery = searchTags; NoteLoaderTask.getInstance().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "getNotesByTag", searchQuery); } else { // Get the intent, verify the action and get the query if (intent.getStringExtra(SearchManager.QUERY) != null) { searchQuery = intent.getStringExtra(SearchManager.QUERY); searchTags = null; } NoteLoaderTask.getInstance().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "getNotesByPattern", searchQuery); } toggleSearchLabel(true); } else { // Check if is launched from a widget with categories if ((Constants.ACTION_WIDGET_SHOW_LIST.equals(intent.getAction()) && intent .hasExtra(Constants.INTENT_WIDGET)) || !TextUtils.isEmpty(mainActivity.navigationTmp)) { String widgetId = intent.hasExtra(Constants.INTENT_WIDGET) ? intent.getExtras() .get(Constants.INTENT_WIDGET).toString() : null; if (widgetId != null) { String sqlCondition = prefs.getString(Constants.PREF_WIDGET_PREFIX + widgetId, ""); String categoryId = TextHelper.checkIntentCategory(sqlCondition); mainActivity.navigationTmp = !TextUtils.isEmpty(categoryId) ? categoryId : null; } intent.removeExtra(Constants.INTENT_WIDGET); if (mainActivity.navigationTmp != null) { Long categoryId = Long.parseLong(mainActivity.navigationTmp); NoteLoaderTask.getInstance().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "getNotesByCategory", categoryId); } else { NoteLoaderTask.getInstance().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "getAllNotes", true); } } else { NoteLoaderTask.getInstance().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "getAllNotes", true); } } } public void toggleSearchLabel(boolean activate) { if (activate) { searchQueryView.setText(Html.fromHtml(getString(R.string.search) + ":<b> " + searchQuery + "</b>")); searchLayout.setVisibility(View.VISIBLE); searchCancel.setOnClickListener(v -> toggleSearchLabel(false)); searchLabelActive = true; } else { if (searchLabelActive) { searchLabelActive = false; AnimationsHelper.expandOrCollapse(searchLayout, false); searchTags = null; searchQuery = null; if (!goBackOnToggleSearchLabel) { mainActivity.getIntent().setAction(Intent.ACTION_MAIN); if (searchView != null) { MenuItemCompat.collapseActionView(searchMenuItem); } initNotesList(mainActivity.getIntent()); } else { mainActivity.onBackPressed(); } goBackOnToggleSearchLabel = false; if (Intent.ACTION_VIEW.equals(mainActivity.getIntent().getAction())) { mainActivity.getIntent().setAction(null); } } } } public void onEvent(NavigationUpdatedNavDrawerClosedEvent navigationUpdatedNavDrawerClosedEvent) { listViewPosition = 0; listViewPositionOffset = 16; initNotesList(mainActivity.getIntent()); setActionItemsVisibility(menu, false); } public void onEvent(CategoriesUpdatedEvent categoriesUpdatedEvent) { initNotesList(mainActivity.getIntent()); } public void onEvent(NotesLoadedEvent notesLoadedEvent) { int layoutSelected = prefs.getBoolean(Constants.PREF_EXPANDED_VIEW, true) ? R.layout.note_layout_expanded : R.layout.note_layout; listAdapter = new NoteAdapter(mainActivity, layoutSelected, notesLoadedEvent.notes); View noteLayout = LayoutInflater.from(mainActivity).inflate(layoutSelected, null, false); noteViewHolder = new NoteViewHolder(noteLayout); if (Navigation.getNavigation() != Navigation.UNCATEGORIZED && prefs.getBoolean(Constants.PREF_ENABLE_SWIPE, true)) { list.enableSwipeToDismiss((viewGroup, reverseSortedPositions) -> { // Avoids conflicts with action mode finishActionMode(); for (int position : reverseSortedPositions) { Note note; try { note = listAdapter.getItem(position); } catch (IndexOutOfBoundsException e) { Log.d(Constants.TAG, "Please stop swiping in the zone beneath the last card"); continue; } getSelectedNotes().add(note); // Depending on settings and note status this action will... // ...restore if (Navigation.checkNavigation(Navigation.TRASH)) { trashNotes(false); } // ...removes category else if (Navigation.checkNavigation(Navigation.CATEGORY)) { categorizeNotesExecute(null); } else { // ...trash if (prefs.getBoolean("settings_swipe_to_trash", false) || Navigation.checkNavigation(Navigation.ARCHIVE)) { trashNotes(true); // ...archive } else { archiveNotes(true); } } } }); } else { list.disableSwipeToDismiss(); } list.setAdapter(listAdapter); // Replace listview with Mr. Jingles if it is empty if (notesLoadedEvent.notes.size() == 0) list.setEmptyView(empyListItem); // Restores listview position when turning back to list or when navigating reminders if (list != null && notesLoadedEvent.notes.size() > 0) { if (Navigation.checkNavigation(Navigation.REMINDERS)) { listViewPosition = listAdapter.getClosestNotePosition(); } restoreListScrollPosition(); } // Fade in the list view animate(progress_wheel).setDuration(getResources().getInteger(R.integer.list_view_fade_anim)).alpha(0); animate(list).setDuration(getResources().getInteger(R.integer.list_view_fade_anim)).alpha(1); closeFab(); } private void restoreListScrollPosition() { if (list.getCount() > listViewPosition) { list.setSelectionFromTop(listViewPosition, listViewPositionOffset); new Handler().postDelayed(fab::showFab, 150); } else { list.setSelectionFromTop(0, 0); } } /** * Batch note trashing */ public void trashNotes(boolean trash) { int selectedNotesSize = getSelectedNotes().size(); // Restore is performed immediately, otherwise undo bar is shown if (trash) { trackModifiedNotes(getSelectedNotes()); for (Note note : getSelectedNotes()) { listAdapter.remove(note); } } else { trashNote(getSelectedNotes(), false); } // If list is empty again Mr Jingles will appear again if (listAdapter.getCount() == 0) list.setEmptyView(empyListItem); finishActionMode(); // Advice to user if (trash) { mainActivity.showMessage(R.string.note_trashed, ONStyle.WARN); } else { mainActivity.showMessage(R.string.note_untrashed, ONStyle.INFO); } // Creation of undo bar if (trash) { ubc.showUndoBar(false, selectedNotesSize + " " + getString(R.string.trashed), null); fab.hideFab(); undoTrash = true; } else { getSelectedNotes().clear(); } } private android.support.v7.view.ActionMode getActionMode() { return actionMode; } private List<Note> getSelectedNotes() { return selectedNotes; } /** * Single note logical deletion */ @SuppressLint("NewApi") protected void trashNote(List<Note> notes, boolean trash) { listAdapter.remove(notes); new NoteProcessorTrash(notes, trash).process(); } /** * Selects all notes in list */ private void selectAllNotes() { for (int i = 0; i < list.getChildCount(); i++) { LinearLayout v = (LinearLayout) list.getChildAt(i).findViewById(R.id.card_layout); // Checks null to avoid the footer if (v != null) { v.setBackgroundColor(getResources().getColor(R.color.list_bg_selected)); } } selectedNotes.clear(); for (int i = 0; i < listAdapter.getCount(); i++) { selectedNotes.add(listAdapter.getItem(i)); listAdapter.addSelectedItem(i); } prepareActionModeMenu(); setCabTitle(); } /** * Batch note permanent deletion */ private void deleteNotes() { new MaterialDialog.Builder(mainActivity) .content(R.string.delete_note_confirmation) .positiveText(R.string.ok) .onPositive((dialog, which) -> mainActivity.requestPassword(mainActivity, getSelectedNotes(), passwordConfirmed -> { if (passwordConfirmed) { deleteNotesExecute(); } })) .build() .show(); } /** * Performs notes permanent deletion after confirmation by the user */ private void deleteNotesExecute() { listAdapter.remove(getSelectedNotes()); new NoteProcessorDelete(getSelectedNotes()).process(); list.clearChoices(); selectedNotes.clear(); finishActionMode(); // If list is empty again Mr Jingles will appear again if (listAdapter.getCount() == 0) list.setEmptyView(empyListItem); mainActivity.showMessage(R.string.note_deleted, ONStyle.ALERT); } /** * Batch note archiviation */ public void archiveNotes(boolean archive) { int selectedNotesSize = getSelectedNotes().size(); // Used in undo bar commit sendToArchive = archive; if (!archive) { archiveNote(getSelectedNotes(), false); } else { trackModifiedNotes(getSelectedNotes()); } for (Note note : getSelectedNotes()) { // If is restore it will be done immediately, otherwise the undo bar will be shown if (archive) { // Saves archived state to eventually undo undoArchivedMap.put(note, note.isArchived()); } // If actual navigation is not "Notes" the item will not be removed but replaced to fit the new state if (Navigation.checkNavigation(Navigation.NOTES) || (Navigation.checkNavigation(Navigation.ARCHIVE) && !archive) || (Navigation.checkNavigation(Navigation.CATEGORY) && prefs.getBoolean(Constants .PREF_FILTER_ARCHIVED_IN_CATEGORIES + Navigation.getCategory(), false))) { listAdapter.remove(note); } else { note.setArchived(archive); listAdapter.replace(note, listAdapter.getPosition(note)); } } listAdapter.notifyDataSetChanged(); finishActionMode(); // If list is empty again Mr Jingles will appear again if (listAdapter.getCount() == 0) list.setEmptyView(empyListItem); // Advice to user int msg = archive ? R.string.note_archived : R.string.note_unarchived; Style style = archive ? ONStyle.WARN : ONStyle.INFO; mainActivity.showMessage(msg, style); // Creation of undo bar if (archive) { ubc.showUndoBar(false, selectedNotesSize + " " + getString(R.string.archived), null); fab.hideFab(); undoArchive = true; } else { getSelectedNotes().clear(); } } /** * Saves notes to be eventually restored at right position */ private void trackModifiedNotes(List<Note> modifiedNotesToTrack) { for (Note note : modifiedNotesToTrack) { undoNotesMap.put(listAdapter.getPosition(note), note); } } private void archiveNote(List<Note> notes, boolean archive) { new NoteProcessorArchive(notes, archive).process(); if (!Navigation.checkNavigation(Navigation.CATEGORY)) { listAdapter.remove(notes); } Log.d(Constants.TAG, "Notes" + (archive ? "archived" : "restored from archive")); } /** * Categories addition and editing */ void editCategory(Category category) { Intent categoryIntent = new Intent(mainActivity, CategoryActivity.class); categoryIntent.putExtra(Constants.INTENT_CATEGORY, category); startActivityForResult(categoryIntent, REQUEST_CODE_CATEGORY); } /** * Associates to or removes categories */ private void categorizeNotes() { // Retrieves all available categories final ArrayList<Category> categories = DbHelper.getInstance().getCategories(); final MaterialDialog dialog = new MaterialDialog.Builder(mainActivity) .title(R.string.categorize_as) .adapter(new NavDrawerCategoryAdapter(mainActivity, categories), null) .positiveText(R.string.add_category) .positiveColorRes(R.color.colorPrimary) .negativeText(R.string.remove_category) .negativeColorRes(R.color.colorAccent) .callback(new MaterialDialog.ButtonCallback() { @Override public void onPositive(MaterialDialog dialog) { keepActionMode = true; Intent intent = new Intent(mainActivity, CategoryActivity.class); intent.putExtra("noHome", true); startActivityForResult(intent, REQUEST_CODE_CATEGORY_NOTES); } @Override public void onNegative(MaterialDialog dialog) { categorizeNotesExecute(null); } }).build(); ListView dialogList = dialog.getListView(); assert dialogList != null; dialogList.setOnItemClickListener((parent, view, position, id) -> { dialog.dismiss(); categorizeNotesExecute(categories.get(position)); }); dialog.show(); } private void categorizeNotesExecute(Category category) { if (category != null) { categorizeNote(getSelectedNotes(), category); } else { trackModifiedNotes(getSelectedNotes()); } for (Note note : getSelectedNotes()) { // If is restore it will be done immediately, otherwise the undo bar // will be shown if (category == null) { // Saves categories associated to eventually undo undoCategoryMap.put(note, note.getCategory()); } // Update adapter content if actual navigation is the category // associated with actually cycled note if ((Navigation.checkNavigation(Navigation.CATEGORY) && !Navigation.checkNavigationCategory(category)) || Navigation.checkNavigation(Navigation.UNCATEGORIZED)) { listAdapter.remove(note); } else { note.setCategory(category); listAdapter.replace(note, listAdapter.getPosition(note)); } } finishActionMode(); // If list is empty again Mr Jingles will appear again if (listAdapter.getCount() == 0) list.setEmptyView(empyListItem); // Advice to user String msg; if (category != null) { msg = getResources().getText(R.string.notes_categorized_as) + " '" + category.getName() + "'"; } else { msg = getResources().getText(R.string.notes_category_removed).toString(); } mainActivity.showMessage(msg, ONStyle.INFO); // Creation of undo bar if (category == null) { ubc.showUndoBar(false, getString(R.string.notes_category_removed), null); fab.hideFab(); undoCategorize = true; undoCategorizeCategory = null; } else { getSelectedNotes().clear(); } } private void categorizeNote(List<Note> notes, Category category) { new NoteProcessorCategorize(notes, category).process(); } /** * Bulk tag selected notes */ private void tagNotes() { // Retrieves all available tags final List<Tag> tags = DbHelper.getInstance().getTags(); // If there is no tag a message will be shown if (tags.size() == 0) { finishActionMode(); mainActivity.showMessage(R.string.no_tags_created, ONStyle.WARN); return; } final Integer[] preSelectedTags = TagsHelper.getPreselectedTagsArray(selectedNotes, tags); new MaterialDialog.Builder(mainActivity) .title(R.string.select_tags) .items(TagsHelper.getTagsArray(tags)) .positiveText(R.string.ok) .itemsCallbackMultiChoice(preSelectedTags, (dialog, which, text) -> { dialog.dismiss(); tagNotesExecute(tags, which, preSelectedTags); return false; }).build().show(); } private void tagNotesExecute(List<Tag> tags, Integer[] selectedTags, Integer[] preSelectedTags) { // Retrieves selected tags for (Note note : getSelectedNotes()) { tagNote(tags, selectedTags, note); } // Clears data structures list.clearChoices(); // Refreshes list list.invalidateViews(); // If list is empty again Mr Jingles will appear again if (listAdapter.getCount() == 0) list.setEmptyView(empyListItem); if (getActionMode() != null) { getActionMode().finish(); } mainActivity.showMessage(R.string.tags_added, ONStyle.INFO); } private void tagNote(List<Tag> tags, Integer[] selectedTags, Note note) { Pair<String, List<Tag>> taggingResult = TagsHelper.addTagToNote(tags, selectedTags, note); if (note.isChecklist()) { note.setTitle(note.getTitle() + System.getProperty("line.separator") + taggingResult.first); } else { StringBuilder sb = new StringBuilder(note.getContent()); if (sb.length() > 0) { sb.append(System.getProperty("line.separator")) .append(System.getProperty("line.separator")); } sb.append(taggingResult.first); note.setContent(sb.toString()); } // Removes unchecked tags Pair<String, String> titleAndContent = TagsHelper.removeTag(note.getTitle(), note.getContent(), taggingResult.second); note.setTitle(titleAndContent.first); note.setContent(titleAndContent.second); DbHelper.getInstance().updateNote(note, false); } // private void synchronizeSelectedNotes() { // new DriveSyncTask(mainActivity).execute(new ArrayList<Note>(getSelectedNotes())); // // Clears data structures // listAdapter.clearSelectedItems(); // list.clearChoices(); // finishActionMode(); // } @Override public void onUndo(Parcelable undoToken) { // Cycles removed items to re-insert into adapter for (Integer notePosition : undoNotesMap.keySet()) { Note currentNote = undoNotesMap.get(notePosition); // Manages uncategorize or archive undo if ((undoCategorize && !Navigation.checkNavigationCategory(undoCategoryMap.get(currentNote))) || undoArchive && !Navigation.checkNavigation(Navigation.NOTES)) { if (undoCategorize) { currentNote.setCategory(undoCategoryMap.get(currentNote)); } else if (undoArchive) { currentNote.setArchived(undoArchivedMap.get(currentNote)); } listAdapter.replace(currentNote, listAdapter.getPosition(currentNote)); // Manages trash undo } else { list.insert(notePosition, currentNote); } } listAdapter.notifyDataSetChanged(); selectedNotes.clear(); undoNotesMap.clear(); undoTrash = false; undoArchive = false; undoCategorize = false; undoNotesMap.clear(); undoCategoryMap.clear(); undoArchivedMap.clear(); undoCategorizeCategory = null; Crouton.cancelAllCroutons(); if (getActionMode() != null) { getActionMode().finish(); } ubc.hideUndoBar(false); fab.showFab(); } void commitPending() { if (undoTrash || undoArchive || undoCategorize) { List<Note> notesList = new ArrayList<>(undoNotesMap.values()); if (undoTrash) trashNote(notesList, true); else if (undoArchive) archiveNote(notesList, sendToArchive); else if (undoCategorize) categorizeNote(notesList, undoCategorizeCategory); undoTrash = false; undoArchive = false; undoCategorize = false; undoCategorizeCategory = null; // Clears data structures selectedNotes.clear(); undoNotesMap.clear(); undoCategoryMap.clear(); undoArchivedMap.clear(); list.clearChoices(); ubc.hideUndoBar(false); fab.showFab(); BaseActivity.notifyAppWidgets(mainActivity); Log.d(Constants.TAG, "Changes committed"); } } /** * Shares the selected note from the list */ private void share() { // Only one note should be selected to perform sharing but they'll be cycled anyhow for (final Note note : getSelectedNotes()) { mainActivity.shareNote(note); } getSelectedNotes().clear(); if (getActionMode() != null) { getActionMode().finish(); } } public void merge() { new MaterialDialog.Builder(mainActivity) .title(R.string.delete_merged) .positiveText(R.string.ok) .negativeText(R.string.no) .callback(new MaterialDialog.ButtonCallback() { @Override public void onPositive(MaterialDialog dialog) { EventBus.getDefault().post(new NotesMergeEvent(false)); } @Override public void onNegative(MaterialDialog dialog) { EventBus.getDefault().post(new NotesMergeEvent(true)); } }).build().show(); } /** * Merges all the selected notes */ public void onEventAsync(NotesMergeEvent notesMergeEvent) { final Note finalMergedNote = NotesHelper.mergeNotes(getSelectedNotes(), notesMergeEvent.keepMergedNotes); new Handler(Looper.getMainLooper()).post(() -> { if (!notesMergeEvent.keepMergedNotes) { ArrayList<String> notesIds = new ArrayList<>(); for (Note selectedNote : getSelectedNotes()) { notesIds.add(String.valueOf(selectedNote.get_id())); } mainActivity.getIntent().putExtra("merged_notes", notesIds); } getSelectedNotes().clear(); if (getActionMode() != null) { getActionMode().finish(); } mainActivity.getIntent().setAction(Constants.ACTION_MERGE); mainActivity.switchToDetail(finalMergedNote); }); } /** * Excludes past reminders */ private void filterReminders(boolean filter) { prefs.edit().putBoolean(Constants.PREF_FILTER_PAST_REMINDERS, filter).apply(); // Change list view initNotesList(mainActivity.getIntent()); // Called to switch menu voices mainActivity.supportInvalidateOptionsMenu(); } /** * Excludes archived notes in categories navigation */ private void filterCategoryArchived(boolean filter) { if (filter) { prefs.edit().putBoolean(Constants.PREF_FILTER_ARCHIVED_IN_CATEGORIES + Navigation.getCategory(), true).apply(); } else { prefs.edit().remove(Constants.PREF_FILTER_ARCHIVED_IN_CATEGORIES + Navigation.getCategory()).apply(); } // Change list view initNotesList(mainActivity.getIntent()); // Called to switch menu voices mainActivity.supportInvalidateOptionsMenu(); } /** * Search notes by tags */ private void filterByTags() { // Retrieves all available categories final List<Tag> tags = TagsHelper.getAllTags(); // If there is no category a message will be shown if (tags.size() == 0) { mainActivity.showMessage(R.string.no_tags_created, ONStyle.WARN); return; } // Dialog and events creation new MaterialDialog.Builder(mainActivity) .title(R.string.select_tags) .items(TagsHelper.getTagsArray(tags)) .positiveText(R.string.ok) .itemsCallbackMultiChoice(new Integer[]{}, (dialog, which, text) -> { // Retrieves selected tags List<String> selectedTags = new ArrayList<>(); for (Integer aWhich : which) { selectedTags.add(tags.get(aWhich).getText()); } // Saved here to allow persisting search searchTags = selectedTags.toString().substring(1, selectedTags.toString().length() - 1) .replace(" ", ""); Intent intent = mainActivity.getIntent(); // Hides keyboard searchView.clearFocus(); KeyboardUtils.hideKeyboard(searchView); intent.removeExtra(SearchManager.QUERY); initNotesList(intent); return false; }).build().show(); } public MenuItem getSearchMenuItem() { return searchMenuItem; } private boolean isFabAllowed() { return isFabAllowed(false); } private boolean isFabAllowed(boolean actionModeFinishing) { boolean isAllowed = true; // Actionmode check isAllowed = isAllowed && (getActionMode() == null || actionModeFinishing); // Navigation check int navigation = Navigation.getNavigation(); isAllowed = isAllowed && navigation != Navigation.ARCHIVE && navigation != Navigation.REMINDERS && navigation != Navigation.TRASH; // Navigation drawer check isAllowed = isAllowed && mainActivity.getDrawerLayout() != null && !mainActivity.getDrawerLayout().isDrawerOpen (GravityCompat.START); return isAllowed; } }