From bc1f4040dad728b5620fa50b6248e0cc45a27797 Mon Sep 17 00:00:00 2001 From: Stefan Niedermann Date: Mon, 19 Oct 2020 11:19:43 +0200 Subject: [PATCH] recyclerview-selection --- app/build.gradle | 3 +- .../owncloud/notes/edit/BaseNoteFragment.java | 2 +- .../notes/edit/NotePreviewFragment.java | 2 +- .../owncloud/notes/main/MainActivity.java | 78 +++++------ .../owncloud/notes/main/MainViewModel.java | 10 +- .../main/MultiSelectedActionModeCallback.java | 35 +++-- .../notes/main/items/ItemAdapter.java | 121 ++++++++++++------ .../notes/main/items/NoteViewHolder.java | 10 +- .../main/items/grid/NoteViewGridHolder.java | 5 +- .../grid/NoteViewGridHolderOnlyTitle.java | 6 +- .../items/list/NoteViewHolderWithExcerpt.java | 5 +- .../list/NoteViewHolderWithoutExcerpt.java | 5 +- .../notes/persistence/NotesDatabase.java | 2 +- .../notes/persistence/dao/NoteDao.java | 4 +- .../singlenote/SingleNoteWidgetFactory.java | 2 +- 15 files changed, 180 insertions(+), 110 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ac7e21de..32594a2a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,7 +1,7 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 29 + compileSdkVersion 30 buildToolsVersion '29.0.3' compileOptions { @@ -81,6 +81,7 @@ dependencies { implementation "androidx.fragment:fragment:1.2.5" implementation "androidx.preference:preference:1.1.1" implementation "androidx.recyclerview:recyclerview:1.1.0" + implementation 'androidx.recyclerview:recyclerview-selection:1.0.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.work:work-runtime:2.4.0' implementation "com.google.android.material:material:1.2.1" diff --git a/app/src/main/java/it/niedermann/owncloud/notes/edit/BaseNoteFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/edit/BaseNoteFragment.java index 0924aacf..6359b4b4 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/edit/BaseNoteFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/edit/BaseNoteFragment.java @@ -108,7 +108,7 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego SingleAccountHelper.setCurrentAccount(requireActivity().getApplicationContext(), localAccount.getAccountName()); } isNew = false; - note = originalNote = db.getNoteDao().getFullNoteWithCategory(localAccount.getId(), id); + note = originalNote = db.getNoteDao().getFullNoteWithCategory(id); } else { NoteWithCategory cloudNote = (NoteWithCategory) requireArguments().getSerializable(PARAM_NEWNOTE); String content = requireArguments().getString(PARAM_CONTENT); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java index 5c6fbbef..5758afe8 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java @@ -213,7 +213,7 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O TextProcessorChain chain = defaultTextProcessorChain(note.getNote()); Account account = db.getAccountDao().getLocalAccountByAccountName(SingleAccountHelper.getCurrentSingleSignOnAccount(requireContext()).name); db.getNoteServerSyncHelper().addCallbackPull(account, () -> { - note = db.getNoteDao().getFullNoteWithCategory(note.getAccountId(), note.getId()); + note = db.getNoteDao().getFullNoteWithCategory(note.getId()); changedText = note.getContent(); binding.singleNoteContent.setText(parseCompat(markdownProcessor, chain.apply(note.getContent()))); binding.swiperefreshlayout.setRefreshing(false); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java index ecd66689..a0752dc0 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java @@ -27,6 +27,10 @@ import androidx.core.view.ViewCompat; import androidx.lifecycle.LiveData; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.selection.SelectionPredicates; +import androidx.recyclerview.selection.SelectionTracker; +import androidx.recyclerview.selection.SelectionTracker.SelectionObserver; +import androidx.recyclerview.selection.StorageStrategy; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.StaggeredGridLayoutManager; @@ -44,6 +48,7 @@ import com.nextcloud.android.sso.exceptions.TokenMismatchException; import com.nextcloud.android.sso.helper.SingleAccountHelper; import java.util.stream.Collectors; +import java.util.stream.Stream; import it.niedermann.owncloud.notes.ImportAccountActivity; import it.niedermann.owncloud.notes.LockedActivity; @@ -111,6 +116,8 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A private NavigationAdapter adapterCategories; private MenuAdapter menuAdapter; + private SelectionTracker tracker; + protected DrawerLayoutBinding binding; protected ActivityNotesListViewBinding activityBinding; protected FloatingActionButton fabCreate; @@ -206,10 +213,10 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A }); }); mainViewModel.getNotesListLiveData().observe(this, notes -> { - adapter.clearSelection(listView); - if (mActionMode != null) { - mActionMode.finish(); - } +// adapter.clearSelection(listView); +// if (mActionMode != null) { +// mActionMode.finish(); +// } adapter.setItemList(notes); binding.activityNotesListView.progressCircular.setVisibility(GONE); binding.activityNotesListView.emptyContentView.getRoot().setVisibility(notes.size() > 0 ? GONE : VISIBLE); @@ -386,6 +393,22 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A }; syncLiveData.observe(this, syncObserver); }); + + tracker = new SelectionTracker.Builder<>( + "selection-1", + listView, + new ItemAdapter.ItemIdKeyProvider(listView), + new ItemAdapter.ItemLookup(listView), + StorageStrategy.createLongStorage() + ).withSelectionPredicate(SelectionPredicates.createSelectAnything()).build(); + adapter.setTracker(tracker); + tracker.addObserver(new SelectionObserver() { + @Override + public void onSelectionChanged() { + super.onSelectionChanged(); + } + }); + } private void setupNavigationList() { @@ -578,21 +601,8 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A @Override public void onNoteClick(int position, View v) { - boolean hasCheckedItems = adapter.getSelected().size() > 0; - if (hasCheckedItems) { - if (!adapter.select(position)) { - v.setSelected(false); - adapter.deselect(position); - } else { - v.setSelected(true); - } - int size = adapter.getSelected().size(); - if (size > 0) { - mActionMode.setTitle(getResources().getQuantityString(R.plurals.ab_selected, size, size)); - } else { - mActionMode.finish(); - } - } else { + boolean hasCheckedItems = tracker.getSelection().size() > 0; + if (!hasCheckedItems) { NoteWithCategory note = (NoteWithCategory) adapter.getItem(position); Intent intent = new Intent(getApplicationContext(), EditNoteActivity.class); intent.putExtra(EditNoteActivity.PARAM_NOTE_ID, note.getId()); @@ -608,14 +618,13 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A @Override public boolean onNoteLongClick(int position, View v) { - boolean selected = adapter.select(position); - if (selected) { - v.setSelected(true); - mActionMode = startSupportActionMode(new MultiSelectedActionModeCallback(this, coordinatorLayout, mainViewModel, this, canMoveNoteToAnotherAccounts, adapter, listView, getSupportFragmentManager(), activityBinding.searchView)); - final int checkedItemCount = adapter.getSelected().size(); - mActionMode.setTitle(getResources().getQuantityString(R.plurals.ab_selected, checkedItemCount, checkedItemCount)); - } - return selected; +// int selected = tracker.getSelection().size(); +// if (selected > 0) { +// v.setSelected(true); +// mActionMode = startSupportActionMode(new MultiSelectedActionModeCallback(this, coordinatorLayout, mainViewModel, this, canMoveNoteToAnotherAccounts, tracker, listView, getSupportFragmentManager(), activityBinding.searchView)); +// mActionMode.setTitle(getResources().getQuantityString(R.plurals.ab_selected, selected, selected)); +// } + return true; } @Override @@ -655,20 +664,17 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A @Override public void onAccountPicked(@NonNull Account account) { - for (Integer i : adapter.getSelected()) { - final LiveData moveLiveData = mainViewModel.moveNoteToAnotherAccount(account, (NoteWithCategory) adapter.getItem(i)); - moveLiveData.observe(this, (v) -> moveLiveData.removeObservers(this)); + for(Long noteId : tracker.getSelection()) { + new Thread(() -> { + final LiveData moveLiveData = mainViewModel.moveNoteToAnotherAccount(account, noteId); + moveLiveData.observe(this, (v) -> moveLiveData.removeObservers(this)); + }).start(); } } @Override public void onCategoryChosen(String category) { - final LiveData categoryLiveData = mainViewModel.setCategory( - adapter.getSelected() - .stream() - .map(item -> ((NoteWithCategory) adapter.getItem(item)).getId()) - .collect(Collectors.toList()) - , category); + final LiveData categoryLiveData = mainViewModel.setCategory(tracker.getSelection(), category); categoryLiveData.observe(this, (next) -> categoryLiveData.removeObservers(this)); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java b/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java index 9515df1d..b5e98735 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java @@ -427,7 +427,7 @@ public class MainViewModel extends AndroidViewModel { return db.getAccountDao().getAccounts(); } - public LiveData setCategory(Collection noteIds, @NonNull String category) { + public LiveData setCategory(Iterable noteIds, @NonNull String category) { return switchMap(getCurrentAccount(), currentAccount -> { if (currentAccount == null) { return new MutableLiveData<>(null); @@ -441,8 +441,8 @@ public class MainViewModel extends AndroidViewModel { }); } - public LiveData moveNoteToAnotherAccount(Account account, NoteWithCategory note) { - return db.moveNoteToAnotherAccount(account, note); + public LiveData moveNoteToAnotherAccount(Account account, Long noteId) { + return db.moveNoteToAnotherAccount(account, db.getNoteDao().getFullNoteWithCategory(noteId)); } @WorkerThread @@ -506,7 +506,7 @@ public class MainViewModel extends AndroidViewModel { new Thread(() -> notes.postValue( ids .stream() - .map(id -> db.getNoteDao().getFullNoteWithCategory(currentAccount.getId(), id)) + .map(id -> db.getNoteDao().getFullNoteWithCategory(id)) .collect(Collectors.toList()) )).start(); return notes; @@ -551,7 +551,7 @@ public class MainViewModel extends AndroidViewModel { new Thread(() -> { final StringBuilder noteContents = new StringBuilder(); for (Long noteId : noteIds) { - final NoteWithCategory fullNote = db.getNoteDao().getFullNoteWithCategory(currentAccount.getId(), noteId); + final NoteWithCategory fullNote = db.getNoteDao().getFullNoteWithCategory(noteId); final String tempFullNote = fullNote.getContent(); if (!TextUtils.isEmpty(tempFullNote)) { if (noteContents.length() > 0) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/MultiSelectedActionModeCallback.java b/app/src/main/java/it/niedermann/owncloud/notes/main/MultiSelectedActionModeCallback.java index b1afad4c..0734f5b6 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/MultiSelectedActionModeCallback.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/MultiSelectedActionModeCallback.java @@ -16,6 +16,7 @@ import androidx.core.graphics.drawable.DrawableCompat; import androidx.fragment.app.FragmentManager; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LiveData; +import androidx.recyclerview.selection.SelectionTracker; import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.snackbar.Snackbar; @@ -23,6 +24,7 @@ import com.google.android.material.snackbar.Snackbar; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.accountpicker.AccountPickerDialogFragment; @@ -46,19 +48,19 @@ public class MultiSelectedActionModeCallback implements Callback { @NonNull private final LifecycleOwner lifecycleOwner; private final boolean canMoveNoteToAnotherAccounts; - private final ItemAdapter adapter; + private final SelectionTracker tracker; private final RecyclerView recyclerView; private final FragmentManager fragmentManager; private final SearchView searchView; public MultiSelectedActionModeCallback( - @NonNull Context context, @NonNull View view, @NonNull MainViewModel mainViewModel, @NonNull LifecycleOwner lifecycleOwner, boolean canMoveNoteToAnotherAccounts, ItemAdapter adapter, RecyclerView recyclerView, FragmentManager fragmentManager, SearchView searchView) { + @NonNull Context context, @NonNull View view, @NonNull MainViewModel mainViewModel, @NonNull LifecycleOwner lifecycleOwner, boolean canMoveNoteToAnotherAccounts, SelectionTracker tracker, RecyclerView recyclerView, FragmentManager fragmentManager, SearchView searchView) { this.context = context; this.view = view; this.mainViewModel = mainViewModel; this.lifecycleOwner = lifecycleOwner; this.canMoveNoteToAnotherAccounts = canMoveNoteToAnotherAccounts; - this.adapter = adapter; + this.tracker = tracker; this.recyclerView = recyclerView; this.fragmentManager = fragmentManager; this.searchView = searchView; @@ -98,7 +100,10 @@ public class MultiSelectedActionModeCallback implements Callback { public boolean onActionItemClicked(ActionMode mode, MenuItem item) { int itemId = item.getItemId(); if (itemId == R.id.menu_delete) { - final List selection = adapter.getSelected().stream().map(itemPosition -> ((NoteWithCategory) adapter.getItem(itemPosition)).getId()).collect(Collectors.toList()); + final List selection = new ArrayList<>(tracker.getSelection().size()); + for(Long sel : tracker.getSelection()) { + selection.add(sel); + } final LiveData> fullNotes$ = mainViewModel.getFullNotesWithCategory(selection); fullNotes$.observe(lifecycleOwner, (fullNotes) -> { fullNotes$.removeObservers(lifecycleOwner); @@ -133,15 +138,17 @@ public class MultiSelectedActionModeCallback implements Callback { }); return true; } else if (itemId == R.id.menu_share) { - final String subject = (adapter.getSelected().size() == 1) - ? ((NoteWithCategory) adapter.getItem(adapter.getSelected().get(0))).getTitle() - : context.getResources().getQuantityString(R.plurals.share_multiple, adapter.getSelected().size(), adapter.getSelected().size()); + final List selection = new ArrayList<>(tracker.getSelection().size()); + for(Long sel : tracker.getSelection()) { + selection.add(sel); + } + // FIXME + final String subject = context.getResources().getQuantityString(R.plurals.share_multiple, selection.size(), selection.size()); +// final String subject = (selection.size() == 1) +// ? ((NoteWithCategory) adapter.getItem(adapter.getSelected().get(0))).getTitle() +// : context.getResources().getQuantityString(R.plurals.share_multiple, adapter.getSelected().size(), adapter.getSelected().size()); - final LiveData contentCollector = mainViewModel.collectNoteContents( - adapter.getSelected() - .stream() - .map(itemPosition -> ((NoteWithCategory) adapter.getItem(itemPosition)).getId()) - .collect(Collectors.toList())); + final LiveData contentCollector = mainViewModel.collectNoteContents(selection); contentCollector.observe(lifecycleOwner, (next) -> { contentCollector.removeObservers(lifecycleOwner); ShareUtil.openShareDialog(context, subject, next); @@ -163,7 +170,7 @@ public class MultiSelectedActionModeCallback implements Callback { @Override public void onDestroyActionMode(ActionMode mode) { - adapter.clearSelection(recyclerView); - adapter.notifyDataSetChanged(); +// adapter.clearSelection(recyclerView); +// adapter.notifyDataSetChanged(); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/items/ItemAdapter.java b/app/src/main/java/it/niedermann/owncloud/notes/main/items/ItemAdapter.java index 39f640a9..81a71ed0 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/items/ItemAdapter.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/items/ItemAdapter.java @@ -4,15 +4,20 @@ import android.content.Context; import android.content.SharedPreferences; import android.graphics.Color; import android.text.TextUtils; -import android.util.Log; import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; import android.view.ViewGroup; import androidx.annotation.ColorInt; import androidx.annotation.IntRange; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.Px; import androidx.preference.PreferenceManager; +import androidx.recyclerview.selection.ItemDetailsLookup; +import androidx.recyclerview.selection.ItemKeyProvider; +import androidx.recyclerview.selection.SelectionTracker; import androidx.recyclerview.widget.RecyclerView; import java.util.ArrayList; @@ -35,6 +40,7 @@ import it.niedermann.owncloud.notes.persistence.entity.NoteWithCategory; import it.niedermann.owncloud.notes.shared.model.Item; import it.niedermann.owncloud.notes.shared.model.NoteClickListener; +import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; import static it.niedermann.owncloud.notes.shared.util.NoteUtil.getFontSizeFromPreferences; public class ItemAdapter extends RecyclerView.Adapter implements Branded { @@ -51,7 +57,8 @@ public class ItemAdapter extends RecyclerView.Adapter i private List itemList = new ArrayList<>(); private boolean showCategory = true; private CharSequence searchQuery; - private final List selected = new ArrayList<>(); + private SelectionTracker tracker = null; + // private final List selected = new ArrayList<>(); @Px private final float fontSize; private final boolean monospace; @@ -68,20 +75,17 @@ public class ItemAdapter extends RecyclerView.Adapter i final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); this.fontSize = getFontSizeFromPreferences(context, sp); this.monospace = sp.getBoolean(context.getString(R.string.pref_key_font), false); - // FIXME see getItemId() - // setHasStableIds(true); + setHasStableIds(true); } - /* - FIXME this causes {@link it.niedermann.owncloud.notes.noteslist.items.list.NotesListViewItemTouchHelper} to not call clearView anymore → After marking a note as favorite, it stays yellow. - @Override - public long getItemId(int position) { - return getItemViewType(position) == TYPE_SECTION - ? ((SectionItem) getItem(position)).getTitle().hashCode() * -1 - : ((NoteEntity) getItem(position)).getId(); - } - */ + // FIXME this causes {@link it.niedermann.owncloud.notes.noteslist.items.list.NotesListViewItemTouchHelper} to not call clearView anymore → After marking a note as favorite, it stays yellow. + @Override + public long getItemId(int position) { + return getItemViewType(position) == TYPE_SECTION + ? ((SectionItem) getItem(position)).getTitle().hashCode() * -1 + : ((NoteWithCategory) getItem(position)).getId(); + } /** * Updates the item list and notifies respective view to update. @@ -134,6 +138,16 @@ public class ItemAdapter extends RecyclerView.Adapter i @Override public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, int position) { + boolean isSelected = false; + if(tracker != null) { + Long itemId = getItemId(position); + if(tracker.isSelected(itemId)) { + tracker.select(itemId); + isSelected = true; + } else { + tracker.deselect(itemId); + } + } switch (getItemViewType(position)) { case TYPE_SECTION: { ((SectionViewHolder) holder).bind((SectionItem) itemList.get(position)); @@ -142,42 +156,73 @@ public class ItemAdapter extends RecyclerView.Adapter i case TYPE_NOTE_WITH_EXCERPT: case TYPE_NOTE_WITHOUT_EXCERPT: case TYPE_NOTE_ONLY_TITLE: { - ((NoteViewHolder) holder).bind((NoteWithCategory) itemList.get(position), showCategory, mainColor, textColor, searchQuery); + ((NoteViewHolder) holder).bind(new ItemDetailsLookup.ItemDetails() { + @Override + public int getPosition() { + return position; + } + + @Override + public Long getSelectionKey() { + return getItemId(position); + } + }, isSelected, (NoteWithCategory) itemList.get(position), showCategory, mainColor, textColor, searchQuery); break; } } } - public boolean select(Integer position) { - return !selected.contains(position) && selected.add(position); + // -------------------------------------------------------------------------------------------- + + + public void setTracker(SelectionTracker tracker) { + this.tracker = tracker; } - public void clearSelection(@NonNull RecyclerView recyclerView) { - for (Integer i : getSelected()) { - RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(i); - if (viewHolder != null) { - viewHolder.itemView.setSelected(false); - } else { - Log.w(TAG, "Could not found " + RecyclerView.ViewHolder.class.getSimpleName() + " to remove selection"); - } + public static class ItemIdKeyProvider extends ItemKeyProvider { + private final RecyclerView recyclerView; + + public ItemIdKeyProvider(RecyclerView recyclerView) { + super(SCOPE_MAPPED); + this.recyclerView = recyclerView; } - selected.clear(); - } - @NonNull - public List getSelected() { - return selected; - } - - public void deselect(Integer position) { - for (int i = 0; i < selected.size(); i++) { - if (selected.get(i).equals(position)) { - //position was selected and removed - selected.remove(i); - return; + @Nullable + @Override + public Long getKey(int position) { + final RecyclerView.Adapter adapter = recyclerView.getAdapter(); + if (adapter == null) { + throw new IllegalStateException("RecyclerView adapter is not set!"); } + return adapter.getItemId(position); + } + + @Override + public int getPosition(@NonNull Long key) { + final RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForItemId(key); + return viewHolder == null ? NO_POSITION : viewHolder.getLayoutPosition(); + } + } + + public static class ItemLookup extends ItemDetailsLookup { + + @NonNull + private final RecyclerView rv; + + public ItemLookup(@NonNull RecyclerView recyclerView) { + this.rv = recyclerView; + } + + @Nullable + @Override + public ItemDetails getItemDetails(@NonNull MotionEvent e) { + View view = rv.findChildViewUnder(e.getX(), e.getY()); + if(view != null) { + return ((NoteViewHolder) rv.getChildViewHolder(view)) + .getItemDetails(); + } + return null; } - // position was not selected } public Item getItem(int notePosition) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/items/NoteViewHolder.java b/app/src/main/java/it/niedermann/owncloud/notes/main/items/NoteViewHolder.java index 01860919..79b6b7a8 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/items/NoteViewHolder.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/items/NoteViewHolder.java @@ -19,6 +19,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.AppCompatImageView; import androidx.core.graphics.drawable.DrawableCompat; +import androidx.recyclerview.selection.ItemDetailsLookup; import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.chip.Chip; @@ -42,6 +43,7 @@ import static it.niedermann.owncloud.notes.shared.util.ColorUtil.isColorDark; public abstract class NoteViewHolder extends RecyclerView.ViewHolder { @NonNull private final NoteClickListener noteClickListener; + private ItemDetailsLookup.ItemDetails itemDetails; public NoteViewHolder(@NonNull View v, @NonNull NoteClickListener noteClickListener) { super(v); @@ -50,7 +52,9 @@ public abstract class NoteViewHolder extends RecyclerView.ViewHolder { } @CallSuper - public void bind(@NonNull NoteWithCategory note, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) { + public void bind(@NonNull ItemDetailsLookup.ItemDetails itemDetails, boolean isSelected, @NonNull NoteWithCategory note, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) { + this.itemDetails = itemDetails; + itemView.setSelected(isSelected); itemView.setOnClickListener((view) -> noteClickListener.onNoteClick(getAdapterPosition(), view)); itemView.setOnLongClickListener((view) -> noteClickListener.onNoteLongClick(getAdapterPosition(), view)); } @@ -137,4 +141,8 @@ public abstract class NoteViewHolder extends RecyclerView.ViewHolder { @Nullable public abstract View getNoteSwipeable(); + + public ItemDetailsLookup.ItemDetails getItemDetails() { + return itemDetails; + } } \ No newline at end of file diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/items/grid/NoteViewGridHolder.java b/app/src/main/java/it/niedermann/owncloud/notes/main/items/grid/NoteViewGridHolder.java index f118de43..6f73e8e4 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/items/grid/NoteViewGridHolder.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/items/grid/NoteViewGridHolder.java @@ -9,6 +9,7 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.Px; +import androidx.recyclerview.selection.ItemDetailsLookup; import it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemGridBinding; import it.niedermann.owncloud.notes.main.items.NoteViewHolder; @@ -40,8 +41,8 @@ public class NoteViewGridHolder extends NoteViewHolder { throw new UnsupportedOperationException(NoteViewGridHolder.class.getSimpleName() + " does not support swiping"); } - public void bind(@NonNull NoteWithCategory noteWithCategory, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) { - super.bind(noteWithCategory, showCategory, mainColor, textColor, searchQuery); + public void bind(@NonNull ItemDetailsLookup.ItemDetails itemDetails, boolean isSelected, @NonNull NoteWithCategory noteWithCategory, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) { + super.bind(itemDetails, isSelected, noteWithCategory, showCategory, mainColor, textColor, searchQuery); Note note = noteWithCategory.getNote(); @NonNull final Context context = itemView.getContext(); bindCategory(context, binding.noteCategory, showCategory, noteWithCategory.getCategory(), mainColor); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/items/grid/NoteViewGridHolderOnlyTitle.java b/app/src/main/java/it/niedermann/owncloud/notes/main/items/grid/NoteViewGridHolderOnlyTitle.java index 11468ba4..b0292d3f 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/items/grid/NoteViewGridHolderOnlyTitle.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/items/grid/NoteViewGridHolderOnlyTitle.java @@ -8,10 +8,10 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.Px; +import androidx.recyclerview.selection.ItemDetailsLookup; import it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemGridOnlyTitleBinding; import it.niedermann.owncloud.notes.main.items.NoteViewHolder; -import it.niedermann.owncloud.notes.persistence.entity.Note; import it.niedermann.owncloud.notes.persistence.entity.NoteWithCategory; import it.niedermann.owncloud.notes.shared.model.NoteClickListener; @@ -33,8 +33,8 @@ public class NoteViewGridHolderOnlyTitle extends NoteViewHolder { throw new UnsupportedOperationException(NoteViewGridHolderOnlyTitle.class.getSimpleName() + " does not support swiping"); } - public void bind(@NonNull NoteWithCategory note, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) { - super.bind(note, showCategory, mainColor, textColor, searchQuery); + public void bind(@NonNull ItemDetailsLookup.ItemDetails itemDetails, boolean isSelected, @NonNull NoteWithCategory note, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) { + super.bind(itemDetails, isSelected, note, showCategory, mainColor, textColor, searchQuery); @NonNull final Context context = itemView.getContext(); bindStatus(binding.noteStatus, note.getStatus(), mainColor); bindFavorite(binding.noteFavorite, note.getFavorite()); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NoteViewHolderWithExcerpt.java b/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NoteViewHolderWithExcerpt.java index 726267a1..80379ada 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NoteViewHolderWithExcerpt.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NoteViewHolderWithExcerpt.java @@ -5,6 +5,7 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.recyclerview.selection.ItemDetailsLookup; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemWithExcerptBinding; @@ -29,8 +30,8 @@ public class NoteViewHolderWithExcerpt extends NoteViewHolder { binding.noteSwipeFrame.setBackgroundResource(left ? R.color.bg_warning : R.color.bg_attention); } - public void bind(@NonNull NoteWithCategory noteWithCategory, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) { - super.bind(noteWithCategory, showCategory, mainColor, textColor, searchQuery); + public void bind(@NonNull ItemDetailsLookup.ItemDetails itemDetails, boolean isSelected, @NonNull NoteWithCategory noteWithCategory, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) { + super.bind(itemDetails, isSelected, noteWithCategory, showCategory, mainColor, textColor, searchQuery); Note note = noteWithCategory.getNote(); @NonNull final Context context = itemView.getContext(); binding.noteSwipeable.setAlpha(DBStatus.LOCAL_DELETED.equals(note.getStatus()) ? 0.5f : 1.0f); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NoteViewHolderWithoutExcerpt.java b/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NoteViewHolderWithoutExcerpt.java index 165329ac..7435ceec 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NoteViewHolderWithoutExcerpt.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NoteViewHolderWithoutExcerpt.java @@ -5,6 +5,7 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.recyclerview.selection.ItemDetailsLookup; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemWithoutExcerptBinding; @@ -29,8 +30,8 @@ public class NoteViewHolderWithoutExcerpt extends NoteViewHolder { binding.noteSwipeFrame.setBackgroundResource(left ? R.color.bg_warning : R.color.bg_attention); } - public void bind(@NonNull NoteWithCategory noteWithCategory, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) { - super.bind(noteWithCategory, showCategory, mainColor, textColor, searchQuery); + public void bind(@NonNull ItemDetailsLookup.ItemDetails itemDetails, boolean isSelected, @NonNull NoteWithCategory noteWithCategory, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) { + super.bind(itemDetails, isSelected, noteWithCategory, showCategory, mainColor, textColor, searchQuery); Note note = noteWithCategory.getNote(); @NonNull final Context context = itemView.getContext(); binding.noteSwipeable.setAlpha(DBStatus.LOCAL_DELETED.equals(note.getStatus()) ? 0.5f : 1.0f); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java index 3fa1c652..c0a32fd4 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java @@ -202,7 +202,7 @@ public abstract class NotesDatabase extends RoomDatabase { entity.setFavorite(note.getFavorite()); entity.setCategoryId(getOrCreateCategoryIdByTitle(accountId, note.getCategory())); entity.setETag(note.getETag()); - return getNoteDao().getFullNoteWithCategory(accountId, getNoteDao().addNote(entity)); + return getNoteDao().getFullNoteWithCategory(getNoteDao().addNote(entity)); } @AnyThread diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/NoteDao.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/NoteDao.java index 58c3d567..54e57a41 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/NoteDao.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/NoteDao.java @@ -73,8 +73,8 @@ public interface NoteDao { @Query("UPDATE NOTE SET scrollY = :scrollY WHERE id = :id") void updateScrollY(long id, int scrollY); - @Query("SELECT NOTE.*, CATEGORY.title as 'category' FROM NOTE INNER JOIN CATEGORY ON categoryId = CATEGORY.id WHERE NOTE.id = :id AND NOTE.accountId = :accountId AND status != :accountId") - NoteWithCategory getFullNoteWithCategory(long accountId, long id); + @Query("SELECT NOTE.*, CATEGORY.title as 'category' FROM NOTE INNER JOIN CATEGORY ON categoryId = CATEGORY.id WHERE NOTE.id = :id") + NoteWithCategory getFullNoteWithCategory(long id); @Query("SELECT NOTE.*, CATEGORY.title as 'category' FROM NOTE INNER JOIN CATEGORY ON categoryId = CATEGORY.id WHERE NOTE.id = :id AND NOTE.accountId = :accountId AND status != :accountId") LiveData getNoteWithCategoryLiveData(long accountId, long id); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/widget/singlenote/SingleNoteWidgetFactory.java b/app/src/main/java/it/niedermann/owncloud/notes/widget/singlenote/SingleNoteWidgetFactory.java index 5206409e..cba7b691 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/widget/singlenote/SingleNoteWidgetFactory.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/widget/singlenote/SingleNoteWidgetFactory.java @@ -61,7 +61,7 @@ public class SingleNoteWidgetFactory implements RemoteViewsService.RemoteViewsFa if (data != null) { final long noteId = data.getNoteId(); Log.v(TAG, "Fetch note with id " + noteId); - note = db.getNoteDao().getFullNoteWithCategory(data.getAccountId(), noteId); + note = db.getNoteDao().getFullNoteWithCategory(noteId); if (note == null) { Log.e(TAG, "Error: note not found");