summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 07:25:56 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 07:25:56 +0000
commitbf4fa7dbaa316a9dc62e49ab155c2bbac70c42e4 (patch)
tree9413722f83f317d6819cd7f7d044249461c3770e
parent33cf4c96e8c4986edf036ec2c51228d5b9905bd2 (diff)
parent47ba3c2a7b5fdb0cb4df38966b4ae81ab3d06cf8 (diff)
downloadsetupcompat-bf4fa7dbaa316a9dc62e49ab155c2bbac70c42e4.tar.gz
Snap for 8564071 from 47ba3c2a7b5fdb0cb4df38966b4ae81ab3d06cf8 to mainline-art-release
Change-Id: If8d3ccd09a3567332968524100028f304adee3b3
-rw-r--r--Android.bp1
-rw-r--r--exempting_lint_checks.txt10
-rw-r--r--main/aidl/com/google/android/setupcompat/ISetupCompatService.aidl2
-rw-r--r--main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java37
-rw-r--r--main/java/com/google/android/setupcompat/internal/ExecutorProvider.java6
-rw-r--r--main/java/com/google/android/setupcompat/internal/FocusChangedMetricHelper.java88
-rw-r--r--main/java/com/google/android/setupcompat/internal/FooterButtonPartnerConfig.java28
-rw-r--r--main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java38
-rw-r--r--main/java/com/google/android/setupcompat/internal/SetupCompatServiceProvider.java15
-rw-r--r--main/java/com/google/android/setupcompat/logging/internal/PartnerCustomizedResourceListMetric.java60
-rw-r--r--main/java/com/google/android/setupcompat/portal/NotificationComponent.java1
-rw-r--r--main/java/com/google/android/setupcompat/template/FooterActionButton.java15
-rw-r--r--main/java/com/google/android/setupcompat/template/FooterBarMixin.java162
-rw-r--r--main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java75
-rw-r--r--main/java/com/google/android/setupcompat/util/BuildCompatUtils.java46
-rw-r--r--main/java/com/google/android/setupcompat/util/WizardManagerHelper.java17
-rw-r--r--main/java/com/google/android/setupcompat/view/ButtonBarLayout.java32
-rw-r--r--main/res/values/styles.xml6
-rw-r--r--partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java58
-rw-r--r--partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java154
-rw-r--r--partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java60
-rw-r--r--portal_extension/aidl/com/google/android/setupcompat/portal/ISetupNotificationServicePortalExtension.aidl29
-rw-r--r--portal_extension/aidl/com/google/android/setupcompat/portal/TaskComponent.aidl23
-rw-r--r--portal_extension/java/com/google/android/setupcompat/portal/PortalExtensionConstants.java26
-rw-r--r--portal_extension/java/com/google/android/setupcompat/portal/TaskComponent.java166
-rw-r--r--setup_extension/info_modules.proto96
26 files changed, 1093 insertions, 158 deletions
diff --git a/Android.bp b/Android.bp
index 01e654d..d3a42a7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -80,6 +80,7 @@ android_library {
],
static_libs: [
"androidx.annotation_annotation",
+ "error_prone_annotations",
],
min_sdk_version: "14",
sdk_version: "current",
diff --git a/exempting_lint_checks.txt b/exempting_lint_checks.txt
index 90b8caf..4129b20 100644
--- a/exempting_lint_checks.txt
+++ b/exempting_lint_checks.txt
@@ -5,3 +5,13 @@ third_party/java_src/android_libs/setupcompat/main/java/com/google/android/setup
third_party/java_src/android_libs/setupcompat/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java: CustomViewStyleable: attrs, R.styleable.SucPartnerCustomizationLayout, defStyleAttr, 0);
third_party/java_src/android_libs/setupcompat/main/java/com/google/android/setupcompat/internal/TemplateLayout.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SucTemplateLayout, defStyleAttr, 0);
third_party/java_src/android_libs/setupcompat/main/java/com/google/android/setupcompat/util/SystemBarHelper.java: AnnotateVersionCheck: public static void setImeInsetView(final View view) {
+third_party/java_src/android_libs/setupcompat/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java: CustomViewStyleable: attrs, R.styleable.SucPartnerCustomizationLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupcompat/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java: ObsoleteSdkInt: @TargetApi(VERSION_CODES.HONEYCOMB)
+third_party/java_src/android_libs/setupcompat/main/java/com/google/android/setupcompat/internal/TemplateLayout.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SucTemplateLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupcompat/main/java/com/google/android/setupcompat/internal/TemplateLayout.java: ObsoleteSdkInt: @TargetApi(VERSION_CODES.HONEYCOMB)
+third_party/java_src/android_libs/setupcompat/main/java/com/google/android/setupcompat/logging/internal/MetricBundleConverter.java: NewApi: bundle.putParcelable(MetricBundleKeys.CUSTOM_EVENT_BUNDLE, CustomEvent.toBundle(customEvent));
+third_party/java_src/android_libs/setupcompat/main/java/com/google/android/setupcompat/template/FooterBarMixin.java: NewApi: onFooterButtonApplyPartnerResource(button, footerButtonPartnerConfig);
+third_party/java_src/android_libs/setupcompat/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java: NewApi: FooterButtonStyleUtils.updateButtonBackgroundWithPartnerConfig(
+third_party/java_src/android_libs/setupcompat/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java: NewApi: FooterButtonStyleUtils.updateButtonRippleColorWithPartnerConfig(
+third_party/java_src/android_libs/setupcompat/main/java/com/google/android/setupcompat/util/SystemBarHelper.java: AnnotateVersionCheck: public static void setImeInsetView(final View view) {
+third_party/java_src/android_libs/setupcompat/main/java/com/google/android/setupcompat/view/StatusBarBackgroundLayout.java: ObsoleteSdkInt: @TargetApi(VERSION_CODES.HONEYCOMB)
diff --git a/main/aidl/com/google/android/setupcompat/ISetupCompatService.aidl b/main/aidl/com/google/android/setupcompat/ISetupCompatService.aidl
index 09f7c9a..e8cb7e5 100644
--- a/main/aidl/com/google/android/setupcompat/ISetupCompatService.aidl
+++ b/main/aidl/com/google/android/setupcompat/ISetupCompatService.aidl
@@ -26,4 +26,6 @@ interface ISetupCompatService {
oneway void validateActivity(String screenName, in Bundle arguments) = 0;
oneway void logMetric(int metricType, in Bundle arguments, in Bundle extras) = 1;
+
+ oneway void onFocusStatusChanged(in Bundle bundle) = 2;
}
diff --git a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
index e5ba0c5..37cc358 100644
--- a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
+++ b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
@@ -29,9 +29,13 @@ import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.view.WindowManager;
+import androidx.annotation.VisibleForTesting;
+import com.google.android.setupcompat.internal.FocusChangedMetricHelper;
import com.google.android.setupcompat.internal.LifecycleFragment;
import com.google.android.setupcompat.internal.PersistableBundles;
+import com.google.android.setupcompat.internal.SetupCompatServiceInvoker;
import com.google.android.setupcompat.internal.TemplateLayout;
import com.google.android.setupcompat.logging.CustomEvent;
import com.google.android.setupcompat.logging.MetricKey;
@@ -44,6 +48,7 @@ import com.google.android.setupcompat.template.SystemNavBarMixin;
import com.google.android.setupcompat.util.BuildCompatUtils;
import com.google.android.setupcompat.util.Logger;
import com.google.android.setupcompat.util.WizardManagerHelper;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
/** A templatization layout with consistent style used in Setup Wizard or app itself. */
public class PartnerCustomizationLayout extends TemplateLayout {
@@ -71,24 +76,33 @@ public class PartnerCustomizationLayout extends TemplateLayout {
private Activity activity;
+ @CanIgnoreReturnValue
public PartnerCustomizationLayout(Context context) {
this(context, 0, 0);
}
+ @CanIgnoreReturnValue
public PartnerCustomizationLayout(Context context, int template) {
this(context, template, 0);
}
+ @CanIgnoreReturnValue
public PartnerCustomizationLayout(Context context, int template, int containerId) {
super(context, template, containerId);
init(null, R.attr.sucLayoutTheme);
}
+ @VisibleForTesting
+ final ViewTreeObserver.OnWindowFocusChangeListener windowFocusChangeListener =
+ this::onFocusChanged;
+
+ @CanIgnoreReturnValue
public PartnerCustomizationLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, R.attr.sucLayoutTheme);
}
+ @CanIgnoreReturnValue
@TargetApi(VERSION_CODES.HONEYCOMB)
public PartnerCustomizationLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
@@ -203,14 +217,17 @@ public class PartnerCustomizationLayout extends TemplateLayout {
protected void onAttachedToWindow() {
super.onAttachedToWindow();
LifecycleFragment.attachNow(activity);
+ if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2
+ && WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
+ getViewTreeObserver().addOnWindowFocusChangeListener(windowFocusChangeListener);
+ }
getMixin(FooterBarMixin.class).onAttachedToWindow();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP
- && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
+ if (VERSION.SDK_INT >= Build.VERSION_CODES.Q
&& WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
FooterBarMixin footerBarMixin = getMixin(FooterBarMixin.class);
footerBarMixin.onDetachedFromWindow();
@@ -233,6 +250,10 @@ public class PartnerCustomizationLayout extends TemplateLayout {
getContext(),
CustomEvent.create(MetricKey.get("SetupCompatMetrics", activity), persistableBundle));
}
+
+ if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
+ getViewTreeObserver().removeOnWindowFocusChangeListener(windowFocusChangeListener);
+ }
}
public static Activity lookupActivityFromContext(Context context) {
@@ -297,4 +318,16 @@ public class PartnerCustomizationLayout extends TemplateLayout {
public boolean useFullDynamicColor() {
return shouldApplyDynamicColor() && useFullDynamicColorAttr;
}
+
+ /**
+ * Invoke the method onFocusStatusChanged when onWindowFocusChangeListener receive onFocusChanged.
+ */
+ private void onFocusChanged(boolean hasFocus) {
+ SetupCompatServiceInvoker.get(getContext())
+ .onFocusStatusChanged(
+ FocusChangedMetricHelper.getScreenName(activity),
+ FocusChangedMetricHelper.getExtraBundle(
+ activity, PartnerCustomizationLayout.this, hasFocus));
+ }
}
+
diff --git a/main/java/com/google/android/setupcompat/internal/ExecutorProvider.java b/main/java/com/google/android/setupcompat/internal/ExecutorProvider.java
index 3c707ae..28ced66 100644
--- a/main/java/com/google/android/setupcompat/internal/ExecutorProvider.java
+++ b/main/java/com/google/android/setupcompat/internal/ExecutorProvider.java
@@ -33,7 +33,6 @@ import java.util.concurrent.TimeUnit;
public final class ExecutorProvider<T extends Executor> {
private static final int SETUP_METRICS_LOGGER_MAX_QUEUED = 50;
- private static final int SETUP_COMPAT_BINDBACK_MAX_QUEUED = 1;
/**
* Creates a single threaded {@link ExecutorService} with a maximum pool size {@code maxSize}.
* Jobs submitted when the pool is full causes {@link
@@ -43,11 +42,6 @@ public final class ExecutorProvider<T extends Executor> {
new ExecutorProvider<>(
createSizeBoundedExecutor("SetupCompatServiceInvoker", SETUP_METRICS_LOGGER_MAX_QUEUED));
- public static final ExecutorProvider<ExecutorService> setupCompatExecutor =
- new ExecutorProvider<>(
- createSizeBoundedExecutor(
- "SetupBindbackServiceExecutor", SETUP_COMPAT_BINDBACK_MAX_QUEUED));
-
private final T executor;
@Nullable private T injectedExecutor;
diff --git a/main/java/com/google/android/setupcompat/internal/FocusChangedMetricHelper.java b/main/java/com/google/android/setupcompat/internal/FocusChangedMetricHelper.java
new file mode 100644
index 0000000..39ade19
--- /dev/null
+++ b/main/java/com/google/android/setupcompat/internal/FocusChangedMetricHelper.java
@@ -0,0 +1,88 @@
+/*
+ * 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.internal;
+
+import android.app.Activity;
+import android.os.Bundle;
+import androidx.annotation.StringDef;
+import com.google.android.setupcompat.internal.FocusChangedMetricHelper.Constants.ExtraKey;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A help class response to generate extra bundle and capture screen name for interruption metric.
+ */
+public class FocusChangedMetricHelper {
+ private FocusChangedMetricHelper() {}
+
+ public static final String getScreenName(Activity activity) {
+ return activity.getComponentName().toShortString();
+ }
+
+ public static final Bundle getExtraBundle(
+ Activity activity, TemplateLayout layout, boolean hasFocus) {
+ Bundle bundle = new Bundle();
+
+ bundle.putString(ExtraKey.PACKAGE_NAME, activity.getComponentName().getPackageName());
+ bundle.putString(ExtraKey.SCREEN_NAME, activity.getComponentName().getShortClassName());
+ bundle.putInt(ExtraKey.HASH_CODE, layout.hashCode());
+ bundle.putBoolean(ExtraKey.HAS_FOCUS, hasFocus);
+ bundle.putLong(ExtraKey.TIME_IN_MILLIS, System.currentTimeMillis());
+
+ return bundle;
+ }
+
+ /**
+ * Constant values used by {@link
+ * com.google.android.setupcompat.internal.FocusChangedMetricHelper}.
+ */
+ public static final class Constants {
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ ExtraKey.PACKAGE_NAME,
+ ExtraKey.SCREEN_NAME,
+ ExtraKey.HASH_CODE,
+ ExtraKey.HAS_FOCUS,
+ ExtraKey.TIME_IN_MILLIS
+ })
+ public @interface ExtraKey {
+
+ /** This key will be used to save the package name. */
+ String PACKAGE_NAME = "packageName";
+
+ /** This key will be used to save the activity name. */
+ String SCREEN_NAME = "screenName";
+
+ /**
+ * This key will be used to save the has code of {@link
+ * com.google.android.setupcompat.PartnerCustomizationLayout}.
+ */
+ String HASH_CODE = "hash";
+
+ /**
+ * This key will be used to save whether the window which is including the {@link
+ * com.google.android.setupcompat.PartnerCustomizationLayout}. has focus or not.
+ */
+ String HAS_FOCUS = "focus";
+
+ /** This key will be use to save the time stamp in milliseconds. */
+ String TIME_IN_MILLIS = "timeInMillis";
+ }
+
+ private Constants() {}
+ }
+}
diff --git a/main/java/com/google/android/setupcompat/internal/FooterButtonPartnerConfig.java b/main/java/com/google/android/setupcompat/internal/FooterButtonPartnerConfig.java
index 5f8bf67..8e23c1a 100644
--- a/main/java/com/google/android/setupcompat/internal/FooterButtonPartnerConfig.java
+++ b/main/java/com/google/android/setupcompat/internal/FooterButtonPartnerConfig.java
@@ -24,8 +24,10 @@ public class FooterButtonPartnerConfig {
private final PartnerConfig buttonBackgroundConfig;
private final PartnerConfig buttonDisableAlphaConfig;
private final PartnerConfig buttonDisableBackgroundConfig;
+ private final PartnerConfig buttonDisableTextColorConfig;
private final PartnerConfig buttonIconConfig;
private final PartnerConfig buttonTextColorConfig;
+ private final PartnerConfig buttonMarginStartConfig;
private final PartnerConfig buttonTextSizeConfig;
private final PartnerConfig buttonMinHeightConfig;
private final PartnerConfig buttonTextTypeFaceConfig;
@@ -39,8 +41,10 @@ public class FooterButtonPartnerConfig {
PartnerConfig buttonBackgroundConfig,
PartnerConfig buttonDisableAlphaConfig,
PartnerConfig buttonDisableBackgroundConfig,
+ PartnerConfig buttonDisableTextColorConfig,
PartnerConfig buttonIconConfig,
PartnerConfig buttonTextColorConfig,
+ PartnerConfig buttonMarginStartConfig,
PartnerConfig buttonTextSizeConfig,
PartnerConfig buttonMinHeightConfig,
PartnerConfig buttonTextTypeFaceConfig,
@@ -50,6 +54,7 @@ public class FooterButtonPartnerConfig {
this.partnerTheme = partnerTheme;
this.buttonTextColorConfig = buttonTextColorConfig;
+ this.buttonMarginStartConfig = buttonMarginStartConfig;
this.buttonTextSizeConfig = buttonTextSizeConfig;
this.buttonMinHeightConfig = buttonMinHeightConfig;
this.buttonTextTypeFaceConfig = buttonTextTypeFaceConfig;
@@ -57,6 +62,7 @@ public class FooterButtonPartnerConfig {
this.buttonBackgroundConfig = buttonBackgroundConfig;
this.buttonDisableAlphaConfig = buttonDisableAlphaConfig;
this.buttonDisableBackgroundConfig = buttonDisableBackgroundConfig;
+ this.buttonDisableTextColorConfig = buttonDisableTextColorConfig;
this.buttonRadiusConfig = buttonRadiusConfig;
this.buttonIconConfig = buttonIconConfig;
this.buttonRippleColorAlphaConfig = buttonRippleColorAlphaConfig;
@@ -78,6 +84,10 @@ public class FooterButtonPartnerConfig {
return buttonDisableBackgroundConfig;
}
+ public PartnerConfig getButtonDisableTextColorConfig() {
+ return buttonDisableTextColorConfig;
+ }
+
public PartnerConfig getButtonIconConfig() {
return buttonIconConfig;
}
@@ -86,6 +96,10 @@ public class FooterButtonPartnerConfig {
return buttonTextColorConfig;
}
+ public PartnerConfig getButtonMarginStartConfig() {
+ return buttonMarginStartConfig;
+ }
+
public PartnerConfig getButtonMinHeightConfig() {
return buttonMinHeightConfig;
}
@@ -116,8 +130,10 @@ public class FooterButtonPartnerConfig {
private PartnerConfig buttonBackgroundConfig = null;
private PartnerConfig buttonDisableAlphaConfig = null;
private PartnerConfig buttonDisableBackgroundConfig = null;
+ private PartnerConfig buttonDisableTextColorConfig = null;
private PartnerConfig buttonIconConfig = null;
private PartnerConfig buttonTextColorConfig = null;
+ private PartnerConfig buttonMarginStartConfig = null;
private PartnerConfig buttonTextSizeConfig = null;
private PartnerConfig buttonMinHeight = null;
private PartnerConfig buttonTextTypeFaceConfig = null;
@@ -149,11 +165,21 @@ public class FooterButtonPartnerConfig {
return this;
}
+ public Builder setButtonDisableTextColorConfig(PartnerConfig buttonDisableTextColorConfig) {
+ this.buttonDisableTextColorConfig = buttonDisableTextColorConfig;
+ return this;
+ }
+
public Builder setButtonIconConfig(PartnerConfig buttonIconConfig) {
this.buttonIconConfig = buttonIconConfig;
return this;
}
+ public Builder setMarginStartConfig(PartnerConfig buttonMarginStartConfig) {
+ this.buttonMarginStartConfig = buttonMarginStartConfig;
+ return this;
+ }
+
public Builder setTextColorConfig(PartnerConfig buttonTextColorConfig) {
this.buttonTextColorConfig = buttonTextColorConfig;
return this;
@@ -200,8 +226,10 @@ public class FooterButtonPartnerConfig {
buttonBackgroundConfig,
buttonDisableAlphaConfig,
buttonDisableBackgroundConfig,
+ buttonDisableTextColorConfig,
buttonIconConfig,
buttonTextColorConfig,
+ buttonMarginStartConfig,
buttonTextSizeConfig,
buttonMinHeight,
buttonTextTypeFaceConfig,
diff --git a/main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java b/main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java
index 149da54..ed9c0e3 100644
--- a/main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java
+++ b/main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java
@@ -54,12 +54,23 @@ public class SetupCompatServiceInvoker {
public void bindBack(String screenName, Bundle bundle) {
try {
- setupCompatExecutor.execute(() -> invokeBindBack(screenName, bundle));
+ loggingExecutor.execute(() -> invokeBindBack(screenName, bundle));
} catch (RejectedExecutionException e) {
LOG.e(String.format("Screen %s bind back fail.", screenName), e);
}
}
+ /**
+ * Help invoke the {@link ISetupCompatService#onFocusStatusChanged} using {@code loggingExecutor}.
+ */
+ public void onFocusStatusChanged(String screenName, Bundle bundle) {
+ try {
+ loggingExecutor.execute(() -> invokeOnWindowFocusChanged(screenName, bundle));
+ } catch (RejectedExecutionException e) {
+ LOG.e(String.format("Screen %s report focus changed failed.", screenName), e);
+ }
+ }
+
private void invokeLogMetric(
@MetricType int metricType, @SuppressWarnings("unused") Bundle args) {
try {
@@ -76,6 +87,29 @@ public class SetupCompatServiceInvoker {
}
}
+ private void invokeOnWindowFocusChanged(String screenName, Bundle bundle) {
+ try {
+ ISetupCompatService setupCompatService =
+ SetupCompatServiceProvider.get(
+ context, waitTimeInMillisForServiceConnection, TimeUnit.MILLISECONDS);
+ if (setupCompatService != null) {
+ setupCompatService.onFocusStatusChanged(bundle);
+ } else {
+ LOG.w(
+ "Report focusChange failed since service reference is null. Are the permission valid?");
+ }
+ } catch (InterruptedException
+ | TimeoutException
+ | RemoteException
+ | UnsupportedOperationException e) {
+ LOG.e(
+ String.format(
+ "Exception occurred while %s trying report windowFocusChange to SetupWizard.",
+ screenName),
+ e);
+ }
+ }
+
private void invokeBindBack(String screenName, Bundle bundle) {
try {
ISetupCompatService setupCompatService =
@@ -96,14 +130,12 @@ public class SetupCompatServiceInvoker {
private SetupCompatServiceInvoker(Context context) {
this.context = context;
this.loggingExecutor = ExecutorProvider.setupCompatServiceInvoker.get();
- this.setupCompatExecutor = ExecutorProvider.setupCompatExecutor.get();
this.waitTimeInMillisForServiceConnection = MAX_WAIT_TIME_FOR_CONNECTION_MS;
}
private final Context context;
private final ExecutorService loggingExecutor;
- private final ExecutorService setupCompatExecutor;
private final long waitTimeInMillisForServiceConnection;
public static synchronized SetupCompatServiceInvoker get(Context context) {
diff --git a/main/java/com/google/android/setupcompat/internal/SetupCompatServiceProvider.java b/main/java/com/google/android/setupcompat/internal/SetupCompatServiceProvider.java
index e75d991..e259f22 100644
--- a/main/java/com/google/android/setupcompat/internal/SetupCompatServiceProvider.java
+++ b/main/java/com/google/android/setupcompat/internal/SetupCompatServiceProvider.java
@@ -82,8 +82,8 @@ public class SetupCompatServiceProvider {
return waitForConnection(timeout, timeUnit);
case NOT_STARTED:
- throw new IllegalStateException(
- "NOT_STARTED state only possible before instance is created.");
+ LOG.w("NOT_STARTED state only possible before instance is created.");
+ return null;
}
throw new IllegalStateException("Unknown state = " + serviceContext.state);
}
@@ -172,7 +172,8 @@ public class SetupCompatServiceProvider {
return serviceContext;
}
- private void swapServiceContextAndNotify(ServiceContext latestServiceContext) {
+ @VisibleForTesting
+ void swapServiceContextAndNotify(ServiceContext latestServiceContext) {
LOG.atInfo(
String.format("State changed: %s -> %s", serviceContext.state, latestServiceContext.state));
@@ -200,7 +201,7 @@ public class SetupCompatServiceProvider {
return countDownLatch;
}
countDownLatch = createCountDownLatch();
- } while (!connectedConditionRef.compareAndSet(/* expect= */ null, countDownLatch));
+ } while (!connectedConditionRef.compareAndSet(/* expectedValue= */ null, countDownLatch));
return countDownLatch;
}
@@ -285,7 +286,8 @@ public class SetupCompatServiceProvider {
REBIND_REQUIRED
}
- private static final class ServiceContext {
+ @VisibleForTesting
+ static final class ServiceContext {
final State state;
@Nullable final ISetupCompatService compatService;
@@ -298,7 +300,8 @@ public class SetupCompatServiceProvider {
}
}
- private ServiceContext(State state) {
+ @VisibleForTesting
+ ServiceContext(State state) {
this(state, /* compatService= */ null);
}
}
diff --git a/main/java/com/google/android/setupcompat/logging/internal/PartnerCustomizedResourceListMetric.java b/main/java/com/google/android/setupcompat/logging/internal/PartnerCustomizedResourceListMetric.java
deleted file mode 100644
index 2aa1240..0000000
--- a/main/java/com/google/android/setupcompat/logging/internal/PartnerCustomizedResourceListMetric.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2018 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.logging.internal;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build.VERSION_CODES;
-import android.os.Bundle;
-import android.os.PersistableBundle;
-import androidx.annotation.VisibleForTesting;
-import com.google.android.setupcompat.logging.CustomEvent;
-import com.google.android.setupcompat.logging.MetricKey;
-import com.google.android.setupcompat.logging.SetupMetricsLogger;
-
-/** Uses to log internal event customization resource list. */
-@TargetApi(VERSION_CODES.Q)
-public class PartnerCustomizedResourceListMetric {
-
- public static void logMetrics(Context context, String deviceDisplayName, Bundle bundle) {
- PersistableBundle logBundle =
- buildLogBundleFromResourceConfigBundle(context.getPackageName(), deviceDisplayName, bundle);
- if (!logBundle.isEmpty()) {
- SetupMetricsLogger.logCustomEvent(
- context,
- CustomEvent.create(
- MetricKey.get("PartnerCustomizationResource", "NoScreenName"), logBundle));
- }
- }
-
- @VisibleForTesting
- public static PersistableBundle buildLogBundleFromResourceConfigBundle(
- String defaultPackageName, String deviceDisplayName, Bundle resourceConfigBundle) {
- PersistableBundle persistableBundle = new PersistableBundle();
- persistableBundle.putString("deviceDisplayName", deviceDisplayName);
- for (String key : resourceConfigBundle.keySet()) {
- Bundle resourceExtra = resourceConfigBundle.getBundle(key);
- if (!resourceExtra.getString("packageName", defaultPackageName).equals(defaultPackageName)) {
- persistableBundle.putBoolean(resourceExtra.getString("resourceName", key), true);
- } else {
- persistableBundle.putBoolean(resourceExtra.getString("resourceName", key), false);
- }
- }
-
- return persistableBundle;
- }
-}
diff --git a/main/java/com/google/android/setupcompat/portal/NotificationComponent.java b/main/java/com/google/android/setupcompat/portal/NotificationComponent.java
index a90963b..c5865fe 100644
--- a/main/java/com/google/android/setupcompat/portal/NotificationComponent.java
+++ b/main/java/com/google/android/setupcompat/portal/NotificationComponent.java
@@ -76,6 +76,7 @@ public class NotificationComponent implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
@IntDef({
+ NotificationType.UNKNOWN,
NotificationType.INITIAL_ONGOING,
NotificationType.PREDEFERRED,
NotificationType.PREDEFERRED_PREPARING,
diff --git a/main/java/com/google/android/setupcompat/template/FooterActionButton.java b/main/java/com/google/android/setupcompat/template/FooterActionButton.java
index 86a06d9..d9726f9 100644
--- a/main/java/com/google/android/setupcompat/template/FooterActionButton.java
+++ b/main/java/com/google/android/setupcompat/template/FooterActionButton.java
@@ -28,6 +28,7 @@ import androidx.annotation.Nullable;
public class FooterActionButton extends Button {
@Nullable private FooterButton footerButton;
+ private boolean isPrimaryButtonStyle = false;
public FooterActionButton(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -54,4 +55,18 @@ public class FooterActionButton extends Button {
}
return super.onTouchEvent(event);
}
+
+ /**
+ * Sets this footer button is primary button style.
+ *
+ * @param isPrimaryButtonStyle True if this button is primary button style.
+ */
+ void setPrimaryButtonStyle(boolean isPrimaryButtonStyle) {
+ this.isPrimaryButtonStyle = isPrimaryButtonStyle;
+ }
+
+ /** Returns true when the footer button is primary button style. */
+ public boolean isPrimaryButtonStyle() {
+ return isPrimaryButtonStyle;
+ }
}
diff --git a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
index b75d972..4d78955 100644
--- a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
@@ -17,11 +17,12 @@
package com.google.android.setupcompat.template;
import static com.google.android.setupcompat.internal.Preconditions.ensureOnMainThread;
+import static java.lang.Math.max;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
-import android.content.res.ColorStateList;
+import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Build;
@@ -29,6 +30,7 @@ import android.os.Build.VERSION_CODES;
import android.os.PersistableBundle;
import android.util.AttributeSet;
import android.view.ContextThemeWrapper;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewStub;
@@ -71,18 +73,18 @@ public class FooterBarMixin implements Mixin {
@VisibleForTesting final boolean applyDynamicColor;
@VisibleForTesting final boolean useFullDynamicColor;
- private LinearLayout buttonContainer;
+ @VisibleForTesting public LinearLayout buttonContainer;
private FooterButton primaryButton;
private FooterButton secondaryButton;
@IdRes private int primaryButtonId;
@IdRes private int secondaryButtonId;
- ColorStateList primaryDefaultTextColor = null;
- ColorStateList secondaryDefaultTextColor = null;
@VisibleForTesting public FooterButtonPartnerConfig primaryButtonPartnerConfigForTesting;
@VisibleForTesting public FooterButtonPartnerConfig secondaryButtonPartnerConfigForTesting;
private int footerBarPaddingTop;
private int footerBarPaddingBottom;
+ @VisibleForTesting int footerBarPaddingStart;
+ @VisibleForTesting int footerBarPaddingEnd;
@VisibleForTesting int defaultPadding;
@ColorInt private final int footerBarPrimaryBackgroundColor;
@ColorInt private final int footerBarSecondaryBackgroundColor;
@@ -104,11 +106,15 @@ public class FooterBarMixin implements Mixin {
if (button != null) {
button.setEnabled(enabled);
if (applyPartnerResources && !applyDynamicColor) {
- updateButtonTextColorWithEnabledState(
+
+ updateButtonTextColorWithStates(
button,
(id == primaryButtonId || isSecondaryButtonInPrimaryStyle)
? PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_COLOR
- : PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR);
+ : PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR,
+ (id == primaryButtonId || isSecondaryButtonInPrimaryStyle)
+ ? PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_DISABLED_TEXT_COLOR
+ : PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_DISABLED_TEXT_COLOR);
}
}
}
@@ -189,6 +195,10 @@ public class FooterBarMixin implements Mixin {
footerBarPaddingBottom =
a.getDimensionPixelSize(
R.styleable.SucFooterBarMixin_sucFooterBarPaddingBottom, defaultPadding);
+ footerBarPaddingStart =
+ a.getDimensionPixelSize(R.styleable.SucFooterBarMixin_sucFooterBarPaddingStart, 0);
+ footerBarPaddingEnd =
+ a.getDimensionPixelSize(R.styleable.SucFooterBarMixin_sucFooterBarPaddingEnd, 0);
footerBarPrimaryBackgroundColor =
a.getColor(R.styleable.SucFooterBarMixin_sucFooterBarPrimaryFooterBackground, 0);
footerBarSecondaryBackgroundColor =
@@ -212,14 +222,34 @@ public class FooterBarMixin implements Mixin {
metrics.logSecondaryButtonInitialStateVisibility(
/* isVisible= */ true, /* isUsingXml= */ true);
}
+
+ FooterButtonStyleUtils.clearSavedDefaultTextColor();
+ }
+
+ protected boolean isFooterButtonAlignedEnd() {
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_FOOTER_BUTTON_ALIGNED_END)) {
+ return PartnerConfigHelper.get(context)
+ .getBoolean(context, PartnerConfig.CONFIG_FOOTER_BUTTON_ALIGNED_END, false);
+ } else {
+ return false;
+ }
+ }
+
+ protected boolean isFooterButtonsEvenlyWeighted() {
+ if (!isSecondaryButtonInPrimaryStyle) {
+ return false;
+ }
+ PartnerConfigHelper.get(context);
+ return PartnerConfigHelper.isNeutralButtonStyleEnabled(context);
}
private View addSpace() {
- LinearLayout buttonContainer = ensureFooterInflated();
- View space = new View(buttonContainer.getContext());
+ LinearLayout buttonContainerlayout = ensureFooterInflated();
+ View space = new View(context);
space.setLayoutParams(new LayoutParams(0, 0, 1.0f));
space.setVisibility(View.INVISIBLE);
- buttonContainer.addView(space);
+ buttonContainerlayout.addView(space);
return space;
}
@@ -253,10 +283,13 @@ public class FooterBarMixin implements Mixin {
}
updateFooterBarPadding(
buttonContainer,
- buttonContainer.getPaddingLeft(),
+ footerBarPaddingStart,
footerBarPaddingTop,
- buttonContainer.getPaddingRight(),
+ footerBarPaddingEnd,
footerBarPaddingBottom);
+ if (isFooterButtonAlignedEnd()) {
+ buttonContainer.setGravity(Gravity.END | Gravity.CENTER_VERTICAL);
+ }
}
/**
@@ -282,19 +315,39 @@ public class FooterBarMixin implements Mixin {
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);
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_FOOTER_BUTTON_PADDING_TOP)) {
+ footerBarPaddingTop =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_FOOTER_BUTTON_PADDING_TOP);
+ }
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_FOOTER_BUTTON_PADDING_BOTTOM)) {
+ footerBarPaddingBottom =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_FOOTER_BUTTON_PADDING_BOTTOM);
+ }
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_FOOTER_BAR_PADDING_START)) {
+ footerBarPaddingStart =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_FOOTER_BAR_PADDING_START);
+ }
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_FOOTER_BAR_PADDING_END)) {
+ footerBarPaddingEnd =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_FOOTER_BAR_PADDING_END);
+ }
updateFooterBarPadding(
buttonContainer,
- buttonContainer.getPaddingLeft(),
+ footerBarPaddingStart,
footerBarPaddingTop,
- buttonContainer.getPaddingRight(),
+ footerBarPaddingEnd,
footerBarPaddingBottom);
if (PartnerConfigHelper.get(context)
@@ -339,10 +392,13 @@ public class FooterBarMixin implements Mixin {
.setButtonBackgroundConfig(PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_BG_COLOR)
.setButtonDisableAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_DISABLED_ALPHA)
.setButtonDisableBackgroundConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_DISABLED_BG_COLOR)
+ .setButtonDisableTextColorConfig(
+ PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_DISABLED_TEXT_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)
+ .setMarginStartConfig(PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_MARGIN_START)
.setTextSizeConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_SIZE)
.setButtonMinHeight(PartnerConfig.CONFIG_FOOTER_BUTTON_MIN_HEIGHT)
.setTextTypeFaceConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_FONT_FAMILY)
@@ -352,10 +408,9 @@ public class FooterBarMixin implements Mixin {
FooterActionButton button = inflateButton(footerButton, footerButtonPartnerConfig);
// update information for primary button. Need to update as long as the button inflated.
primaryButtonId = button.getId();
- primaryDefaultTextColor = button.getTextColors();
+ button.setPrimaryButtonStyle(/* isPrimaryButtonStyle= */ true);
primaryButton = footerButton;
primaryButtonPartnerConfigForTesting = footerButtonPartnerConfig;
-
onFooterButtonInflated(button, footerBarPrimaryBackgroundColor);
onFooterButtonApplyPartnerResource(button, footerButtonPartnerConfig);
@@ -410,6 +465,10 @@ public class FooterBarMixin implements Mixin {
: PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_BG_COLOR)
.setButtonDisableAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_DISABLED_ALPHA)
.setButtonDisableBackgroundConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_DISABLED_BG_COLOR)
+ .setButtonDisableTextColorConfig(
+ usePrimaryStyle
+ ? PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_DISABLED_TEXT_COLOR
+ : PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_DISABLED_TEXT_COLOR)
.setButtonIconConfig(getDrawablePartnerConfig(footerButton.getButtonType()))
.setButtonRadiusConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RADIUS)
.setButtonRippleColorAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RIPPLE_COLOR_ALPHA)
@@ -417,6 +476,7 @@ public class FooterBarMixin implements Mixin {
usePrimaryStyle
? PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_COLOR
: PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR)
+ .setMarginStartConfig(PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_MARGIN_START)
.setTextSizeConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_SIZE)
.setButtonMinHeight(PartnerConfig.CONFIG_FOOTER_BUTTON_MIN_HEIGHT)
.setTextTypeFaceConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_FONT_FAMILY)
@@ -426,7 +486,7 @@ public class FooterBarMixin implements Mixin {
FooterActionButton button = inflateButton(footerButton, footerButtonPartnerConfig);
// update information for secondary button. Need to update as long as the button inflated.
secondaryButtonId = button.getId();
- secondaryDefaultTextColor = button.getTextColors();
+ button.setPrimaryButtonStyle(usePrimaryStyle);
secondaryButton = footerButton;
secondaryButtonPartnerConfigForTesting = footerButtonPartnerConfig;
@@ -448,6 +508,14 @@ public class FooterBarMixin implements Mixin {
Button tempSecondaryButton = getSecondaryButtonView();
buttonContainer.removeAllViews();
+ boolean isEvenlyWeightedButtons = isFooterButtonsEvenlyWeighted();
+ boolean isLandscape =
+ context.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
+ if (isLandscape && isEvenlyWeightedButtons && isFooterButtonAlignedEnd()) {
+ addSpace();
+ }
+
if (tempSecondaryButton != null) {
if (isSecondaryButtonInPrimaryStyle) {
// Since the secondary button has the same style (with background) as the primary button,
@@ -461,10 +529,30 @@ public class FooterBarMixin implements Mixin {
}
buttonContainer.addView(tempSecondaryButton);
}
- addSpace();
+ if (!isFooterButtonAlignedEnd()) {
+ addSpace();
+ }
if (tempPrimaryButton != null) {
buttonContainer.addView(tempPrimaryButton);
}
+
+ if (isEvenlyWeightedButtons) {
+ setEvenlyWeightedButtons(tempPrimaryButton, tempSecondaryButton);
+ }
+ }
+
+ private void setEvenlyWeightedButtons(Button primaryButton, Button secondaryButton) {
+ if (primaryButton != null && secondaryButton != null) {
+ primaryButton.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ int primaryButtonMeasuredWidth = primaryButton.getMeasuredWidth();
+ secondaryButton.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+
+ int secondaryButtonMeasuredWidth = secondaryButton.getMeasuredWidth();
+ int maxButtonMeasureWidth = max(primaryButtonMeasuredWidth, secondaryButtonMeasuredWidth);
+
+ primaryButton.getLayoutParams().width = maxButtonMeasureWidth;
+ secondaryButton.getLayoutParams().width = maxButtonMeasureWidth;
+ }
}
/**
@@ -500,10 +588,7 @@ public class FooterBarMixin implements Mixin {
int color = PartnerConfigHelper.get(context).getColor(context, buttonBackgroundColorConfig);
if (color == Color.TRANSPARENT) {
overrideTheme = R.style.SucPartnerCustomizationButton_Secondary;
- } else if (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.
+ } else {
overrideTheme = R.style.SucPartnerCustomizationButton_Primary;
}
}
@@ -616,22 +701,23 @@ public class FooterBarMixin implements Mixin {
footerButtonPartnerConfig);
if (!applyDynamicColor) {
// adjust text color based on enabled state
- updateButtonTextColorWithEnabledState(
- button, footerButtonPartnerConfig.getButtonTextColorConfig());
+ updateButtonTextColorWithStates(
+ button,
+ footerButtonPartnerConfig.getButtonTextColorConfig(),
+ footerButtonPartnerConfig.getButtonDisableTextColorConfig());
}
}
- private void updateButtonTextColorWithEnabledState(
- Button button, PartnerConfig buttonTextColorConfig) {
+ private void updateButtonTextColorWithStates(
+ Button button,
+ PartnerConfig buttonTextColorConfig,
+ PartnerConfig buttonTextDisabledColorConfig) {
if (button.isEnabled()) {
FooterButtonStyleUtils.updateButtonTextEnabledColorWithPartnerConfig(
context, button, buttonTextColorConfig);
} else {
- FooterButtonStyleUtils.updateButtonTextDisableColor(
- button,
- /* is Primary= */ (primaryButtonId == button.getId() || isSecondaryButtonInPrimaryStyle)
- ? primaryDefaultTextColor
- : secondaryDefaultTextColor);
+ FooterButtonStyleUtils.updateButtonTextDisabledColorWithPartnerConfig(
+ context, button, buttonTextDisabledColorConfig);
}
}
diff --git a/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java b/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java
index ef45b5c..476d45b 100644
--- a/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java
+++ b/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java
@@ -32,19 +32,22 @@ import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.util.StateSet;
import android.util.TypedValue;
+import android.view.ViewGroup;
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;
+import java.util.HashMap;
/** Utils for updating the button style. */
public class FooterButtonStyleUtils {
private static final float DEFAULT_DISABLED_ALPHA = 0.26f;
+ private static final HashMap<Integer, ColorStateList> defaultTextColor = new HashMap<>();
+
/** Apply the partner primary button style to given {@code button}. */
public static void applyPrimaryButtonPartnerResource(
Context context, Button button, boolean applyDynamicColor) {
@@ -55,9 +58,12 @@ public class FooterButtonStyleUtils {
.setButtonBackgroundConfig(PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_BG_COLOR)
.setButtonDisableAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_DISABLED_ALPHA)
.setButtonDisableBackgroundConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_DISABLED_BG_COLOR)
+ .setButtonDisableTextColorConfig(
+ PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_DISABLED_TEXT_COLOR)
.setButtonRadiusConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RADIUS)
.setButtonRippleColorAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RIPPLE_COLOR_ALPHA)
.setTextColorConfig(PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_COLOR)
+ .setMarginStartConfig(PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_MARGIN_START)
.setTextSizeConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_SIZE)
.setButtonMinHeight(PartnerConfig.CONFIG_FOOTER_BUTTON_MIN_HEIGHT)
.setTextTypeFaceConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_FONT_FAMILY)
@@ -89,9 +95,12 @@ public class FooterButtonStyleUtils {
.setButtonBackgroundConfig(PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_BG_COLOR)
.setButtonDisableAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_DISABLED_ALPHA)
.setButtonDisableBackgroundConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_DISABLED_BG_COLOR)
+ .setButtonDisableTextColorConfig(
+ PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_DISABLED_TEXT_COLOR)
.setButtonRadiusConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RADIUS)
.setButtonRippleColorAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RIPPLE_COLOR_ALPHA)
.setTextColorConfig(PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR)
+ .setMarginStartConfig(PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_MARGIN_START)
.setTextSizeConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_SIZE)
.setButtonMinHeight(PartnerConfig.CONFIG_FOOTER_BUTTON_MIN_HEIGHT)
.setTextTypeFaceConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_FONT_FAMILY)
@@ -112,6 +121,9 @@ public class FooterButtonStyleUtils {
boolean isButtonIconAtEnd,
FooterButtonPartnerConfig footerButtonPartnerConfig) {
+ // Save defualt text color for the partner config disable button text color not available.
+ saveButtonDefaultTextColor(button);
+
// If dynamic color enabled, these colors won't be overrode by partner config.
// Instead, these colors align with the current theme colors.
if (!applyDynamicColor) {
@@ -119,6 +131,9 @@ public class FooterButtonStyleUtils {
if (button.isEnabled()) {
FooterButtonStyleUtils.updateButtonTextEnabledColorWithPartnerConfig(
context, button, footerButtonPartnerConfig.getButtonTextColorConfig());
+ } else {
+ FooterButtonStyleUtils.updateButtonTextDisabledColorWithPartnerConfig(
+ context, button, footerButtonPartnerConfig.getButtonDisableTextColorConfig());
}
FooterButtonStyleUtils.updateButtonBackgroundWithPartnerConfig(
context,
@@ -133,6 +148,8 @@ public class FooterButtonStyleUtils {
applyDynamicColor,
footerButtonPartnerConfig.getButtonTextColorConfig(),
footerButtonPartnerConfig.getButtonRippleColorAlphaConfig());
+ FooterButtonStyleUtils.updateButtonMarginStartWithPartnerConfig(
+ context, button, footerButtonPartnerConfig.getButtonMarginStartConfig());
FooterButtonStyleUtils.updateButtonTextSizeWithPartnerConfig(
context, button, footerButtonPartnerConfig.getButtonTextSizeConfig());
FooterButtonStyleUtils.updateButtonMinHeightWithPartnerConfig(
@@ -161,10 +178,24 @@ public class FooterButtonStyleUtils {
}
}
- static void updateButtonTextDisableColor(Button button, ColorStateList disabledTextColor) {
- // TODO : add disable footer button text color partner config
+ static void updateButtonTextDisabledColorWithPartnerConfig(
+ Context context, Button button, PartnerConfig buttonDisableTextColorConfig) {
+ if (PartnerConfigHelper.get(context).isPartnerConfigAvailable(buttonDisableTextColorConfig)) {
+ @ColorInt
+ int color = PartnerConfigHelper.get(context).getColor(context, buttonDisableTextColorConfig);
+ updateButtonTextDisabledColor(button, color);
+ } else {
+ updateButtonTextDisableDefaultColor(button, getButtonDefaultTextCorlor(button));
+ }
+ }
+
+ static void updateButtonTextDisabledColor(Button button, @ColorInt int textColor) {
+ if (textColor != Color.TRANSPARENT) {
+ button.setTextColor(ColorStateList.valueOf(textColor));
+ }
+ }
- // disable state will use the default disable state color
+ static void updateButtonTextDisableDefaultColor(Button button, ColorStateList disabledTextColor) {
button.setTextColor(disabledTextColor);
}
@@ -266,16 +297,31 @@ public class FooterButtonStyleUtils {
}
int[] pressedState = {android.R.attr.state_pressed};
+ int[] focusState = {android.R.attr.state_focused};
+ int argbColor = convertRgbToArgb(textColor, rippleAlpha);
// Set text color for ripple.
ColorStateList colorStateList =
new ColorStateList(
- new int[][] {pressedState, StateSet.NOTHING},
- new int[] {convertRgbToArgb(textColor, rippleAlpha), Color.TRANSPARENT});
+ new int[][] {pressedState, focusState, StateSet.NOTHING},
+ new int[] {argbColor, argbColor, Color.TRANSPARENT});
rippleDrawable.setColor(colorStateList);
}
}
+ static void updateButtonMarginStartWithPartnerConfig(
+ Context context, Button button, PartnerConfig buttonMarginStartConfig) {
+ ViewGroup.LayoutParams lp = button.getLayoutParams();
+ boolean partnerConfigAvailable =
+ PartnerConfigHelper.get(context).isPartnerConfigAvailable(buttonMarginStartConfig);
+ if (partnerConfigAvailable && lp instanceof ViewGroup.MarginLayoutParams) {
+ final ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
+ int startMargin =
+ (int) PartnerConfigHelper.get(context).getDimension(context, buttonMarginStartConfig);
+ mlp.setMargins(startMargin, mlp.topMargin, mlp.rightMargin, mlp.bottomMargin);
+ }
+ }
+
static void updateButtonTextSizeWithPartnerConfig(
Context context, Button button, PartnerConfig buttonTextSizeConfig) {
float size = PartnerConfigHelper.get(context).getDimension(context, buttonTextSizeConfig);
@@ -367,7 +413,22 @@ public class FooterButtonStyleUtils {
button.getBackground().mutate().setColorFilter(color, Mode.SRC_ATOP);
}
- @VisibleForTesting
+ private static void saveButtonDefaultTextColor(Button button) {
+ defaultTextColor.put(button.getId(), button.getTextColors());
+ }
+
+ private static ColorStateList getButtonDefaultTextCorlor(Button button) {
+ if (!defaultTextColor.containsKey(button.getId())) {
+ throw new IllegalStateException("There is no saved default color for button");
+ }
+ return defaultTextColor.get(button.getId());
+ }
+
+ static void clearSavedDefaultTextColor() {
+ defaultTextColor.clear();
+ }
+
+ /** Gets {@code GradientDrawable} from given {@code button}. */
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
diff --git a/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java b/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java
index ea54745..8fa862f 100644
--- a/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java
+++ b/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java
@@ -17,6 +17,7 @@
package com.google.android.setupcompat.util;
import android.os.Build;
+import androidx.annotation.ChecksSdkIntAtLeast;
/**
* An util class to check whether the current OS version is higher or equal to sdk version of
@@ -25,6 +26,25 @@ import android.os.Build;
public final class BuildCompatUtils {
/**
+ * Implementation of BuildCompat.isAtLeastR() suitable for use in Setup
+ *
+ * @return Whether the current OS version is higher or equal to R.
+ */
+ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R)
+ public static boolean isAtLeastR() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
+ }
+ /**
+ * Implementation of BuildCompat.isAtLeastS() suitable for use in Setup
+ *
+ * @return Whether the current OS version is higher or equal to S.
+ */
+ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S)
+ public static boolean isAtLeastS() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
+ }
+
+ /**
* Implementation of BuildCompat.isAtLeast*() suitable for use in Setup
*
* <p>BuildCompat.isAtLeast*() can be changed by Android Release team, and once that is changed it
@@ -40,26 +60,26 @@ public final class BuildCompatUtils {
* <p>Supported configurations:
*
* <ul>
- * <li>For current Android release: while new API is not finalized yet (CODENAME = "S", SDK_INT
- * = 30|31)
- * <li>For current Android release: when new API is finalized (CODENAME = "REL", SDK_INT = 31)
- * <li>For next Android release (CODENAME = "T", SDK_INT = 30+)
+ * <li>For current Android release: while new API is not finalized yet (CODENAME = "Tiramisu",
+ * SDK_INT = 33)
+ * <li>For current Android release: when new API is finalized (CODENAME = "REL", SDK_INT = 32)
+ * <li>For next Android release (CODENAME = "U", SDK_INT = 34+)
* </ul>
*
- * <p>Note that Build.VERSION_CODES.S cannot be used here until final SDK is available in all
- * Google3 channels, because it is equal to Build.VERSION_CODES.CUR_DEVELOPMENT before API
- * finalization.
+ * <p>Note that Build.VERSION_CODES.T cannot be used here until final SDK is available in all
+ * channels, because it is equal to Build.VERSION_CODES.CUR_DEVELOPMENT before API finalization.
*
- * @return Whether the current OS version is higher or equal to S.
+ * @return Whether the current OS version is higher or equal to T.
*/
- public static boolean isAtLeastS() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ public static boolean isAtLeastT() {
+ if (!isAtLeastS()) {
return false;
}
- return (Build.VERSION.CODENAME.equals("REL") && Build.VERSION.SDK_INT >= 31)
+ return (Build.VERSION.CODENAME.equals("REL") && Build.VERSION.SDK_INT >= 33)
|| (Build.VERSION.CODENAME.length() == 1
- && Build.VERSION.CODENAME.charAt(0) >= 'S'
- && Build.VERSION.CODENAME.charAt(0) <= 'Z');
+ && Build.VERSION.CODENAME.charAt(0) >= 'T'
+ && Build.VERSION.CODENAME.charAt(0) <= 'Z')
+ || (Build.VERSION.CODENAME.equals("Tiramisu") && Build.VERSION.SDK_INT >= 32);
}
private BuildCompatUtils() {}
diff --git a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
index 79976bc..90de25e 100644
--- a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
+++ b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
@@ -65,6 +65,9 @@ public final class WizardManagerHelper {
*/
public static final String EXTRA_IS_SETUP_FLOW = "isSetupFlow";
+ /** Extra for notifying an activity that was called from suggested action activity. */
+ public static final String EXTRA_IS_SUW_SUGGESTED_ACTION_FLOW = "isSuwSuggestedActionFlow";
+
public static final String EXTRA_THEME = "theme";
public static final String EXTRA_USE_IMMERSIVE_MODE = "useImmersiveMode";
@@ -122,7 +125,8 @@ public final class WizardManagerHelper {
EXTRA_IS_DEFERRED_SETUP,
EXTRA_IS_PRE_DEFERRED_SETUP,
EXTRA_IS_PORTAL_SETUP,
- EXTRA_IS_SETUP_FLOW)) {
+ EXTRA_IS_SETUP_FLOW,
+ EXTRA_IS_SUW_SUGGESTED_ACTION_FLOW)) {
dstIntent.putExtra(key, srcIntent.getBooleanExtra(key, false));
}
@@ -182,6 +186,17 @@ public final class WizardManagerHelper {
}
/**
+ * Checks whether an intent is running in the portal setup wizard flow.
+ *
+ * @param originalIntent The original intent that was used to start the step, usually via {@link
+ * Activity#getIntent()}.
+ * @return true if the intent passed in was running in portal setup wizard.
+ */
+ public static boolean isPortalSetupWizard(Intent originalIntent) {
+ return originalIntent != null && originalIntent.getBooleanExtra(EXTRA_IS_PORTAL_SETUP, false);
+ }
+
+ /**
* Checks whether an intent is running in the deferred setup wizard flow.
*
* @param originalIntent The original intent that was used to start the step, usually via {@link
diff --git a/main/java/com/google/android/setupcompat/view/ButtonBarLayout.java b/main/java/com/google/android/setupcompat/view/ButtonBarLayout.java
index da1ab34..1157fae 100644
--- a/main/java/com/google/android/setupcompat/view/ButtonBarLayout.java
+++ b/main/java/com/google/android/setupcompat/view/ButtonBarLayout.java
@@ -18,9 +18,12 @@ package com.google.android.setupcompat.view;
import android.content.Context;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import com.google.android.setupcompat.R;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupcompat.template.FooterActionButton;
/**
* An extension of LinearLayout that automatically switches to vertical orientation when it can't
@@ -62,7 +65,7 @@ public class ButtonBarLayout extends LinearLayout {
super.onMeasure(initialWidthMeasureSpec, heightMeasureSpec);
- if (getMeasuredWidth() > widthSize) {
+ if (!isFooterButtonsEventlyWeighted(getContext()) && (getMeasuredWidth() > widthSize)) {
setStacked(true);
// Measure again in the new orientation.
@@ -86,6 +89,7 @@ public class ButtonBarLayout extends LinearLayout {
if (stacked) {
child.setTag(R.id.suc_customization_original_weight, childParams.weight);
childParams.weight = 0;
+ childParams.leftMargin = 0;
} else {
Float weight = (Float) child.getTag(R.id.suc_customization_original_weight);
if (weight != null) {
@@ -103,6 +107,8 @@ public class ButtonBarLayout extends LinearLayout {
}
if (stacked) {
+ // When stacked, the buttons need to be kept in the center of the button bar.
+ setHorizontalGravity(Gravity.CENTER);
// HACK: In the default button bar style, the left and right paddings are not
// balanced to compensate for different alignment for borderless (left) button and
// the raised (right) button. When it's stacked, we want the buttons to be centered,
@@ -115,4 +121,28 @@ public class ButtonBarLayout extends LinearLayout {
setPadding(originalPaddingLeft, getPaddingTop(), originalPaddingRight, getPaddingBottom());
}
}
+
+ private boolean isFooterButtonsEventlyWeighted(Context context) {
+ int childCount = getChildCount();
+ int primayButtonCount = 0;
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child instanceof FooterActionButton) {
+ if (((FooterActionButton) child).isPrimaryButtonStyle()) {
+ primayButtonCount += 1;
+ }
+ }
+ }
+ if (primayButtonCount != 2) {
+ return false;
+ }
+
+ // TODO: Support neutral button style in glif layout for phone and tablet
+ if (context.getResources().getConfiguration().smallestScreenWidthDp >= 600
+ && PartnerConfigHelper.shouldApplyExtendedPartnerConfig(context)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
}
diff --git a/main/res/values/styles.xml b/main/res/values/styles.xml
index 6474426..6625e83 100644
--- a/main/res/values/styles.xml
+++ b/main/res/values/styles.xml
@@ -45,13 +45,13 @@
<item name="android:theme">@style/SucPartnerCustomizationButton.Primary</item>
<!-- Values used in styles -->
- <item name="android:fontFamily">?attr/sucFooterBarButtonFontFamily</item>
+ <item name="android:fontFamily" tools:targetApi="jelly_bean">?attr/sucFooterBarButtonFontFamily</item>
<item name="android:paddingLeft">?attr/sucFooterButtonPaddingStart</item>
<item name="android:paddingStart" tools:ignore="NewApi">?attr/sucFooterButtonPaddingStart</item>
<item name="android:paddingRight">?attr/sucFooterButtonPaddingEnd</item>
<item name="android:paddingEnd" tools:ignore="NewApi">?attr/sucFooterButtonPaddingEnd</item>
<item name="android:textAllCaps">?attr/sucFooterBarButtonAllCaps</item>
- <item name="android:stateListAnimator">@null</item>
+ <item name="android:stateListAnimator" tools:ignore="NewApi">@null</item>
<!-- Values used in themes -->
<item name="android:buttonCornerRadius" tools:ignore="NewApi">?attr/sucFooterBarButtonCornerRadius</item>
@@ -65,7 +65,7 @@
<item name="android:theme">@style/SucPartnerCustomizationButton.Secondary</item>
<!-- Values used in styles -->
- <item name="android:fontFamily">?attr/sucFooterBarButtonFontFamily</item>
+ <item name="android:fontFamily" tools:targetApi="jelly_bean">?attr/sucFooterBarButtonFontFamily</item>
<item name="android:minWidth">0dp</item>
<item name="android:paddingLeft">?attr/sucFooterButtonPaddingStart</item>
<item name="android:paddingStart" tools:ignore="NewApi">?attr/sucFooterButtonPaddingStart</item>
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
index 280ab81..c9a1966 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
@@ -40,6 +40,14 @@ public enum PartnerConfig {
// The min height of the footer buttons
CONFIG_FOOTER_BAR_MIN_HEIGHT(PartnerConfigKey.KEY_FOOTER_BAR_MIN_HEIGHT, ResourceType.DIMENSION),
+ // The padding start of the footer bar
+ CONFIG_FOOTER_BAR_PADDING_START(
+ PartnerConfigKey.KEY_FOOTER_BAR_PADDING_START, ResourceType.DIMENSION),
+
+ // The padding end of the footer bar
+ CONFIG_FOOTER_BAR_PADDING_END(
+ PartnerConfigKey.KEY_FOOTER_BAR_PADDING_END, ResourceType.DIMENSION),
+
// The same as "windowLightNavigationBar". If set true, the navigation bar icons will be drawn
// such that it is compatible with a light navigation bar background.
CONFIG_LIGHT_NAVIGATION_BAR(PartnerConfigKey.KEY_LIGHT_NAVIGATION_BAR, ResourceType.BOOL),
@@ -108,6 +116,10 @@ public enum PartnerConfig {
CONFIG_FOOTER_BUTTON_MIN_HEIGHT(
PartnerConfigKey.KEY_FOOTER_BUTTON_MIN_HEIGHT, ResourceType.DIMENSION),
+ // Make the footer buttons all aligned the end
+ CONFIG_FOOTER_BUTTON_ALIGNED_END(
+ PartnerConfigKey.KEY_FOOTER_BUTTON_ALIGNED_END, ResourceType.BOOL),
+
// Disabled background alpha of the footer buttons
CONFIG_FOOTER_BUTTON_DISABLED_ALPHA(
PartnerConfigKey.KEY_FOOTER_BUTTON_DISABLED_ALPHA, ResourceType.FRACTION),
@@ -116,6 +128,14 @@ public enum PartnerConfig {
CONFIG_FOOTER_BUTTON_DISABLED_BG_COLOR(
PartnerConfigKey.KEY_FOOTER_BUTTON_DISABLED_BG_COLOR, ResourceType.COLOR),
+ // Disabled text color of the primary footer button
+ CONFIG_FOOTER_PRIMARY_BUTTON_DISABLED_TEXT_COLOR(
+ PartnerConfigKey.KEY_PRIMARY_BUTTON_DISABLED_TEXT_COLOR, ResourceType.COLOR),
+
+ // Disabled text color of the secondary footer button
+ CONFIG_FOOTER_SECONDARY_BUTTON_DISABLED_TEXT_COLOR(
+ PartnerConfigKey.KEY_SECONDARY_BUTTON_DISABLED_TEXT_COLOR, ResourceType.COLOR),
+
// Background color of the primary footer button
CONFIG_FOOTER_PRIMARY_BUTTON_BG_COLOR(
PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_BG_COLOR, ResourceType.COLOR),
@@ -124,6 +144,10 @@ public enum PartnerConfig {
CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_COLOR(
PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_TEXT_COLOR, ResourceType.COLOR),
+ // Margin start of the primary footer button
+ CONFIG_FOOTER_PRIMARY_BUTTON_MARGIN_START(
+ PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_MARGIN_START, ResourceType.DIMENSION),
+
// Background color of the secondary footer button
CONFIG_FOOTER_SECONDARY_BUTTON_BG_COLOR(
PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_BG_COLOR, ResourceType.COLOR),
@@ -132,6 +156,10 @@ public enum PartnerConfig {
CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR(
PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_TEXT_COLOR, ResourceType.COLOR),
+ // Margin start of the secondary footer button
+ CONFIG_FOOTER_SECONDARY_BUTTON_MARGIN_START(
+ PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_MARGIN_START, ResourceType.DIMENSION),
+
// Background color of layout
CONFIG_LAYOUT_BACKGROUND_COLOR(PartnerConfigKey.KEY_LAYOUT_BACKGROUND_COLOR, ResourceType.COLOR),
@@ -141,6 +169,10 @@ public enum PartnerConfig {
// Margin end of the layout
CONFIG_LAYOUT_MARGIN_END(PartnerConfigKey.KEY_LAYOUT_MARGIN_END, ResourceType.DIMENSION),
+ // Middle horizontal spacing of the landscape layout
+ CONFIG_LAND_MIDDLE_HORIZONTAL_SPACING(
+ PartnerConfigKey.KEY_LAND_MIDDLE_HORIZONTAL_SPACING, ResourceType.DIMENSION),
+
// Text color of the header
CONFIG_HEADER_TEXT_COLOR(PartnerConfigKey.KEY_HEADER_TEXT_COLOR, ResourceType.COLOR),
@@ -207,6 +239,10 @@ public enum PartnerConfig {
// Font family of the description
CONFIG_DESCRIPTION_FONT_FAMILY(PartnerConfigKey.KEY_DESCRIPTION_FONT_FAMILY, ResourceType.STRING),
+ // Font family of the link text
+ CONFIG_DESCRIPTION_LINK_FONT_FAMILY(
+ PartnerConfigKey.KEY_DESCRIPTION_LINK_FONT_FAMILY, ResourceType.STRING),
+
// Margin top of the description text
CONFIG_DESCRIPTION_TEXT_MARGIN_TOP(
PartnerConfigKey.KEY_DESCRIPTION_TEXT_MARGIN_TOP, ResourceType.DIMENSION),
@@ -291,11 +327,11 @@ public enum PartnerConfig {
// The divider of list items are showing on the pages.
CONFIG_ITEMS_DIVIDER_SHOWN(PartnerConfigKey.KEY_ITEMS_DIVIDER_SHOWN, ResourceType.BOOL),
- // The intrinsic width of the card view for foldabe/tablet.
+ // The intrinsic width of the card view for foldable/tablet.
CONFIG_CARD_VIEW_INTRINSIC_WIDTH(
PartnerConfigKey.KEY_CARD_VIEW_INTRINSIC_WIDTH, ResourceType.DIMENSION),
- // The intrinsic height of the card view for foldabe/tablet.
+ // The intrinsic height of the card view for foldable/tablet.
CONFIG_CARD_VIEW_INTRINSIC_HEIGHT(
PartnerConfigKey.KEY_CARD_VIEW_INTRINSIC_HEIGHT, ResourceType.DIMENSION),
@@ -414,7 +450,23 @@ public enum PartnerConfig {
// The height of the header of the loading layout.
CONFIG_LOADING_LAYOUT_HEADER_HEIGHT(
- PartnerConfigKey.KEY_LOADING_LAYOUT_HEADER_HEIGHT, ResourceType.DIMENSION);
+ PartnerConfigKey.KEY_LOADING_LAYOUT_HEADER_HEIGHT, ResourceType.DIMENSION),
+
+ // Use the fullscreen style lottie animation.
+ CONFIG_LOADING_LAYOUT_FULL_SCREEN_ILLUSTRATION_ENABLED(
+ PartnerConfigKey.KEY_LOADING_LAYOUT_FULL_SCREEN_ILLUSTRATION_ENABLED, ResourceType.BOOL),
+
+ // Waiting for the animation finished before process to the next page/action.
+ CONFIG_LOADING_LAYOUT_WAIT_FOR_ANIMATION_FINISHED(
+ PartnerConfigKey.KEY_LOADING_LAYOUT_WAIT_FOR_ANIMATION_FINISHED, ResourceType.BOOL),
+
+ // The margin top of progress bar.
+ CONFIG_PROGRESS_BAR_MARGIN_TOP(
+ PartnerConfigKey.KEY_PROGRESS_BAR_MARGIN_TOP, ResourceType.DIMENSION),
+
+ // The margin bottom of progress bar.
+ CONFIG_PROGRESS_BAR_MARGIN_BOTTOM(
+ PartnerConfigKey.KEY_PROGRESS_BAR_MARGIN_BOTTOM, ResourceType.DIMENSION);
/** Resource type of the partner resources type. */
public enum ResourceType {
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
index 2ca8876..aca9a07 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
@@ -35,6 +35,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.google.android.setupcompat.partnerconfig.PartnerConfig.ResourceType;
+import com.google.android.setupcompat.util.BuildCompatUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
@@ -60,14 +61,27 @@ public class PartnerConfigHelper {
"isExtendedPartnerConfigEnabled";
@VisibleForTesting
+ public static final String IS_MATERIAL_YOU_STYLE_ENABLED_METHOD = "IsMaterialYouStyleEnabled";
+
+ @VisibleForTesting
public static final String IS_DYNAMIC_COLOR_ENABLED_METHOD = "isDynamicColorEnabled";
+ @VisibleForTesting
+ public static final String IS_NEUTRAL_BUTTON_STYLE_ENABLED_METHOD = "isNeutralButtonStyleEnabled";
+
+ @VisibleForTesting public static final String SUW_PACKAGE_NAME = "com.google.android.setupwizard";
+ @VisibleForTesting public static final String MATERIAL_YOU_RESOURCE_SUFFIX = "_material_you";
+
@VisibleForTesting static Bundle suwDayNightEnabledBundle = null;
@VisibleForTesting public static Bundle applyExtendedPartnerConfigBundle = null;
+ @VisibleForTesting public static Bundle applyMaterialYouConfigBundle = null;
+
@VisibleForTesting public static Bundle applyDynamicColorBundle = null;
+ @VisibleForTesting public static Bundle applyNeutralButtonStyleBundle = null;
+
private static PartnerConfigHelper instance = null;
@VisibleForTesting Bundle resultBundle = null;
@@ -79,7 +93,16 @@ public class PartnerConfigHelper {
private static int savedConfigUiMode;
- private static int savedOrientation = Configuration.ORIENTATION_PORTRAIT;
+ @VisibleForTesting
+ public static int savedOrientation = Configuration.ORIENTATION_PORTRAIT;
+
+ /**
+ * When testing related to fake PartnerConfigHelper instance, should sync the following saved
+ * config with testing environment.
+ */
+ @VisibleForTesting public static int savedScreenHeight = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+
+ @VisibleForTesting public static int savedScreenWidth = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
public static synchronized PartnerConfigHelper get(@NonNull Context context) {
if (!isValidInstance(context)) {
@@ -93,15 +116,21 @@ public class PartnerConfigHelper {
if (instance == null) {
savedConfigUiMode = currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
savedOrientation = currentConfig.orientation;
+ savedScreenWidth = currentConfig.screenWidthDp;
+ savedScreenHeight = currentConfig.screenHeightDp;
return false;
} else {
- if (isSetupWizardDayNightEnabled(context)
- && (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) != savedConfigUiMode) {
+ boolean uiModeChanged =
+ isSetupWizardDayNightEnabled(context)
+ && (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) != savedConfigUiMode;
+ if (uiModeChanged
+ || currentConfig.orientation != savedOrientation
+ || currentConfig.screenWidthDp != savedScreenWidth
+ || currentConfig.screenHeightDp != savedScreenHeight) {
savedConfigUiMode = currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
- resetInstance();
- return false;
- } else if (currentConfig.orientation != savedOrientation) {
savedOrientation = currentConfig.orientation;
+ savedScreenHeight = currentConfig.screenHeightDp;
+ savedScreenWidth = currentConfig.screenWidthDp;
resetInstance();
return false;
}
@@ -315,7 +344,7 @@ public class PartnerConfigHelper {
result = resource.getBoolean(resId);
partnerResourceCache.put(resourceConfig, result);
- } catch (NullPointerException exception) {
+ } catch (NullPointerException | NotFoundException exception) {
// fall through
}
return result;
@@ -364,7 +393,7 @@ public class PartnerConfigHelper {
result =
getDimensionFromTypedValue(
context, (TypedValue) partnerResourceCache.get(resourceConfig));
- } catch (NullPointerException exception) {
+ } catch (NullPointerException | NotFoundException exception) {
// fall through
}
return result;
@@ -408,7 +437,7 @@ public class PartnerConfigHelper {
result = resource.getFraction(resId, 1, 1);
partnerResourceCache.put(resourceConfig, result);
- } catch (NullPointerException exception) {
+ } catch (NullPointerException | NotFoundException exception) {
// fall through
}
return result;
@@ -441,7 +470,7 @@ public class PartnerConfigHelper {
result = resource.getInteger(resId);
partnerResourceCache.put(resourceConfig, result);
- } catch (NullPointerException exception) {
+ } catch (NullPointerException | NotFoundException exception) {
// fall through
}
return result;
@@ -503,6 +532,8 @@ public class PartnerConfigHelper {
/* arg= */ null,
/* extras= */ null);
partnerResourceCache.clear();
+ Log.i(
+ TAG, "PartnerConfigsBundle=" + (resultBundle != null ? resultBundle.size() : "(null)"));
} catch (IllegalArgumentException | SecurityException exception) {
Log.w(TAG, "Fail to get config from suw provider");
}
@@ -517,9 +548,10 @@ public class PartnerConfigHelper {
if (fallbackBundle != null) {
resourceEntryBundle.putBundle(KEY_FALLBACK_CONFIG, fallbackBundle.getBundle(resourceName));
}
-
- return adjustResourceEntryDayNightMode(
- context, ResourceEntry.fromBundle(context, resourceEntryBundle));
+ ResourceEntry adjustResourceEntry =
+ adjustResourceEntryDefaultValue(
+ context, ResourceEntry.fromBundle(context, resourceEntryBundle));
+ return adjustResourceEntryDayNightMode(context, adjustResourceEntry);
}
/**
@@ -544,12 +576,52 @@ public class PartnerConfigHelper {
return resourceEntry;
}
+ // Check the MNStyle flag and replace the inputResourceEntry.resourceName &
+ // inputResourceEntry.resourceId after T, that means if using Gliv4 before S, will always use
+ // glifv3 resources.
+ ResourceEntry adjustResourceEntryDefaultValue(Context context, ResourceEntry inputResourceEntry) {
+ if (BuildCompatUtils.isAtLeastT() && shouldApplyMaterialYouStyle(context)) {
+ // If not overlay resource
+ try {
+ if (SUW_PACKAGE_NAME.equals(inputResourceEntry.getPackageName())) {
+ String resourceTypeName =
+ inputResourceEntry
+ .getResources()
+ .getResourceTypeName(inputResourceEntry.getResourceId());
+ // try to update resourceName & resourceId
+ String materialYouResourceName =
+ inputResourceEntry.getResourceName().concat(MATERIAL_YOU_RESOURCE_SUFFIX);
+ int materialYouResourceId =
+ inputResourceEntry
+ .getResources()
+ .getIdentifier(
+ materialYouResourceName,
+ resourceTypeName,
+ inputResourceEntry.getPackageName());
+ if (materialYouResourceId != 0) {
+ Log.i(TAG, "use material you resource:" + materialYouResourceName);
+ return new ResourceEntry(
+ inputResourceEntry.getPackageName(),
+ materialYouResourceName,
+ materialYouResourceId,
+ inputResourceEntry.getResources());
+ }
+ }
+ } catch (NotFoundException ex) {
+ // fall through
+ }
+ }
+ return inputResourceEntry;
+ }
+
@VisibleForTesting
public static synchronized void resetInstance() {
instance = null;
suwDayNightEnabledBundle = null;
applyExtendedPartnerConfigBundle = null;
+ applyMaterialYouConfigBundle = null;
applyDynamicColorBundle = null;
+ applyNeutralButtonStyleBundle = null;
}
/**
@@ -606,6 +678,39 @@ public class PartnerConfigHelper {
IS_EXTENDED_PARTNER_CONFIG_ENABLED_METHOD, false));
}
+ /**
+ * Returns true if the SetupWizard is flow enabled "Material You(Glifv4)" style, or the result of
+ * shouldApplyExtendedPartnerConfig() in SDK S as fallback.
+ */
+ public static boolean shouldApplyMaterialYouStyle(@NonNull Context context) {
+ if (applyMaterialYouConfigBundle == null || applyMaterialYouConfigBundle.isEmpty()) {
+ try {
+ applyMaterialYouConfigBundle =
+ context
+ .getContentResolver()
+ .call(
+ getContentUri(),
+ IS_MATERIAL_YOU_STYLE_ENABLED_METHOD,
+ /* arg= */ null,
+ /* extras= */ null);
+ // The suw version did not support the flag yet, fallback to
+ // shouldApplyExtendedPartnerConfig() for SDK S.
+ if (applyMaterialYouConfigBundle != null
+ && applyMaterialYouConfigBundle.isEmpty()
+ && !BuildCompatUtils.isAtLeastT()) {
+ return shouldApplyExtendedPartnerConfig(context);
+ }
+ } catch (IllegalArgumentException | SecurityException exception) {
+ Log.w(TAG, "SetupWizard Material You configs supporting status unknown; return as false.");
+ applyMaterialYouConfigBundle = null;
+ return false;
+ }
+ }
+
+ return (applyMaterialYouConfigBundle != null
+ && applyMaterialYouConfigBundle.getBoolean(IS_MATERIAL_YOU_STYLE_ENABLED_METHOD, false));
+ }
+
/** Returns true if the SetupWizard supports the dynamic color during setup flow. */
public static boolean isSetupWizardDynamicColorEnabled(@NonNull Context context) {
if (applyDynamicColorBundle == null) {
@@ -629,6 +734,29 @@ public class PartnerConfigHelper {
&& applyDynamicColorBundle.getBoolean(IS_DYNAMIC_COLOR_ENABLED_METHOD, false));
}
+ /** Returns true if the SetupWizard supports the neutral button style during setup flow. */
+ public static boolean isNeutralButtonStyleEnabled(@NonNull Context context) {
+ if (applyNeutralButtonStyleBundle == null) {
+ try {
+ applyNeutralButtonStyleBundle =
+ context
+ .getContentResolver()
+ .call(
+ getContentUri(),
+ IS_NEUTRAL_BUTTON_STYLE_ENABLED_METHOD,
+ /* arg= */ null,
+ /* extras= */ null);
+ } catch (IllegalArgumentException | SecurityException exception) {
+ Log.w(TAG, "Neutral button style supporting status unknown; return as false.");
+ applyNeutralButtonStyleBundle = null;
+ return false;
+ }
+ }
+
+ return (applyNeutralButtonStyleBundle != null
+ && applyNeutralButtonStyleBundle.getBoolean(IS_NEUTRAL_BUTTON_STYLE_ENABLED_METHOD, false));
+ }
+
@VisibleForTesting
static Uri getContentUri() {
return new Uri.Builder()
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
index c7444a5..9554ff3 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
@@ -31,6 +31,8 @@ import java.lang.annotation.RetentionPolicy;
PartnerConfigKey.KEY_NAVIGATION_BAR_DIVIDER_COLOR,
PartnerConfigKey.KEY_FOOTER_BAR_BG_COLOR,
PartnerConfigKey.KEY_FOOTER_BAR_MIN_HEIGHT,
+ PartnerConfigKey.KEY_FOOTER_BAR_PADDING_START,
+ PartnerConfigKey.KEY_FOOTER_BAR_PADDING_END,
PartnerConfigKey.KEY_FOOTER_BUTTON_FONT_FAMILY,
PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_ADD_ANOTHER,
PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_CANCEL,
@@ -47,15 +49,21 @@ import java.lang.annotation.RetentionPolicy;
PartnerConfigKey.KEY_FOOTER_BUTTON_TEXT_SIZE,
PartnerConfigKey.KEY_FOOTER_BUTTON_TEXT_STYLE,
PartnerConfigKey.KEY_FOOTER_BUTTON_MIN_HEIGHT,
+ PartnerConfigKey.KEY_FOOTER_BUTTON_ALIGNED_END,
PartnerConfigKey.KEY_FOOTER_BUTTON_DISABLED_ALPHA,
PartnerConfigKey.KEY_FOOTER_BUTTON_DISABLED_BG_COLOR,
PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_BG_COLOR,
PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_TEXT_COLOR,
+ PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_MARGIN_START,
+ PartnerConfigKey.KEY_PRIMARY_BUTTON_DISABLED_TEXT_COLOR,
PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_BG_COLOR,
PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_TEXT_COLOR,
+ PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_MARGIN_START,
+ PartnerConfigKey.KEY_SECONDARY_BUTTON_DISABLED_TEXT_COLOR,
PartnerConfigKey.KEY_LAYOUT_BACKGROUND_COLOR,
PartnerConfigKey.KEY_LAYOUT_MARGIN_START,
PartnerConfigKey.KEY_LAYOUT_MARGIN_END,
+ PartnerConfigKey.KEY_LAND_MIDDLE_HORIZONTAL_SPACING,
PartnerConfigKey.KEY_HEADER_TEXT_SIZE,
PartnerConfigKey.KEY_HEADER_TEXT_COLOR,
PartnerConfigKey.KEY_HEADER_FONT_FAMILY,
@@ -75,6 +83,7 @@ import java.lang.annotation.RetentionPolicy;
PartnerConfigKey.KEY_DESCRIPTION_TEXT_COLOR,
PartnerConfigKey.KEY_DESCRIPTION_LINK_TEXT_COLOR,
PartnerConfigKey.KEY_DESCRIPTION_FONT_FAMILY,
+ PartnerConfigKey.KEY_DESCRIPTION_LINK_FONT_FAMILY,
PartnerConfigKey.KEY_DESCRIPTION_TEXT_MARGIN_TOP,
PartnerConfigKey.KEY_DESCRIPTION_TEXT_MARGIN_BOTTOM,
PartnerConfigKey.KEY_CONTENT_TEXT_SIZE,
@@ -128,6 +137,10 @@ import java.lang.annotation.RetentionPolicy;
PartnerConfigKey.KEY_LOADING_LAYOUT_CONTENT_PADDING_END,
PartnerConfigKey.KEY_LOADING_LAYOUT_CONTENT_PADDING_BOTTOM,
PartnerConfigKey.KEY_LOADING_LAYOUT_HEADER_HEIGHT,
+ PartnerConfigKey.KEY_LOADING_LAYOUT_FULL_SCREEN_ILLUSTRATION_ENABLED,
+ PartnerConfigKey.KEY_LOADING_LAYOUT_WAIT_FOR_ANIMATION_FINISHED,
+ PartnerConfigKey.KEY_PROGRESS_BAR_MARGIN_TOP,
+ PartnerConfigKey.KEY_PROGRESS_BAR_MARGIN_BOTTOM,
})
// TODO: can be removed and always reference PartnerConfig.getResourceName()?
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
@@ -155,6 +168,12 @@ public @interface PartnerConfigKey {
// The min height of the footer bar
String KEY_FOOTER_BAR_MIN_HEIGHT = "setup_compat_footer_bar_min_height";
+ // The padding start of the footer bar
+ String KEY_FOOTER_BAR_PADDING_START = "setup_compat_footer_bar_padding_start";
+
+ // The padding end of the footer bar
+ String KEY_FOOTER_BAR_PADDING_END = "setup_compat_footer_bar_padding_end";
+
// The font face used in footer buttons. This must be a string reference to a font that is
// available in the system. Font references (@font or @xml) are not allowed.
String KEY_FOOTER_BUTTON_FONT_FAMILY = "setup_compat_footer_button_font_family";
@@ -204,6 +223,9 @@ public @interface PartnerConfigKey {
// The min height of the footer buttons
String KEY_FOOTER_BUTTON_MIN_HEIGHT = "setup_compat_footer_button_min_height";
+ // Make the footer buttons all aligned the end
+ String KEY_FOOTER_BUTTON_ALIGNED_END = "setup_compat_footer_button_aligned_end";
+
// Disabled background alpha of the footer buttons
String KEY_FOOTER_BUTTON_DISABLED_ALPHA = "setup_compat_footer_button_disabled_alpha";
@@ -216,12 +238,26 @@ public @interface PartnerConfigKey {
// Text color of the primary footer button
String KEY_FOOTER_PRIMARY_BUTTON_TEXT_COLOR = "setup_compat_footer_primary_button_text_color";
+ // Margin start of the primary footer button
+ String KEY_FOOTER_PRIMARY_BUTTON_MARGIN_START = "setup_compat_footer_primary_button_margin_start";
+
+ // Disabled text color of the primary footer button
+ String KEY_PRIMARY_BUTTON_DISABLED_TEXT_COLOR = "setup_compat_primary_button_disabled_text_color";
+
// Background color of the secondary footer button
String KEY_FOOTER_SECONDARY_BUTTON_BG_COLOR = "setup_compat_footer_secondary_button_bg_color";
// Text color of the secondary footer button
String KEY_FOOTER_SECONDARY_BUTTON_TEXT_COLOR = "setup_compat_footer_secondary_button_text_color";
+ // Margin start of the secondary footer button
+ String KEY_FOOTER_SECONDARY_BUTTON_MARGIN_START =
+ "setup_compat_footer_secondary_button_margin_start";
+
+ // Disabled text color of the secondary footer button
+ String KEY_SECONDARY_BUTTON_DISABLED_TEXT_COLOR =
+ "setup_compat_secondary_button_disabled_text_color";
+
// Background color of layout
String KEY_LAYOUT_BACKGROUND_COLOR = "setup_design_layout_bg_color";
@@ -231,6 +267,9 @@ public @interface PartnerConfigKey {
// Margin end of the layout
String KEY_LAYOUT_MARGIN_END = "setup_design_layout_margin_end";
+ // Middle horizontal spacing of the landscape layout
+ String KEY_LAND_MIDDLE_HORIZONTAL_SPACING = "setup_design_land_middle_horizontal_spacing";
+
// Text size of the header
String KEY_HEADER_TEXT_SIZE = "setup_design_header_text_size";
@@ -290,6 +329,9 @@ public @interface PartnerConfigKey {
// Font family of the description
String KEY_DESCRIPTION_FONT_FAMILY = "setup_design_description_font_family";
+ // Font family of the link text
+ String KEY_DESCRIPTION_LINK_FONT_FAMILY = "setup_design_description_link_font_family";
+
// Margin top of the header text
String KEY_DESCRIPTION_TEXT_MARGIN_TOP = "setup_design_description_text_margin_top";
@@ -362,10 +404,10 @@ public @interface PartnerConfigKey {
// The divider of list items are showing.
String KEY_ITEMS_DIVIDER_SHOWN = "setup_design_items_divider_shown";
- // The intrinsic width of the card view for foldabe/tablet.
+ // The intrinsic width of the card view for foldable/tablet.
String KEY_CARD_VIEW_INTRINSIC_WIDTH = "setup_design_card_view_intrinsic_width";
- // The intrinsic height of the card view for foldabe/tablet.
+ // The intrinsic height of the card view for foldable/tablet.
String KEY_CARD_VIEW_INTRINSIC_HEIGHT = "setup_design_card_view_intrinsic_height";
// The animation of loading screen used in those activities which is non of below type.
@@ -474,4 +516,18 @@ public @interface PartnerConfigKey {
// A height of the header of loading layout.
String KEY_LOADING_LAYOUT_HEADER_HEIGHT = "loading_layout_header_height";
+
+ // Use the fullscreen style lottie animation.
+ String KEY_LOADING_LAYOUT_FULL_SCREEN_ILLUSTRATION_ENABLED =
+ "loading_layout_full_screen_illustration_enabled";
+
+ // Waiting the animation finished before process to the next page/action.
+ String KEY_LOADING_LAYOUT_WAIT_FOR_ANIMATION_FINISHED =
+ "loading_layout_wait_for_animation_finished";
+
+ // A margin top of the content frame of progress bar.
+ String KEY_PROGRESS_BAR_MARGIN_TOP = "setup_design_progress_bar_margin_top";
+
+ // A margin bottom of the content frame of progress bar.
+ String KEY_PROGRESS_BAR_MARGIN_BOTTOM = "setup_design_progress_bar_margin_bottom";
}
diff --git a/portal_extension/aidl/com/google/android/setupcompat/portal/ISetupNotificationServicePortalExtension.aidl b/portal_extension/aidl/com/google/android/setupcompat/portal/ISetupNotificationServicePortalExtension.aidl
new file mode 100644
index 0000000..2b83576
--- /dev/null
+++ b/portal_extension/aidl/com/google/android/setupcompat/portal/ISetupNotificationServicePortalExtension.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 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.portal;
+
+import com.google.android.setupcompat.portal.IPortalProgressCallback;
+import com.google.android.setupcompat.portal.TaskComponent;
+
+/**
+ * Declares the interface for portal used by GmsCore.
+ */
+interface ISetupNotificationServicePortalExtension {
+ IPortalProgressCallback registerTask(in TaskComponent component) = 1;
+
+ boolean removeTask(String packageName, String taskName) = 2;
+} \ No newline at end of file
diff --git a/portal_extension/aidl/com/google/android/setupcompat/portal/TaskComponent.aidl b/portal_extension/aidl/com/google/android/setupcompat/portal/TaskComponent.aidl
new file mode 100644
index 0000000..8a4dfb3
--- /dev/null
+++ b/portal_extension/aidl/com/google/android/setupcompat/portal/TaskComponent.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 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.portal;
+
+/**
+ * A class that represents how a persistent notification is to be presented to the user using the
+ * {@link com.google.android.setupcompat.portal.ISetupNotificationServicePortalExtension }.
+ */
+parcelable TaskComponent; \ No newline at end of file
diff --git a/portal_extension/java/com/google/android/setupcompat/portal/PortalExtensionConstants.java b/portal_extension/java/com/google/android/setupcompat/portal/PortalExtensionConstants.java
new file mode 100644
index 0000000..4f1b884
--- /dev/null
+++ b/portal_extension/java/com/google/android/setupcompat/portal/PortalExtensionConstants.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 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.portal;
+
+/** Constant values used for PortalExtension */
+public class PortalExtensionConstants {
+ public static final String BIND_SERVICE_INTENT_ACTION =
+ "com.google.android.setupcompat.portal.SetupNotificationService.BIND_EXTENSION";
+
+ private PortalExtensionConstants() {}
+ ;
+}
diff --git a/portal_extension/java/com/google/android/setupcompat/portal/TaskComponent.java b/portal_extension/java/com/google/android/setupcompat/portal/TaskComponent.java
new file mode 100644
index 0000000..16d35a3
--- /dev/null
+++ b/portal_extension/java/com/google/android/setupcompat/portal/TaskComponent.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2020 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.portal;
+
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
+import com.google.android.setupcompat.internal.Preconditions;
+
+/**
+ * A class that represents how a persistent notification is to be presented to the user using the
+ * {@link com.google.android.setupcompat.portal.ISetupNotificationServicePortalExtension }.
+ */
+public class TaskComponent implements Parcelable {
+ private final String packageName;
+ private final String taskName;
+ @StringRes private final int displayNameResId;
+ @DrawableRes private final int displayIconResId;
+ private final Intent itemClickIntent;
+
+ private TaskComponent(
+ String packageName,
+ String taskName,
+ @StringRes int displayNameResId,
+ @DrawableRes int displayIconResId,
+ Intent itemClickIntent) {
+ this.packageName = packageName;
+ this.taskName = taskName;
+ this.displayNameResId = displayNameResId;
+ this.displayIconResId = displayIconResId;
+ this.itemClickIntent = itemClickIntent;
+ }
+
+ /** Returns a new instance of {@link Builder}. */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /** Returns the package name where the service exist. */
+ @NonNull
+ public String getPackageName() {
+ return packageName;
+ }
+
+ /** Returns the service class name */
+ @NonNull
+ public String getTaskName() {
+ return taskName;
+ }
+
+ /** Returns the string resource id of display name. */
+ @StringRes
+ public int getDisplayName() {
+ return displayNameResId;
+ }
+
+ /** Returns the drawable resource id of display icon. */
+ @DrawableRes
+ public int getDisplayIcon() {
+ return displayIconResId;
+ }
+
+ /** Returns the Intent to start the user interface while progress item click. */
+ public Intent getItemClickIntent() {
+ return itemClickIntent;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(getPackageName());
+ dest.writeString(getTaskName());
+ dest.writeInt(getDisplayName());
+ dest.writeInt(getDisplayIcon());
+ dest.writeParcelable(getItemClickIntent(), 0);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<TaskComponent> CREATOR =
+ new Creator<TaskComponent>() {
+ @Override
+ public TaskComponent createFromParcel(Parcel in) {
+ return TaskComponent.newBuilder()
+ .setPackageName(in.readString())
+ .setTaskName(in.readString())
+ .setDisplayName(in.readInt())
+ .setDisplayIcon(in.readInt())
+ .setItemClickIntent(in.readParcelable(Intent.class.getClassLoader()))
+ .build();
+ }
+
+ @Override
+ public TaskComponent[] newArray(int size) {
+ return new TaskComponent[size];
+ }
+ };
+
+ /** Builder class for {@link com.google.android.setupcompat.portal.TaskComponent} objects. */
+ public static class Builder {
+ private String packageName;
+ private String taskName;
+ @StringRes private int displayNameResId;
+ @DrawableRes private int displayIconResId;
+ private Intent itemClickIntent;
+
+ /** Sets the packages name which is the service exists */
+ public Builder setPackageName(@NonNull String packageName) {
+ this.packageName = packageName;
+ return this;
+ }
+
+ /** Sets a name to identify what task this progress is. */
+ public Builder setTaskName(@NonNull String taskName) {
+ this.taskName = taskName;
+ return this;
+ }
+
+ /** Sets the name which is displayed on PortalActivity */
+ public Builder setDisplayName(@StringRes int displayNameResId) {
+ this.displayNameResId = displayNameResId;
+ return this;
+ }
+
+ /** Sets the icon which is display on PortalActivity */
+ public Builder setDisplayIcon(@DrawableRes int displayIconResId) {
+ this.displayIconResId = displayIconResId;
+ return this;
+ }
+
+ public Builder setItemClickIntent(Intent itemClickIntent) {
+ this.itemClickIntent = itemClickIntent;
+ return this;
+ }
+
+ public TaskComponent build() {
+ Preconditions.checkNotNull(packageName, "packageName cannot be null.");
+ Preconditions.checkNotNull(taskName, "serviceClass cannot be null.");
+ Preconditions.checkNotNull(itemClickIntent, "Item click intent cannot be null");
+ Preconditions.checkArgument(displayNameResId != 0, "Invalidate resource id of display name");
+ Preconditions.checkArgument(displayIconResId != 0, "Invalidate resource id of display icon");
+
+ return new TaskComponent(
+ packageName, taskName, displayNameResId, displayIconResId, itemClickIntent);
+ }
+ }
+}
diff --git a/setup_extension/info_modules.proto b/setup_extension/info_modules.proto
new file mode 100644
index 0000000..5027c4c
--- /dev/null
+++ b/setup_extension/info_modules.proto
@@ -0,0 +1,96 @@
+// Copyright (C) 2022 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.
+//
+// Author: yuchaoyu@google.com (Yu Chao)
+
+syntax = "proto2";
+
+package portal_tips_proto;
+
+option java_package = "com.google.android.setupcompat";
+option java_outer_classname = "InfoModules";
+
+// A list of informational modules to be displayed under the deferred setup
+// items.
+// Next index: 5
+message InfoModuleList {
+ // Contains the title of the entire info module section. This is displayed
+ // before the subtitle and the list of info modules.
+ optional string section_title = 1;
+
+ // Contains the subtitle of the entire info module section. This is displayed
+ // before the list of info modules.
+ optional string section_subtitle = 2;
+
+ // A module represents a single informational item to be displayed in the list
+ // under the deferred setup page.
+ //
+ // Each module contains a unique id, three sections of text labels (title,
+ // description, footer), the Intent URI to launch when being clicked, the raw
+ // icon, and a boolean to determine whether or not the icon should be colored.
+ // Next index: 8
+ message InfoModule {
+
+ // The id to uniquely identify an InfoModule.
+ optional string id = 1;
+
+ // Contains the title of the module. This is displayed as the first section
+ // of the text content.
+ optional string title = 2;
+
+ // Contains the main paragraphs of the module. This is displayed as the
+ // second section of the text content.
+ optional string description = 3;
+
+ // Contains the footer text of the module. This is displayed as the third
+ // and final section of the text content.
+ optional string footer = 4;
+
+ // Contains the Intent string to be launched when the module is clicked.
+ //
+ // This string could be generated from an `Intent` object through the
+ // `toUri()` method.
+ optional string intent_uri = 5;
+
+ // Contains the icon of the module in raw bytes of bitmap format.
+ optional bytes raw_icon = 6;
+
+ // Determines whether the icon should be colored according to the theme.
+ // Generally speaking, this should be set to true for monoline icons (such
+ // as Material Icons) because their colors change according to the theme.
+ // For icons which their colors should be preserved (such as package icons),
+ // this should be set to false.
+ optional bool should_apply_theme_color_on_icon = 7;
+ }
+
+ // Contains a list of informational modules to be displayed.
+ repeated InfoModule info_module_list = 3;
+
+ // A trailing link is a button item appearing below the info module list.
+ // Next index: 3
+ message TrailingLink {
+ // Contains the text that should be displayed on the link button.
+ optional string text = 1;
+
+ // Contains the Intent string to be launched when the link button is
+ // clicked.
+ //
+ // This string could be generated from an `Intent` object through the
+ // `toUri()` method.
+ optional string intent_uri = 2;
+ }
+
+ // Contains a trailing link to be displayed below the info modules.
+ optional TrailingLink trailing_link = 4;
+}