Skip to content

Commit 5581004

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

11 files changed

+171
-14
lines changed

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
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;
1415
import android.graphics.Color;
1516
import android.graphics.PorterDuff;
17+
import android.graphics.drawable.ColorDrawable;
1618
import android.graphics.drawable.LayerDrawable;
19+
import android.graphics.drawable.StateListDrawable;
1720
import android.util.Log;
1821
import android.view.View;
1922
import android.widget.ImageView;
@@ -209,4 +212,33 @@ public void themeToolbarSearchView(@NonNull SearchView searchView) {
209212
return searchView;
210213
});
211214
}
215+
216+
public void themeBackgroundView(View view, Activity activity) {
217+
withScheme(view, scheme -> {
218+
activity.getWindow().getDecorView().setBackgroundColor(dynamicColor.surface().getArgb(scheme));
219+
view.setBackgroundColor(dynamicColor.surface().getArgb(scheme));
220+
return view;
221+
});
222+
}
223+
224+
public void themeBackgroundItemView(View view) {
225+
withScheme(view, scheme -> {
226+
StateListDrawable res = new StateListDrawable();
227+
res.addState(new int[]{android.R.attr.state_activated}, new ColorDrawable(dynamicColor.secondaryContainer().getArgb(scheme)));
228+
res.addState(new int[]{}, new ColorDrawable(dynamicColor.surface().getArgb(scheme)));
229+
view.setBackground(res);
230+
return view;
231+
});
232+
}
233+
234+
public void themeCard(@NonNull MaterialCardView view) {
235+
withScheme(view, scheme -> {
236+
view.setBackgroundTintList(buildColorStateList(
237+
new Pair<>(android.R.attr.state_activated, dynamicColor.secondaryContainer().getArgb(scheme)),
238+
new Pair<>(-android.R.attr.state_activated, dynamicColor.surface().getArgb(scheme)))
239+
);
240+
view.setStrokeColor(dynamicColor.outlineVariant().getArgb(scheme));
241+
return view;
242+
});
243+
}
212244
}

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: 51 additions & 10 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;
@@ -104,13 +112,19 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int
104112
if (gridView) {
105113
switch (viewType) {
106114
case TYPE_SECTION -> {
107-
return new SectionViewHolder(ItemNotesListSectionItemBinding.inflate(inflater));
115+
ItemNotesListSectionItemBinding binding = ItemNotesListSectionItemBinding.inflate(inflater);
116+
BrandingUtil.of(color, parent.getContext()).platform.colorTextView(binding.sectionTitle);
117+
return new SectionViewHolder(binding);
108118
}
109119
case TYPE_NOTE_ONLY_TITLE -> {
110-
return new NoteViewGridHolderOnlyTitle(ItemNotesListNoteItemGridOnlyTitleBinding.inflate(inflater, parent, false), noteClickListener, monospace, fontSize);
120+
ItemNotesListNoteItemGridOnlyTitleBinding binding = ItemNotesListNoteItemGridOnlyTitleBinding.inflate(inflater, parent, false);
121+
BrandingUtil.of(color, parent.getContext()).notes.themeCard(binding.card);
122+
return new NoteViewGridHolderOnlyTitle(binding, noteClickListener, monospace, fontSize);
111123
}
112124
case TYPE_NOTE_WITH_EXCERPT, TYPE_NOTE_WITHOUT_EXCERPT -> {
113-
return new NoteViewGridHolder(ItemNotesListNoteItemGridBinding.inflate(inflater, parent, false), noteClickListener, monospace, fontSize);
125+
ItemNotesListNoteItemGridBinding binding = ItemNotesListNoteItemGridBinding.inflate(inflater, parent, false);
126+
BrandingUtil.of(color, parent.getContext()).notes.themeCard(binding.card);
127+
return new NoteViewGridHolder(binding, noteClickListener, monospace, fontSize);
114128
}
115129
default -> {
116130
throw new IllegalArgumentException("Not supported viewType: " + viewType);
@@ -119,13 +133,19 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int
119133
} else {
120134
switch (viewType) {
121135
case TYPE_SECTION -> {
122-
return new SectionViewHolder(ItemNotesListSectionItemBinding.inflate(inflater));
136+
ItemNotesListSectionItemBinding binding = ItemNotesListSectionItemBinding.inflate(inflater);
137+
BrandingUtil.of(color, parent.getContext()).platform.colorTextView(binding.sectionTitle);
138+
return new SectionViewHolder(binding);
123139
}
124140
case TYPE_NOTE_WITH_EXCERPT -> {
125-
return new NoteViewHolderWithExcerpt(ItemNotesListNoteItemWithExcerptBinding.inflate(inflater, parent, false), noteClickListener);
141+
ItemNotesListNoteItemWithExcerptBinding binding = ItemNotesListNoteItemWithExcerptBinding.inflate(inflater, parent, false);
142+
BrandingUtil.of(color, parent.getContext()).notes.themeBackgroundItemView(binding.noteSwipeable);
143+
return new NoteViewHolderWithExcerpt(binding, noteClickListener);
126144
}
127145
case TYPE_NOTE_ONLY_TITLE, TYPE_NOTE_WITHOUT_EXCERPT -> {
128-
return new NoteViewHolderWithoutExcerpt(ItemNotesListNoteItemWithoutExcerptBinding.inflate(inflater, parent, false), noteClickListener);
146+
ItemNotesListNoteItemWithoutExcerptBinding binding = ItemNotesListNoteItemWithoutExcerptBinding.inflate(inflater, parent, false);
147+
BrandingUtil.of(color, parent.getContext()).notes.themeBackgroundItemView(binding.noteSwipeable);
148+
return new NoteViewHolderWithoutExcerpt(binding, noteClickListener);
129149
}
130150
default -> {
131151
throw new IllegalArgumentException("Not supported viewType: " + viewType);
@@ -149,17 +169,38 @@ public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, int
149169
switch (getItemViewType(position)) {
150170
case TYPE_SECTION ->
151171
((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);
172+
case TYPE_NOTE_WITH_EXCERPT, TYPE_NOTE_WITHOUT_EXCERPT, TYPE_NOTE_ONLY_TITLE -> {
173+
holder.itemView.findViewById(R.id.custom_checkbox).setVisibility(tracker != null && tracker.hasSelection() ? View.VISIBLE : View.GONE);
174+
if (isSelected) {
175+
holder.itemView.setBackgroundColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.bg_highlighted));
176+
((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));
177+
} else {
178+
holder.itemView.setBackgroundColor(holder.itemView.getContext().getColor(com.nextcloud.android.common.ui.R.color.bg_default));
179+
((ImageView) holder.itemView.findViewById(R.id.custom_checkbox)).setImageResource(R.drawable.ic_checkbox_blank_outline);
180+
}
181+
holder.itemView.findViewById(R.id.custom_checkbox).setVisibility(isMultiSelect ? View.VISIBLE : View.GONE);
182+
((NoteViewHolder) holder).bind(isSelected, (Note) itemList.get(position), showCategory, color, searchQuery);
183+
}
156184
}
157185
}
158186

159187
public void setTracker(SelectionTracker<Long> tracker) {
160188
this.tracker = tracker;
161189
}
162190

191+
@SuppressLint("NotifyDataSetChanged")
192+
public void setMultiSelect(boolean bool) {
193+
if (isMultiSelect != bool) {
194+
isMultiSelect = bool;
195+
// endless loop incoming...
196+
//notifyDataSetChanged();
197+
}
198+
}
199+
200+
public boolean isMultiSelect() {
201+
return this.isMultiSelect;
202+
}
203+
163204
public Item getItem(int notePosition) {
164205
return itemList.get(notePosition);
165206
}

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 & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,12 @@
1212
android:layout_width="match_parent"
1313
android:layout_height="wrap_content"
1414
android:focusable="true"
15-
app:cardBackgroundColor="@color/appbar"
1615
app:cardCornerRadius="@dimen/spacer_1x">
1716

1817
<LinearLayout
1918
android:id="@+id/wrapper"
2019
android:layout_width="match_parent"
2120
android:layout_height="match_parent"
22-
android:background="@drawable/grid_item_background_selector"
2321
android:orientation="vertical"
2422
android:paddingBottom="@dimen/spacer_1x">
2523

@@ -107,6 +105,18 @@
107105
tools:maxLength="50"
108106
tools:text="@tools:sample/lorem/random" />
109107
</FrameLayout>
108+
109+
<ImageView
110+
android:id="@+id/custom_checkbox"
111+
android:layout_width="wrap_content"
112+
android:layout_height="match_parent"
113+
android:layout_gravity="top"
114+
android:clickable="false"
115+
android:contentDescription="@null"
116+
android:focusable="false"
117+
android:paddingStart="@dimen/spacer_1x"
118+
android:paddingEnd="@dimen/spacer_1x"
119+
android:src="@drawable/ic_checkbox_blank_outline" />
110120
</LinearLayout>
111121
</LinearLayout>
112122
</com.google.android.material.card.MaterialCardView>

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,12 @@
1212
android:layout_width="match_parent"
1313
android:layout_height="wrap_content"
1414
android:focusable="true"
15-
app:cardBackgroundColor="@color/appbar"
1615
app:cardCornerRadius="@dimen/spacer_1x">
1716

1817
<LinearLayout
1918
android:id="@+id/wrapper"
2019
android:layout_width="match_parent"
2120
android:layout_height="match_parent"
22-
android:background="@drawable/grid_item_background_selector"
2321
android:orientation="horizontal"
2422
android:paddingBottom="@dimen/spacer_1x">
2523

@@ -62,5 +60,17 @@
6260
android:textColor="@color/fg_default"
6361
tools:maxLength="50"
6462
tools:text="@tools:sample/lorem/random" />
63+
64+
<ImageView
65+
android:id="@+id/custom_checkbox"
66+
android:layout_width="wrap_content"
67+
android:layout_height="match_parent"
68+
android:layout_gravity="top"
69+
android:clickable="false"
70+
android:contentDescription="@null"
71+
android:focusable="false"
72+
android:paddingStart="@dimen/spacer_1x"
73+
android:paddingEnd="@dimen/spacer_1x"
74+
android:src="@drawable/ic_checkbox_blank_outline" />
6575
</LinearLayout>
6676
</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)