Skip to content

🎨 Update Material Design to be more M3 #2729

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 25, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -232,5 +232,7 @@ public void applyBrand(int color) {
final var util = BrandingUtil.of(color, this);
util.platform.themeStatusBar(this);
util.material.themeToolbar(binding.toolbar);
util.platform.colorViewBackground(getWindow().getDecorView());
util.platform.colorViewBackground(binding.getRoot());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

import android.os.Bundle;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
Expand Down Expand Up @@ -58,6 +57,8 @@ public void applyBrand(int color) {
util.platform.themeStatusBar(this);
util.material.themeToolbar(binding.toolbar);
util.material.themeTabLayoutOnSurface(binding.tabs);
util.platform.colorViewBackground(getWindow().getDecorView());
util.platform.colorViewBackground(binding.getRoot());
}

private static class TabsStateAdapter extends FragmentStateAdapter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.StateListDrawable;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
Expand All @@ -26,6 +28,7 @@
import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.core.content.res.ResourcesCompat;
import androidx.core.graphics.drawable.DrawableCompat;

import com.google.android.material.appbar.AppBarLayout;
Expand Down Expand Up @@ -209,4 +212,44 @@ public void themeToolbarSearchView(@NonNull SearchView searchView) {
return searchView;
});
}

public void themeInternalLinkIcon(ImageView view) {
withScheme(view, scheme -> {
view
.getBackground()
.setColorFilter(ResourcesCompat.getColor(view.getContext().getResources(),
R.color.nc_grey,
null),
PorterDuff.Mode.SRC_IN);
view
.getDrawable()
.mutate()
.setColorFilter(ResourcesCompat.getColor(view.getContext().getResources(),
R.color.icon_on_nc_grey,
null),
PorterDuff.Mode.SRC_IN);
return view;
});
}

public void themeBackgroundItemView(View view) {
withScheme(view, scheme -> {
StateListDrawable res = new StateListDrawable();
res.addState(new int[]{android.R.attr.state_activated}, new ColorDrawable(dynamicColor.secondaryContainer().getArgb(scheme)));
res.addState(new int[]{}, new ColorDrawable(dynamicColor.surface().getArgb(scheme)));
view.setBackground(res);
return view;
});
}

public void themeCard(@NonNull MaterialCardView view) {
withScheme(view, scheme -> {
view.setBackgroundTintList(buildColorStateList(
new Pair<>(android.R.attr.state_activated, dynamicColor.secondaryContainer().getArgb(scheme)),
new Pair<>(-android.R.attr.state_activated, dynamicColor.surface().getArgb(scheme)))
);
view.setStrokeColor(dynamicColor.outlineVariant().getArgb(scheme));
return view;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -448,5 +448,7 @@ public void applyBrand(int color) {
final var util = BrandingUtil.of(color, this);
util.platform.themeStatusBar(this);
util.material.themeToolbar(binding.toolbar);
util.platform.colorViewBackground(getWindow().getDecorView());
util.platform.colorViewBackground(binding.getRoot());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,7 @@ public void onSelectionChanged() {
super.onSelectionChanged();
if (tracker.hasSelection() && mActionMode == null) {
mActionMode = startSupportActionMode(new MultiSelectedActionModeCallback(MainActivity.this,MainActivity.this, coordinatorLayout, binding.activityNotesListView.fabCreate, mainViewModel, MainActivity.this, canMoveNoteToAnotherAccounts, tracker, getSupportFragmentManager()));
adapter.setMultiSelect(true);
}
if (mActionMode != null) {
if (tracker.hasSelection()) {
Expand All @@ -527,6 +528,7 @@ public void onSelectionChanged() {
} else {
mActionMode.finish();
mActionMode = null;
adapter.setMultiSelect(false);
}
}
}
Expand Down Expand Up @@ -604,6 +606,9 @@ public void applyBrand(int color) {
util.platform.colorNavigationView(binding.navigationView);
util.material.themeFAB(activityBinding.fabCreate);
util.notes.themeSearchCardView(binding.activityNotesListView.searchBarWrapper);
util.platform.colorViewBackground(getWindow().getDecorView());
util.platform.colorViewBackground(binding.getRoot());
util.platform.colorViewBackground(binding.activityNotesListView.activityNotesListView);
util.platform.colorTextView(binding.activityNotesListView.searchText, ColorRole.ON_SURFACE_VARIANT);
util.notes.themeSearchToolbar(binding.activityNotesListView.searchToolbar);
util.notes.themeToolbarSearchView(binding.activityNotesListView.searchView);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@

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

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import androidx.annotation.ColorInt;
import androidx.annotation.IntRange;
Expand All @@ -23,11 +28,14 @@
import androidx.recyclerview.selection.SelectionTracker;
import androidx.recyclerview.widget.RecyclerView;

import com.nextcloud.android.common.ui.theme.utils.ColorRole;

import java.util.ArrayList;
import java.util.List;

import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.branding.Branded;
import it.niedermann.owncloud.notes.branding.BrandingUtil;
import it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemGridBinding;
import it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemGridOnlyTitleBinding;
import it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemWithExcerptBinding;
Expand Down Expand Up @@ -66,6 +74,8 @@ public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> i
@Nullable
private Integer swipedPosition;

private boolean isMultiSelect = false;

public <T extends Context & NoteClickListener> ItemAdapter(@NonNull T context, boolean gridView) {
this.noteClickListener = context;
this.gridView = gridView;
Expand Down Expand Up @@ -104,13 +114,19 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int
if (gridView) {
switch (viewType) {
case TYPE_SECTION -> {
return new SectionViewHolder(ItemNotesListSectionItemBinding.inflate(inflater));
ItemNotesListSectionItemBinding binding = ItemNotesListSectionItemBinding.inflate(inflater);
BrandingUtil.of(color, parent.getContext()).platform.colorTextView(binding.sectionTitle);
return new SectionViewHolder(binding);
}
case TYPE_NOTE_ONLY_TITLE -> {
return new NoteViewGridHolderOnlyTitle(ItemNotesListNoteItemGridOnlyTitleBinding.inflate(inflater, parent, false), noteClickListener, monospace, fontSize);
ItemNotesListNoteItemGridOnlyTitleBinding binding = ItemNotesListNoteItemGridOnlyTitleBinding.inflate(inflater, parent, false);
BrandingUtil.of(color, parent.getContext()).notes.themeCard(binding.card);
return new NoteViewGridHolderOnlyTitle(binding, noteClickListener, monospace, fontSize);
}
case TYPE_NOTE_WITH_EXCERPT, TYPE_NOTE_WITHOUT_EXCERPT -> {
return new NoteViewGridHolder(ItemNotesListNoteItemGridBinding.inflate(inflater, parent, false), noteClickListener, monospace, fontSize);
ItemNotesListNoteItemGridBinding binding = ItemNotesListNoteItemGridBinding.inflate(inflater, parent, false);
BrandingUtil.of(color, parent.getContext()).notes.themeCard(binding.card);
return new NoteViewGridHolder(binding, noteClickListener, monospace, fontSize);
}
default -> {
throw new IllegalArgumentException("Not supported viewType: " + viewType);
Expand All @@ -119,13 +135,19 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int
} else {
switch (viewType) {
case TYPE_SECTION -> {
return new SectionViewHolder(ItemNotesListSectionItemBinding.inflate(inflater));
ItemNotesListSectionItemBinding binding = ItemNotesListSectionItemBinding.inflate(inflater);
BrandingUtil.of(color, parent.getContext()).platform.colorTextView(binding.sectionTitle);
return new SectionViewHolder(binding);
}
case TYPE_NOTE_WITH_EXCERPT -> {
return new NoteViewHolderWithExcerpt(ItemNotesListNoteItemWithExcerptBinding.inflate(inflater, parent, false), noteClickListener);
ItemNotesListNoteItemWithExcerptBinding binding = ItemNotesListNoteItemWithExcerptBinding.inflate(inflater, parent, false);
BrandingUtil.of(color, parent.getContext()).notes.themeBackgroundItemView(binding.noteSwipeable);
return new NoteViewHolderWithExcerpt(binding, noteClickListener);
}
case TYPE_NOTE_ONLY_TITLE, TYPE_NOTE_WITHOUT_EXCERPT -> {
return new NoteViewHolderWithoutExcerpt(ItemNotesListNoteItemWithoutExcerptBinding.inflate(inflater, parent, false), noteClickListener);
ItemNotesListNoteItemWithoutExcerptBinding binding = ItemNotesListNoteItemWithoutExcerptBinding.inflate(inflater, parent, false);
BrandingUtil.of(color, parent.getContext()).notes.themeBackgroundItemView(binding.noteSwipeable);
return new NoteViewHolderWithoutExcerpt(binding, noteClickListener);
}
default -> {
throw new IllegalArgumentException("Not supported viewType: " + viewType);
Expand All @@ -149,17 +171,37 @@ public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, int
switch (getItemViewType(position)) {
case TYPE_SECTION ->
((SectionViewHolder) holder).bind((SectionItem) itemList.get(position));
case TYPE_NOTE_WITH_EXCERPT,
TYPE_NOTE_WITHOUT_EXCERPT,
TYPE_NOTE_ONLY_TITLE ->
((NoteViewHolder) holder).bind(isSelected, (Note) itemList.get(position), showCategory, color, searchQuery);
case TYPE_NOTE_WITH_EXCERPT, TYPE_NOTE_WITHOUT_EXCERPT, TYPE_NOTE_ONLY_TITLE -> {
holder.itemView.findViewById(R.id.custom_checkbox).setVisibility(tracker != null && tracker.hasSelection() ? View.VISIBLE : View.GONE);
if (isSelected) {
holder.itemView.setBackgroundColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.bg_highlighted));
((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));
} else {
holder.itemView.setBackgroundColor(holder.itemView.getContext().getColor(com.nextcloud.android.common.ui.R.color.bg_default));
((ImageView) holder.itemView.findViewById(R.id.custom_checkbox)).setImageResource(R.drawable.ic_checkbox_blank_outline);
}
holder.itemView.findViewById(R.id.custom_checkbox).setVisibility(isMultiSelect ? View.VISIBLE : View.GONE);
((NoteViewHolder) holder).bind(isSelected, (Note) itemList.get(position), showCategory, color, searchQuery);
}
}
}

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

@SuppressLint("NotifyDataSetChanged")
public void setMultiSelect(boolean bool) {
if (isMultiSelect != bool) {
isMultiSelect = bool;
new Handler(Looper.getMainLooper()).post(this::notifyDataSetChanged);
}
}

public boolean isMultiSelect() {
return this.isMultiSelect;
}

public Item getItem(int notePosition) {
return itemList.get(notePosition);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public NoteViewHolder(@NonNull View v, @NonNull NoteClickListener noteClickListe

@CallSuper
public void bind(boolean isSelected, @NonNull Note note, boolean showCategory, @ColorInt int color, @Nullable CharSequence searchQuery) {
itemView.setActivated(isSelected);
itemView.setSelected(isSelected);
itemView.setOnClickListener((view) -> noteClickListener.onNoteClick(getLayoutPosition(), view));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,5 +228,7 @@ public void applyBrand(int color) {
final var util = BrandingUtil.of(color, this);
util.platform.themeStatusBar(this);
util.material.themeToolbar(binding.toolbar);
util.platform.colorViewBackground(getWindow().getDecorView());
util.platform.colorViewBackground(binding.getRoot());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,7 @@ public void applyBrand(int color) {
final var util = BrandingUtil.of(color, this);
util.platform.themeStatusBar(this);
util.material.themeToolbar(binding.toolbar);
util.platform.colorViewBackground(getWindow().getDecorView());
util.platform.colorViewBackground(binding.getRoot());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,9 @@ public void applyBrand(int color) {
util.platform.themeStatusBar(this);
util.androidx.themeToolbarSearchView(binding.searchView);
util.platform.themeHorizontalProgressBar(binding.progressBar);
util.platform.colorViewBackground(getWindow().getDecorView());
util.platform.colorViewBackground(binding.getRoot());
util.platform.colorTextButtons(binding.btnShareButton);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ class NoteShareDetailActivity : BrandedActivity(),
colorMaterialButtonPrimaryOutlined(shareProcessBtnCancel)
}
}
util.platform.colorViewBackground(window.decorView)
util.platform.colorViewBackground(binding.getRoot())
}

override fun onConfigurationChanged(newConfig: Configuration) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,30 @@ package it.niedermann.owncloud.notes.share.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.ColorInt
import androidx.recyclerview.widget.RecyclerView
import com.nextcloud.android.common.ui.theme.utils.ColorRole
import it.niedermann.owncloud.notes.branding.Branded
import it.niedermann.owncloud.notes.branding.BrandingUtil
import it.niedermann.owncloud.notes.databinding.ItemQuickSharePermissionsBinding
import it.niedermann.owncloud.notes.share.model.QuickPermissionModel

class QuickSharingPermissionsAdapter(
private val quickPermissionList: MutableList<QuickPermissionModel>,
private val onPermissionChangeListener: QuickSharingPermissionViewHolder.OnPermissionChangeListener,
private var color: Int = 0
) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
RecyclerView.Adapter<RecyclerView.ViewHolder>(), Branded {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val binding = ItemQuickSharePermissionsBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return QuickSharingPermissionViewHolder(binding, binding.root, onPermissionChangeListener)
BrandingUtil.of(color, parent.context).platform.colorImageView(binding.tvQuickShareCheckIcon, ColorRole.PRIMARY)
return QuickSharingPermissionViewHolder(
binding,
binding.root,
onPermissionChangeListener,
BrandingUtil.of(color, binding.root.context)
)
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
Expand All @@ -33,18 +45,23 @@ class QuickSharingPermissionsAdapter(
return quickPermissionList.size
}

override fun applyBrand(color: Int) {
this.color = color
notifyDataSetChanged()
}

class QuickSharingPermissionViewHolder(
val binding: ItemQuickSharePermissionsBinding,
itemView: View,
val onPermissionChangeListener: OnPermissionChangeListener,
val util: BrandingUtil,
) :
RecyclerView
.ViewHolder(itemView) {

fun bindData(quickPermissionModel: QuickPermissionModel) {
binding.tvQuickShareName.text = quickPermissionModel.permissionName
if (quickPermissionModel.isSelected) {
// viewThemeUtils.platform.colorImageView(binding.tvQuickShareCheckIcon)
binding.tvQuickShareCheckIcon.visibility = View.VISIBLE
} else {
binding.tvQuickShareCheckIcon.visibility = View.INVISIBLE
Expand Down
Loading
Loading