summaryrefslogtreecommitdiff
path: root/main/java/com/google/android/setupcompat/template
diff options
context:
space:
mode:
Diffstat (limited to 'main/java/com/google/android/setupcompat/template')
-rw-r--r--main/java/com/google/android/setupcompat/template/FooterActionButton.java2
-rw-r--r--main/java/com/google/android/setupcompat/template/FooterBarMixin.java360
-rw-r--r--main/java/com/google/android/setupcompat/template/FooterButton.java63
-rw-r--r--main/java/com/google/android/setupcompat/template/FooterButtonInflater.java2
-rw-r--r--main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java412
-rw-r--r--main/java/com/google/android/setupcompat/template/StatusBarMixin.java10
-rw-r--r--main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java59
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