[automerger skipped] Skip ab/6749736 in stage. am: db2c7c5552 -s ours am: f21c6d415f -s ours am: a66b5ed98f -s ours
am skip reason: Change-Id I164bb65835bac8d60cd5b9d21b698ed9cc611dc0 with SHA-1 501c2de16a is in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/external/setupcompat/+/12796112
Change-Id: I354392f29e88c6d3a9f7646e9e4a196fd640a724
diff --git a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
index fac4b39..896840c 100644
--- a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
+++ b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
@@ -132,15 +132,13 @@
@Override
protected void onBeforeTemplateInflated(AttributeSet attrs, int defStyleAttr) {
- boolean isSetupFlow;
-
// Sets default value to true since this timing
// before PartnerCustomization members initialization
usePartnerResourceAttr = true;
activity = lookupActivityFromContext(getContext());
- isSetupFlow = WizardManagerHelper.isAnySetupWizard(activity.getIntent());
+ boolean isSetupFlow = WizardManagerHelper.isAnySetupWizard(activity.getIntent());
TypedArray a =
getContext()
diff --git a/main/java/com/google/android/setupcompat/internal/FallbackThemeWrapper.java b/main/java/com/google/android/setupcompat/internal/FallbackThemeWrapper.java
index af17a62..574f614 100644
--- a/main/java/com/google/android/setupcompat/internal/FallbackThemeWrapper.java
+++ b/main/java/com/google/android/setupcompat/internal/FallbackThemeWrapper.java
@@ -18,8 +18,8 @@
import android.content.Context;
import android.content.res.Resources.Theme;
-import androidx.annotation.StyleRes;
import android.view.ContextThemeWrapper;
+import androidx.annotation.StyleRes;
/**
* Same as {@link ContextThemeWrapper}, but the base context's theme attributes take precedence over
diff --git a/main/java/com/google/android/setupcompat/internal/PersistableBundles.java b/main/java/com/google/android/setupcompat/internal/PersistableBundles.java
index 1197645..f02265a 100644
--- a/main/java/com/google/android/setupcompat/internal/PersistableBundles.java
+++ b/main/java/com/google/android/setupcompat/internal/PersistableBundles.java
@@ -29,7 +29,7 @@
import java.util.List;
/** Contains utility methods related to {@link PersistableBundle}. */
-@TargetApi(VERSION_CODES.Q)
+@TargetApi(VERSION_CODES.LOLLIPOP_MR1)
public final class PersistableBundles {
/**
diff --git a/main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java b/main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java
index a1ca156..779cc8a 100644
--- a/main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java
+++ b/main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java
@@ -20,8 +20,8 @@
import android.content.Context;
import android.os.Bundle;
import android.os.RemoteException;
-import androidx.annotation.VisibleForTesting;
import android.util.Log;
+import androidx.annotation.VisibleForTesting;
import com.google.android.setupcompat.ISetupCompatService;
import com.google.android.setupcompat.logging.internal.SetupMetricsLoggingConstants.MetricType;
import java.util.concurrent.ExecutorService;
@@ -68,7 +68,7 @@
} else {
Log.w(TAG, "logMetric failed since service reference is null. Are the permissions valid?");
}
- } catch (InterruptedException | TimeoutException | RemoteException e) {
+ } catch (InterruptedException | TimeoutException | RemoteException | IllegalStateException e) {
Log.e(TAG, String.format("Exception occurred while trying to log metric = [%s]", args), e);
}
}
diff --git a/main/java/com/google/android/setupcompat/internal/SetupCompatServiceProvider.java b/main/java/com/google/android/setupcompat/internal/SetupCompatServiceProvider.java
index 2043a81..12c0a92 100644
--- a/main/java/com/google/android/setupcompat/internal/SetupCompatServiceProvider.java
+++ b/main/java/com/google/android/setupcompat/internal/SetupCompatServiceProvider.java
@@ -23,10 +23,10 @@
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Looper;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import android.util.Log;
import com.google.android.setupcompat.ISetupCompatService;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
diff --git a/main/java/com/google/android/setupcompat/internal/TemplateLayout.java b/main/java/com/google/android/setupcompat/internal/TemplateLayout.java
index 34179d6..25a3c5b 100644
--- a/main/java/com/google/android/setupcompat/internal/TemplateLayout.java
+++ b/main/java/com/google/android/setupcompat/internal/TemplateLayout.java
@@ -20,15 +20,15 @@
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build.VERSION_CODES;
-import androidx.annotation.Keep;
-import androidx.annotation.LayoutRes;
-import androidx.annotation.StyleRes;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
+import androidx.annotation.Keep;
+import androidx.annotation.LayoutRes;
+import androidx.annotation.StyleRes;
import com.google.android.setupcompat.R;
import com.google.android.setupcompat.template.Mixin;
import java.util.HashMap;
diff --git a/main/java/com/google/android/setupcompat/logging/internal/PartnerCustomizedResourceListMetric.java b/main/java/com/google/android/setupcompat/logging/internal/PartnerCustomizedResourceListMetric.java
index 7d0b731..2aa1240 100644
--- a/main/java/com/google/android/setupcompat/logging/internal/PartnerCustomizedResourceListMetric.java
+++ b/main/java/com/google/android/setupcompat/logging/internal/PartnerCustomizedResourceListMetric.java
@@ -30,24 +30,28 @@
@TargetApi(VERSION_CODES.Q)
public class PartnerCustomizedResourceListMetric {
- public static void logMetrics(Context context, String screenName, Bundle bundle) {
+ public static void logMetrics(Context context, String deviceDisplayName, Bundle bundle) {
PersistableBundle logBundle =
- buildLogBundleFromResourceConfigBundle(context.getPackageName(), bundle);
+ buildLogBundleFromResourceConfigBundle(context.getPackageName(), deviceDisplayName, bundle);
if (!logBundle.isEmpty()) {
SetupMetricsLogger.logCustomEvent(
context,
- CustomEvent.create(MetricKey.get("PartnerCustomizationResource", screenName), logBundle));
+ CustomEvent.create(
+ MetricKey.get("PartnerCustomizationResource", "NoScreenName"), logBundle));
}
}
@VisibleForTesting
public static PersistableBundle buildLogBundleFromResourceConfigBundle(
- String defaultPackageName, Bundle resourceConfigBundle) {
+ 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);
}
}
diff --git a/main/java/com/google/android/setupcompat/template/FooterActionButton.java b/main/java/com/google/android/setupcompat/template/FooterActionButton.java
index bb26d19..86a06d9 100644
--- a/main/java/com/google/android/setupcompat/template/FooterActionButton.java
+++ b/main/java/com/google/android/setupcompat/template/FooterActionButton.java
@@ -18,11 +18,11 @@
import android.annotation.SuppressLint;
import android.content.Context;
-import androidx.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
+import androidx.annotation.Nullable;
/** Button that can react to touch when disabled. */
public class FooterActionButton extends Button {
diff --git a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
index bc9e5c1..973b8f1 100644
--- a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
@@ -34,16 +34,6 @@
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.PersistableBundle;
-import androidx.annotation.AttrRes;
-import androidx.annotation.CallSuper;
-import androidx.annotation.ColorInt;
-import androidx.annotation.IdRes;
-import androidx.annotation.LayoutRes;
-import androidx.annotation.MainThread;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.StyleRes;
-import androidx.annotation.VisibleForTesting;
import android.util.AttributeSet;
import android.util.StateSet;
import android.util.TypedValue;
@@ -54,6 +44,16 @@
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
+import androidx.annotation.AttrRes;
+import androidx.annotation.CallSuper;
+import androidx.annotation.ColorInt;
+import androidx.annotation.IdRes;
+import androidx.annotation.LayoutRes;
+import androidx.annotation.MainThread;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StyleRes;
+import androidx.annotation.VisibleForTesting;
import com.google.android.setupcompat.PartnerCustomizationLayout;
import com.google.android.setupcompat.R;
import com.google.android.setupcompat.internal.FooterButtonPartnerConfig;
diff --git a/main/java/com/google/android/setupcompat/template/FooterButton.java b/main/java/com/google/android/setupcompat/template/FooterButton.java
index 2fa8c7c..b23b1bb 100644
--- a/main/java/com/google/android/setupcompat/template/FooterButton.java
+++ b/main/java/com/google/android/setupcompat/template/FooterButton.java
@@ -23,14 +23,14 @@
import android.content.res.TypedArray;
import android.os.Build.VERSION_CODES;
import android.os.PersistableBundle;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.View.OnClickListener;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.annotation.StyleRes;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
import com.google.android.setupcompat.R;
import com.google.android.setupcompat.logging.CustomEvent;
import java.lang.annotation.Retention;
diff --git a/main/java/com/google/android/setupcompat/template/FooterButtonInflater.java b/main/java/com/google/android/setupcompat/template/FooterButtonInflater.java
index fe2538b..10aa052 100644
--- a/main/java/com/google/android/setupcompat/template/FooterButtonInflater.java
+++ b/main/java/com/google/android/setupcompat/template/FooterButtonInflater.java
@@ -19,10 +19,10 @@
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
-import androidx.annotation.NonNull;
import android.util.AttributeSet;
import android.util.Xml;
import android.view.InflateException;
+import androidx.annotation.NonNull;
import java.io.IOException;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/main/java/com/google/android/setupcompat/template/StatusBarMixin.java b/main/java/com/google/android/setupcompat/template/StatusBarMixin.java
index 1bd6949..a818793 100644
--- a/main/java/com/google/android/setupcompat/template/StatusBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/StatusBarMixin.java
@@ -25,13 +25,13 @@
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Build.VERSION_CODES;
-import androidx.annotation.AttrRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.Window;
import android.widget.LinearLayout;
+import androidx.annotation.AttrRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.google.android.setupcompat.PartnerCustomizationLayout;
import com.google.android.setupcompat.R;
import com.google.android.setupcompat.partnerconfig.PartnerConfig;
diff --git a/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java b/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java
index e055d28..8050406 100644
--- a/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java
@@ -24,13 +24,13 @@
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.Window;
import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.Window;
import com.google.android.setupcompat.PartnerCustomizationLayout;
import com.google.android.setupcompat.R;
import com.google.android.setupcompat.internal.TemplateLayout;
@@ -83,6 +83,19 @@
setLightSystemNavBar(
a.getBoolean(
R.styleable.SucSystemNavBarMixin_sucLightSystemNavBar, isLightSystemNavBar()));
+
+ // Support updating system navigation bar divider color from P.
+ if (VERSION.SDK_INT >= VERSION_CODES.P) {
+ // get fallback value from theme
+ int[] navBarDividerColorAttr = new int[] {android.R.attr.navigationBarDividerColor};
+ TypedArray typedArray =
+ templateLayout.getContext().obtainStyledAttributes(navBarDividerColorAttr);
+ int defaultColor = typedArray.getColor(/* index= */ 0, /* defValue= */ 0);
+ int sucSystemNavBarDividerColor =
+ a.getColor(R.styleable.SucSystemNavBarMixin_sucSystemNavBarDividerColor, defaultColor);
+ setSystemNavBarDividerColor(sucSystemNavBarDividerColor);
+ typedArray.recycle();
+ }
a.recycle();
}
}
@@ -120,6 +133,7 @@
*
* @param isLight true means compatible with light theme, otherwise compatible with dark theme
*/
+
public void setLightSystemNavBar(boolean isLight) {
if (Build.VERSION.SDK_INT >= VERSION_CODES.O && windowOfActivity != null) {
if (applyPartnerResources) {
@@ -158,6 +172,28 @@
}
/**
+ * Sets the divider color of navigation bar. The color will be overridden by partner resource if
+ * the activity is running in setup wizard flow.
+ *
+ * @param color the default divider color of navigation bar
+ */
+ public void setSystemNavBarDividerColor(int color) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.P && windowOfActivity != null) {
+ if (applyPartnerResources) {
+ Context context = templateLayout.getContext();
+ // Do nothing if the old version partner provider did not contain the new config.
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_NAVIGATION_BAR_DIVIDER_COLOR)) {
+ color =
+ PartnerConfigHelper.get(context)
+ .getColor(context, PartnerConfig.CONFIG_NAVIGATION_BAR_DIVIDER_COLOR);
+ }
+ }
+ windowOfActivity.setNavigationBarDividerColor(color);
+ }
+ }
+
+ /**
* Hides the navigation bar, make the color of the status and navigation bars transparent, and
* specify {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} flag so that the content is laid-out
* behind the transparent status bar. This is commonly used with {@link
diff --git a/main/java/com/google/android/setupcompat/util/SystemBarHelper.java b/main/java/com/google/android/setupcompat/util/SystemBarHelper.java
index 75e5dd3..d61ff8b 100644
--- a/main/java/com/google/android/setupcompat/util/SystemBarHelper.java
+++ b/main/java/com/google/android/setupcompat/util/SystemBarHelper.java
@@ -24,13 +24,13 @@
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Handler;
-import androidx.annotation.RequiresPermission;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
+import androidx.annotation.RequiresPermission;
/**
* A helper class to manage the system navigation bar and status bar. This will add various
diff --git a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
index bfe1dbb..96fedbc 100644
--- a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
+++ b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
@@ -34,7 +34,7 @@
*/
public final class WizardManagerHelper {
- private static final String ACTION_NEXT = "com.android.wizard.NEXT";
+ @VisibleForTesting public static final String ACTION_NEXT = "com.android.wizard.NEXT";
// EXTRA_SCRIPT_URI and EXTRA_ACTION_ID are used in setup wizard in versions before M and are
// kept for backwards compatibility.
@@ -43,10 +43,24 @@
@VisibleForTesting static final String EXTRA_WIZARD_BUNDLE = "wizardBundle";
private static final String EXTRA_RESULT_CODE = "com.android.setupwizard.ResultCode";
- @VisibleForTesting public static final String EXTRA_IS_FIRST_RUN = "firstRun";
- @VisibleForTesting static final String EXTRA_IS_DEFERRED_SETUP = "deferredSetup";
- @VisibleForTesting static final String EXTRA_IS_PRE_DEFERRED_SETUP = "preDeferredSetup";
- @VisibleForTesting public static final String EXTRA_IS_SETUP_FLOW = "isSetupFlow";
+
+ /** Extra for notifying an Activity that it is inside the first SetupWizard flow or not. */
+ public static final String EXTRA_IS_FIRST_RUN = "firstRun";
+
+ /** Extra for notifying an Activity that it is inside the Deferred SetupWizard flow or not. */
+ public static final String EXTRA_IS_DEFERRED_SETUP = "deferredSetup";
+
+ /** Extra for notifying an Activity that it is inside the "Pre-Deferred Setup" flow. */
+ public static final String EXTRA_IS_PRE_DEFERRED_SETUP = "preDeferredSetup";
+
+ /**
+ * Extra for notifying an Activity that it is inside the any setup flow.
+ *
+ * <p>Apps that target API levels below {@link android.os.Build.VERSION_CODES#Q} is able to
+ * determine whether Activity is inside the any setup flow by one of {@link #EXTRA_IS_FIRST_RUN},
+ * {@link #EXTRA_IS_DEFERRED_SETUP}, and {@link #EXTRA_IS_PRE_DEFERRED_SETUP} is true.
+ */
+ public static final String EXTRA_IS_SETUP_FLOW = "isSetupFlow";
public static final String EXTRA_THEME = "theme";
public static final String EXTRA_USE_IMMERSIVE_MODE = "useImmersiveMode";
diff --git a/main/res/values/attrs.xml b/main/res/values/attrs.xml
index 1a5342c..d075e81 100644
--- a/main/res/values/attrs.xml
+++ b/main/res/values/attrs.xml
@@ -52,6 +52,10 @@
"android:windowTranslucentNavigation" should be set to false. -->
<attr name="sucSystemNavBarBackgroundColor" format="color" />
<attr name="sucLightSystemNavBar" format="boolean" />
+ <!-- The color for the system navigation bar divider. For this to take effect,
+ "android:windowDrawsSystemBarBackgrounds" should be set to true and
+ "android:windowTranslucentNavigation" should be set to false. -->
+ <attr name="sucSystemNavBarDividerColor" format="color" />
</declare-styleable>
<!-- FooterButton attributes -->
diff --git a/partnerconfig/AndroidManifest.xml b/partnerconfig/AndroidManifest.xml
index c95a4dd..43e2041 100644
--- a/partnerconfig/AndroidManifest.xml
+++ b/partnerconfig/AndroidManifest.xml
@@ -16,9 +16,16 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
package="com.google.android.setupcompat.partnerconfig">
- <uses-sdk
- android:minSdkVersion="14"
- android:targetSdkVersion="28" />
+ <uses-sdk />
+
+ <!-- after SDK 30, package need to declare its visible packages. -->
+ <queries tools:node="merge">
+ <intent>
+ <action android:name="com.android.setupwizard.action.PARTNER_CUSTOMIZATION" />
+ </intent>
+ <provider android:authorities="com.google.android.setupwizard.partner" />
+ </queries>
</manifest>
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
index 56256c9..f0baef3 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
@@ -30,6 +30,10 @@
// Navigation bar background color
CONFIG_NAVIGATION_BAR_BG_COLOR(PartnerConfigKey.KEY_NAVIGATION_BAR_BG_COLOR, ResourceType.COLOR),
+ // Navigation bar divider color
+ CONFIG_NAVIGATION_BAR_DIVIDER_COLOR(
+ PartnerConfigKey.KEY_NAVIGATION_BAR_DIVIDER_COLOR, ResourceType.COLOR),
+
// Background color of the footer bar.
CONFIG_FOOTER_BAR_BG_COLOR(PartnerConfigKey.KEY_FOOTER_BAR_BG_COLOR, ResourceType.COLOR),
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
index 7b9f65b..754f462 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
@@ -1,11 +1,11 @@
/*
- * Copyright 2018 The Android Open Source Project
+ * 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
+ * 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,
@@ -16,22 +16,25 @@
package com.google.android.setupcompat.partnerconfig;
+import android.app.UiModeManager;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
+import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.TypedValue;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.TypedValue;
import com.google.android.setupcompat.partnerconfig.PartnerConfig.ResourceType;
import java.util.EnumMap;
@@ -47,6 +50,11 @@
@VisibleForTesting public static final String KEY_FALLBACK_CONFIG = "fallbackConfig";
+ @VisibleForTesting
+ public static final String IS_SUW_DAY_NIGHT_ENABLED_METHOD = "isSuwDayNightEnabled";
+
+ @VisibleForTesting static Bundle suwDayNightEnabledBundle = null;
+
private static PartnerConfigHelper instance = null;
@VisibleForTesting Bundle resultBundle = null;
@@ -54,6 +62,8 @@
@VisibleForTesting
final EnumMap<PartnerConfig, Object> partnerResourceCache = new EnumMap<>(PartnerConfig.class);
+ private static ContentObserver contentObserver;
+
public static synchronized PartnerConfigHelper get(@NonNull Context context) {
if (instance == null) {
instance = new PartnerConfigHelper(context);
@@ -63,6 +73,8 @@
private PartnerConfigHelper(Context context) {
getPartnerConfigBundle(context);
+
+ registerContentObserver(context);
}
/**
@@ -75,12 +87,22 @@
}
/**
+ * Returns whether the given {@code resourceConfig} are available. This is true if setup wizard's
+ * content provider returns us a non-empty bundle, and this result bundle includes the given
+ * {@code resourceConfig} even if all the values are default, and none are customized by the
+ * overlay APK.
+ */
+ public boolean isPartnerConfigAvailable(PartnerConfig resourceConfig) {
+ return isAvailable() && resultBundle.containsKey(resourceConfig.getResourceName());
+ }
+
+ /**
* Returns the color of given {@code resourceConfig}, or 0 if the given {@code resourceConfig} is
* not found. If the {@code ResourceType} of the given {@code resourceConfig} is not color,
* IllegalArgumentException will be thrown.
*
* @param context The context of client activity
- * @param resourceConfig The {@code PartnerConfig} of target resource
+ * @param resourceConfig The {@link PartnerConfig} of target resource
*/
@ColorInt
public int getColor(@NonNull Context context, PartnerConfig resourceConfig) {
@@ -362,17 +384,14 @@
private void getPartnerConfigBundle(Context context) {
if (resultBundle == null || resultBundle.isEmpty()) {
try {
- Uri contentUri =
- new Uri.Builder()
- .scheme(ContentResolver.SCHEME_CONTENT)
- .authority(SUW_AUTHORITY)
- .appendPath(SUW_GET_PARTNER_CONFIG_METHOD)
- .build();
resultBundle =
context
.getContentResolver()
.call(
- contentUri, SUW_GET_PARTNER_CONFIG_METHOD, /* arg= */ null, /* extras= */ null);
+ getContentUri(),
+ SUW_GET_PARTNER_CONFIG_METHOD,
+ /* arg= */ null,
+ /* extras= */ null);
partnerResourceCache.clear();
} catch (IllegalArgumentException | SecurityException exception) {
Log.w(TAG, "Fail to get config from suw provider");
@@ -381,21 +400,88 @@
}
@Nullable
- private ResourceEntry getResourceEntryFromKey(Context context, String resourceName) {
+ @VisibleForTesting
+ ResourceEntry getResourceEntryFromKey(Context context, String resourceName) {
Bundle resourceEntryBundle = resultBundle.getBundle(resourceName);
Bundle fallbackBundle = resultBundle.getBundle(KEY_FALLBACK_CONFIG);
if (fallbackBundle != null) {
resourceEntryBundle.putBundle(KEY_FALLBACK_CONFIG, fallbackBundle.getBundle(resourceName));
}
- return ResourceEntry.fromBundle(context, resourceEntryBundle);
+
+ return adjustResourceEntryDayNightMode(
+ context, ResourceEntry.fromBundle(context, resourceEntryBundle));
+ }
+
+ /**
+ * Force to day mode if setup wizard does not support day/night mode and current system is in
+ * night mode.
+ */
+ private static ResourceEntry adjustResourceEntryDayNightMode(
+ Context context, ResourceEntry resourceEntry) {
+ if (!isSetupWizardDayNightEnabled(context) && isNightMode(context)) {
+ if (resourceEntry == null) {
+ Log.w(TAG, "resourceEntry is null, skip to force day mode.");
+ return resourceEntry;
+ }
+ Resources resource = resourceEntry.getResources();
+ Configuration configuration = resource.getConfiguration();
+ configuration.uiMode =
+ Configuration.UI_MODE_NIGHT_NO
+ | (configuration.uiMode & ~Configuration.UI_MODE_NIGHT_MASK);
+ resource.updateConfiguration(configuration, resource.getDisplayMetrics());
+ }
+
+ return resourceEntry;
+ }
+
+ private static boolean isNightMode(Context context) {
+ UiModeManager uiModeManager = (UiModeManager) context.getSystemService(Context.UI_MODE_SERVICE);
+ return uiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_YES;
}
@VisibleForTesting
- public static synchronized void resetForTesting() {
+ public static synchronized void resetInstance() {
instance = null;
+ suwDayNightEnabledBundle = null;
}
- private TypedValue getTypedValueFromResource(Resources resource, int resId, int type) {
+ /**
+ * Checks whether SetupWizard supports the DayNight theme during setup flow; if return false setup
+ * flow should force to light theme.
+ *
+ * <p>Returns true if the setupwizard is listening to system DayNight theme setting.
+ */
+ public static boolean isSetupWizardDayNightEnabled(@NonNull Context context) {
+ if (suwDayNightEnabledBundle == null) {
+ try {
+ suwDayNightEnabledBundle =
+ context
+ .getContentResolver()
+ .call(
+ getContentUri(),
+ IS_SUW_DAY_NIGHT_ENABLED_METHOD,
+ /* arg= */ null,
+ /* extras= */ null);
+ } catch (IllegalArgumentException | SecurityException exception) {
+ Log.w(TAG, "SetupWizard DayNight supporting status unknown; return as false.");
+ suwDayNightEnabledBundle = null;
+ return false;
+ }
+ }
+
+ return (suwDayNightEnabledBundle != null
+ && suwDayNightEnabledBundle.getBoolean(IS_SUW_DAY_NIGHT_ENABLED_METHOD, false));
+ }
+
+ @VisibleForTesting
+ static Uri getContentUri() {
+ return new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(SUW_AUTHORITY)
+ .build();
+ }
+
+ private static TypedValue getTypedValueFromResource(Resources resource, int resId, int type) {
TypedValue value = new TypedValue();
resource.getValue(resId, value, true);
if (value.type != type) {
@@ -409,8 +495,42 @@
return value;
}
- private float getDimensionFromTypedValue(Context context, TypedValue value) {
+ private static float getDimensionFromTypedValue(Context context, TypedValue value) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
return value.getDimension(displayMetrics);
}
+
+ private static void registerContentObserver(Context context) {
+ if (isSetupWizardDayNightEnabled(context)) {
+ if (contentObserver != null) {
+ unregisterContentObserver(context);
+ }
+
+ Uri contentUri = getContentUri();
+ try {
+ contentObserver =
+ new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ resetInstance();
+ }
+ };
+ context
+ .getContentResolver()
+ .registerContentObserver(contentUri, /* notifyForDescendants= */ true, contentObserver);
+ } catch (SecurityException | NullPointerException | IllegalArgumentException e) {
+ Log.w(TAG, "Failed to register content observer for " + contentUri + ": " + e);
+ }
+ }
+ }
+
+ private static void unregisterContentObserver(Context context) {
+ try {
+ context.getContentResolver().unregisterContentObserver(contentObserver);
+ contentObserver = null;
+ } catch (SecurityException | NullPointerException | IllegalArgumentException e) {
+ Log.w(TAG, "Failed to unregister content observer: " + e);
+ }
+ }
}
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
index e5c5442..840472e 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
@@ -28,6 +28,7 @@
PartnerConfigKey.KEY_LIGHT_STATUS_BAR,
PartnerConfigKey.KEY_NAVIGATION_BAR_BG_COLOR,
PartnerConfigKey.KEY_LIGHT_NAVIGATION_BAR,
+ PartnerConfigKey.KEY_NAVIGATION_BAR_DIVIDER_COLOR,
PartnerConfigKey.KEY_FOOTER_BAR_BG_COLOR,
PartnerConfigKey.KEY_FOOTER_BUTTON_FONT_FAMILY,
PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_ADD_ANOTHER,
@@ -87,6 +88,9 @@
// such that it is compatible with a light navigation bar background.
String KEY_LIGHT_NAVIGATION_BAR = "setup_compat_light_navigation_bar";
+ // Navigation bar divider color
+ String KEY_NAVIGATION_BAR_DIVIDER_COLOR = "setup_compat_navigation_bar_divider_color";
+
// Background color of the footer bar.
String KEY_FOOTER_BAR_BG_COLOR = "setup_compat_footer_bar_bg_color";
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/ResourceEntry.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/ResourceEntry.java
index 8f7c9d8..c8b8623 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/ResourceEntry.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/ResourceEntry.java
@@ -23,10 +23,10 @@
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import android.util.Log;
/**
* A potentially cross-package resource entry, which can then be retrieved using {@link