diff options
Diffstat (limited to 'main/java/com/google/android/setupcompat/template')
7 files changed, 646 insertions, 262 deletions
diff --git a/main/java/com/google/android/setupcompat/template/FooterActionButton.java b/main/java/com/google/android/setupcompat/template/FooterActionButton.java index bb26d19..86a06d9 100644 --- a/main/java/com/google/android/setupcompat/template/FooterActionButton.java +++ b/main/java/com/google/android/setupcompat/template/FooterActionButton.java @@ -18,11 +18,11 @@ package com.google.android.setupcompat.template; import android.annotation.SuppressLint; import android.content.Context; -import androidx.annotation.Nullable; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.Button; +import androidx.annotation.Nullable; /** Button that can react to touch when disabled. */ public class FooterActionButton extends Button { diff --git a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java index bc9e5c1..b75d972 100644 --- a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java +++ b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java @@ -24,16 +24,17 @@ import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Color; -import android.graphics.PorterDuff.Mode; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.GradientDrawable; -import android.graphics.drawable.InsetDrawable; -import android.graphics.drawable.LayerDrawable; -import android.graphics.drawable.RippleDrawable; import android.os.Build; import android.os.Build.VERSION_CODES; import android.os.PersistableBundle; +import android.util.AttributeSet; +import android.view.ContextThemeWrapper; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewStub; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.LinearLayout.LayoutParams; import androidx.annotation.AttrRes; import androidx.annotation.CallSuper; import androidx.annotation.ColorInt; @@ -44,25 +45,15 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StyleRes; import androidx.annotation.VisibleForTesting; -import android.util.AttributeSet; -import android.util.StateSet; -import android.util.TypedValue; -import android.view.ContextThemeWrapper; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewStub; -import android.widget.Button; -import android.widget.LinearLayout; -import android.widget.LinearLayout.LayoutParams; import com.google.android.setupcompat.PartnerCustomizationLayout; import com.google.android.setupcompat.R; import com.google.android.setupcompat.internal.FooterButtonPartnerConfig; -import com.google.android.setupcompat.internal.Preconditions; import com.google.android.setupcompat.internal.TemplateLayout; import com.google.android.setupcompat.logging.internal.FooterBarMixinMetrics; import com.google.android.setupcompat.partnerconfig.PartnerConfig; import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper; import com.google.android.setupcompat.template.FooterButton.ButtonType; +import java.util.Locale; import java.util.concurrent.atomic.AtomicInteger; /** @@ -77,6 +68,8 @@ public class FooterBarMixin implements Mixin { @Nullable private final ViewStub footerStub; @VisibleForTesting final boolean applyPartnerResources; + @VisibleForTesting final boolean applyDynamicColor; + @VisibleForTesting final boolean useFullDynamicColor; private LinearLayout buttonContainer; private FooterButton primaryButton; @@ -94,8 +87,8 @@ public class FooterBarMixin implements Mixin { @ColorInt private final int footerBarPrimaryBackgroundColor; @ColorInt private final int footerBarSecondaryBackgroundColor; private boolean removeFooterBarWhenEmpty = true; + private boolean isSecondaryButtonInPrimaryStyle = false; - private static final float DEFAULT_DISABLED_ALPHA = 0.26f; private static final AtomicInteger nextGeneratedId = new AtomicInteger(1); @VisibleForTesting public final FooterBarMixinMetrics metrics = new FooterBarMixinMetrics(); @@ -110,10 +103,10 @@ public class FooterBarMixin implements Mixin { Button button = buttonContainer.findViewById(id); if (button != null) { button.setEnabled(enabled); - if (applyPartnerResources) { - updateButtonTextColorWithPartnerConfig( + if (applyPartnerResources && !applyDynamicColor) { + updateButtonTextColorWithEnabledState( button, - (id == primaryButtonId) + (id == primaryButtonId || isSecondaryButtonInPrimaryStyle) ? PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_COLOR : PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR); } @@ -141,6 +134,25 @@ public class FooterBarMixin implements Mixin { } } } + + @Override + @TargetApi(VERSION_CODES.JELLY_BEAN_MR1) + public void onLocaleChanged(Locale locale) { + if (buttonContainer != null) { + Button button = buttonContainer.findViewById(id); + if (button != null && locale != null) { + button.setTextLocale(locale); + } + } + } + + @Override + @TargetApi(VERSION_CODES.JELLY_BEAN_MR1) + public void onDirectionChanged(int direction) { + if (buttonContainer != null && direction != -1) { + buttonContainer.setLayoutDirection(direction); + } + } }; } @@ -159,6 +171,14 @@ public class FooterBarMixin implements Mixin { layout instanceof PartnerCustomizationLayout && ((PartnerCustomizationLayout) layout).shouldApplyPartnerResource(); + applyDynamicColor = + layout instanceof PartnerCustomizationLayout + && ((PartnerCustomizationLayout) layout).shouldApplyDynamicColor(); + + useFullDynamicColor = + layout instanceof PartnerCustomizationLayout + && ((PartnerCustomizationLayout) layout).useFullDynamicColor(); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SucFooterBarMixin, defStyleAttr, 0); defaultPadding = @@ -253,11 +273,14 @@ public class FooterBarMixin implements Mixin { return; } - @ColorInt - int color = - PartnerConfigHelper.get(context) - .getColor(context, PartnerConfig.CONFIG_FOOTER_BAR_BG_COLOR); - buttonContainer.setBackgroundColor(color); + // skip apply partner resources on footerbar background if dynamic color enabled + if (!useFullDynamicColor) { + @ColorInt + int color = + PartnerConfigHelper.get(context) + .getColor(context, PartnerConfig.CONFIG_FOOTER_BAR_BG_COLOR); + buttonContainer.setBackgroundColor(color); + } footerBarPaddingTop = (int) @@ -273,6 +296,17 @@ public class FooterBarMixin implements Mixin { footerBarPaddingTop, buttonContainer.getPaddingRight(), footerBarPaddingBottom); + + if (PartnerConfigHelper.get(context) + .isPartnerConfigAvailable(PartnerConfig.CONFIG_FOOTER_BAR_MIN_HEIGHT)) { + int minHeight = + (int) + PartnerConfigHelper.get(context) + .getDimension(context, PartnerConfig.CONFIG_FOOTER_BAR_MIN_HEIGHT); + if (minHeight > 0) { + buttonContainer.setMinimumHeight(minHeight); + } + } } /** @@ -310,7 +344,9 @@ public class FooterBarMixin implements Mixin { .setButtonRippleColorAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RIPPLE_COLOR_ALPHA) .setTextColorConfig(PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_COLOR) .setTextSizeConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_SIZE) + .setButtonMinHeight(PartnerConfig.CONFIG_FOOTER_BUTTON_MIN_HEIGHT) .setTextTypeFaceConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_FONT_FAMILY) + .setTextStyleConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_STYLE) .build(); FooterActionButton button = inflateButton(footerButton, footerButtonPartnerConfig); @@ -346,7 +382,14 @@ public class FooterBarMixin implements Mixin { /** Sets secondary button for footer. */ @MainThread public void setSecondaryButton(FooterButton footerButton) { + setSecondaryButton(footerButton, /*usePrimaryStyle= */ false); + } + + /** Sets secondary button for footer. Allow to use the primary button style. */ + @MainThread + public void setSecondaryButton(FooterButton footerButton, boolean usePrimaryStyle) { ensureOnMainThread("setSecondaryButton"); + isSecondaryButtonInPrimaryStyle = usePrimaryStyle; ensureFooterInflated(); // Setup button partner config @@ -355,18 +398,29 @@ public class FooterBarMixin implements Mixin { .setPartnerTheme( getPartnerTheme( footerButton, - /* defaultPartnerTheme= */ R.style.SucPartnerCustomizationButton_Secondary, - /* buttonBackgroundColorConfig= */ PartnerConfig - .CONFIG_FOOTER_SECONDARY_BUTTON_BG_COLOR)) - .setButtonBackgroundConfig(PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_BG_COLOR) + /* defaultPartnerTheme= */ usePrimaryStyle + ? R.style.SucPartnerCustomizationButton_Primary + : R.style.SucPartnerCustomizationButton_Secondary, + /* buttonBackgroundColorConfig= */ usePrimaryStyle + ? PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_BG_COLOR + : PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_BG_COLOR)) + .setButtonBackgroundConfig( + usePrimaryStyle + ? PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_BG_COLOR + : PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_BG_COLOR) .setButtonDisableAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_DISABLED_ALPHA) .setButtonDisableBackgroundConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_DISABLED_BG_COLOR) .setButtonIconConfig(getDrawablePartnerConfig(footerButton.getButtonType())) .setButtonRadiusConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RADIUS) .setButtonRippleColorAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RIPPLE_COLOR_ALPHA) - .setTextColorConfig(PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR) + .setTextColorConfig( + usePrimaryStyle + ? PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_COLOR + : PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR) .setTextSizeConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_SIZE) + .setButtonMinHeight(PartnerConfig.CONFIG_FOOTER_BUTTON_MIN_HEIGHT) .setTextTypeFaceConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_FONT_FAMILY) + .setTextStyleConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_STYLE) .build(); FooterActionButton button = inflateButton(footerButton, footerButtonPartnerConfig); @@ -395,6 +449,16 @@ public class FooterBarMixin implements Mixin { buttonContainer.removeAllViews(); if (tempSecondaryButton != null) { + if (isSecondaryButtonInPrimaryStyle) { + // Since the secondary button has the same style (with background) as the primary button, + // we need to have the left padding equal to the right padding. + updateFooterBarPadding( + buttonContainer, + buttonContainer.getPaddingRight(), + buttonContainer.getPaddingTop(), + buttonContainer.getPaddingRight(), + buttonContainer.getPaddingBottom()); + } buttonContainer.addView(tempSecondaryButton); } addSpace(); @@ -411,7 +475,7 @@ public class FooterBarMixin implements Mixin { protected void onFooterButtonInflated(Button button, @ColorInt int defaultButtonBackgroundColor) { // Try to set default background if (defaultButtonBackgroundColor != 0) { - updateButtonBackground(button, defaultButtonBackgroundColor); + FooterButtonStyleUtils.updateButtonBackground(button, defaultButtonBackgroundColor); } else { // TODO: get button background color from activity theme } @@ -544,187 +608,30 @@ public class FooterBarMixin implements Mixin { if (!applyPartnerResources) { return; } - updateButtonTextColorWithPartnerConfig( - button, footerButtonPartnerConfig.getButtonTextColorConfig()); - updateButtonTextSizeWithPartnerConfig( - button, footerButtonPartnerConfig.getButtonTextSizeConfig()); - updateButtonTypeFaceWithPartnerConfig( - button, footerButtonPartnerConfig.getButtonTextTypeFaceConfig()); - updateButtonBackgroundWithPartnerConfig( + FooterButtonStyleUtils.applyButtonPartnerResources( + context, button, - footerButtonPartnerConfig.getButtonBackgroundConfig(), - footerButtonPartnerConfig.getButtonDisableAlphaConfig(), - footerButtonPartnerConfig.getButtonDisableBackgroundConfig()); - updateButtonRadiusWithPartnerConfig(button, footerButtonPartnerConfig.getButtonRadiusConfig()); - updateButtonIconWithPartnerConfig(button, footerButtonPartnerConfig.getButtonIconConfig()); - updateButtonRippleColorWithPartnerConfig(button, footerButtonPartnerConfig); + applyDynamicColor, + /* isButtonIconAtEnd= */ (button.getId() == primaryButtonId), + footerButtonPartnerConfig); + if (!applyDynamicColor) { + // adjust text color based on enabled state + updateButtonTextColorWithEnabledState( + button, footerButtonPartnerConfig.getButtonTextColorConfig()); + } } - private void updateButtonTextColorWithPartnerConfig( + private void updateButtonTextColorWithEnabledState( Button button, PartnerConfig buttonTextColorConfig) { if (button.isEnabled()) { - @ColorInt - int color = PartnerConfigHelper.get(context).getColor(context, buttonTextColorConfig); - if (color != Color.TRANSPARENT) { - button.setTextColor(ColorStateList.valueOf(color)); - } + FooterButtonStyleUtils.updateButtonTextEnabledColorWithPartnerConfig( + context, button, buttonTextColorConfig); } else { - // disable state will use the default disable state color - button.setTextColor( - button.getId() == primaryButtonId ? primaryDefaultTextColor : secondaryDefaultTextColor); - } - } - - private void updateButtonTextSizeWithPartnerConfig( - Button button, PartnerConfig buttonTextSizeConfig) { - float size = PartnerConfigHelper.get(context).getDimension(context, buttonTextSizeConfig); - if (size > 0) { - button.setTextSize(TypedValue.COMPLEX_UNIT_PX, size); - } - } - - private void updateButtonTypeFaceWithPartnerConfig( - Button button, PartnerConfig buttonTextTypeFaceConfig) { - String fontFamilyName = - PartnerConfigHelper.get(context).getString(context, buttonTextTypeFaceConfig); - Typeface font = Typeface.create(fontFamilyName, Typeface.NORMAL); - if (font != null) { - button.setTypeface(font); - } - } - - @TargetApi(VERSION_CODES.Q) - private void updateButtonBackgroundWithPartnerConfig( - Button button, - PartnerConfig buttonBackgroundConfig, - PartnerConfig buttonDisableAlphaConfig, - PartnerConfig buttonDisableBackgroundConfig) { - Preconditions.checkArgument( - Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q, - "Update button background only support on sdk Q or higher"); - @ColorInt int color; - @ColorInt int disabledColor; - float disabledAlpha; - int[] DISABLED_STATE_SET = {-android.R.attr.state_enabled}; - int[] ENABLED_STATE_SET = {}; - color = PartnerConfigHelper.get(context).getColor(context, buttonBackgroundConfig); - disabledAlpha = - PartnerConfigHelper.get(context).getFraction(context, buttonDisableAlphaConfig, 0f); - disabledColor = - PartnerConfigHelper.get(context).getColor(context, buttonDisableBackgroundConfig); - - if (color != Color.TRANSPARENT) { - if (disabledAlpha <= 0f) { - // if no partner resource, fallback to theme disable alpha - float alpha; - TypedArray a = context.obtainStyledAttributes(new int[] {android.R.attr.disabledAlpha}); - alpha = a.getFloat(0, DEFAULT_DISABLED_ALPHA); - a.recycle(); - disabledAlpha = alpha; - } - if (disabledColor == Color.TRANSPARENT) { - // if no partner resource, fallback to button background color - disabledColor = color; - } - - // Set text color for ripple. - ColorStateList colorStateList = - new ColorStateList( - new int[][] {DISABLED_STATE_SET, ENABLED_STATE_SET}, - new int[] {convertRgbToArgb(disabledColor, disabledAlpha), color}); - - // b/129482013: When a LayerDrawable is mutated, a new clone of its children drawables are - // created, but without copying the state from the parent drawable. So even though the - // parent is getting the correct drawable state from the view, the children won't get those - // states until a state change happens. - // As a workaround, we mutate the drawable and forcibly set the state to empty, and then - // refresh the state so the children will have the updated states. - button.getBackground().mutate().setState(new int[0]); - button.refreshDrawableState(); - button.setBackgroundTintList(colorStateList); - } - } - - private void updateButtonBackground(Button button, @ColorInt int color) { - button.getBackground().mutate().setColorFilter(color, Mode.SRC_ATOP); - } - - private void updateButtonRadiusWithPartnerConfig( - Button button, PartnerConfig buttonRadiusConfig) { - if (Build.VERSION.SDK_INT >= VERSION_CODES.N) { - float radius = PartnerConfigHelper.get(context).getDimension(context, buttonRadiusConfig); - GradientDrawable gradientDrawable = getGradientDrawable(button); - if (gradientDrawable != null) { - gradientDrawable.setCornerRadius(radius); - } - } - } - - private void updateButtonRippleColorWithPartnerConfig( - Button button, FooterButtonPartnerConfig footerButtonPartnerConfig) { - // RippleDrawable is available after sdk 21. And because on lower sdk the RippleDrawable is - // unavailable. Since Stencil customization provider only works on Q+, there is no need to - // perform any customization for versions 21. - if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { - RippleDrawable rippleDrawable = getRippleDrawable(button); - if (rippleDrawable == null) { - return; - } - - int[] pressedState = {android.R.attr.state_pressed}; - @ColorInt int color; - // Get partner text color. - color = - PartnerConfigHelper.get(context) - .getColor(context, footerButtonPartnerConfig.getButtonTextColorConfig()); - - float alpha = - PartnerConfigHelper.get(context) - .getFraction(context, footerButtonPartnerConfig.getButtonRippleColorAlphaConfig()); - - // Set text color for ripple. - ColorStateList colorStateList = - new ColorStateList( - new int[][] {pressedState, StateSet.NOTHING}, - new int[] {convertRgbToArgb(color, alpha), Color.TRANSPARENT}); - rippleDrawable.setColor(colorStateList); - } - } - - private void updateButtonIconWithPartnerConfig(Button button, PartnerConfig buttonIconConfig) { - if (button == null) { - return; - } - Drawable icon = null; - if (buttonIconConfig != null) { - icon = PartnerConfigHelper.get(context).getDrawable(context, buttonIconConfig); - } - setButtonIcon(button, icon); - } - - private void setButtonIcon(Button button, Drawable icon) { - if (button == null) { - return; - } - - if (icon != null) { - // TODO: restrict the icons to a reasonable size - int h = icon.getIntrinsicHeight(); - int w = icon.getIntrinsicWidth(); - icon.setBounds(0, 0, w, h); - } - - Drawable iconStart = null; - Drawable iconEnd = null; - if (button.getId() == primaryButtonId) { - iconEnd = icon; - } else if (button.getId() == secondaryButtonId) { - iconStart = icon; - } - if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) { - button.setCompoundDrawablesRelative(iconStart, null, iconEnd, null); - } else { - button.setCompoundDrawables(iconStart, null, iconEnd, null); + FooterButtonStyleUtils.updateButtonTextDisableColor( + button, + /* is Primary= */ (primaryButtonId == button.getId() || isSecondaryButtonInPrimaryStyle) + ? primaryDefaultTextColor + : secondaryDefaultTextColor); } } @@ -763,43 +670,6 @@ public class FooterBarMixin implements Mixin { return result; } - GradientDrawable getGradientDrawable(Button button) { - // RippleDrawable is available after sdk 21, InsetDrawable#getDrawable is available after - // sdk 19. So check the sdk is higher than sdk 21 and since Stencil customization provider only - // works on Q+, there is no need to perform any customization for versions 21. - if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { - Drawable drawable = button.getBackground(); - if (drawable instanceof InsetDrawable) { - LayerDrawable layerDrawable = (LayerDrawable) ((InsetDrawable) drawable).getDrawable(); - return (GradientDrawable) layerDrawable.getDrawable(0); - } else if (drawable instanceof RippleDrawable) { - InsetDrawable insetDrawable = (InsetDrawable) ((RippleDrawable) drawable).getDrawable(0); - return (GradientDrawable) insetDrawable.getDrawable(); - } - } - return null; - } - - RippleDrawable getRippleDrawable(Button button) { - // RippleDrawable is available after sdk 21. And because on lower sdk the RippleDrawable is - // unavailable. Since Stencil customization provider only works on Q+, there is no need to - // perform any customization for versions 21. - if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { - Drawable drawable = button.getBackground(); - if (drawable instanceof InsetDrawable) { - return (RippleDrawable) ((InsetDrawable) drawable).getDrawable(); - } else if (drawable instanceof RippleDrawable) { - return (RippleDrawable) drawable; - } - } - return null; - } - - @ColorInt - private static int convertRgbToArgb(@ColorInt int color, float alpha) { - return Color.argb((int) (alpha * 255), Color.red(color), Color.green(color), Color.blue(color)); - } - protected View inflateFooter(@LayoutRes int footer) { if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { LayoutInflater inflater = diff --git a/main/java/com/google/android/setupcompat/template/FooterButton.java b/main/java/com/google/android/setupcompat/template/FooterButton.java index 2fa8c7c..90c13ec 100644 --- a/main/java/com/google/android/setupcompat/template/FooterButton.java +++ b/main/java/com/google/android/setupcompat/template/FooterButton.java @@ -23,17 +23,18 @@ import android.content.Context; import android.content.res.TypedArray; import android.os.Build.VERSION_CODES; import android.os.PersistableBundle; +import android.util.AttributeSet; +import android.view.View; +import android.view.View.OnClickListener; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.annotation.StyleRes; -import android.util.AttributeSet; -import android.view.View; -import android.view.View.OnClickListener; import com.google.android.setupcompat.R; import com.google.android.setupcompat.logging.CustomEvent; import java.lang.annotation.Retention; +import java.util.Locale; /** * Definition of a footer button. Clients can use this class to customize attributes like text, @@ -53,6 +54,8 @@ public final class FooterButton implements OnClickListener { private OnClickListener onClickListenerWhenDisabled; private OnButtonEventListener buttonListener; private int clickCount = 0; + private Locale locale; + private int direction; public FooterButton(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SucFooterButton); @@ -78,11 +81,15 @@ public final class FooterButton implements OnClickListener { CharSequence text, @Nullable OnClickListener listener, @ButtonType int buttonType, - @StyleRes int theme) { + @StyleRes int theme, + Locale locale, + int direction) { this.text = text; onClickListener = listener; this.buttonType = buttonType; this.theme = theme; + this.locale = locale; + this.direction = direction; } /** Returns the text that this footer button is displaying. */ @@ -142,6 +149,16 @@ public final class FooterButton implements OnClickListener { return enabled; } + /** Returns the layout direction for this footer button. */ + public int getLayoutDirection() { + return direction; + } + + /** Returns the text locale for this footer button. */ + public Locale getTextLocale() { + return locale; + } + /** * Sets the visibility state of this footer button. * @@ -172,6 +189,22 @@ public final class FooterButton implements OnClickListener { } } + /** Sets the text locale to be displayed on footer button. */ + public void setTextLocale(Locale locale) { + this.locale = locale; + if (buttonListener != null) { + buttonListener.onLocaleChanged(locale); + } + } + + /** Sets the layout direction to be displayed on footer button. */ + public void setLayoutDirection(int direction) { + this.direction = direction; + if (buttonListener != null) { + buttonListener.onDirectionChanged(direction); + } + } + /** * Registers a callback to be invoked when footer button API has set. * @@ -201,6 +234,10 @@ public final class FooterButton implements OnClickListener { void onVisibilityChanged(int visibility); void onTextChanged(CharSequence text); + + void onLocaleChanged(Locale locale); + + void onDirectionChanged(int direction); } /** Maximum valid value of ButtonType */ @@ -308,12 +345,16 @@ public final class FooterButton implements OnClickListener { * .setListener(primaryButton) * .setButtonType(ButtonType.NEXT) * .setTheme(R.style.SuwGlifButton_Primary) + * .setTextLocale(Locale.CANADA) + * .setLayoutDirection(View.LAYOUT_DIRECTION_LTR) * .build(); * </pre> */ public static class Builder { private final Context context; private String text = ""; + private Locale locale = null; + private int direction = -1; private OnClickListener onClickListener = null; @ButtonType private int buttonType = ButtonType.OTHER; private int theme = 0; @@ -334,6 +375,18 @@ public final class FooterButton implements OnClickListener { return this; } + /** Sets the {@code locale} of FooterButton. */ + public Builder setTextLocale(Locale locale) { + this.locale = locale; + return this; + } + + /** Sets the {@code direction} of FooterButton. */ + public Builder setLayoutDirection(int direction) { + this.direction = direction; + return this; + } + /** Sets the {@code listener} of FooterButton. */ public Builder setListener(@Nullable OnClickListener listener) { onClickListener = listener; @@ -353,7 +406,7 @@ public final class FooterButton implements OnClickListener { } public FooterButton build() { - return new FooterButton(text, onClickListener, buttonType, theme); + return new FooterButton(text, onClickListener, buttonType, theme, locale, direction); } } } diff --git a/main/java/com/google/android/setupcompat/template/FooterButtonInflater.java b/main/java/com/google/android/setupcompat/template/FooterButtonInflater.java index fe2538b..10aa052 100644 --- a/main/java/com/google/android/setupcompat/template/FooterButtonInflater.java +++ b/main/java/com/google/android/setupcompat/template/FooterButtonInflater.java @@ -19,10 +19,10 @@ package com.google.android.setupcompat.template; import android.content.Context; import android.content.res.Resources; import android.content.res.XmlResourceParser; -import androidx.annotation.NonNull; import android.util.AttributeSet; import android.util.Xml; import android.view.InflateException; +import androidx.annotation.NonNull; import java.io.IOException; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java b/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java new file mode 100644 index 0000000..ef45b5c --- /dev/null +++ b/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.setupcompat.template; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.PorterDuff.Mode; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.InsetDrawable; +import android.graphics.drawable.LayerDrawable; +import android.graphics.drawable.RippleDrawable; +import android.os.Build; +import android.os.Build.VERSION_CODES; +import android.util.StateSet; +import android.util.TypedValue; +import android.widget.Button; +import androidx.annotation.ColorInt; +import androidx.annotation.VisibleForTesting; +import com.google.android.setupcompat.R; +import com.google.android.setupcompat.internal.FooterButtonPartnerConfig; +import com.google.android.setupcompat.internal.Preconditions; +import com.google.android.setupcompat.partnerconfig.PartnerConfig; +import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper; + +/** Utils for updating the button style. */ +public class FooterButtonStyleUtils { + private static final float DEFAULT_DISABLED_ALPHA = 0.26f; + + /** Apply the partner primary button style to given {@code button}. */ + public static void applyPrimaryButtonPartnerResource( + Context context, Button button, boolean applyDynamicColor) { + + FooterButtonPartnerConfig footerButtonPartnerConfig = + new FooterButtonPartnerConfig.Builder(null) + .setPartnerTheme(R.style.SucPartnerCustomizationButton_Primary) + .setButtonBackgroundConfig(PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_BG_COLOR) + .setButtonDisableAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_DISABLED_ALPHA) + .setButtonDisableBackgroundConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_DISABLED_BG_COLOR) + .setButtonRadiusConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RADIUS) + .setButtonRippleColorAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RIPPLE_COLOR_ALPHA) + .setTextColorConfig(PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_COLOR) + .setTextSizeConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_SIZE) + .setButtonMinHeight(PartnerConfig.CONFIG_FOOTER_BUTTON_MIN_HEIGHT) + .setTextTypeFaceConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_FONT_FAMILY) + .setTextStyleConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_STYLE) + .build(); + applyButtonPartnerResources( + context, + button, + applyDynamicColor, + /* isButtonIconAtEnd= */ true, + footerButtonPartnerConfig); + } + + /** Apply the partner secondary button style to given {@code button}. */ + public static void applySecondaryButtonPartnerResource( + Context context, Button button, boolean applyDynamicColor) { + + int defaultTheme = R.style.SucPartnerCustomizationButton_Secondary; + int color = + PartnerConfigHelper.get(context) + .getColor(context, PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_BG_COLOR); + if (color != Color.TRANSPARENT) { + defaultTheme = R.style.SucPartnerCustomizationButton_Primary; + } + // Setup button partner config + FooterButtonPartnerConfig footerButtonPartnerConfig = + new FooterButtonPartnerConfig.Builder(null) + .setPartnerTheme(defaultTheme) + .setButtonBackgroundConfig(PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_BG_COLOR) + .setButtonDisableAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_DISABLED_ALPHA) + .setButtonDisableBackgroundConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_DISABLED_BG_COLOR) + .setButtonRadiusConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RADIUS) + .setButtonRippleColorAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RIPPLE_COLOR_ALPHA) + .setTextColorConfig(PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR) + .setTextSizeConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_SIZE) + .setButtonMinHeight(PartnerConfig.CONFIG_FOOTER_BUTTON_MIN_HEIGHT) + .setTextTypeFaceConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_FONT_FAMILY) + .setTextStyleConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_STYLE) + .build(); + applyButtonPartnerResources( + context, + button, + applyDynamicColor, + /* isButtonIconAtEnd= */ false, + footerButtonPartnerConfig); + } + + static void applyButtonPartnerResources( + Context context, + Button button, + boolean applyDynamicColor, + boolean isButtonIconAtEnd, + FooterButtonPartnerConfig footerButtonPartnerConfig) { + + // If dynamic color enabled, these colors won't be overrode by partner config. + // Instead, these colors align with the current theme colors. + if (!applyDynamicColor) { + // use default disable color util we support the partner disable text color + if (button.isEnabled()) { + FooterButtonStyleUtils.updateButtonTextEnabledColorWithPartnerConfig( + context, button, footerButtonPartnerConfig.getButtonTextColorConfig()); + } + FooterButtonStyleUtils.updateButtonBackgroundWithPartnerConfig( + context, + button, + footerButtonPartnerConfig.getButtonBackgroundConfig(), + footerButtonPartnerConfig.getButtonDisableAlphaConfig(), + footerButtonPartnerConfig.getButtonDisableBackgroundConfig()); + } + FooterButtonStyleUtils.updateButtonRippleColorWithPartnerConfig( + context, + button, + applyDynamicColor, + footerButtonPartnerConfig.getButtonTextColorConfig(), + footerButtonPartnerConfig.getButtonRippleColorAlphaConfig()); + FooterButtonStyleUtils.updateButtonTextSizeWithPartnerConfig( + context, button, footerButtonPartnerConfig.getButtonTextSizeConfig()); + FooterButtonStyleUtils.updateButtonMinHeightWithPartnerConfig( + context, button, footerButtonPartnerConfig.getButtonMinHeightConfig()); + FooterButtonStyleUtils.updateButtonTypeFaceWithPartnerConfig( + context, + button, + footerButtonPartnerConfig.getButtonTextTypeFaceConfig(), + footerButtonPartnerConfig.getButtonTextStyleConfig()); + FooterButtonStyleUtils.updateButtonRadiusWithPartnerConfig( + context, button, footerButtonPartnerConfig.getButtonRadiusConfig()); + FooterButtonStyleUtils.updateButtonIconWithPartnerConfig( + context, button, footerButtonPartnerConfig.getButtonIconConfig(), isButtonIconAtEnd); + } + + static void updateButtonTextEnabledColorWithPartnerConfig( + Context context, Button button, PartnerConfig buttonEnableTextColorConfig) { + @ColorInt + int color = PartnerConfigHelper.get(context).getColor(context, buttonEnableTextColorConfig); + updateButtonTextEnabledColor(button, color); + } + + static void updateButtonTextEnabledColor(Button button, @ColorInt int textColor) { + if (textColor != Color.TRANSPARENT) { + button.setTextColor(ColorStateList.valueOf(textColor)); + } + } + + static void updateButtonTextDisableColor(Button button, ColorStateList disabledTextColor) { + // TODO : add disable footer button text color partner config + + // disable state will use the default disable state color + button.setTextColor(disabledTextColor); + } + + @TargetApi(VERSION_CODES.Q) + static void updateButtonBackgroundWithPartnerConfig( + Context context, + Button button, + PartnerConfig buttonBackgroundConfig, + PartnerConfig buttonDisableAlphaConfig, + PartnerConfig buttonDisableBackgroundConfig) { + Preconditions.checkArgument( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q, + "Update button background only support on sdk Q or higher"); + @ColorInt + int color = PartnerConfigHelper.get(context).getColor(context, buttonBackgroundConfig); + float disabledAlpha = + PartnerConfigHelper.get(context).getFraction(context, buttonDisableAlphaConfig, 0f); + @ColorInt + int disabledColor = + PartnerConfigHelper.get(context).getColor(context, buttonDisableBackgroundConfig); + + updateButtonBackgroundTintList(context, button, color, disabledAlpha, disabledColor); + } + + @TargetApi(VERSION_CODES.Q) + static void updateButtonBackgroundTintList( + Context context, + Button button, + @ColorInt int color, + float disabledAlpha, + @ColorInt int disabledColor) { + int[] DISABLED_STATE_SET = {-android.R.attr.state_enabled}; + int[] ENABLED_STATE_SET = {}; + + if (color != Color.TRANSPARENT) { + if (disabledAlpha <= 0f) { + // if no partner resource, fallback to theme disable alpha + TypedArray a = context.obtainStyledAttributes(new int[] {android.R.attr.disabledAlpha}); + float alpha = a.getFloat(0, DEFAULT_DISABLED_ALPHA); + a.recycle(); + disabledAlpha = alpha; + } + if (disabledColor == Color.TRANSPARENT) { + // if no partner resource, fallback to button background color + disabledColor = color; + } + + // Set text color for ripple. + ColorStateList colorStateList = + new ColorStateList( + new int[][] {DISABLED_STATE_SET, ENABLED_STATE_SET}, + new int[] {convertRgbToArgb(disabledColor, disabledAlpha), color}); + + // b/129482013: When a LayerDrawable is mutated, a new clone of its children drawables are + // created, but without copying the state from the parent drawable. So even though the + // parent is getting the correct drawable state from the view, the children won't get those + // states until a state change happens. + // As a workaround, we mutate the drawable and forcibly set the state to empty, and then + // refresh the state so the children will have the updated states. + button.getBackground().mutate().setState(new int[0]); + button.refreshDrawableState(); + button.setBackgroundTintList(colorStateList); + } + } + + @TargetApi(VERSION_CODES.Q) + static void updateButtonRippleColorWithPartnerConfig( + Context context, + Button button, + boolean applyDynamicColor, + PartnerConfig buttonTextColorConfig, + PartnerConfig buttonRippleColorAlphaConfig) { + if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { + + @ColorInt int textDefaultColor; + if (applyDynamicColor) { + // Get dynamic text color + textDefaultColor = button.getTextColors().getDefaultColor(); + } else { + // Get partner text color. + textDefaultColor = + PartnerConfigHelper.get(context).getColor(context, buttonTextColorConfig); + } + float alpha = + PartnerConfigHelper.get(context).getFraction(context, buttonRippleColorAlphaConfig); + updateButtonRippleColor(button, textDefaultColor, alpha); + } + } + + private static void updateButtonRippleColor( + Button button, @ColorInt int textColor, float rippleAlpha) { + // RippleDrawable is available after sdk 21. And because on lower sdk the RippleDrawable is + // unavailable. Since Stencil customization provider only works on Q+, there is no need to + // perform any customization for versions 21. + if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { + RippleDrawable rippleDrawable = getRippleDrawable(button); + if (rippleDrawable == null) { + return; + } + + int[] pressedState = {android.R.attr.state_pressed}; + + // Set text color for ripple. + ColorStateList colorStateList = + new ColorStateList( + new int[][] {pressedState, StateSet.NOTHING}, + new int[] {convertRgbToArgb(textColor, rippleAlpha), Color.TRANSPARENT}); + rippleDrawable.setColor(colorStateList); + } + } + + static void updateButtonTextSizeWithPartnerConfig( + Context context, Button button, PartnerConfig buttonTextSizeConfig) { + float size = PartnerConfigHelper.get(context).getDimension(context, buttonTextSizeConfig); + if (size > 0) { + button.setTextSize(TypedValue.COMPLEX_UNIT_PX, size); + } + } + + static void updateButtonMinHeightWithPartnerConfig( + Context context, Button button, PartnerConfig buttonMinHeightConfig) { + if (PartnerConfigHelper.get(context).isPartnerConfigAvailable(buttonMinHeightConfig)) { + float size = PartnerConfigHelper.get(context).getDimension(context, buttonMinHeightConfig); + if (size > 0) { + button.setMinHeight((int) size); + } + } + } + + static void updateButtonTypeFaceWithPartnerConfig( + Context context, + Button button, + PartnerConfig buttonTextTypeFaceConfig, + PartnerConfig buttonTextStyleConfig) { + String fontFamilyName = + PartnerConfigHelper.get(context).getString(context, buttonTextTypeFaceConfig); + + int textStyleValue = Typeface.NORMAL; + if (PartnerConfigHelper.get(context).isPartnerConfigAvailable(buttonTextStyleConfig)) { + textStyleValue = + PartnerConfigHelper.get(context) + .getInteger(context, buttonTextStyleConfig, Typeface.NORMAL); + } + Typeface font = Typeface.create(fontFamilyName, textStyleValue); + if (font != null) { + button.setTypeface(font); + } + } + + static void updateButtonRadiusWithPartnerConfig( + Context context, Button button, PartnerConfig buttonRadiusConfig) { + if (Build.VERSION.SDK_INT >= VERSION_CODES.N) { + float radius = PartnerConfigHelper.get(context).getDimension(context, buttonRadiusConfig); + GradientDrawable gradientDrawable = getGradientDrawable(button); + if (gradientDrawable != null) { + gradientDrawable.setCornerRadius(radius); + } + } + } + + static void updateButtonIconWithPartnerConfig( + Context context, Button button, PartnerConfig buttonIconConfig, boolean isButtonIconAtEnd) { + if (button == null) { + return; + } + Drawable icon = null; + if (buttonIconConfig != null) { + icon = PartnerConfigHelper.get(context).getDrawable(context, buttonIconConfig); + } + setButtonIcon(button, icon, isButtonIconAtEnd); + } + + private static void setButtonIcon(Button button, Drawable icon, boolean isButtonIconAtEnd) { + if (button == null) { + return; + } + + if (icon != null) { + // TODO: restrict the icons to a reasonable size + int h = icon.getIntrinsicHeight(); + int w = icon.getIntrinsicWidth(); + icon.setBounds(0, 0, w, h); + } + + Drawable iconStart = null; + Drawable iconEnd = null; + if (isButtonIconAtEnd) { + iconEnd = icon; + } else { + iconStart = icon; + } + if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) { + button.setCompoundDrawablesRelative(iconStart, null, iconEnd, null); + } else { + button.setCompoundDrawables(iconStart, null, iconEnd, null); + } + } + + static void updateButtonBackground(Button button, @ColorInt int color) { + button.getBackground().mutate().setColorFilter(color, Mode.SRC_ATOP); + } + + @VisibleForTesting + public static GradientDrawable getGradientDrawable(Button button) { + // RippleDrawable is available after sdk 21, InsetDrawable#getDrawable is available after + // sdk 19. So check the sdk is higher than sdk 21 and since Stencil customization provider only + // works on Q+, there is no need to perform any customization for versions 21. + if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { + Drawable drawable = button.getBackground(); + if (drawable instanceof InsetDrawable) { + LayerDrawable layerDrawable = (LayerDrawable) ((InsetDrawable) drawable).getDrawable(); + return (GradientDrawable) layerDrawable.getDrawable(0); + } else if (drawable instanceof RippleDrawable) { + if (((RippleDrawable) drawable).getDrawable(0) instanceof GradientDrawable) { + return (GradientDrawable) ((RippleDrawable) drawable).getDrawable(0); + } + InsetDrawable insetDrawable = (InsetDrawable) ((RippleDrawable) drawable).getDrawable(0); + return (GradientDrawable) insetDrawable.getDrawable(); + } + } + return null; + } + + static RippleDrawable getRippleDrawable(Button button) { + // RippleDrawable is available after sdk 21. And because on lower sdk the RippleDrawable is + // unavailable. Since Stencil customization provider only works on Q+, there is no need to + // perform any customization for versions 21. + if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { + Drawable drawable = button.getBackground(); + if (drawable instanceof InsetDrawable) { + return (RippleDrawable) ((InsetDrawable) drawable).getDrawable(); + } else if (drawable instanceof RippleDrawable) { + return (RippleDrawable) drawable; + } + } + return null; + } + + @ColorInt + private static int convertRgbToArgb(@ColorInt int color, float alpha) { + return Color.argb((int) (alpha * 255), Color.red(color), Color.green(color), Color.blue(color)); + } + + private FooterButtonStyleUtils() {} +} diff --git a/main/java/com/google/android/setupcompat/template/StatusBarMixin.java b/main/java/com/google/android/setupcompat/template/StatusBarMixin.java index 1bd6949..c0f1c45 100644 --- a/main/java/com/google/android/setupcompat/template/StatusBarMixin.java +++ b/main/java/com/google/android/setupcompat/template/StatusBarMixin.java @@ -25,13 +25,13 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Build.VERSION_CODES; -import androidx.annotation.AttrRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import android.util.AttributeSet; import android.view.View; import android.view.Window; import android.widget.LinearLayout; +import androidx.annotation.AttrRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.google.android.setupcompat.PartnerCustomizationLayout; import com.google.android.setupcompat.R; import com.google.android.setupcompat.partnerconfig.PartnerConfig; @@ -112,10 +112,14 @@ public class StatusBarMixin implements Mixin { */ public void setStatusBarBackground(Drawable background) { if (partnerCustomizationLayout.shouldApplyPartnerResource()) { + // If full dynamic color enabled which means this activity is running outside of setup + // flow, the colors should refer to R.style.SudFullDynamicColorThemeGlifV3. + if (!partnerCustomizationLayout.useFullDynamicColor()) { Context context = partnerCustomizationLayout.getContext(); background = PartnerConfigHelper.get(context) .getDrawable(context, PartnerConfig.CONFIG_STATUS_BAR_BACKGROUND); + } } if (statusBarLayout == null) { diff --git a/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java b/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java index e055d28..32c708c 100644 --- a/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java +++ b/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java @@ -24,13 +24,13 @@ import android.graphics.Color; import android.os.Build; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; +import android.util.AttributeSet; +import android.view.View; +import android.view.Window; import androidx.annotation.AttrRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import android.util.AttributeSet; -import android.view.View; -import android.view.Window; import com.google.android.setupcompat.PartnerCustomizationLayout; import com.google.android.setupcompat.R; import com.google.android.setupcompat.internal.TemplateLayout; @@ -47,6 +47,7 @@ public class SystemNavBarMixin implements Mixin { private final TemplateLayout templateLayout; @Nullable private final Window windowOfActivity; @VisibleForTesting final boolean applyPartnerResources; + @VisibleForTesting final boolean useFullDynamicColor; private int sucSystemNavBarBackgroundColor = 0; /** @@ -61,6 +62,10 @@ public class SystemNavBarMixin implements Mixin { this.applyPartnerResources = layout instanceof PartnerCustomizationLayout && ((PartnerCustomizationLayout) layout).shouldApplyPartnerResource(); + + this.useFullDynamicColor = + layout instanceof PartnerCustomizationLayout + && ((PartnerCustomizationLayout) layout).useFullDynamicColor(); } /** @@ -83,6 +88,19 @@ public class SystemNavBarMixin implements Mixin { setLightSystemNavBar( a.getBoolean( R.styleable.SucSystemNavBarMixin_sucLightSystemNavBar, isLightSystemNavBar())); + + // Support updating system navigation bar divider color from P. + if (VERSION.SDK_INT >= VERSION_CODES.P) { + // get fallback value from theme + int[] navBarDividerColorAttr = new int[] {android.R.attr.navigationBarDividerColor}; + TypedArray typedArray = + templateLayout.getContext().obtainStyledAttributes(navBarDividerColorAttr); + int defaultColor = typedArray.getColor(/* index= */ 0, /* defValue= */ 0); + int sucSystemNavBarDividerColor = + a.getColor(R.styleable.SucSystemNavBarMixin_sucSystemNavBarDividerColor, defaultColor); + setSystemNavBarDividerColor(sucSystemNavBarDividerColor); + typedArray.recycle(); + } a.recycle(); } } @@ -96,10 +114,14 @@ public class SystemNavBarMixin implements Mixin { public void setSystemNavBarBackground(int color) { if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && windowOfActivity != null) { if (applyPartnerResources) { - Context context = templateLayout.getContext(); - color = - PartnerConfigHelper.get(context) - .getColor(context, PartnerConfig.CONFIG_NAVIGATION_BAR_BG_COLOR); + // If full dynamic color enabled which means this activity is running outside of setup + // flow, the colors should refer to R.style.SudFullDynamicColorThemeGlifV3. + if (!useFullDynamicColor) { + Context context = templateLayout.getContext(); + color = + PartnerConfigHelper.get(context) + .getColor(context, PartnerConfig.CONFIG_NAVIGATION_BAR_BG_COLOR); + } } windowOfActivity.setNavigationBarColor(color); } @@ -120,6 +142,7 @@ public class SystemNavBarMixin implements Mixin { * * @param isLight true means compatible with light theme, otherwise compatible with dark theme */ + public void setLightSystemNavBar(boolean isLight) { if (Build.VERSION.SDK_INT >= VERSION_CODES.O && windowOfActivity != null) { if (applyPartnerResources) { @@ -158,6 +181,28 @@ public class SystemNavBarMixin implements Mixin { } /** + * Sets the divider color of navigation bar. The color will be overridden by partner resource if + * the activity is running in setup wizard flow. + * + * @param color the default divider color of navigation bar + */ + public void setSystemNavBarDividerColor(int color) { + if (Build.VERSION.SDK_INT >= VERSION_CODES.P && windowOfActivity != null) { + if (applyPartnerResources) { + Context context = templateLayout.getContext(); + // Do nothing if the old version partner provider did not contain the new config. + if (PartnerConfigHelper.get(context) + .isPartnerConfigAvailable(PartnerConfig.CONFIG_NAVIGATION_BAR_DIVIDER_COLOR)) { + color = + PartnerConfigHelper.get(context) + .getColor(context, PartnerConfig.CONFIG_NAVIGATION_BAR_DIVIDER_COLOR); + } + } + windowOfActivity.setNavigationBarDividerColor(color); + } + } + + /** * Hides the navigation bar, make the color of the status and navigation bars transparent, and * specify {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} flag so that the content is laid-out * behind the transparent status bar. This is commonly used with {@link |