From 039fb0c50556514d215967f66acce813a7cafceb Mon Sep 17 00:00:00 2001 From: Grishka Date: Fri, 17 Mar 2023 22:07:28 +0300 Subject: [PATCH] Profile redesign: header --- mastodon/build.gradle | 2 +- .../fragments/BaseStatusListFragment.java | 4 +- .../fragments/HashtagTimelineFragment.java | 2 +- .../android/fragments/HomeFragment.java | 2 +- .../android/fragments/ProfileFragment.java | 316 +++++++------ .../onboarding/AccountActivationFragment.java | 7 - .../GoogleMadeMeAddThisFragment.java | 4 - .../InstanceChooserLoginFragment.java | 1 - .../onboarding/InstanceRulesFragment.java | 4 - .../OnboardingFollowSuggestionsFragment.java | 4 - .../OnboardingProfileSetupFragment.java | 4 - .../fragments/onboarding/SignupFragment.java | 4 - .../CoverOverlayGradientDrawable.java | 58 --- .../android/ui/utils/UiUtils.java | 6 + .../android/ui/views/CoverImageView.java | 5 +- .../utils/ElevationOnScrollListener.java | 4 +- .../drawable/bg_button_borderless_rounded.xml | 9 + mastodon/src/main/res/drawable/bg_fab.xml | 14 +- .../main/res/drawable/edit_avatar_overlay.xml | 10 +- ...d_photo_alternate_48px_dark_on_surface.xml | 9 + .../main/res/drawable/ic_bookmark_24px.xml | 9 + .../src/main/res/drawable/ic_edit_24px.xml | 9 + .../src/main/res/drawable/ic_save_24px.xml | 9 + .../src/main/res/drawable/ic_share_24px.xml | 9 + .../src/main/res/drawable/ic_star_24px.xml | 9 + .../src/main/res/drawable/profile_ava_bg.xml | 4 +- .../main/res/drawable/tab_indicator_m3.xml | 10 + ...fragment_onboarding_follow_suggestions.xml | 2 +- .../fragment_onboarding_profile_setup.xml | 5 +- .../src/main/res/layout/fragment_profile.xml | 425 ++++++++++-------- .../res/layout/recycler_fragment_with_fab.xml | 10 +- mastodon/src/main/res/menu/profile_own.xml | 6 +- mastodon/src/main/res/values-v27/colors.xml | 2 +- mastodon/src/main/res/values/colors.xml | 2 +- mastodon/src/main/res/values/ids.xml | 2 + mastodon/src/main/res/values/strings.xml | 1 + mastodon/src/main/res/values/styles.xml | 35 +- 37 files changed, 545 insertions(+), 473 deletions(-) delete mode 100644 mastodon/src/main/java/org/joinmastodon/android/ui/drawables/CoverOverlayGradientDrawable.java create mode 100644 mastodon/src/main/res/drawable/bg_button_borderless_rounded.xml create mode 100644 mastodon/src/main/res/drawable/ic_add_photo_alternate_48px_dark_on_surface.xml create mode 100644 mastodon/src/main/res/drawable/ic_bookmark_24px.xml create mode 100644 mastodon/src/main/res/drawable/ic_edit_24px.xml create mode 100644 mastodon/src/main/res/drawable/ic_save_24px.xml create mode 100644 mastodon/src/main/res/drawable/ic_share_24px.xml create mode 100644 mastodon/src/main/res/drawable/ic_star_24px.xml create mode 100644 mastodon/src/main/res/drawable/tab_indicator_m3.xml diff --git a/mastodon/build.gradle b/mastodon/build.gradle index cdb7525d..22492c75 100644 --- a/mastodon/build.gradle +++ b/mastodon/build.gradle @@ -70,7 +70,7 @@ dependencies { implementation 'me.grishka.litex:viewpager:1.0.0' implementation 'me.grishka.litex:viewpager2:1.0.0' implementation 'me.grishka.litex:palette:1.0.0' - implementation 'me.grishka.appkit:appkit:1.2.7' + implementation 'me.grishka.appkit:appkit:1.2.8' implementation 'com.google.code.gson:gson:2.8.9' implementation 'org.jsoup:jsoup:1.14.3' implementation 'com.squareup:otto:1.3.8' diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java index 0bfdc7d3..4157513c 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java @@ -629,9 +629,9 @@ public abstract class BaseStatusListFragment exten private int currentMediaHiddenLayoutsWidth=0; { - dividerPaint.setColor(UiUtils.getThemeColor(getActivity(), R.attr.colorPollVoted)); + dividerPaint.setColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Outline)); dividerPaint.setStyle(Paint.Style.STROKE); - dividerPaint.setStrokeWidth(V.dp(1)); + dividerPaint.setStrokeWidth(V.dp(0.5f)); } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java index 70f13217..43a68898 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java @@ -66,6 +66,6 @@ public class HashtagTimelineFragment extends StatusListFragment{ @Override protected void onSetFabBottomInset(int inset){ - ((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(24)+inset; + ((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(16)+inset; } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java index 5b8072c0..c50489c3 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java @@ -164,7 +164,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene @Override public boolean wantsLightStatusBar(){ - return currentTab!=R.id.tab_profile && !UiUtils.isDarkTheme(); + return !UiUtils.isDarkTheme(); } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java index 50be8f64..1a3fdaa7 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java @@ -10,16 +10,21 @@ import android.content.ClipData; import android.content.ClipboardManager; import android.content.Intent; import android.content.res.Configuration; +import android.content.res.TypedArray; import android.graphics.Outline; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.style.ImageSpan; -import android.view.Gravity; +import android.transition.ChangeBounds; +import android.transition.Fade; +import android.transition.TransitionManager; +import android.transition.TransitionSet; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -29,12 +34,11 @@ import android.view.ViewGroup; import android.view.ViewOutlineProvider; import android.view.ViewTreeObserver; import android.view.WindowInsets; -import android.widget.Button; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.ProgressBar; -import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import android.widget.Toolbar; @@ -55,9 +59,10 @@ import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.AccountField; import org.joinmastodon.android.model.Attachment; import org.joinmastodon.android.model.Relationship; +import org.joinmastodon.android.ui.M3AlertDialogBuilder; +import org.joinmastodon.android.ui.OutlineProviders; import org.joinmastodon.android.ui.SimpleViewHolder; import org.joinmastodon.android.ui.SingleImagePhotoViewerListener; -import org.joinmastodon.android.ui.drawables.CoverOverlayGradientDrawable; import org.joinmastodon.android.ui.photoviewer.PhotoViewer; import org.joinmastodon.android.ui.tabs.TabLayout; import org.joinmastodon.android.ui.tabs.TabLayoutMediator; @@ -67,6 +72,7 @@ import org.joinmastodon.android.ui.utils.UiUtils; import org.joinmastodon.android.ui.views.CoverImageView; import org.joinmastodon.android.ui.views.NestedRecyclerScrollView; import org.joinmastodon.android.ui.views.ProgressBarButton; +import org.joinmastodon.android.utils.ElevationOnScrollListener; import org.parceler.Parcels; import java.time.LocalDateTime; @@ -92,6 +98,7 @@ import me.grishka.appkit.imageloader.ViewImageLoader; import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest; import me.grishka.appkit.utils.CubicBezierInterpolator; import me.grishka.appkit.utils.V; +import me.grishka.appkit.views.FragmentRootLinearLayout; public class ProfileFragment extends LoaderFragment implements OnBackPressedListener, ScrollableToTop{ private static final int AVATAR_RESULT=722; @@ -100,7 +107,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList private ImageView avatar; private CoverImageView cover; private View avatarBorder; - private TextView name, username, bio, followersCount, followersLabel, followingCount, followingLabel, postsCount, postsLabel; + private TextView name, username, bio, followersCount, followersLabel, followingCount, followingLabel; private ProgressBarButton actionButton; private ViewPager2 pager; private NestedRecyclerScrollView scrollView; @@ -108,19 +115,20 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList private ProfileAboutFragment aboutFragment; private TabLayout tabbar; private SwipeRefreshLayout refreshLayout; - private CoverOverlayGradientDrawable coverGradient=new CoverOverlayGradientDrawable(); - private float titleTransY; - private View postsBtn, followersBtn, followingBtn; + private View followersBtn, followingBtn; private EditText nameEdit, bioEdit; private ProgressBar actionProgress; private FrameLayout[] tabViews; private TabLayoutMediator tabLayoutMediator; private TextView followsYouView; + private LinearLayout countersLayout; + private View nameEditWrap, bioEditWrap; + private View tabsDivider; + private View actionButtonWrap; private Account account; private String accountID; private Relationship relationship; - private int statusBarHeight; private boolean isOwnProfile; private ArrayList fields=new ArrayList<>(); @@ -132,10 +140,11 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList private WindowInsets childInsets; private PhotoViewer currentPhotoViewer; private boolean editModeLoading; - - public ProfileFragment(){ - super(R.layout.loader_fragment_overlay_toolbar); - } + private ElevationOnScrollListener onScrollListener; + private Drawable tabsColorBackground; + private boolean tabBarIsAtTop; + private Animator tabBarColorAnim; + private MenuItem editSaveMenuItem; @Override public void onCreate(Bundle savedInstanceState){ @@ -180,9 +189,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList followingCount=content.findViewById(R.id.following_count); followingLabel=content.findViewById(R.id.following_label); followingBtn=content.findViewById(R.id.following_btn); - postsCount=content.findViewById(R.id.posts_count); - postsLabel=content.findViewById(R.id.posts_label); - postsBtn=content.findViewById(R.id.posts_btn); actionButton=content.findViewById(R.id.profile_action_btn); pager=content.findViewById(R.id.pager); scrollView=content.findViewById(R.id.scroller); @@ -190,24 +196,22 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList refreshLayout=content.findViewById(R.id.refresh_layout); nameEdit=content.findViewById(R.id.name_edit); bioEdit=content.findViewById(R.id.bio_edit); + nameEditWrap=content.findViewById(R.id.name_edit_wrap); + bioEditWrap=content.findViewById(R.id.bio_edit_wrap); actionProgress=content.findViewById(R.id.action_progress); fab=content.findViewById(R.id.fab); followsYouView=content.findViewById(R.id.follows_you); + countersLayout=content.findViewById(R.id.profile_counters); + tabsDivider=content.findViewById(R.id.tabs_divider); + actionButtonWrap=content.findViewById(R.id.profile_action_btn_wrap); - avatar.setOutlineProvider(new ViewOutlineProvider(){ - @Override - public void getOutline(View view, Outline outline){ - outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), V.dp(25)); - } - }); + avatar.setOutlineProvider(OutlineProviders.roundedRect(24)); avatar.setClipToOutline(true); FrameLayout sizeWrapper=new FrameLayout(getActivity()){ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ - Toolbar toolbar=getToolbar(); - pager.getLayoutParams().height=MeasureSpec.getSize(heightMeasureSpec)-getPaddingTop()-getPaddingBottom()-toolbar.getLayoutParams().height-statusBarHeight-V.dp(38); - coverGradient.setTopPadding(statusBarHeight+toolbar.getLayoutParams().height); + pager.getLayoutParams().height=MeasureSpec.getSize(heightMeasureSpec)-getPaddingTop()-getPaddingBottom()-V.dp(48); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } }; @@ -235,7 +239,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList sizeWrapper.addView(content); - tabbar.setTabTextColors(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorSecondary), UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary)); + tabbar.setTabTextColors(UiUtils.getThemeColor(getActivity(), R.attr.colorM3OnSurfaceVariant), UiUtils.getThemeColor(getActivity(), R.attr.colorM3Primary)); tabbar.setTabTextSize(V.dp(16)); tabLayoutMediator=new TabLayoutMediator(tabbar, pager, new TabLayoutMediator.TabConfigurationStrategy(){ @Override @@ -250,7 +254,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList } }); - cover.setForeground(coverGradient); cover.setOutlineProvider(new ViewOutlineProvider(){ @Override public void getOutline(View view, Outline outline){ @@ -375,12 +378,23 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList } }); + tabsColorBackground=((LayerDrawable)tabbar.getBackground()).findDrawableByLayerId(R.id.color_overlay); + + onScrollListener=new ElevationOnScrollListener((FragmentRootLinearLayout) view, getToolbar()); scrollView.setOnScrollChangeListener(this::onScrollChanged); - titleTransY=getToolbar().getLayoutParams().height; - if(toolbarTitleView!=null){ - toolbarTitleView.setTranslationY(titleTransY); - toolbarSubtitleView.setTranslationY(titleTransY); - } + scrollView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){ + @Override + public boolean onPreDraw(){ + scrollView.getViewTreeObserver().removeOnPreDrawListener(this); + + tabBarIsAtTop=!scrollView.canScrollVertically(1) && scrollView.getHeight()>0; + tabsColorBackground.setAlpha(tabBarIsAtTop ? 20 : 0); + tabbar.setTranslationZ(tabBarIsAtTop ? V.dp(3) : 0); + tabsDivider.setAlpha(tabBarIsAtTop ? 0 : 1); + + return true; + } + }); } @Override @@ -396,21 +410,18 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList @Override public void onApplyWindowInsets(WindowInsets insets){ - statusBarHeight=insets.getSystemWindowInsetTop(); if(contentView!=null){ - ((ViewGroup.MarginLayoutParams) getToolbar().getLayoutParams()).topMargin=statusBarHeight; - refreshLayout.setProgressViewEndTarget(true, statusBarHeight+refreshLayout.getProgressCircleDiameter()+V.dp(24)); if(Build.VERSION.SDK_INT>=29 && insets.getTappableElementInsets().bottom==0){ int insetBottom=insets.getSystemWindowInsetBottom(); childInsets=insets.inset(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), 0); - ((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(24)+insetBottom; + ((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(16)+insetBottom; applyChildWindowInsets(); insets=insets.inset(0, 0, 0, insetBottom); }else{ - ((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(24); + ((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(16); } } - super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), 0, insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom())); + super.onApplyWindowInsets(insets); } private void applyChildWindowInsets(){ @@ -459,16 +470,20 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList } followersCount.setText(UiUtils.abbreviateNumber(account.followersCount)); followingCount.setText(UiUtils.abbreviateNumber(account.followingCount)); - postsCount.setText(UiUtils.abbreviateNumber(account.statusesCount)); followersLabel.setText(getResources().getQuantityString(R.plurals.followers, (int)Math.min(999, account.followersCount))); followingLabel.setText(getResources().getQuantityString(R.plurals.following, (int)Math.min(999, account.followingCount))); - postsLabel.setText(getResources().getQuantityString(R.plurals.posts, (int)Math.min(999, account.statusesCount))); UiUtils.loadCustomEmojiInTextView(name); UiUtils.loadCustomEmojiInTextView(bio); if(AccountSessionManager.getInstance().isSelf(accountID, account)){ actionButton.setText(R.string.edit_profile); + TypedArray ta=actionButton.getContext().obtainStyledAttributes(R.style.Widget_Mastodon_M3_Button_Tonal, new int[]{android.R.attr.background}); + actionButton.setBackground(ta.getDrawable(0)); + ta.recycle(); + ta=actionButton.getContext().obtainStyledAttributes(R.style.Widget_Mastodon_M3_Button_Tonal, new int[]{android.R.attr.textColor}); + actionButton.setTextColor(ta.getColorStateList(0)); + ta.recycle(); }else{ actionButton.setVisibility(View.GONE); } @@ -503,33 +518,21 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList } private void updateToolbar(){ - getToolbar().setBackgroundColor(0); - if(toolbarTitleView!=null){ - toolbarTitleView.setTranslationY(titleTransY); - toolbarSubtitleView.setTranslationY(titleTransY); - } getToolbar().setOnClickListener(v->scrollToTop()); getToolbar().setNavigationContentDescription(R.string.back); - } - - @Override - public boolean wantsLightStatusBar(){ - return false; + UiUtils.setToolbarWithSubtitleAppearance(getToolbar()); + if(onScrollListener!=null){ + onScrollListener.setViews(getToolbar()); + } } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){ if(isOwnProfile && isInEditMode){ - Button cancelButton=new Button(getActivity(), null, 0, R.style.Widget_Mastodon_Button_Secondary_LightOnDark); - cancelButton.setText(R.string.cancel); - cancelButton.setOnClickListener(v->exitEditMode()); - FrameLayout wrap=new FrameLayout(getActivity()); - wrap.addView(cancelButton, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.TOP|Gravity.LEFT)); - wrap.setPadding(V.dp(16), V.dp(4), V.dp(16), V.dp(8)); - wrap.setClipToPadding(false); - MenuItem item=menu.add(R.string.cancel); - item.setActionView(wrap); - item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + editSaveMenuItem=menu.add(0, R.id.save, 0, R.string.save_changes); + editSaveMenuItem.setIcon(R.drawable.ic_save_24px); + editSaveMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + editSaveMenuItem.setVisible(!isActionButtonInView()); return; } if(relationship==null && !isOwnProfile) @@ -599,15 +602,13 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList Bundle args=new Bundle(); args.putString("account", accountID); Nav.go(getActivity(), FavoritedStatusListFragment.class, args); + }else if(id==R.id.save){ + if(isInEditMode) + saveAndExitEditMode(); } return true; } - @Override - protected int getToolbarResource(){ - return R.layout.profile_toolbar; - } - private void loadRelationship(){ new GetAccountRelationships(Collections.singletonList(account.id)) .setCallback(new Callback<>(){ @@ -630,41 +631,64 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList private void updateRelationship(){ invalidateOptionsMenu(); actionButton.setVisibility(View.VISIBLE); - UiUtils.setRelationshipToActionButton(relationship, actionButton); + UiUtils.setRelationshipToActionButtonM3(relationship, actionButton); actionProgress.setIndeterminateTintList(actionButton.getTextColors()); followsYouView.setVisibility(relationship.followedBy ? View.VISIBLE : View.GONE); } private void onScrollChanged(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){ - int topBarsH=getToolbar().getHeight()+statusBarHeight; - if(scrollY>avatarBorder.getTop()-topBarsH){ - float avaAlpha=Math.max(1f-((scrollY-(avatarBorder.getTop()-topBarsH))/(float)V.dp(38)), 0f); - avatarBorder.setAlpha(avaAlpha); - }else{ - avatarBorder.setAlpha(1f); - } - if(scrollY>cover.getHeight()-topBarsH){ - cover.setTranslationY(scrollY-(cover.getHeight()-topBarsH)); + if(scrollY>cover.getHeight()){ + cover.setTranslationY(scrollY-(cover.getHeight())); cover.setTranslationZ(V.dp(10)); - cover.setTransform(cover.getHeight()/2f-topBarsH/2f, 1f); + cover.setTransform(cover.getHeight()/2f); }else{ cover.setTranslationY(0f); cover.setTranslationZ(0f); - cover.setTransform(scrollY/2f, 1f); + cover.setTransform(scrollY/2f); } - coverGradient.setTopOffset(scrollY); cover.invalidate(); - titleTransY=getToolbar().getHeight(); - if(scrollY>name.getTop()-topBarsH){ - titleTransY=Math.max(0f, titleTransY-(scrollY-(name.getTop()-topBarsH))); - } - if(toolbarTitleView!=null){ - toolbarTitleView.setTranslationY(titleTransY); - toolbarSubtitleView.setTranslationY(titleTransY); - } if(currentPhotoViewer!=null){ currentPhotoViewer.offsetView(0, oldScrollY-scrollY); } + onScrollListener.onScrollChange(v, scrollX, scrollY, oldScrollX, oldScrollY); + + boolean newTabBarIsAtTop=!scrollView.canScrollVertically(1); + if(newTabBarIsAtTop!=tabBarIsAtTop){ + tabBarIsAtTop=newTabBarIsAtTop; + + if(tabBarIsAtTop){ + // ScrollView would sometimes leave 1 pixel unscrolled, force it into the correct scrollY + int maxY=scrollView.getChildAt(0).getHeight()-scrollView.getHeight(); + if(scrollView.getScrollY()!=maxY) + scrollView.scrollTo(0, maxY); + } + + if(tabBarColorAnim!=null) + tabBarColorAnim.cancel(); + AnimatorSet set=new AnimatorSet(); + set.playTogether( + ObjectAnimator.ofInt(tabsColorBackground, "alpha", tabBarIsAtTop ? 20 : 0), + ObjectAnimator.ofFloat(tabbar, View.TRANSLATION_Z, tabBarIsAtTop ? V.dp(3) : 0), + ObjectAnimator.ofFloat(getToolbar(), View.TRANSLATION_Z, tabBarIsAtTop ? 0 : V.dp(3)), + ObjectAnimator.ofFloat(tabsDivider, View.ALPHA, tabBarIsAtTop ? 0 : 1) + ); + set.setDuration(150); + set.setInterpolator(CubicBezierInterpolator.DEFAULT); + set.addListener(new AnimatorListenerAdapter(){ + @Override + public void onAnimationEnd(Animator animation){ + tabBarColorAnim=null; + } + }); + tabBarColorAnim=set; + set.start(); + } + if(isInEditMode && editSaveMenuItem!=null){ + boolean buttonInView=isActionButtonInView(); + if(buttonInView==editSaveMenuItem.isVisible()){ + editSaveMenuItem.setVisible(!buttonInView); + } + } } private Fragment getFragmentForPage(int page){ @@ -695,6 +719,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList private void setActionProgressVisible(boolean visible){ actionButton.setTextVisible(!visible); actionProgress.setVisibility(visible ? View.VISIBLE : View.GONE); + if(visible) + actionProgress.setIndeterminateTintList(actionButton.getTextColors()); actionButton.setClickable(!visible); } @@ -732,38 +758,38 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList isInEditMode=true; invalidateOptionsMenu(); pager.setUserInputEnabled(false); - actionButton.setText(R.string.done); + actionButton.setText(R.string.save_changes); pager.setCurrentItem(3); - ArrayList animators=new ArrayList<>(); for(int i=0;i<3;i++){ - animators.add(ObjectAnimator.ofFloat(tabbar.getTabAt(i).view, View.ALPHA, .3f)); tabbar.getTabAt(i).view.setEnabled(false); } Drawable overlay=getResources().getDrawable(R.drawable.edit_avatar_overlay).mutate(); avatar.setForeground(overlay); - animators.add(ObjectAnimator.ofInt(overlay, "alpha", 0, 255)); - nameEdit.setVisibility(View.VISIBLE); + Toolbar toolbar=getToolbar(); + Drawable close=getToolbarContext().getDrawable(R.drawable.ic_baseline_close_24).mutate(); + close.setTint(UiUtils.getThemeColor(getToolbarContext(), R.attr.colorM3OnSurfaceVariant)); + toolbar.setNavigationIcon(close); + toolbar.setNavigationContentDescription(R.string.discard); + + ViewGroup parent=contentView.findViewById(R.id.scrollable_content); + TransitionManager.beginDelayedTransition(parent, new TransitionSet() + .addTransition(new Fade(Fade.IN | Fade.OUT)) + .addTransition(new ChangeBounds()) + .setDuration(250) + .setInterpolator(CubicBezierInterpolator.DEFAULT) + ); + + name.setVisibility(View.GONE); + username.setVisibility(View.GONE); + bio.setVisibility(View.GONE); + countersLayout.setVisibility(View.GONE); + + nameEditWrap.setVisibility(View.VISIBLE); nameEdit.setText(account.displayName); - RelativeLayout.LayoutParams lp=(RelativeLayout.LayoutParams) username.getLayoutParams(); - lp.addRule(RelativeLayout.BELOW, R.id.name_edit); - username.getParent().requestLayout(); - animators.add(ObjectAnimator.ofFloat(nameEdit, View.ALPHA, 0f, 1f)); - bioEdit.setVisibility(View.VISIBLE); + bioEditWrap.setVisibility(View.VISIBLE); bioEdit.setText(account.source.note); - animators.add(ObjectAnimator.ofFloat(bioEdit, View.ALPHA, 0f, 1f)); - animators.add(ObjectAnimator.ofFloat(bio, View.ALPHA, 0f)); - - animators.add(ObjectAnimator.ofFloat(postsBtn, View.ALPHA, .3f)); - animators.add(ObjectAnimator.ofFloat(followersBtn, View.ALPHA, .3f)); - animators.add(ObjectAnimator.ofFloat(followingBtn, View.ALPHA, .3f)); - - AnimatorSet set=new AnimatorSet(); - set.playTogether(animators); - set.setDuration(300); - set.setInterpolator(CubicBezierInterpolator.DEFAULT); - set.start(); aboutFragment.enterEditMode(account.source.fields); } @@ -774,39 +800,37 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList isInEditMode=false; invalidateOptionsMenu(); - ArrayList animators=new ArrayList<>(); actionButton.setText(R.string.edit_profile); for(int i=0;i<3;i++){ - animators.add(ObjectAnimator.ofFloat(tabbar.getTabAt(i).view, View.ALPHA, 1f)); + tabbar.getTabAt(i).view.setEnabled(true); } - animators.add(ObjectAnimator.ofInt(avatar.getForeground(), "alpha", 0)); - animators.add(ObjectAnimator.ofFloat(nameEdit, View.ALPHA, 0f)); - animators.add(ObjectAnimator.ofFloat(bioEdit, View.ALPHA, 0f)); - animators.add(ObjectAnimator.ofFloat(bio, View.ALPHA, 1f)); - animators.add(ObjectAnimator.ofFloat(postsBtn, View.ALPHA, 1f)); - animators.add(ObjectAnimator.ofFloat(followersBtn, View.ALPHA, 1f)); - animators.add(ObjectAnimator.ofFloat(followingBtn, View.ALPHA, 1f)); + pager.setUserInputEnabled(true); + avatar.setForeground(null); - AnimatorSet set=new AnimatorSet(); - set.playTogether(animators); - set.setDuration(200); - set.setInterpolator(CubicBezierInterpolator.DEFAULT); - set.addListener(new AnimatorListenerAdapter(){ - @Override - public void onAnimationEnd(Animator animation){ - for(int i=0;i<3;i++){ - tabbar.getTabAt(i).view.setEnabled(true); - } - pager.setUserInputEnabled(true); - nameEdit.setVisibility(View.GONE); - bioEdit.setVisibility(View.GONE); - RelativeLayout.LayoutParams lp=(RelativeLayout.LayoutParams) username.getLayoutParams(); - lp.addRule(RelativeLayout.BELOW, R.id.name); - username.getParent().requestLayout(); - avatar.setForeground(null); - } - }); - set.start(); + Toolbar toolbar=getToolbar(); + if(canGoBack()){ + Drawable back=getToolbarContext().getDrawable(R.drawable.ic_arrow_back).mutate(); + back.setTint(UiUtils.getThemeColor(getToolbarContext(), R.attr.colorM3OnSurfaceVariant)); + toolbar.setNavigationIcon(back); + toolbar.setNavigationContentDescription(0); + }else{ + toolbar.setNavigationIcon(null); + } + editSaveMenuItem=null; + + ViewGroup parent=contentView.findViewById(R.id.scrollable_content); + TransitionManager.beginDelayedTransition(parent, new TransitionSet() + .addTransition(new Fade(Fade.IN | Fade.OUT)) + .addTransition(new ChangeBounds()) + .setDuration(250) + .setInterpolator(CubicBezierInterpolator.DEFAULT) + ); + nameEditWrap.setVisibility(View.GONE); + bioEditWrap.setVisibility(View.GONE); + name.setVisibility(View.VISIBLE); + username.setVisibility(View.VISIBLE); + bio.setVisibility(View.VISIBLE); + countersLayout.setVisibility(View.VISIBLE); bindHeaderView(); } @@ -850,7 +874,11 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList @Override public boolean onBackPressed(){ if(isInEditMode){ - exitEditMode(); + new M3AlertDialogBuilder(getActivity()) + .setTitle(R.string.discard_changes) + .setPositiveButton(R.string.discard, (dlg, btn)->exitEditMode()) + .setNegativeButton(R.string.cancel, null) + .show(); return true; } return false; @@ -901,9 +929,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList } private void startImagePicker(int requestCode){ - Intent intent=new Intent(Intent.ACTION_GET_CONTENT); - intent.setType("image/*"); - intent.addCategory(Intent.CATEGORY_OPENABLE); + Intent intent=UiUtils.getMediaPickerIntent(new String[]{"image/*"}, 1); startActivityForResult(intent, requestCode); } @@ -912,10 +938,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList if(resultCode==Activity.RESULT_OK){ if(requestCode==AVATAR_RESULT){ editNewAvatar=data.getData(); - ViewImageLoader.load(avatar, null, new UrlImageLoaderRequest(editNewAvatar, V.dp(100), V.dp(100))); + ViewImageLoader.loadWithoutAnimation(avatar, null, new UrlImageLoaderRequest(editNewAvatar, V.dp(100), V.dp(100))); }else if(requestCode==COVER_RESULT){ editNewCover=data.getData(); - ViewImageLoader.load(cover, null, new UrlImageLoaderRequest(editNewCover, V.dp(1000), V.dp(1000))); + ViewImageLoader.loadWithoutAnimation(cover, null, new UrlImageLoaderRequest(editNewCover, V.dp(1000), V.dp(1000))); } } } @@ -940,6 +966,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList Nav.go(getActivity(), cls, args); } + private boolean isActionButtonInView(){ + return actionButton.getVisibility()==View.VISIBLE && actionButtonWrap.getTop()+actionButtonWrap.getHeight()>scrollView.getScrollY(); + } + private class ProfilePagerAdapter extends RecyclerView.Adapter{ @NonNull @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/AccountActivationFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/AccountActivationFragment.java index 92eb026a..19bb1759 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/AccountActivationFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/AccountActivationFragment.java @@ -89,13 +89,6 @@ public class AccountActivationFragment extends ToolbarFragment{ return !UiUtils.isDarkTheme(); } - @Override - public void onViewCreated(View view, Bundle savedInstanceState){ - super.onViewCreated(view, savedInstanceState); - setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background)); - view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background)); - } - @Override protected void onUpdateToolbar(){ super.onUpdateToolbar(); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/GoogleMadeMeAddThisFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/GoogleMadeMeAddThisFragment.java index 5f0404ac..bf5f7420 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/GoogleMadeMeAddThisFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/GoogleMadeMeAddThisFragment.java @@ -123,16 +123,12 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{ @Override public void onViewCreated(View view, Bundle savedInstanceState){ super.onViewCreated(view, savedInstanceState); - setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background)); - view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background)); list.addOnScrollListener(onScrollListener=new ElevationOnScrollListener((FragmentRootLinearLayout) view, buttonBar, getToolbar())); } @Override protected void onUpdateToolbar(){ super.onUpdateToolbar(); - getToolbar().setBackgroundResource(R.drawable.bg_onboarding_panel); - getToolbar().setElevation(0); if(onScrollListener!=null){ onScrollListener.setViews(buttonBar, getToolbar()); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceChooserLoginFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceChooserLoginFragment.java index 83528dd7..e4ce6401 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceChooserLoginFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceChooserLoginFragment.java @@ -164,7 +164,6 @@ public class InstanceChooserLoginFragment extends InstanceCatalogFragment{ @Override public void onViewCreated(View view, Bundle savedInstanceState){ super.onViewCreated(view, savedInstanceState); - setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background)); list.addItemDecoration(new RecyclerView.ItemDecoration(){ @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceRulesFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceRulesFragment.java index 5eb4a55c..307908f6 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceRulesFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceRulesFragment.java @@ -90,16 +90,12 @@ public class InstanceRulesFragment extends ToolbarFragment{ @Override public void onViewCreated(View view, Bundle savedInstanceState){ super.onViewCreated(view, savedInstanceState); - setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background)); - view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background)); list.addOnScrollListener(onScrollListener=new ElevationOnScrollListener((FragmentRootLinearLayout) view, buttonBar, getToolbar())); } @Override protected void onUpdateToolbar(){ super.onUpdateToolbar(); - getToolbar().setBackgroundResource(R.drawable.bg_onboarding_panel); - getToolbar().setElevation(0); if(onScrollListener!=null){ onScrollListener.setViews(buttonBar, getToolbar()); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/OnboardingFollowSuggestionsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/OnboardingFollowSuggestionsFragment.java index 37872778..70917413 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/OnboardingFollowSuggestionsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/OnboardingFollowSuggestionsFragment.java @@ -77,8 +77,6 @@ public class OnboardingFollowSuggestionsFragment extends BaseRecyclerFragment + + + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/bg_fab.xml b/mastodon/src/main/res/drawable/bg_fab.xml index 320e3e27..011d01bd 100644 --- a/mastodon/src/main/res/drawable/bg_fab.xml +++ b/mastodon/src/main/res/drawable/bg_fab.xml @@ -1,9 +1,21 @@ + + + + + + - + + + + + + + diff --git a/mastodon/src/main/res/drawable/edit_avatar_overlay.xml b/mastodon/src/main/res/drawable/edit_avatar_overlay.xml index 5ce6af70..f4ccc200 100644 --- a/mastodon/src/main/res/drawable/edit_avatar_overlay.xml +++ b/mastodon/src/main/res/drawable/edit_avatar_overlay.xml @@ -1,12 +1,10 @@ - - - + - \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_add_photo_alternate_48px_dark_on_surface.xml b/mastodon/src/main/res/drawable/ic_add_photo_alternate_48px_dark_on_surface.xml new file mode 100644 index 00000000..bd82949c --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_add_photo_alternate_48px_dark_on_surface.xml @@ -0,0 +1,9 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_bookmark_24px.xml b/mastodon/src/main/res/drawable/ic_bookmark_24px.xml new file mode 100644 index 00000000..53de975b --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_bookmark_24px.xml @@ -0,0 +1,9 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_edit_24px.xml b/mastodon/src/main/res/drawable/ic_edit_24px.xml new file mode 100644 index 00000000..173e2c95 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_edit_24px.xml @@ -0,0 +1,9 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_save_24px.xml b/mastodon/src/main/res/drawable/ic_save_24px.xml new file mode 100644 index 00000000..55f8fb1a --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_save_24px.xml @@ -0,0 +1,9 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_share_24px.xml b/mastodon/src/main/res/drawable/ic_share_24px.xml new file mode 100644 index 00000000..3e3613ca --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_share_24px.xml @@ -0,0 +1,9 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_star_24px.xml b/mastodon/src/main/res/drawable/ic_star_24px.xml new file mode 100644 index 00000000..f44bd0d0 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_star_24px.xml @@ -0,0 +1,9 @@ + + + diff --git a/mastodon/src/main/res/drawable/profile_ava_bg.xml b/mastodon/src/main/res/drawable/profile_ava_bg.xml index 83b8d031..a8ae1c61 100644 --- a/mastodon/src/main/res/drawable/profile_ava_bg.xml +++ b/mastodon/src/main/res/drawable/profile_ava_bg.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/tab_indicator_m3.xml b/mastodon/src/main/res/drawable/tab_indicator_m3.xml new file mode 100644 index 00000000..152d5a91 --- /dev/null +++ b/mastodon/src/main/res/drawable/tab_indicator_m3.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/layout/fragment_onboarding_follow_suggestions.xml b/mastodon/src/main/res/layout/fragment_onboarding_follow_suggestions.xml index cc021220..e7a180c7 100644 --- a/mastodon/src/main/res/layout/fragment_onboarding_follow_suggestions.xml +++ b/mastodon/src/main/res/layout/fragment_onboarding_follow_suggestions.xml @@ -6,7 +6,7 @@ android:orientation="vertical" android:id="@+id/appkit_loader_root" xmlns:android="http://schemas.android.com/apk/res/android" - android:background="?android:colorBackground"> + android:background="?colorM3Background"> diff --git a/mastodon/src/main/res/layout/fragment_onboarding_profile_setup.xml b/mastodon/src/main/res/layout/fragment_onboarding_profile_setup.xml index f6835018..4b1e3b8e 100644 --- a/mastodon/src/main/res/layout/fragment_onboarding_profile_setup.xml +++ b/mastodon/src/main/res/layout/fragment_onboarding_profile_setup.xml @@ -82,17 +82,18 @@ @@ -15,23 +15,25 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:nestedScrollingEnabled="true"> + + + android:layout_height="wrap_content"> + android:scaleType="centerCrop" /> + tools:visibility="visible" /> + android:layout_marginStart="12dp" + android:layout_marginTop="-44dp" + android:background="@drawable/profile_ava_bg" + android:outlineProvider="@null"> - - - - - - - - - - - - - - - - - + android:layout_marginTop="16dp" + android:layout_marginEnd="16dp"> + + style="@style/Widget.Mastodon.M3.Button.Filled" + android:layout_width="156dp" + android:layout_height="40dp" + tools:text="Edit Profile" /> + + android:outlineProvider="none" + android:visibility="gone" /> - + + + + + + + + + + + + + + + + + android:layout_width="wrap_content" + android:layout_height="28dp" + android:gravity="center" + android:text="ยท" + android:textAppearance="@style/m3_label_large" + android:textColor="?colorM3OnSurfaceVariant" /> - + + + + + + + + + + + + + + - - + + + android:layout_height="wrap_content"> + + + + + + + android:tint="?colorM3Primary" /> diff --git a/mastodon/src/main/res/layout/recycler_fragment_with_fab.xml b/mastodon/src/main/res/layout/recycler_fragment_with_fab.xml index 4dcdac75..5af99776 100644 --- a/mastodon/src/main/res/layout/recycler_fragment_with_fab.xml +++ b/mastodon/src/main/res/layout/recycler_fragment_with_fab.xml @@ -22,17 +22,17 @@ + android:src="@drawable/ic_edit_24px"/> \ No newline at end of file diff --git a/mastodon/src/main/res/menu/profile_own.xml b/mastodon/src/main/res/menu/profile_own.xml index c8895bb8..ffacd852 100644 --- a/mastodon/src/main/res/menu/profile_own.xml +++ b/mastodon/src/main/res/menu/profile_own.xml @@ -1,6 +1,6 @@ - - - + + + \ No newline at end of file diff --git a/mastodon/src/main/res/values-v27/colors.xml b/mastodon/src/main/res/values-v27/colors.xml index ec2c6204..9ec7edb8 100644 --- a/mastodon/src/main/res/values-v27/colors.xml +++ b/mastodon/src/main/res/values-v27/colors.xml @@ -1,4 +1,4 @@ - @color/gray_50 + @color/m3_sys_light_background \ No newline at end of file diff --git a/mastodon/src/main/res/values/colors.xml b/mastodon/src/main/res/values/colors.xml index 11ccff9c..a66197aa 100644 --- a/mastodon/src/main/res/values/colors.xml +++ b/mastodon/src/main/res/values/colors.xml @@ -87,7 +87,7 @@ #3E1C96 #282C37 - #282C37 + #282C37 #30FFFFFF #18000000 diff --git a/mastodon/src/main/res/values/ids.xml b/mastodon/src/main/res/values/ids.xml index 6545f9ca..9947a762 100644 --- a/mastodon/src/main/res/values/ids.xml +++ b/mastodon/src/main/res/values/ids.xml @@ -15,4 +15,6 @@ + + \ No newline at end of file diff --git a/mastodon/src/main/res/values/strings.xml b/mastodon/src/main/res/values/strings.xml index 7c7edd90..9a8f839e 100644 --- a/mastodon/src/main/res/values/strings.xml +++ b/mastodon/src/main/res/values/strings.xml @@ -441,4 +441,5 @@ Show anyway Re-hide Choose one or more + Save changes \ No newline at end of file diff --git a/mastodon/src/main/res/values/styles.xml b/mastodon/src/main/res/values/styles.xml index d6bc4c40..476d7b4c 100644 --- a/mastodon/src/main/res/values/styles.xml +++ b/mastodon/src/main/res/values/styles.xml @@ -12,7 +12,6 @@ @style/Widget.Mastodon.Button.Secondary_DarkOnLight @style/Widget.Mastodon.Button.Large.Primary_DarkOnLight @style/Widget.Mastodon.Button.Large.Secondary_DarkOnLight - @color/primary_700 @color/gray_800 @color/gray_100 @color/gray_800 @@ -22,8 +21,6 @@ @color/gray_50 @color/gray_25 @color/gray_900 - @color/gray_50 - @color/navigation_bar_bg @style/Theme.Mastodon.Toolbar @style/Theme.Mastodon.Dialog.Alert @color/primary_500 @@ -70,6 +67,9 @@ #410E0B ?colorM3Background + ?colorM3Background + @color/navigation_bar_bg_light + ?colorM3Primary - -