Skip to content

Commit 57c8716

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

File tree

9 files changed

+151
-8
lines changed

9 files changed

+151
-8
lines changed

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

Lines changed: 34 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.RippleDrawable;
1720
import android.util.Log;
1821
import android.view.View;
1922
import android.widget.ImageView;
@@ -38,6 +41,7 @@
3841
import dynamiccolor.MaterialDynamicColors;
3942
import it.niedermann.android.util.ColorUtil;
4043
import it.niedermann.owncloud.notes.R;
44+
import it.niedermann.owncloud.notes.databinding.DrawerLayoutBinding;
4145
import it.niedermann.owncloud.notes.main.navigation.NavigationItem;
4246
import it.niedermann.owncloud.notes.shared.util.NotesColorUtil;
4347
import kotlin.Pair;
@@ -209,4 +213,34 @@ public void themeToolbarSearchView(@NonNull SearchView searchView) {
209213
return searchView;
210214
});
211215
}
216+
217+
public void themeBackgroundView(View view, Activity activity) {
218+
withScheme(view, scheme -> {
219+
activity.getWindow().getDecorView().setBackgroundColor(dynamicColor.surface().getArgb(scheme));
220+
view.setBackgroundColor(dynamicColor.surface().getArgb(scheme));
221+
return view;
222+
});
223+
}
224+
225+
public void themeBackgroundItemView(View view) {
226+
withScheme(view, scheme -> {
227+
// Create a ColorStateList for the ripple color
228+
ColorStateList rippleColor = buildColorStateList(
229+
new Pair<>(-android.R.attr.state_selected, dynamicColor.surface().getArgb(scheme)),
230+
new Pair<>(-android.R.attr.state_activated, dynamicColor.surface().getArgb(scheme)),
231+
new Pair<>(android.R.attr.state_activated, Color.BLUE),
232+
new Pair<>(android.R.attr.state_selected, Color.BLUE)
233+
);
234+
235+
// Create a RippleDrawable with the specified color and a transparent mask (or your content drawable)
236+
RippleDrawable rippleDrawable = new RippleDrawable(rippleColor, new ColorDrawable(dynamicColor.surface().getArgb(scheme)), null);
237+
238+
239+
// Set the RippleDrawable as the background of the view
240+
view.setBackground(rippleDrawable);
241+
return view;
242+
});
243+
}
244+
245+
212246
}

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;
@@ -518,6 +519,7 @@ public void onError(@NonNull Throwable t) {
518519
public void onSelectionChanged() {
519520
super.onSelectionChanged();
520521
if (tracker.hasSelection() && mActionMode == null) {
522+
adapter.setMultiSelect(true);
521523
mActionMode = startSupportActionMode(new MultiSelectedActionModeCallback(MainActivity.this,MainActivity.this, coordinatorLayout, binding.activityNotesListView.fabCreate, mainViewModel, MainActivity.this, canMoveNoteToAnotherAccounts, tracker, getSupportFragmentManager()));
522524
}
523525
if (mActionMode != null) {
@@ -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: 33 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,32 @@ 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+
isMultiSelect = bool;
186+
// endless loop incoming...
187+
//notifyDataSetChanged();
188+
}
189+
163190
public Item getItem(int notePosition) {
164191
return itemList.get(notePosition);
165192
}
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: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
android:id="@+id/noteSwipeable"
3636
android:layout_width="match_parent"
3737
android:layout_height="wrap_content"
38-
android:background="@drawable/list_item_background_selector"
3938
android:baselineAligned="false"
4039
android:paddingStart="@dimen/spacer_activity_sides"
4140
android:paddingEnd="@null">
@@ -122,6 +121,18 @@
122121
tools:text="@tools:sample/lorem/random" />
123122
</LinearLayout>
124123
</LinearLayout>
124+
125+
<ImageView
126+
android:id="@+id/custom_checkbox"
127+
android:layout_width="wrap_content"
128+
android:layout_height="match_parent"
129+
android:layout_gravity="top"
130+
android:clickable="false"
131+
android:contentDescription="@null"
132+
android:focusable="false"
133+
android:paddingStart="@dimen/spacer_1x"
134+
android:paddingEnd="@dimen/spacer_1x"
135+
android:src="@drawable/ic_checkbox_blank_outline" />
125136
</LinearLayout>
126137

127138
</FrameLayout>

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
android:id="@+id/noteSwipeable"
3636
android:layout_width="match_parent"
3737
android:layout_height="wrap_content"
38-
android:background="@drawable/list_item_background_selector"
3938
android:baselineAligned="false"
4039
android:gravity="center_vertical"
4140
android:paddingStart="@dimen/spacer_activity_sides"
@@ -94,6 +93,18 @@
9493
android:textSize="@dimen/secondary_font_size"
9594
tools:maxLength="15"
9695
tools:text="@tools:sample/lorem/random" />
96+
97+
<ImageView
98+
android:id="@+id/custom_checkbox"
99+
android:layout_width="wrap_content"
100+
android:layout_height="match_parent"
101+
android:layout_gravity="top"
102+
android:clickable="false"
103+
android:contentDescription="@null"
104+
android:focusable="false"
105+
android:paddingStart="@dimen/spacer_1x"
106+
android:paddingEnd="@dimen/spacer_1x"
107+
android:src="@drawable/ic_checkbox_blank_outline" />
97108
</LinearLayout>
98109

99110
</FrameLayout>

0 commit comments

Comments
 (0)