Skip to content

Commit e8652dc

Browse files
style(multiselect): implement M3 multiselect visuals
Signed-off-by: Andy Scherzinger <[email protected]>
1 parent 0a7e458 commit e8652dc

10 files changed

+152
-6
lines changed

app/src/main/java/it/niedermann/owncloud/notes/branding/NotesViewThemeUtils.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,16 @@
99
import static com.nextcloud.android.common.ui.util.ColorStateListUtilsKt.buildColorStateList;
1010
import static com.nextcloud.android.common.ui.util.PlatformThemeUtil.isDarkMode;
1111

12+
import android.app.Activity;
1213
import android.content.Context;
1314
import android.content.res.ColorStateList;
15+
import android.content.res.Resources;
1416
import android.graphics.Color;
1517
import android.graphics.PorterDuff;
18+
import android.graphics.drawable.ColorDrawable;
1619
import android.graphics.drawable.LayerDrawable;
20+
import android.graphics.drawable.RippleDrawable;
21+
import android.graphics.drawable.StateListDrawable;
1722
import android.util.Log;
1823
import android.view.View;
1924
import android.widget.ImageView;
@@ -26,7 +31,9 @@
2631
import androidx.appcompat.widget.SearchView;
2732
import androidx.appcompat.widget.Toolbar;
2833
import androidx.core.content.ContextCompat;
34+
import androidx.core.content.res.ResourcesCompat;
2935
import androidx.core.graphics.drawable.DrawableCompat;
36+
import androidx.recyclerview.widget.RecyclerView;
3037

3138
import com.google.android.material.appbar.AppBarLayout;
3239
import com.google.android.material.appbar.MaterialToolbar;
@@ -38,6 +45,7 @@
3845
import dynamiccolor.MaterialDynamicColors;
3946
import it.niedermann.android.util.ColorUtil;
4047
import it.niedermann.owncloud.notes.R;
48+
import it.niedermann.owncloud.notes.databinding.DrawerLayoutBinding;
4149
import it.niedermann.owncloud.notes.main.navigation.NavigationItem;
4250
import it.niedermann.owncloud.notes.shared.util.NotesColorUtil;
4351
import kotlin.Pair;
@@ -209,4 +217,22 @@ public void themeToolbarSearchView(@NonNull SearchView searchView) {
209217
return searchView;
210218
});
211219
}
220+
221+
public void themeBackgroundView(View view, Activity activity) {
222+
withScheme(view, scheme -> {
223+
activity.getWindow().getDecorView().setBackgroundColor(dynamicColor.surface().getArgb(scheme));
224+
view.setBackgroundColor(dynamicColor.surface().getArgb(scheme));
225+
return view;
226+
});
227+
}
228+
229+
public void themeBackgroundItemView(View view) {
230+
withScheme(view, scheme -> {
231+
StateListDrawable res = new StateListDrawable();
232+
res.addState(new int[]{android.R.attr.state_activated}, new ColorDrawable(view.getResources().getColor(R.color.bg_highlighted)));
233+
res.addState(new int[]{}, new ColorDrawable(dynamicColor.surface().getArgb(scheme)));
234+
view.setBackground(res);
235+
return view;
236+
});
237+
}
212238
}

app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import android.os.Bundle;
2828
import android.text.TextUtils;
2929
import android.util.Log;
30+
import android.view.Menu;
3031
import android.view.View;
3132

3233
import androidx.annotation.ColorInt;
@@ -519,6 +520,7 @@ public void onSelectionChanged() {
519520
super.onSelectionChanged();
520521
if (tracker.hasSelection() && mActionMode == null) {
521522
mActionMode = startSupportActionMode(new MultiSelectedActionModeCallback(MainActivity.this,MainActivity.this, coordinatorLayout, binding.activityNotesListView.fabCreate, mainViewModel, MainActivity.this, canMoveNoteToAnotherAccounts, tracker, getSupportFragmentManager()));
523+
adapter.setMultiSelect(true);
522524
}
523525
if (mActionMode != null) {
524526
if (tracker.hasSelection()) {
@@ -527,6 +529,7 @@ public void onSelectionChanged() {
527529
} else {
528530
mActionMode.finish();
529531
mActionMode = null;
532+
adapter.setMultiSelect(false);
530533
}
531534
}
532535
}
@@ -604,6 +607,7 @@ public void applyBrand(int color) {
604607
util.platform.colorNavigationView(binding.navigationView);
605608
util.material.themeFAB(activityBinding.fabCreate);
606609
util.notes.themeSearchCardView(binding.activityNotesListView.searchBarWrapper);
610+
util.notes.themeBackgroundView(binding.activityNotesListView.getRoot(), this);
607611
util.platform.colorTextView(binding.activityNotesListView.searchText, ColorRole.ON_SURFACE_VARIANT);
608612
util.notes.themeSearchToolbar(binding.activityNotesListView.searchToolbar);
609613
util.notes.themeToolbarSearchView(binding.activityNotesListView.searchView);

app/src/main/java/it/niedermann/owncloud/notes/main/items/ItemAdapter.java

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88

99
import static it.niedermann.owncloud.notes.shared.util.NoteUtil.getFontSizeFromPreferences;
1010

11+
import android.annotation.SuppressLint;
1112
import android.content.Context;
1213
import android.text.TextUtils;
1314
import android.view.LayoutInflater;
15+
import android.view.View;
1416
import android.view.ViewGroup;
17+
import android.widget.ImageView;
1518

1619
import androidx.annotation.ColorInt;
1720
import androidx.annotation.IntRange;
@@ -23,11 +26,14 @@
2326
import androidx.recyclerview.selection.SelectionTracker;
2427
import androidx.recyclerview.widget.RecyclerView;
2528

29+
import com.nextcloud.android.common.ui.theme.utils.ColorRole;
30+
2631
import java.util.ArrayList;
2732
import java.util.List;
2833

2934
import it.niedermann.owncloud.notes.R;
3035
import it.niedermann.owncloud.notes.branding.Branded;
36+
import it.niedermann.owncloud.notes.branding.BrandingUtil;
3137
import it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemGridBinding;
3238
import it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemGridOnlyTitleBinding;
3339
import it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemWithExcerptBinding;
@@ -66,6 +72,8 @@ public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> i
6672
@Nullable
6773
private Integer swipedPosition;
6874

75+
private boolean isMultiSelect = false;
76+
6977
public <T extends Context & NoteClickListener> ItemAdapter(@NonNull T context, boolean gridView) {
7078
this.noteClickListener = context;
7179
this.gridView = gridView;
@@ -122,10 +130,14 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int
122130
return new SectionViewHolder(ItemNotesListSectionItemBinding.inflate(inflater));
123131
}
124132
case TYPE_NOTE_WITH_EXCERPT -> {
125-
return new NoteViewHolderWithExcerpt(ItemNotesListNoteItemWithExcerptBinding.inflate(inflater, parent, false), noteClickListener);
133+
ItemNotesListNoteItemWithExcerptBinding binding = ItemNotesListNoteItemWithExcerptBinding.inflate(inflater, parent, false);
134+
BrandingUtil.of(color, parent.getContext()).notes.themeBackgroundItemView(binding.noteSwipeable);
135+
return new NoteViewHolderWithExcerpt(binding, noteClickListener);
126136
}
127137
case TYPE_NOTE_ONLY_TITLE, TYPE_NOTE_WITHOUT_EXCERPT -> {
128-
return new NoteViewHolderWithoutExcerpt(ItemNotesListNoteItemWithoutExcerptBinding.inflate(inflater, parent, false), noteClickListener);
138+
ItemNotesListNoteItemWithoutExcerptBinding binding = ItemNotesListNoteItemWithoutExcerptBinding.inflate(inflater, parent, false);
139+
BrandingUtil.of(color, parent.getContext()).notes.themeBackgroundItemView(binding.noteSwipeable);
140+
return new NoteViewHolderWithoutExcerpt(binding, noteClickListener);
129141
}
130142
default -> {
131143
throw new IllegalArgumentException("Not supported viewType: " + viewType);
@@ -149,17 +161,38 @@ public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, int
149161
switch (getItemViewType(position)) {
150162
case TYPE_SECTION ->
151163
((SectionViewHolder) holder).bind((SectionItem) itemList.get(position));
152-
case TYPE_NOTE_WITH_EXCERPT,
153-
TYPE_NOTE_WITHOUT_EXCERPT,
154-
TYPE_NOTE_ONLY_TITLE ->
155-
((NoteViewHolder) holder).bind(isSelected, (Note) itemList.get(position), showCategory, color, searchQuery);
164+
case TYPE_NOTE_WITH_EXCERPT, TYPE_NOTE_WITHOUT_EXCERPT, TYPE_NOTE_ONLY_TITLE -> {
165+
holder.itemView.findViewById(R.id.custom_checkbox).setVisibility(tracker != null && tracker.hasSelection() ? View.VISIBLE : View.GONE);
166+
if (isSelected) {
167+
holder.itemView.setBackgroundColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.bg_highlighted));
168+
((ImageView) holder.itemView.findViewById(R.id.custom_checkbox)).setImageDrawable(BrandingUtil.getInstance(holder.itemView.getContext()).platform.tintDrawable(holder.itemView.getContext(), R.drawable.ic_checkbox_marked, ColorRole.PRIMARY));
169+
} else {
170+
holder.itemView.setBackgroundColor(holder.itemView.getContext().getColor(com.nextcloud.android.common.ui.R.color.bg_default));
171+
((ImageView) holder.itemView.findViewById(R.id.custom_checkbox)).setImageResource(R.drawable.ic_checkbox_blank_outline);
172+
}
173+
holder.itemView.findViewById(R.id.custom_checkbox).setVisibility(isMultiSelect ? View.VISIBLE : View.GONE);
174+
((NoteViewHolder) holder).bind(isSelected, (Note) itemList.get(position), showCategory, color, searchQuery);
175+
}
156176
}
157177
}
158178

159179
public void setTracker(SelectionTracker<Long> tracker) {
160180
this.tracker = tracker;
161181
}
162182

183+
@SuppressLint("NotifyDataSetChanged")
184+
public void setMultiSelect(boolean bool) {
185+
if (isMultiSelect != bool) {
186+
isMultiSelect = bool;
187+
// endless loop incoming...
188+
//notifyDataSetChanged();
189+
}
190+
}
191+
192+
public boolean isMultiSelect() {
193+
return this.isMultiSelect;
194+
}
195+
163196
public Item getItem(int notePosition) {
164197
return itemList.get(notePosition);
165198
}

app/src/main/java/it/niedermann/owncloud/notes/main/items/NoteViewHolder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public NoteViewHolder(@NonNull View v, @NonNull NoteClickListener noteClickListe
4545

4646
@CallSuper
4747
public void bind(boolean isSelected, @NonNull Note note, boolean showCategory, @ColorInt int color, @Nullable CharSequence searchQuery) {
48+
itemView.setActivated(isSelected);
4849
itemView.setSelected(isSelected);
4950
itemView.setOnClickListener((view) -> noteClickListener.onNoteClick(getLayoutPosition(), view));
5051
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!--
2+
~ Nextcloud Notes - Android Client
3+
~
4+
~ SPDX-FileCopyrightText: 2018-2025 Google LLC
5+
~ SPDX-License-Identifier: Apache-2.0
6+
-->
7+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
8+
android:width="24dp"
9+
android:height="24dp"
10+
android:tint="#767676"
11+
android:viewportWidth="960"
12+
android:viewportHeight="960">
13+
<path
14+
android:fillColor="@android:color/white"
15+
android:pathData="M480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z" />
16+
</vector>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!--
2+
~ Nextcloud Notes - Android Client
3+
~
4+
~ SPDX-FileCopyrightText: 2018-2025 Google LLC
5+
~ SPDX-License-Identifier: Apache-2.0
6+
-->
7+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
8+
android:width="24dp"
9+
android:height="24dp"
10+
android:tint="#0082c9"
11+
android:viewportWidth="960"
12+
android:viewportHeight="960">
13+
<path
14+
android:fillColor="@android:color/white"
15+
android:pathData="M424,664L706,382L650,326L424,552L310,438L254,494L424,664ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880Z" />
16+
</vector>

app/src/main/res/layout/item_notes_list_note_item_grid.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,18 @@
107107
tools:maxLength="50"
108108
tools:text="@tools:sample/lorem/random" />
109109
</FrameLayout>
110+
111+
<ImageView
112+
android:id="@+id/custom_checkbox"
113+
android:layout_width="wrap_content"
114+
android:layout_height="match_parent"
115+
android:layout_gravity="top"
116+
android:clickable="false"
117+
android:contentDescription="@null"
118+
android:focusable="false"
119+
android:paddingStart="@dimen/spacer_1x"
120+
android:paddingEnd="@dimen/spacer_1x"
121+
android:src="@drawable/ic_checkbox_blank_outline" />
110122
</LinearLayout>
111123
</LinearLayout>
112124
</com.google.android.material.card.MaterialCardView>

app/src/main/res/layout/item_notes_list_note_item_grid_only_title.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,17 @@
6262
android:textColor="@color/fg_default"
6363
tools:maxLength="50"
6464
tools:text="@tools:sample/lorem/random" />
65+
66+
<ImageView
67+
android:id="@+id/custom_checkbox"
68+
android:layout_width="wrap_content"
69+
android:layout_height="match_parent"
70+
android:layout_gravity="top"
71+
android:clickable="false"
72+
android:contentDescription="@null"
73+
android:focusable="false"
74+
android:paddingStart="@dimen/spacer_1x"
75+
android:paddingEnd="@dimen/spacer_1x"
76+
android:src="@drawable/ic_checkbox_blank_outline" />
6577
</LinearLayout>
6678
</com.google.android.material.card.MaterialCardView>

app/src/main/res/layout/item_notes_list_note_item_with_excerpt.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
xmlns:app="http://schemas.android.com/apk/res-auto"
1010
xmlns:tools="http://schemas.android.com/tools"
1111
android:id="@+id/noteSwipeFrame"
12+
android:clickable="true"
13+
android:focusable="true"
1214
android:layout_width="match_parent"
1315
android:layout_height="wrap_content"
1416
android:background="@color/bg_attention">
@@ -122,6 +124,18 @@
122124
tools:text="@tools:sample/lorem/random" />
123125
</LinearLayout>
124126
</LinearLayout>
127+
128+
<ImageView
129+
android:id="@+id/custom_checkbox"
130+
android:layout_width="wrap_content"
131+
android:layout_height="match_parent"
132+
android:layout_gravity="top"
133+
android:clickable="false"
134+
android:contentDescription="@null"
135+
android:focusable="false"
136+
android:paddingStart="@dimen/spacer_1x"
137+
android:paddingEnd="@dimen/spacer_1x"
138+
android:src="@drawable/ic_checkbox_blank_outline" />
125139
</LinearLayout>
126140

127141
</FrameLayout>

app/src/main/res/layout/item_notes_list_note_item_without_excerpt.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,18 @@
9494
android:textSize="@dimen/secondary_font_size"
9595
tools:maxLength="15"
9696
tools:text="@tools:sample/lorem/random" />
97+
98+
<ImageView
99+
android:id="@+id/custom_checkbox"
100+
android:layout_width="wrap_content"
101+
android:layout_height="match_parent"
102+
android:layout_gravity="top"
103+
android:clickable="false"
104+
android:contentDescription="@null"
105+
android:focusable="false"
106+
android:paddingStart="@dimen/spacer_1x"
107+
android:paddingEnd="@dimen/spacer_1x"
108+
android:src="@drawable/ic_checkbox_blank_outline" />
97109
</LinearLayout>
98110

99111
</FrameLayout>

0 commit comments

Comments
 (0)