summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2019-05-11 23:17:42 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2019-05-11 23:17:42 +0000
commit4148989c031617b317b128b71ef1c49dd21b5e92 (patch)
treeb71b724c0225aac13934f68cafc39737011ef49a
parent23051c853af2ff83efa8d0f1587757f83897e6de (diff)
parent3266cd8b60eab2ff75573e7df5ff62b2a8f7db00 (diff)
downloadsetupcompat-4148989c031617b317b128b71ef1c49dd21b5e92.tar.gz
Snap for 5558509 from 3266cd8b60eab2ff75573e7df5ff62b2a8f7db00 to qt-release
Change-Id: Ie408a01fe0b90210ed542e92ba57452970d4a37c
-rw-r--r--main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java5
-rw-r--r--main/java/com/google/android/setupcompat/internal/BuildCompat.java18
-rw-r--r--main/java/com/google/android/setupcompat/internal/FooterButtonPartnerConfig.java155
-rw-r--r--main/java/com/google/android/setupcompat/internal/PersistableBundles.java86
-rw-r--r--main/java/com/google/android/setupcompat/logging/CustomEvent.java66
-rw-r--r--main/java/com/google/android/setupcompat/logging/MetricKey.java24
-rw-r--r--main/java/com/google/android/setupcompat/logging/SetupMetricsLogger.java25
-rw-r--r--main/java/com/google/android/setupcompat/logging/internal/MetricBundleConverter.java34
-rw-r--r--main/java/com/google/android/setupcompat/logging/internal/SetupMetricsLoggingConstants.java23
-rw-r--r--main/java/com/google/android/setupcompat/template/FooterBarMixin.java410
-rw-r--r--main/java/com/google/android/setupcompat/template/FooterButton.java10
-rw-r--r--main/java/com/google/android/setupcompat/template/StatusBarMixin.java24
-rw-r--r--main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java52
-rw-r--r--main/java/com/google/android/setupcompat/util/WizardManagerHelper.java4
-rw-r--r--main/res/values/attrs.xml4
15 files changed, 662 insertions, 278 deletions
diff --git a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
index 95d6af4..360a0a0 100644
--- a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
+++ b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
@@ -31,7 +31,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
-import com.google.android.setupcompat.internal.BuildCompat;
import com.google.android.setupcompat.internal.LifecycleFragment;
import com.google.android.setupcompat.internal.PersistableBundles;
import com.google.android.setupcompat.internal.TemplateLayout;
@@ -192,7 +191,7 @@ public class PartnerCustomizationLayout extends TemplateLayout {
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP
- && BuildCompat.isAtLeastQ()
+ && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
&& WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
FooterBarMixin footerBarMixin = getMixin(FooterBarMixin.class);
footerBarMixin.onDetachedFromWindow();
@@ -247,7 +246,7 @@ public class PartnerCustomizationLayout extends TemplateLayout {
if (!usePartnerResourceAttr) {
return false;
}
- if (!BuildCompat.isAtLeastQ()) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
return false;
}
if (!PartnerConfigHelper.get(getContext()).isAvailable()) {
diff --git a/main/java/com/google/android/setupcompat/internal/BuildCompat.java b/main/java/com/google/android/setupcompat/internal/BuildCompat.java
deleted file mode 100644
index 7aeb85d..0000000
--- a/main/java/com/google/android/setupcompat/internal/BuildCompat.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.google.android.setupcompat.internal;
-
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
-
-/** Utility methods for detecting the build API version. */
-public final class BuildCompat {
-
- private BuildCompat() {}
-
- // TODO: remove the code for pre-release version of Android Q
- public static boolean isAtLeastQ() {
- return (VERSION.SDK_INT > VERSION_CODES.P)
- || (VERSION.CODENAME.length() == 1
- && VERSION.CODENAME.charAt(0) >= 'Q'
- && VERSION.CODENAME.charAt(0) <= 'Z');
- }
-}
diff --git a/main/java/com/google/android/setupcompat/internal/FooterButtonPartnerConfig.java b/main/java/com/google/android/setupcompat/internal/FooterButtonPartnerConfig.java
new file mode 100644
index 0000000..6a019fd
--- /dev/null
+++ b/main/java/com/google/android/setupcompat/internal/FooterButtonPartnerConfig.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2019 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.internal;
+
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.template.FooterButton;
+
+/** Keep the partner configuration of a footer button. Used when the button is inflated. */
+public class FooterButtonPartnerConfig {
+ private final PartnerConfig buttonBackgroundConfig;
+ private final PartnerConfig buttonIconConfig;
+ private final PartnerConfig buttonTextColorConfig;
+ private final PartnerConfig buttonTextSizeConfig;
+ private final PartnerConfig buttonTextTypeFaceConfig;
+ private final PartnerConfig buttonRadiusConfig;
+ private final PartnerConfig buttonRippleColorAlphaConfig;
+ private final int partnerTheme;
+
+ private FooterButtonPartnerConfig(
+ int partnerTheme,
+ PartnerConfig buttonBackgroundConfig,
+ PartnerConfig buttonIconConfig,
+ PartnerConfig buttonTextColorConfig,
+ PartnerConfig buttonTextSizeConfig,
+ PartnerConfig buttonTextTypeFaceConfig,
+ PartnerConfig buttonRadiusConfig,
+ PartnerConfig buttonRippleColorAlphaConfig) {
+ this.partnerTheme = partnerTheme;
+
+ this.buttonTextColorConfig = buttonTextColorConfig;
+ this.buttonTextSizeConfig = buttonTextSizeConfig;
+ this.buttonTextTypeFaceConfig = buttonTextTypeFaceConfig;
+ this.buttonBackgroundConfig = buttonBackgroundConfig;
+ this.buttonRadiusConfig = buttonRadiusConfig;
+ this.buttonIconConfig = buttonIconConfig;
+ this.buttonRippleColorAlphaConfig = buttonRippleColorAlphaConfig;
+ }
+
+ public int getPartnerTheme() {
+ return partnerTheme;
+ }
+
+ public PartnerConfig getButtonBackgroundConfig() {
+ return buttonBackgroundConfig;
+ }
+
+ public PartnerConfig getButtonIconConfig() {
+ return buttonIconConfig;
+ }
+
+ public PartnerConfig getButtonTextColorConfig() {
+ return buttonTextColorConfig;
+ }
+
+ public PartnerConfig getButtonTextSizeConfig() {
+ return buttonTextSizeConfig;
+ }
+
+ public PartnerConfig getButtonTextTypeFaceConfig() {
+ return buttonTextTypeFaceConfig;
+ }
+
+ public PartnerConfig getButtonRadiusConfig() {
+ return buttonRadiusConfig;
+ }
+
+ public PartnerConfig getButtonRippleColorAlphaConfig() {
+ return buttonRippleColorAlphaConfig;
+ }
+
+ /** Builder class for constructing {@code FooterButtonPartnerConfig} objects. */
+ public static class Builder {
+ private final FooterButton footerButton;
+ private PartnerConfig buttonBackgroundConfig = null;
+ private PartnerConfig buttonIconConfig = null;
+ private PartnerConfig buttonTextColorConfig = null;
+ private PartnerConfig buttonTextSizeConfig = null;
+ private PartnerConfig buttonTextTypeFaceConfig = null;
+ private PartnerConfig buttonRadiusConfig = null;
+ private PartnerConfig buttonRippleColorAlphaConfig = null;
+ private int partnerTheme;
+
+ public Builder(FooterButton footerButton) {
+ this.footerButton = footerButton;
+ // default partnerTheme should be the same as footerButton.getTheme();
+ this.partnerTheme = this.footerButton.getTheme();
+ }
+
+ public Builder setButtonBackgroundConfig(PartnerConfig buttonBackgroundConfig) {
+ this.buttonBackgroundConfig = buttonBackgroundConfig;
+ return this;
+ }
+
+ public Builder setButtonIconConfig(PartnerConfig buttonIconConfig) {
+ this.buttonIconConfig = buttonIconConfig;
+ return this;
+ }
+
+ public Builder setTextColorConfig(PartnerConfig buttonTextColorConfig) {
+ this.buttonTextColorConfig = buttonTextColorConfig;
+ return this;
+ }
+
+ public Builder setTextSizeConfig(PartnerConfig buttonTextSizeConfig) {
+ this.buttonTextSizeConfig = buttonTextSizeConfig;
+ return this;
+ }
+
+ public Builder setTextTypeFaceConfig(PartnerConfig buttonTextTypeFaceConfig) {
+ this.buttonTextTypeFaceConfig = buttonTextTypeFaceConfig;
+ return this;
+ }
+
+ public Builder setButtonRadiusConfig(PartnerConfig buttonRadiusConfig) {
+ this.buttonRadiusConfig = buttonRadiusConfig;
+ return this;
+ }
+
+ public Builder setButtonRippleColorAlphaConfig(PartnerConfig buttonRippleColorAlphaConfig) {
+ this.buttonRippleColorAlphaConfig = buttonRippleColorAlphaConfig;
+ return this;
+ }
+
+ public Builder setPartnerTheme(int partnerTheme) {
+ this.partnerTheme = partnerTheme;
+ return this;
+ }
+
+ public FooterButtonPartnerConfig build() {
+ return new FooterButtonPartnerConfig(
+ partnerTheme,
+ buttonBackgroundConfig,
+ buttonIconConfig,
+ buttonTextColorConfig,
+ buttonTextSizeConfig,
+ buttonTextTypeFaceConfig,
+ buttonRadiusConfig,
+ buttonRippleColorAlphaConfig);
+ }
+ }
+}
diff --git a/main/java/com/google/android/setupcompat/internal/PersistableBundles.java b/main/java/com/google/android/setupcompat/internal/PersistableBundles.java
index 6d7d17c..1197645 100644
--- a/main/java/com/google/android/setupcompat/internal/PersistableBundles.java
+++ b/main/java/com/google/android/setupcompat/internal/PersistableBundles.java
@@ -17,8 +17,12 @@
package com.google.android.setupcompat.internal;
import android.annotation.TargetApi;
+import android.os.BaseBundle;
import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
import android.os.PersistableBundle;
+import android.util.ArrayMap;
+import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -54,7 +58,89 @@ public final class PersistableBundles {
return result;
}
+ /** Returns a {@link Bundle} that contains all the values from {@code persistableBundle}. */
+ public static Bundle toBundle(PersistableBundle persistableBundle) {
+ Bundle bundle = new Bundle();
+ bundle.putAll(persistableBundle);
+ return bundle;
+ }
+
+ /**
+ * Returns a {@link PersistableBundle} that contains values from {@code bundle} that are supported
+ * by the logging API. Un-supported value types are dropped.
+ */
+ public static PersistableBundle fromBundle(Bundle bundle) {
+ PersistableBundle to = new PersistableBundle();
+ ArrayMap<String, Object> map = toMap(bundle);
+ for (String key : map.keySet()) {
+ Object value = map.get(key);
+ if (value instanceof Long) {
+ to.putLong(key, (Long) value);
+ } else if (value instanceof Integer) {
+ to.putInt(key, (Integer) value);
+ } else if (value instanceof Double) {
+ to.putDouble(key, (Double) value);
+ } else if (value instanceof Boolean) {
+ to.putBoolean(key, (Boolean) value);
+ } else if (value instanceof String) {
+ to.putString(key, (String) value);
+ } else {
+ throw new AssertionError(String.format("Missing put* for valid data type? = %s", value));
+ }
+ }
+ return to;
+ }
+
+ /** Returns {@code true} if {@code left} contains same set of values as {@code right}. */
+ public static boolean equals(PersistableBundle left, PersistableBundle right) {
+ return (left == right) || toMap(left).equals(toMap(right));
+ }
+
+ /** Asserts that {@code persistableBundle} contains only supported data types. */
+ public static PersistableBundle assertIsValid(PersistableBundle persistableBundle) {
+ Preconditions.checkNotNull(persistableBundle, "PersistableBundle cannot be null!");
+ for (String key : persistableBundle.keySet()) {
+ Object value = persistableBundle.get(key);
+ Preconditions.checkArgument(
+ isSupportedDataType(value),
+ String.format("Unknown/unsupported data type [%s] for key %s", value, key));
+ }
+ return persistableBundle;
+ }
+
+ /**
+ * Returns a new {@link ArrayMap} that contains values from {@code bundle} that are supported by
+ * the logging API.
+ */
+ private static ArrayMap<String, Object> toMap(BaseBundle baseBundle) {
+ if (baseBundle == null || baseBundle.isEmpty()) {
+ return new ArrayMap<>(0);
+ }
+
+ ArrayMap<String, Object> map = new ArrayMap<>(baseBundle.size());
+ for (String key : baseBundle.keySet()) {
+ Object value = baseBundle.get(key);
+ if (!isSupportedDataType(value)) {
+ Log.w(TAG, String.format("Unknown/unsupported data type [%s] for key %s", value, key));
+ continue;
+ }
+ map.put(key, baseBundle.get(key));
+ }
+ return map;
+ }
+
+ private static boolean isSupportedDataType(Object value) {
+ return value instanceof Integer
+ || value instanceof Long
+ || value instanceof Double
+ || value instanceof Float
+ || value instanceof String
+ || value instanceof Boolean;
+ }
+
private PersistableBundles() {
throw new AssertionError("Should not be instantiated");
}
+
+ private static final String TAG = "SetupCompat.PersistBls";
}
diff --git a/main/java/com/google/android/setupcompat/logging/CustomEvent.java b/main/java/com/google/android/setupcompat/logging/CustomEvent.java
index 18d8f3c..88ac05e 100644
--- a/main/java/com/google/android/setupcompat/logging/CustomEvent.java
+++ b/main/java/com/google/android/setupcompat/logging/CustomEvent.java
@@ -19,13 +19,15 @@ package com.google.android.setupcompat.logging;
import static com.google.android.setupcompat.internal.Validations.assertLengthInRange;
import android.annotation.TargetApi;
+import android.os.Build;
import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import androidx.annotation.VisibleForTesting;
-import com.google.android.setupcompat.internal.BuildCompat;
import com.google.android.setupcompat.internal.ClockProvider;
+import com.google.android.setupcompat.internal.PersistableBundles;
import com.google.android.setupcompat.internal.Preconditions;
import com.google.android.setupcompat.util.ObjectUtils;
@@ -37,22 +39,59 @@ import com.google.android.setupcompat.util.ObjectUtils;
*/
@TargetApi(VERSION_CODES.Q)
public final class CustomEvent implements Parcelable {
+ private static final String BUNDLE_KEY_TIMESTAMP = "CustomEvent_timestamp";
+ private static final String BUNDLE_KEY_METRICKEY = "CustomEvent_metricKey";
+ private static final String BUNDLE_KEY_BUNDLE_VALUES = "CustomEvent_bundleValues";
+ private static final String BUNDLE_KEY_BUNDLE_PII_VALUES = "CustomEvent_pii_bundleValues";
+ private static final String BUNDLE_VERSION = "CustomEvent_version";
+ private static final int VERSION = 1;
/** Creates a new instance of {@code CustomEvent}. Null arguments are not allowed. */
public static CustomEvent create(
MetricKey metricKey, PersistableBundle bundle, PersistableBundle piiValues) {
Preconditions.checkArgument(
- BuildCompat.isAtLeastQ(), "The constructor only support on sdk Q or higher");
- return new CustomEvent(ClockProvider.timeInMillis(), metricKey, bundle, piiValues);
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q,
+ "The constructor only support on sdk Q or higher");
+ return new CustomEvent(
+ ClockProvider.timeInMillis(),
+ metricKey,
+ // Assert only in factory methods since these methods are directly used by API consumers
+ // while constructor is used directly only when data is de-serialized from bundle (which
+ // might have been sent by a client using a newer API)
+ PersistableBundles.assertIsValid(bundle),
+ PersistableBundles.assertIsValid(piiValues));
}
/** Creates a new instance of {@code CustomEvent}. Null arguments are not allowed. */
public static CustomEvent create(MetricKey metricKey, PersistableBundle bundle) {
Preconditions.checkArgument(
- BuildCompat.isAtLeastQ(), "The constructor only support on sdk Q or higher.");
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q,
+ "The constructor only support on sdk Q or higher.");
return create(metricKey, bundle, PersistableBundle.EMPTY);
}
+ /** Converts {@link Bundle} into {@link CustomEvent}. */
+ public static CustomEvent toCustomEvent(Bundle bundle) {
+ return new CustomEvent(
+ bundle.getLong(BUNDLE_KEY_TIMESTAMP, /* defaultValue= */ Long.MIN_VALUE),
+ MetricKey.toMetricKey(bundle.getBundle(BUNDLE_KEY_METRICKEY)),
+ PersistableBundles.fromBundle(bundle.getBundle(BUNDLE_KEY_BUNDLE_VALUES)),
+ PersistableBundles.fromBundle(bundle.getBundle(BUNDLE_KEY_BUNDLE_PII_VALUES)));
+ }
+
+ /** Converts {@link CustomEvent} into {@link Bundle}. */
+ public static Bundle toBundle(CustomEvent customEvent) {
+ Preconditions.checkNotNull(customEvent, "CustomEvent cannot be null");
+ Bundle bundle = new Bundle();
+ bundle.putInt(BUNDLE_VERSION, VERSION);
+ bundle.putLong(BUNDLE_KEY_TIMESTAMP, customEvent.timestampMillis());
+ bundle.putBundle(BUNDLE_KEY_METRICKEY, MetricKey.fromMetricKey(customEvent.metricKey()));
+ bundle.putBundle(BUNDLE_KEY_BUNDLE_VALUES, PersistableBundles.toBundle(customEvent.values()));
+ bundle.putBundle(
+ BUNDLE_KEY_BUNDLE_PII_VALUES, PersistableBundles.toBundle(customEvent.piiValues()));
+ return bundle;
+ }
+
public static final Creator<CustomEvent> CREATOR =
new Creator<CustomEvent>() {
@Override
@@ -117,8 +156,8 @@ public final class CustomEvent implements Parcelable {
CustomEvent that = (CustomEvent) o;
return timestampMillis == that.timestampMillis
&& ObjectUtils.equals(metricKey, that.metricKey)
- && ObjectUtils.equals(persistableBundle, that.persistableBundle)
- && ObjectUtils.equals(piiValues, that.piiValues);
+ && PersistableBundles.equals(persistableBundle, that.persistableBundle)
+ && PersistableBundles.equals(piiValues, that.piiValues);
}
@Override
@@ -152,19 +191,6 @@ public final class CustomEvent implements Parcelable {
for (String key : bundle.keySet()) {
assertLengthInRange(key, "bundle key", MIN_BUNDLE_KEY_LENGTH, MAX_STR_LENGTH);
Object value = bundle.get(key);
- boolean valid = false;
- for (Class<?> clazz : CUSTOM_EVENT_ALLOWED_DATA_TYPES) {
- if (clazz.isInstance(value)) {
- valid = true;
- break;
- }
- }
- Preconditions.checkArgument(
- valid,
- String.format(
- "Invalid data type for key='%s'. Expected values of type %s, but found [%s].",
- key, CUSTOM_EVENT_ALLOWED_DATA_TYPES, value));
-
if (value instanceof String) {
Preconditions.checkArgument(
((String) value).length() <= MAX_STR_LENGTH,
@@ -175,8 +201,6 @@ public final class CustomEvent implements Parcelable {
}
}
- private static final Class<?>[] CUSTOM_EVENT_ALLOWED_DATA_TYPES =
- new Class<?>[] {Integer.class, Long.class, Double.class, String.class, Boolean.class};
@VisibleForTesting static final int MAX_STR_LENGTH = 50;
@VisibleForTesting static final int MIN_BUNDLE_KEY_LENGTH = 3;
}
diff --git a/main/java/com/google/android/setupcompat/logging/MetricKey.java b/main/java/com/google/android/setupcompat/logging/MetricKey.java
index 8d3692c..125dee9 100644
--- a/main/java/com/google/android/setupcompat/logging/MetricKey.java
+++ b/main/java/com/google/android/setupcompat/logging/MetricKey.java
@@ -18,6 +18,7 @@ package com.google.android.setupcompat.logging;
import static com.google.android.setupcompat.internal.Validations.assertLengthInRange;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
@@ -31,6 +32,11 @@ import java.util.regex.Pattern;
*/
public final class MetricKey implements Parcelable {
+ private static final String METRIC_KEY_BUNDLE_NAME_KEY = "MetricKey_name";
+ private static final String METRIC_KEY_BUNDLE_SCREEN_NAME_KEY = "MetricKey_screenName";
+ private static final String METRIC_KEY_BUNDLE_VERSION = "MetricKey_version";
+ private static final int VERSION = 1;
+
/**
* Creates a new instance of MetricKey.
*
@@ -56,6 +62,24 @@ public final class MetricKey implements Parcelable {
return new MetricKey(name, screenName);
}
+ /** Converts {@link MetricKey} into {@link Bundle}. */
+ public static Bundle fromMetricKey(MetricKey metricKey) {
+ Preconditions.checkNotNull(metricKey, "MetricKey cannot be null.");
+ Bundle bundle = new Bundle();
+ bundle.putInt(METRIC_KEY_BUNDLE_VERSION, VERSION);
+ bundle.putString(METRIC_KEY_BUNDLE_NAME_KEY, metricKey.name());
+ bundle.putString(METRIC_KEY_BUNDLE_SCREEN_NAME_KEY, metricKey.screenName());
+ return bundle;
+ }
+
+ /** Converts {@link Bundle} into {@link MetricKey}. */
+ public static MetricKey toMetricKey(Bundle bundle) {
+ Preconditions.checkNotNull(bundle, "Bundle cannot be null");
+ return MetricKey.get(
+ bundle.getString(METRIC_KEY_BUNDLE_NAME_KEY),
+ bundle.getString(METRIC_KEY_BUNDLE_SCREEN_NAME_KEY));
+ }
+
public static final Creator<MetricKey> CREATOR =
new Creator<MetricKey>() {
@Override
diff --git a/main/java/com/google/android/setupcompat/logging/SetupMetricsLogger.java b/main/java/com/google/android/setupcompat/logging/SetupMetricsLogger.java
index 2869c65..8d696e0 100644
--- a/main/java/com/google/android/setupcompat/logging/SetupMetricsLogger.java
+++ b/main/java/com/google/android/setupcompat/logging/SetupMetricsLogger.java
@@ -17,11 +17,10 @@
package com.google.android.setupcompat.logging;
import android.content.Context;
-import android.os.Bundle;
import androidx.annotation.NonNull;
import com.google.android.setupcompat.internal.Preconditions;
import com.google.android.setupcompat.internal.SetupCompatServiceInvoker;
-import com.google.android.setupcompat.logging.internal.SetupMetricsLoggingConstants.MetricBundleKeys;
+import com.google.android.setupcompat.logging.internal.MetricBundleConverter;
import com.google.android.setupcompat.logging.internal.SetupMetricsLoggingConstants.MetricType;
import java.util.concurrent.TimeUnit;
@@ -32,9 +31,9 @@ public class SetupMetricsLogger {
public static void logCustomEvent(@NonNull Context context, @NonNull CustomEvent customEvent) {
Preconditions.checkNotNull(context, "Context cannot be null.");
Preconditions.checkNotNull(customEvent, "CustomEvent cannot be null.");
- Bundle bundle = new Bundle();
- bundle.putParcelable(MetricBundleKeys.CUSTOM_EVENT, customEvent);
- SetupCompatServiceInvoker.get(context).logMetricEvent(MetricType.CUSTOM_EVENT, bundle);
+ SetupCompatServiceInvoker.get(context)
+ .logMetricEvent(
+ MetricType.CUSTOM_EVENT, MetricBundleConverter.createBundleForLogging(customEvent));
}
/** Increments the counter value with the name {@code counterName} by {@code times}. */
@@ -43,10 +42,10 @@ public class SetupMetricsLogger {
Preconditions.checkNotNull(context, "Context cannot be null.");
Preconditions.checkNotNull(counterName, "CounterName cannot be null.");
Preconditions.checkArgument(times > 0, "Counter cannot be negative.");
- Bundle bundle = new Bundle();
- bundle.putParcelable(MetricBundleKeys.METRIC_KEY, counterName);
- bundle.putInt(MetricBundleKeys.COUNTER_INT, times);
- SetupCompatServiceInvoker.get(context).logMetricEvent(MetricType.COUNTER_EVENT, bundle);
+ SetupCompatServiceInvoker.get(context)
+ .logMetricEvent(
+ MetricType.COUNTER_EVENT,
+ MetricBundleConverter.createBundleForLoggingCounter(counterName, times));
}
/**
@@ -67,9 +66,9 @@ public class SetupMetricsLogger {
Preconditions.checkNotNull(context, "Context cannot be null.");
Preconditions.checkNotNull(timerName, "Timer name cannot be null.");
Preconditions.checkArgument(timeInMillis >= 0, "Duration cannot be negative.");
- Bundle bundle = new Bundle();
- bundle.putParcelable(MetricBundleKeys.METRIC_KEY, timerName);
- bundle.putLong(MetricBundleKeys.TIME_MILLIS_LONG, timeInMillis);
- SetupCompatServiceInvoker.get(context).logMetricEvent(MetricType.DURATION_EVENT, bundle);
+ SetupCompatServiceInvoker.get(context)
+ .logMetricEvent(
+ MetricType.DURATION_EVENT,
+ MetricBundleConverter.createBundleForLoggingTimer(timerName, timeInMillis));
}
}
diff --git a/main/java/com/google/android/setupcompat/logging/internal/MetricBundleConverter.java b/main/java/com/google/android/setupcompat/logging/internal/MetricBundleConverter.java
new file mode 100644
index 0000000..e1a3909
--- /dev/null
+++ b/main/java/com/google/android/setupcompat/logging/internal/MetricBundleConverter.java
@@ -0,0 +1,34 @@
+package com.google.android.setupcompat.logging.internal;
+
+import android.os.Bundle;
+import com.google.android.setupcompat.logging.CustomEvent;
+import com.google.android.setupcompat.logging.MetricKey;
+import com.google.android.setupcompat.logging.internal.SetupMetricsLoggingConstants.MetricBundleKeys;
+
+/** Collection of helper methods for reading and writing {@link CustomEvent}, {@link MetricKey}. */
+public final class MetricBundleConverter {
+
+ public static Bundle createBundleForLogging(CustomEvent customEvent) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(MetricBundleKeys.CUSTOM_EVENT_BUNDLE, CustomEvent.toBundle(customEvent));
+ return bundle;
+ }
+
+ public static Bundle createBundleForLoggingCounter(MetricKey counterName, int times) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(MetricBundleKeys.METRIC_KEY_BUNDLE, MetricKey.fromMetricKey(counterName));
+ bundle.putInt(MetricBundleKeys.COUNTER_INT, times);
+ return bundle;
+ }
+
+ public static Bundle createBundleForLoggingTimer(MetricKey timerName, long timeInMillis) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(MetricBundleKeys.METRIC_KEY_BUNDLE, MetricKey.fromMetricKey(timerName));
+ bundle.putLong(MetricBundleKeys.TIME_MILLIS_LONG, timeInMillis);
+ return bundle;
+ }
+
+ private MetricBundleConverter() {
+ throw new AssertionError("Cannot instantiate MetricBundleConverter");
+ }
+}
diff --git a/main/java/com/google/android/setupcompat/logging/internal/SetupMetricsLoggingConstants.java b/main/java/com/google/android/setupcompat/logging/internal/SetupMetricsLoggingConstants.java
index 9d70056..57a7272 100644
--- a/main/java/com/google/android/setupcompat/logging/internal/SetupMetricsLoggingConstants.java
+++ b/main/java/com/google/android/setupcompat/logging/internal/SetupMetricsLoggingConstants.java
@@ -55,7 +55,9 @@ public interface SetupMetricsLoggingConstants {
@Retention(RetentionPolicy.SOURCE)
@StringDef({
MetricBundleKeys.METRIC_KEY,
+ MetricBundleKeys.METRIC_KEY_BUNDLE,
MetricBundleKeys.CUSTOM_EVENT,
+ MetricBundleKeys.CUSTOM_EVENT_BUNDLE,
MetricBundleKeys.TIME_MILLIS_LONG,
MetricBundleKeys.COUNTER_INT
})
@@ -63,14 +65,18 @@ public interface SetupMetricsLoggingConstants {
/**
* {@link MetricKey} of the data being logged. This will be set when {@code metricType} is
* either {@link MetricType#COUNTER_EVENT} or {@link MetricType#DURATION_EVENT}.
+ *
+ * @deprecated Use {@link #METRIC_KEY_BUNDLE} instead.
*/
- String METRIC_KEY = "MetricKey";
+ @Deprecated String METRIC_KEY = "MetricKey";
/**
* This key will be used when {@code metricType} is {@link MetricType#CUSTOM_EVENT} with the
* value being a parcelable of type {@link com.google.android.setupcompat.logging.CustomEvent}.
+ *
+ * @deprecated Use {@link #CUSTOM_EVENT_BUNDLE} instead.
*/
- String CUSTOM_EVENT = "CustomEvent";
+ @Deprecated String CUSTOM_EVENT = "CustomEvent";
/**
* This key will be set when {@code metricType} is {@link MetricType#DURATION_EVENT} with the
@@ -85,5 +91,18 @@ public interface SetupMetricsLoggingConstants {
* MetricKey}.
*/
String COUNTER_INT = "counter";
+
+ /**
+ * {@link MetricKey} of the data being logged. This will be set when {@code metricType} is
+ * either {@link MetricType#COUNTER_EVENT} or {@link MetricType#DURATION_EVENT}.
+ */
+ String METRIC_KEY_BUNDLE = "MetricKey_bundle";
+
+ /**
+ * This key will be used when {@code metricType} is {@link MetricType#CUSTOM_EVENT} with the
+ * value being a Bundle which can be used to read {@link
+ * com.google.android.setupcompat.logging.CustomEvent}.
+ */
+ String CUSTOM_EVENT_BUNDLE = "CustomEvent_bundle";
}
}
diff --git a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
index f623382..c931de8 100644
--- a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
@@ -35,6 +35,7 @@ import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.PersistableBundle;
import androidx.annotation.AttrRes;
+import androidx.annotation.CallSuper;
import androidx.annotation.ColorInt;
import androidx.annotation.IdRes;
import androidx.annotation.LayoutRes;
@@ -55,7 +56,7 @@ 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.BuildCompat;
+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;
@@ -82,8 +83,10 @@ public class FooterBarMixin implements Mixin {
private FooterButton secondaryButton;
@IdRes private int primaryButtonId;
@IdRes private int secondaryButtonId;
- ColorStateList primaryTextColorStateList = null;
- ColorStateList secondaryTextColorStateList = null;
+ ColorStateList primaryDefaultTextColor = null;
+ ColorStateList secondaryDefaultTextColor = null;
+ @VisibleForTesting public FooterButtonPartnerConfig primaryButtonPartnerConfigForTesting;
+ @VisibleForTesting public FooterButtonPartnerConfig secondaryButtonPartnerConfigForTesting;
private int footerBarPaddingTop;
private int footerBarPaddingBottom;
@@ -108,7 +111,11 @@ public class FooterBarMixin implements Mixin {
if (button != null) {
button.setEnabled(enabled);
if (applyPartnerResources && !enabled) {
- updateButtonTextColorWithPartnerConfig(button, id == primaryButtonId);
+ updateButtonTextColorWithPartnerConfig(
+ button,
+ (id == primaryButtonId)
+ ? PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_COLOR
+ : PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR);
}
}
}
@@ -203,25 +210,77 @@ public class FooterBarMixin implements Mixin {
throw new IllegalStateException("Footer stub is not found in this template");
}
buttonContainer = (LinearLayout) inflateFooter(R.layout.suc_footer_button_bar);
- if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
- buttonContainer.setId(View.generateViewId());
- } else {
- buttonContainer.setId(generateViewId());
- }
- updateFooterBarPadding();
- if (applyPartnerResources) {
- @ColorInt
- int color =
- PartnerConfigHelper.get(context)
- .getColor(context, PartnerConfig.CONFIG_FOOTER_BAR_BG_COLOR);
- buttonContainer.setBackgroundColor(color);
- }
+ onFooterBarInflated(buttonContainer);
+ onFooterBarApplyPartnerResource(buttonContainer);
}
return buttonContainer;
}
+ /**
+ * Notifies that the footer bar has been inflated to the view hierarchy. Calling super is
+ * necessary while subclass implement it.
+ */
+ @CallSuper
+ protected void onFooterBarInflated(LinearLayout buttonContainer) {
+ if (buttonContainer == null) {
+ // Ignore action since buttonContainer is null
+ return;
+ }
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
+ buttonContainer.setId(View.generateViewId());
+ } else {
+ buttonContainer.setId(generateViewId());
+ }
+ updateFooterBarPadding(
+ buttonContainer,
+ buttonContainer.getPaddingLeft(),
+ footerBarPaddingTop,
+ buttonContainer.getPaddingRight(),
+ footerBarPaddingBottom);
+ }
+
+ /**
+ * Notifies while the footer bar apply Partner Resource. Calling super is necessary while subclass
+ * implement it.
+ */
+ @CallSuper
+ protected void onFooterBarApplyPartnerResource(LinearLayout buttonContainer) {
+ if (buttonContainer == null) {
+ // Ignore action since buttonContainer is null
+ return;
+ }
+ if (!applyPartnerResources) {
+ return;
+ }
+
+ @ColorInt
+ int color =
+ PartnerConfigHelper.get(context)
+ .getColor(context, PartnerConfig.CONFIG_FOOTER_BAR_BG_COLOR);
+ buttonContainer.setBackgroundColor(color);
+
+ footerBarPaddingTop =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_FOOTER_BUTTON_PADDING_TOP);
+ footerBarPaddingBottom =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_FOOTER_BUTTON_PADDING_BOTTOM);
+ updateFooterBarPadding(
+ buttonContainer,
+ buttonContainer.getPaddingLeft(),
+ footerBarPaddingTop,
+ buttonContainer.getPaddingRight(),
+ footerBarPaddingBottom);
+ }
+
+ /**
+ * Inflate FooterActionButton with layout "suc_button". Subclasses can implement this method to
+ * modify the footer button layout as necessary.
+ */
@SuppressLint("InflateParams")
- private FooterActionButton createThemedButton(Context context, @StyleRes int theme) {
+ protected FooterActionButton createThemedButton(Context context, @StyleRes int theme) {
// Inflate a single button from XML, which when using support lib, will take advantage of
// the injected layout inflater and give us AppCompatButton instead.
LayoutInflater inflater = LayoutInflater.from(new ContextThemeWrapper(context, theme));
@@ -232,41 +291,35 @@ public class FooterBarMixin implements Mixin {
@MainThread
public void setPrimaryButton(FooterButton footerButton) {
ensureOnMainThread("setPrimaryButton");
- LinearLayout buttonContainer = ensureFooterInflated();
-
- // Set the default theme if theme is not set, or when running in setup flow.
- if (footerButton.getTheme() == 0 || applyPartnerResources) {
- footerButton.setTheme(R.style.SucPartnerCustomizationButton_Primary);
- }
- // TODO: Make sure customize attributes in theme can be applied during setup flow.
- // If sets background color to full transparent, the button changes to colored borderless ink
- // button style.
- if (applyPartnerResources
- && PartnerConfigHelper.get(context)
- .getColor(context, PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_BG_COLOR)
- == Color.TRANSPARENT) {
- footerButton.setTheme(R.style.SucPartnerCustomizationButton_Secondary);
- }
-
- FooterActionButton button = inflateButton(footerButton);
+ ensureFooterInflated();
+
+ // Setup button partner config
+ FooterButtonPartnerConfig footerButtonPartnerConfig =
+ new FooterButtonPartnerConfig.Builder(footerButton)
+ .setPartnerTheme(
+ getPartnerTheme(
+ footerButton,
+ /* defaultPartnerTheme= */ R.style.SucPartnerCustomizationButton_Primary,
+ /* buttonBackgroundColorConfig= */ PartnerConfig
+ .CONFIG_FOOTER_PRIMARY_BUTTON_BG_COLOR))
+ .setButtonBackgroundConfig(PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_BG_COLOR)
+ .setButtonIconConfig(getDrawablePartnerConfig(footerButton.getButtonType()))
+ .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)
+ .setTextTypeFaceConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_FONT_FAMILY)
+ .build();
+
+ FooterActionButton button = inflateButton(footerButton, footerButtonPartnerConfig);
+ // update information for primary button. Need to update as long as the button inflated.
primaryButtonId = button.getId();
- buttonContainer.addView(button);
- autoSetButtonBarVisibility();
-
- if (applyPartnerResources) {
- // This API should only be called after primaryButtonId is set.
- updateButtonAttrsWithPartnerConfig(button, true, footerButton.getButtonType());
- } else {
- // Try to set default value
- if (footerBarPrimaryBackgroundColor != 0) {
- updateButtonBackground(button, footerBarPrimaryBackgroundColor);
- } else {
- // TODO: get button background color from activity theme
- }
- }
-
- footerButton.setOnButtonEventListener(createButtonEventListener(primaryButtonId));
+ primaryDefaultTextColor = button.getTextColors();
primaryButton = footerButton;
+ primaryButtonPartnerConfigForTesting = footerButtonPartnerConfig;
+
+ onFooterButtonInflated(button, footerBarPrimaryBackgroundColor);
+ onFooterButtonApplyPartnerResource(button, footerButtonPartnerConfig);
// Make sure the position of buttons are correctly and prevent primary button create twice or
// more.
@@ -278,7 +331,7 @@ public class FooterBarMixin implements Mixin {
return primaryButton;
}
- @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
public Button getPrimaryButtonView() {
return buttonContainer == null ? null : buttonContainer.findViewById(primaryButtonId);
}
@@ -292,47 +345,46 @@ public class FooterBarMixin implements Mixin {
@MainThread
public void setSecondaryButton(FooterButton footerButton) {
ensureOnMainThread("setSecondaryButton");
- LinearLayout buttonContainer = ensureFooterInflated();
-
- // Set the default theme if theme is not set, or when running in setup flow.
- if (footerButton.getTheme() == 0 || applyPartnerResources) {
- footerButton.setTheme(R.style.SucPartnerCustomizationButton_Secondary);
- }
- int color =
- PartnerConfigHelper.get(context)
- .getColor(context, PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_BG_COLOR);
- // TODO: Make sure customize attributes in theme can be applied during setup flow.
- // If doesn't set background color to full transparent or white, the button changes to colored
- // bordered ink button style.
- if (applyPartnerResources && (color != Color.TRANSPARENT && color != Color.WHITE)) {
- footerButton.setTheme(R.style.SucPartnerCustomizationButton_Primary);
- }
-
- FooterActionButton button = inflateButton(footerButton);
+ ensureFooterInflated();
+
+ // Setup button partner config
+ FooterButtonPartnerConfig footerButtonPartnerConfig =
+ new FooterButtonPartnerConfig.Builder(footerButton)
+ .setPartnerTheme(
+ getPartnerTheme(
+ footerButton,
+ /* defaultPartnerTheme= */ R.style.SucPartnerCustomizationButton_Secondary,
+ /* buttonBackgroundColorConfig= */ PartnerConfig
+ .CONFIG_FOOTER_SECONDARY_BUTTON_BG_COLOR))
+ .setButtonBackgroundConfig(PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_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)
+ .setTextSizeConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_SIZE)
+ .setTextTypeFaceConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_FONT_FAMILY)
+ .build();
+
+ FooterActionButton button = inflateButton(footerButton, footerButtonPartnerConfig);
+ // update information for secondary button. Need to update as long as the button inflated.
secondaryButtonId = button.getId();
- buttonContainer.addView(button);
-
- if (applyPartnerResources) {
- // This API should only be called after secondaryButtonId is set.
- updateButtonAttrsWithPartnerConfig(button, false, footerButton.getButtonType());
- } else {
- // Try to set default value
- if (footerBarSecondaryBackgroundColor != 0) {
- updateButtonBackground(button, footerBarSecondaryBackgroundColor);
- } else {
- // TODO: get button background color from activity theme
- }
- }
-
- footerButton.setOnButtonEventListener(createButtonEventListener(secondaryButtonId));
+ secondaryDefaultTextColor = button.getTextColors();
secondaryButton = footerButton;
+ secondaryButtonPartnerConfigForTesting = footerButtonPartnerConfig;
+
+ onFooterButtonInflated(button, footerBarSecondaryBackgroundColor);
+ onFooterButtonApplyPartnerResource(button, footerButtonPartnerConfig);
// Make sure the position of buttons are correctly and prevent secondary button create twice or
// more.
repopulateButtons();
}
- private void repopulateButtons() {
+ /**
+ * Corrects the order of footer buttons after the button has been inflated to the view hierarchy.
+ * Subclasses can implement this method to modify the order of footer buttons as necessary.
+ */
+ protected void repopulateButtons() {
LinearLayout buttonContainer = ensureFooterInflated();
Button tempPrimaryButton = getPrimaryButtonView();
Button tempSecondaryButton = getSecondaryButtonView();
@@ -347,8 +399,49 @@ public class FooterBarMixin implements Mixin {
}
}
+ /**
+ * Notifies that the footer button has been inInflated and add to the view hierarchy. Calling
+ * super is necessary while subclass implement it.
+ */
+ @CallSuper
+ protected void onFooterButtonInflated(Button button, @ColorInt int defaultButtonBackgroundColor) {
+ // Try to set default background
+ if (defaultButtonBackgroundColor != 0) {
+ updateButtonBackground(button, defaultButtonBackgroundColor);
+ } else {
+ // TODO: get button background color from activity theme
+ }
+ buttonContainer.addView(button);
+ autoSetButtonBarVisibility();
+ }
+
+ private int getPartnerTheme(
+ FooterButton footerButton,
+ int defaultPartnerTheme,
+ PartnerConfig buttonBackgroundColorConfig) {
+ int overrideTheme = footerButton.getTheme();
+
+ // Set the default theme if theme is not set, or when running in setup flow.
+ if (footerButton.getTheme() == 0 || applyPartnerResources) {
+ overrideTheme = defaultPartnerTheme;
+ }
+ // TODO: Make sure customize attributes in theme can be applied during setup flow.
+ // If sets background color to full transparent, the button changes to colored borderless ink
+ // button style.
+ int color = PartnerConfigHelper.get(context).getColor(context, buttonBackgroundColorConfig);
+ if (applyPartnerResources && color == Color.TRANSPARENT) {
+ overrideTheme = R.style.SucPartnerCustomizationButton_Secondary;
+ } else if (applyPartnerResources && (color != Color.TRANSPARENT)) {
+ // TODO: remove the constrain (color != Color.WHITE), need to check all pages go
+ // well without customization. It should be fine since the default value of secondary bg color
+ // is set as transparent.
+ overrideTheme = R.style.SucPartnerCustomizationButton_Primary;
+ }
+ return overrideTheme;
+ }
+
@VisibleForTesting
- LinearLayout getButtonContainer() {
+ public LinearLayout getButtonContainer() {
return buttonContainer;
}
@@ -392,7 +485,7 @@ public class FooterBarMixin implements Mixin {
return buttonContainer.getVisibility();
}
- @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
public Button getSecondaryButtonView() {
return buttonContainer == null ? null : buttonContainer.findViewById(secondaryButtonId);
}
@@ -417,8 +510,10 @@ public class FooterBarMixin implements Mixin {
}
}
- private FooterActionButton inflateButton(FooterButton footerButton) {
- FooterActionButton button = createThemedButton(context, footerButton.getTheme());
+ private FooterActionButton inflateButton(
+ FooterButton footerButton, FooterButtonPartnerConfig footerButtonPartnerConfig) {
+ FooterActionButton button =
+ createThemedButton(context, footerButtonPartnerConfig.getPartnerTheme());
if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
button.setId(View.generateViewId());
} else {
@@ -432,61 +527,57 @@ public class FooterBarMixin implements Mixin {
button.setEnabled(footerButton.isEnabled());
button.setFooterButton(footerButton);
+ footerButton.setOnButtonEventListener(createButtonEventListener(button.getId()));
return button;
}
// TODO: Make sure customize attributes in theme can be applied during setup flow.
@TargetApi(VERSION_CODES.Q)
- private void updateButtonAttrsWithPartnerConfig(
- Button button, boolean isPrimaryButton, @ButtonType int buttonType) {
- updateButtonTextColorWithPartnerConfig(button, isPrimaryButton);
- updateButtonTextSizeWithPartnerConfig(button);
- updateButtonTypeFaceWithPartnerConfig(button);
- updateButtonBackgroundWithPartnerConfig(button, isPrimaryButton);
- updateButtonRadiusWithPartnerConfig(button);
- updateButtonIconWithPartnerConfig(button, buttonType);
- updateButtonRippleColorWithPartnerConfig(button, isPrimaryButton);
- }
-
- private void updateButtonTextColorWithPartnerConfig(Button button, boolean isPrimaryButton) {
- @ColorInt int color;
- if (isPrimaryButton) {
- color =
- PartnerConfigHelper.get(context)
- .getColor(context, PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_COLOR);
- if (primaryTextColorStateList == null) {
- primaryTextColorStateList = button.getTextColors();
- }
- } else {
- color =
- PartnerConfigHelper.get(context)
- .getColor(context, PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR);
- if (secondaryTextColorStateList == null) {
- secondaryTextColorStateList = button.getTextColors();
- }
+ private void onFooterButtonApplyPartnerResource(
+ Button button, FooterButtonPartnerConfig footerButtonPartnerConfig) {
+ if (!applyPartnerResources) {
+ return;
}
-
+ updateButtonTextColorWithPartnerConfig(
+ button, footerButtonPartnerConfig.getButtonTextColorConfig());
+ updateButtonTextSizeWithPartnerConfig(
+ button, footerButtonPartnerConfig.getButtonTextSizeConfig());
+ updateButtonTypeFaceWithPartnerConfig(
+ button, footerButtonPartnerConfig.getButtonTextTypeFaceConfig());
+ updateButtonBackgroundWithPartnerConfig(
+ button, footerButtonPartnerConfig.getButtonBackgroundConfig());
+ updateButtonRadiusWithPartnerConfig(button, footerButtonPartnerConfig.getButtonRadiusConfig());
+ updateButtonIconWithPartnerConfig(button, footerButtonPartnerConfig.getButtonIconConfig());
+ updateButtonRippleColorWithPartnerConfig(button, footerButtonPartnerConfig);
+ }
+
+ private void updateButtonTextColorWithPartnerConfig(
+ Button button, PartnerConfig buttonTextColorConfig) {
if (button.isEnabled()) {
- button.setTextColor(ColorStateList.valueOf(color));
+ @ColorInt
+ int color = PartnerConfigHelper.get(context).getColor(context, buttonTextColorConfig);
+ if (color != Color.TRANSPARENT) {
+ button.setTextColor(ColorStateList.valueOf(color));
+ }
} else {
+ // disable state will use the default disable state color
button.setTextColor(
- isPrimaryButton ? primaryTextColorStateList : secondaryTextColorStateList);
+ button.getId() == primaryButtonId ? primaryDefaultTextColor : secondaryDefaultTextColor);
}
}
- private void updateButtonTextSizeWithPartnerConfig(Button button) {
- float size =
- PartnerConfigHelper.get(context)
- .getDimension(context, PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_SIZE);
+ 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) {
+ private void updateButtonTypeFaceWithPartnerConfig(
+ Button button, PartnerConfig buttonTextTypeFaceConfig) {
String fontFamilyName =
- PartnerConfigHelper.get(context)
- .getString(context, PartnerConfig.CONFIG_FOOTER_BUTTON_FONT_FAMILY);
+ PartnerConfigHelper.get(context).getString(context, buttonTextTypeFaceConfig);
Typeface font = Typeface.create(fontFamilyName, Typeface.NORMAL);
if (font != null) {
button.setTypeface(font);
@@ -494,21 +585,15 @@ public class FooterBarMixin implements Mixin {
}
@TargetApi(VERSION_CODES.Q)
- private void updateButtonBackgroundWithPartnerConfig(Button button, boolean isPrimaryButton) {
+ private void updateButtonBackgroundWithPartnerConfig(
+ Button button, PartnerConfig buttonBackgroundConfig) {
Preconditions.checkArgument(
- BuildCompat.isAtLeastQ(), "Update button background only support on sdk Q or higher");
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q,
+ "Update button background only support on sdk Q or higher");
@ColorInt int color;
int[] DISABLED_STATE_SET = {-android.R.attr.state_enabled};
int[] ENABLED_STATE_SET = {};
- if (isPrimaryButton) {
- color =
- PartnerConfigHelper.get(context)
- .getColor(context, PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_BG_COLOR);
- } else {
- color =
- PartnerConfigHelper.get(context)
- .getColor(context, PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_BG_COLOR);
- }
+ color = PartnerConfigHelper.get(context).getColor(context, buttonBackgroundConfig);
if (color != Color.TRANSPARENT) {
TypedArray a = context.obtainStyledAttributes(new int[] {android.R.attr.disabledAlpha});
@@ -537,11 +622,10 @@ public class FooterBarMixin implements Mixin {
button.getBackground().mutate().setColorFilter(color, Mode.SRC_ATOP);
}
- private void updateButtonRadiusWithPartnerConfig(Button button) {
+ private void updateButtonRadiusWithPartnerConfig(
+ Button button, PartnerConfig buttonRadiusConfig) {
if (Build.VERSION.SDK_INT >= VERSION_CODES.N) {
- float radius =
- PartnerConfigHelper.get(context)
- .getDimension(context, PartnerConfig.CONFIG_FOOTER_BUTTON_RADIUS);
+ float radius = PartnerConfigHelper.get(context).getDimension(context, buttonRadiusConfig);
GradientDrawable gradientDrawable = getGradientDrawable(button);
if (gradientDrawable != null) {
gradientDrawable.setCornerRadius(radius);
@@ -549,7 +633,8 @@ public class FooterBarMixin implements Mixin {
}
}
- private void updateButtonRippleColorWithPartnerConfig(Button button, boolean isPrimaryButton) {
+ 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.
@@ -562,19 +647,13 @@ public class FooterBarMixin implements Mixin {
int[] pressedState = {android.R.attr.state_pressed};
@ColorInt int color;
// Get partner text color.
- if (isPrimaryButton) {
- color =
- PartnerConfigHelper.get(context)
- .getColor(context, PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_COLOR);
- } else {
- color =
- PartnerConfigHelper.get(context)
- .getColor(context, PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR);
- }
+ color =
+ PartnerConfigHelper.get(context)
+ .getColor(context, footerButtonPartnerConfig.getButtonTextColorConfig());
float alpha =
PartnerConfigHelper.get(context)
- .getFraction(context, PartnerConfig.CONFIG_FOOTER_BUTTON_RIPPLE_COLOR_ALPHA);
+ .getFraction(context, footerButtonPartnerConfig.getButtonRippleColorAlphaConfig());
// Set text color for ripple.
ColorStateList colorStateList =
@@ -585,14 +664,13 @@ public class FooterBarMixin implements Mixin {
}
}
- private void updateButtonIconWithPartnerConfig(Button button, @ButtonType int buttonType) {
+ private void updateButtonIconWithPartnerConfig(Button button, PartnerConfig buttonIconConfig) {
if (button == null) {
return;
}
Drawable icon = null;
- PartnerConfig id = getDrawablePartnerConfig(buttonType);
- if (id != null) {
- icon = PartnerConfigHelper.get(context).getDrawable(context, id);
+ if (buttonIconConfig != null) {
+ icon = PartnerConfigHelper.get(context).getDrawable(context, buttonIconConfig);
}
setButtonIcon(button, icon);
}
@@ -706,27 +784,13 @@ public class FooterBarMixin implements Mixin {
return footerStub.inflate();
}
- private void updateFooterBarPadding() {
+ private void updateFooterBarPadding(
+ LinearLayout buttonContainer, int left, int top, int right, int bottom) {
if (buttonContainer == null) {
// Ignore action since buttonContainer is null
return;
}
-
- if (applyPartnerResources) {
- footerBarPaddingTop =
- (int)
- PartnerConfigHelper.get(context)
- .getDimension(context, PartnerConfig.CONFIG_FOOTER_BUTTON_PADDING_TOP);
- footerBarPaddingBottom =
- (int)
- PartnerConfigHelper.get(context)
- .getDimension(context, PartnerConfig.CONFIG_FOOTER_BUTTON_PADDING_BOTTOM);
- }
- buttonContainer.setPadding(
- buttonContainer.getPaddingLeft(),
- footerBarPaddingTop,
- buttonContainer.getPaddingRight(),
- footerBarPaddingBottom);
+ buttonContainer.setPadding(left, top, right, bottom);
}
/** Returns the paddingTop of footer bar. */
diff --git a/main/java/com/google/android/setupcompat/template/FooterButton.java b/main/java/com/google/android/setupcompat/template/FooterButton.java
index 457a116..a4d2c87 100644
--- a/main/java/com/google/android/setupcompat/template/FooterButton.java
+++ b/main/java/com/google/android/setupcompat/template/FooterButton.java
@@ -202,16 +202,6 @@ public final class FooterButton implements OnClickListener {
void onTextChanged(CharSequence text);
}
- /**
- * Sets the default theme for footer button, the method only for internal use in {@link
- * FooterBarMixin} and there will have no influence during setup wizard flow.
- *
- * @param theme The theme for footer button.
- */
- public void setTheme(@StyleRes int theme) {
- this.theme = theme;
- }
-
/** Maximum valid value of ButtonType */
private static final int MAX_BUTTON_TYPE = 8;
diff --git a/main/java/com/google/android/setupcompat/template/StatusBarMixin.java b/main/java/com/google/android/setupcompat/template/StatusBarMixin.java
index 9996128..1bd6949 100644
--- a/main/java/com/google/android/setupcompat/template/StatusBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/StatusBarMixin.java
@@ -78,20 +78,20 @@ public class StatusBarMixin implements Mixin {
decorView = window.getDecorView();
- // Override the color of status bar to transparent such that the color of
- // StatusBarBackgroundLayout can be seen.
- if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ // Support updating system status bar background color and is light system status bar from M.
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
+ // Override the color of status bar to transparent such that the color of
+ // StatusBarBackgroundLayout can be seen.
window.setStatusBarColor(Color.TRANSPARENT);
+ TypedArray a =
+ partnerCustomizationLayout
+ .getContext()
+ .obtainStyledAttributes(attrs, R.styleable.SucStatusBarMixin, defStyleAttr, 0);
+ setLightStatusBar(
+ a.getBoolean(R.styleable.SucStatusBarMixin_sucLightStatusBar, isLightStatusBar()));
+ setStatusBarBackground(a.getDrawable(R.styleable.SucStatusBarMixin_sucStatusBarBackground));
+ a.recycle();
}
-
- TypedArray a =
- partnerCustomizationLayout
- .getContext()
- .obtainStyledAttributes(attrs, R.styleable.SucStatusBarMixin, defStyleAttr, 0);
- setLightStatusBar(
- a.getBoolean(R.styleable.SucStatusBarMixin_sucLightStatusBar, isLightStatusBar()));
- setStatusBarBackground(a.getDrawable(R.styleable.SucStatusBarMixin_sucStatusBarBackground));
- a.recycle();
}
/**
diff --git a/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java b/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java
index a3ce567..e055d28 100644
--- a/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java
@@ -70,16 +70,21 @@ public class SystemNavBarMixin implements Mixin {
* @param defStyleAttr The default style attribute as given to the constructor of the layout.
*/
public void applyPartnerCustomizations(@Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
- TypedArray a =
- templateLayout
- .getContext()
- .obtainStyledAttributes(attrs, R.styleable.SucSystemNavBarMixin, defStyleAttr, 0);
- sucSystemNavBarBackgroundColor =
- a.getColor(R.styleable.SucSystemNavBarMixin_sucSystemNavBarBackgroundColor, 0);
- setSystemNavBarBackground(sucSystemNavBarBackgroundColor);
- setLightSystemNavBar(
- a.getBoolean(R.styleable.SucSystemNavBarMixin_sucLightSystemNavBar, isLightSystemNavBar()));
- a.recycle();
+ // Support updating system navigation bar background color and is light system navigation bar
+ // from O.
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.O_MR1) {
+ TypedArray a =
+ templateLayout
+ .getContext()
+ .obtainStyledAttributes(attrs, R.styleable.SucSystemNavBarMixin, defStyleAttr, 0);
+ sucSystemNavBarBackgroundColor =
+ a.getColor(R.styleable.SucSystemNavBarMixin_sucSystemNavBarBackgroundColor, 0);
+ setSystemNavBarBackground(sucSystemNavBarBackgroundColor);
+ setLightSystemNavBar(
+ a.getBoolean(
+ R.styleable.SucSystemNavBarMixin_sucLightSystemNavBar, isLightSystemNavBar()));
+ a.recycle();
+ }
}
/**
@@ -194,20 +199,23 @@ public class SystemNavBarMixin implements Mixin {
window.setStatusBarColor(Color.TRANSPARENT);
window.setNavigationBarColor(partnerNavigationBarColor);
} else {
+ // noinspection AndroidLintInlinedApi
+ TypedArray typedArray =
+ context.obtainStyledAttributes(
+ new int[] {android.R.attr.statusBarColor, android.R.attr.navigationBarColor});
+ int statusBarColor = typedArray.getColor(0, 0);
+ int navigationBarColor = typedArray.getColor(1, 0);
if (templateLayout instanceof PartnerCustomizationLayout) {
- window.setStatusBarColor(Color.TRANSPARENT);
- window.setNavigationBarColor(sucSystemNavBarBackgroundColor);
- } else {
- // noinspection AndroidLintInlinedApi
- final TypedArray typedArray =
- context.obtainStyledAttributes(
- new int[] {android.R.attr.statusBarColor, android.R.attr.navigationBarColor});
- final int statusBarColor = typedArray.getColor(0, 0);
- final int navigationBarColor = typedArray.getColor(1, 0);
- window.setStatusBarColor(statusBarColor);
- window.setNavigationBarColor(navigationBarColor);
- typedArray.recycle();
+ if (VERSION.SDK_INT >= VERSION_CODES.M) {
+ statusBarColor = Color.TRANSPARENT;
+ }
+ if (VERSION.SDK_INT >= VERSION_CODES.O_MR1) {
+ navigationBarColor = sucSystemNavBarBackgroundColor;
+ }
}
+ window.setStatusBarColor(statusBarColor);
+ window.setNavigationBarColor(navigationBarColor);
+ typedArray.recycle();
}
}
}
diff --git a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
index 61cd760..3896731 100644
--- a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
+++ b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
@@ -19,12 +19,12 @@ package com.google.android.setupcompat.util;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.provider.Settings;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import com.google.android.setupcompat.internal.BuildCompat;
import java.util.Arrays;
/**
@@ -202,7 +202,7 @@ public class WizardManagerHelper {
return false;
}
- if (BuildCompat.isAtLeastQ()) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return originalIntent.getBooleanExtra(EXTRA_IS_SETUP_FLOW, false);
} else {
return isSetupWizardIntent(originalIntent)
diff --git a/main/res/values/attrs.xml b/main/res/values/attrs.xml
index e336905..1a5342c 100644
--- a/main/res/values/attrs.xml
+++ b/main/res/values/attrs.xml
@@ -34,7 +34,7 @@
<attr name="sucUsePartnerResource" format="boolean" />
</declare-styleable>
- <!-- Status bar attributes -->
+ <!-- Status bar attributes; only takes effect on M or above -->
<declare-styleable name="SucStatusBarMixin">
<!-- The color for the status bar. For this to take effect,
"android:windowDrawsSystemBarBackgrounds" should be set to true and
@@ -45,7 +45,7 @@
<attr name="sucLightStatusBar" format="boolean" />
</declare-styleable>
- <!-- System navigation bar attributes -->
+ <!-- System navigation bar attributes; only takes effect on O_MR1 or above -->
<declare-styleable name="SucSystemNavBarMixin">
<!-- The color for the system navigation bar. For this to take effect,
"android:windowDrawsSystemBarBackgrounds" should be set to true and