Merge "Import updated Android SetupCompat Library 573702360" into main
diff --git a/Android.bp b/Android.bp
index effa39b..ce42ba2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -96,9 +96,10 @@
],
static_libs: [
"androidx.annotation_annotation",
+ "androidx.window_window",
"error_prone_annotations",
],
- min_sdk_version: "14",
+ min_sdk_version: "19",
sdk_version: "current",
optimize: {
proguard_flags_files: ["proguard.flags"],
@@ -120,9 +121,10 @@
],
static_libs: [
"androidx.annotation_annotation",
+ "androidx.window_window",
"error_prone_annotations",
],
- min_sdk_version: "14",
+ min_sdk_version: "19",
sdk_version: "current",
optimize: {
proguard_flags_files: ["proguard.flags"],
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index cae5fe3..ccd7122 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -15,7 +15,9 @@
limitations under the License.
-->
-<manifest package="com.google.android.setupcompat">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.setupcompat" >
<!-- Set in the BUILD or gradle file -->
- <uses-sdk />
+ <uses-sdk
+ android:minSdkVersion="19" />
</manifest>
diff --git a/build.gradle b/build.gradle
index 7398305..a52cb6d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -19,7 +19,7 @@
// Not specifying compileSdkVersion here so clients can specify it; must be at least Q
defaultConfig {
- minSdkVersion 14
+ minSdkVersion 19
targetSdkVersion 28
}
diff --git a/main/aidl/com/google/android/setupcompat/portal/v1_1/IPortalProgressCallback.aidl b/main/aidl/com/google/android/setupcompat/portal/v1_1/IPortalProgressCallback.aidl
new file mode 100644
index 0000000..2ed44b9
--- /dev/null
+++ b/main/aidl/com/google/android/setupcompat/portal/v1_1/IPortalProgressCallback.aidl
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 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.v1_1;
+
+import android.os.Bundle;
+
+/**
+ * Interface for progress service to update progress to SUW. Clients should
+ * update progress at least once a minute, or set a pending reason to stop
+ * update progress. Without progress update and pending reason. We considering
+ * the progress service is no response will suspend it and unbinde service.
+ */
+interface IPortalProgressCallback {
+ /**
+ * Sets completed amount.
+ */
+ Bundle setProgressCount(int completed, int failed, int total) = 0;
+
+ /**
+ * Sets completed percentage.
+ */
+ Bundle setProgressPercentage(int percentage) = 1;
+
+ /**
+ * Sets the summary shows on portal activity.
+ */
+ Bundle setSummary(String summary) = 2;
+
+ /**
+ * Let SUW knows the progress is pending now, and stop update progress.
+ * @param reasonResName The name of resource identifier.
+ * @param quantity The number used to get the correct string for the current language's
+ * plural rules
+ * @param formatArgs The format arguments that will be used for substitution.
+ */
+ Bundle setPendingReason(String reasonResName, int quantity, in int[] formatArgs, int reason) = 3;
+
+ /**
+ * Once the progress completed, and service can be destroy. Call this function.
+ * SUW will unbind the service.
+ * @param resName The name of resource identifier.
+ * @param quantity The number used to get the correct string for the current language's
+ * plural rules
+ * @param formatArgs The format arguments that will be used for substitution.
+ */
+ Bundle setComplete(String resName, int quantity, in int[] formatArgs) = 4;
+
+ /**
+ * Once the progress failed, and not able to finish the progress. Should call
+ * this function. SUW will unbind service after receive setFailure. Client can
+ * registerProgressService again once the service is ready to execute.
+ * @param resName The name of resource identifier.
+ * @param quantity The number used to get the correct string for the current language's
+ * plural rules
+ * @param formatArgs The format arguments that will be used for substitution.
+ */
+ Bundle setFailure(String resName, int quantity, in int[] formatArgs) = 5;
+}
diff --git a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
index 21928c8..70b28d9 100644
--- a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
+++ b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
@@ -16,10 +16,8 @@
package com.google.android.setupcompat;
-import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Build.VERSION;
@@ -101,7 +99,6 @@
}
@CanIgnoreReturnValue
- @TargetApi(VERSION_CODES.HONEYCOMB)
public PartnerCustomizationLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs, defStyleAttr);
@@ -219,8 +216,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
LifecycleFragment.attachNow(activity);
- if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2
- && WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
+ if (WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
getViewTreeObserver().addOnWindowFocusChangeListener(windowFocusChangeListener);
}
getMixin(FooterBarMixin.class).onAttachedToWindow();
@@ -258,10 +254,7 @@
getContext(),
CustomEvent.create(MetricKey.get("SetupCompatMetrics", activity), persistableBundle));
}
-
- if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
- getViewTreeObserver().removeOnWindowFocusChangeListener(windowFocusChangeListener);
- }
+ getViewTreeObserver().removeOnWindowFocusChangeListener(windowFocusChangeListener);
}
/**
@@ -279,13 +272,7 @@
}
public static Activity lookupActivityFromContext(Context context) {
- if (context instanceof Activity) {
- return (Activity) context;
- } else if (context instanceof ContextWrapper) {
- return lookupActivityFromContext(((ContextWrapper) context).getBaseContext());
- } else {
- throw new IllegalArgumentException("Cannot find instance of Activity in parent tree");
- }
+ return PartnerConfigHelper.lookupActivityFromContext(context);
}
/**
diff --git a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
index 2c8cb8c..b417857 100644
--- a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
@@ -25,7 +25,6 @@
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Color;
-import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.PersistableBundle;
import android.util.AttributeSet;
@@ -57,7 +56,6 @@
import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
import com.google.android.setupcompat.template.FooterButton.ButtonType;
import java.util.Locale;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* A {@link Mixin} for managing buttons. By default, the button bar expects that buttons on the
@@ -93,8 +91,6 @@
private boolean removeFooterBarWhenEmpty = true;
private boolean isSecondaryButtonInPrimaryStyle = false;
- private static final AtomicInteger nextGeneratedId = new AtomicInteger(1);
-
@VisibleForTesting public final FooterBarMixinMetrics metrics = new FooterBarMixinMetrics();
private FooterButton.OnButtonEventListener createButtonEventListener(@IdRes int id) {
@@ -144,7 +140,6 @@
}
@Override
- @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
public void onLocaleChanged(Locale locale) {
if (buttonContainer != null) {
Button button = buttonContainer.findViewById(id);
@@ -155,7 +150,6 @@
}
@Override
- @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
public void onDirectionChanged(int direction) {
if (buttonContainer != null && direction != -1) {
buttonContainer.setLayoutDirection(direction);
@@ -281,11 +275,7 @@
// Ignore action since buttonContainer is null
return;
}
- if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
- buttonContainer.setId(View.generateViewId());
- } else {
- buttonContainer.setId(generateViewId());
- }
+ buttonContainer.setId(View.generateViewId());
updateFooterBarPadding(
buttonContainer,
footerBarPaddingStart,
@@ -430,7 +420,13 @@
return primaryButton;
}
- @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ /**
+ * Returns the {@link Button} of primary button.
+ *
+ * @apiNote It is not recommended to apply style to the view directly. The setup library will
+ * handle the button style. There is no guarantee that changes made directly to the button
+ * style will not cause unexpected behavior.
+ */
public Button getPrimaryButtonView() {
return buttonContainer == null ? null : buttonContainer.findViewById(primaryButtonId);
}
@@ -667,7 +663,13 @@
return buttonContainer.getVisibility();
}
- @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ /**
+ * Returns the {@link Button} of secondary button.
+ *
+ * @apiNote It is not recommended to apply style to the view directly. The setup library will
+ * handle the button style. There is no guarantee that changes made directly to the button
+ * style will not cause unexpected behavior.
+ */
public Button getSecondaryButtonView() {
return buttonContainer == null ? null : buttonContainer.findViewById(secondaryButtonId);
}
@@ -678,29 +680,11 @@
&& getSecondaryButtonView().getVisibility() == View.VISIBLE;
}
- private static int generateViewId() {
- for (; ; ) {
- final int result = nextGeneratedId.get();
- // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
- int newValue = result + 1;
- if (newValue > 0x00FFFFFF) {
- newValue = 1; // Roll over to 1, not 0.
- }
- if (nextGeneratedId.compareAndSet(result, newValue)) {
- return result;
- }
- }
- }
-
private FooterActionButton inflateButton(
FooterButton footerButton, FooterButtonPartnerConfig footerButtonPartnerConfig) {
FooterActionButton button =
createThemedButton(context, footerButtonPartnerConfig.getPartnerTheme());
- if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
- button.setId(View.generateViewId());
- } else {
- button.setId(generateViewId());
- }
+ button.setId(View.generateViewId());
// apply initial configuration into button view.
button.setText(footerButton.getText());
@@ -784,12 +768,11 @@
}
protected View inflateFooter(@LayoutRes int footer) {
- if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
- LayoutInflater inflater =
- LayoutInflater.from(
- new ContextThemeWrapper(context, R.style.SucPartnerCustomizationButtonBar_Stackable));
- footerStub.setLayoutInflater(inflater);
- }
+ LayoutInflater inflater =
+ LayoutInflater.from(
+ new ContextThemeWrapper(context, R.style.SucPartnerCustomizationButtonBar_Stackable));
+ footerStub.setLayoutInflater(inflater);
+
footerStub.setLayoutResource(footer);
return footerStub.inflate();
}
diff --git a/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java b/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java
index c4818d9..fc56aad 100644
--- a/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java
+++ b/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java
@@ -426,11 +426,7 @@
} else {
iconStart = icon;
}
- if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
- button.setCompoundDrawablesRelative(iconStart, null, iconEnd, null);
- } else {
- button.setCompoundDrawables(iconStart, null, iconEnd, null);
- }
+ button.setCompoundDrawablesRelative(iconStart, null, iconEnd, null);
}
static void updateButtonBackground(Button button, @ColorInt int color) {
diff --git a/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java b/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java
index cccc413..55f3ad6 100644
--- a/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java
+++ b/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java
@@ -84,7 +84,7 @@
*/
public static boolean isAtLeastU() {
return (Build.VERSION.CODENAME.equals("REL") && Build.VERSION.SDK_INT >= 34)
- || isAtLeastPreReleaseCodename("UpsideDownCake");
+ || isAtLeastPreReleaseCodename("UpsideDownCake");
}
private static boolean isAtLeastPreReleaseCodename(String codename) {
@@ -99,4 +99,4 @@
}
private BuildCompatUtils() {}
-}
+}
\ No newline at end of file
diff --git a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
index f28cd6d..60b2e21 100644
--- a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
+++ b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
@@ -20,8 +20,6 @@
import android.content.Context;
import android.content.Intent;
import android.os.Build;
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
import android.provider.Settings;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -151,17 +149,9 @@
* @see #isDeviceProvisioned(Context)
*/
public static boolean isUserSetupComplete(Context context) {
- if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
- return Settings.Secure.getInt(
- context.getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0)
- == 1;
- } else {
- // For versions below JB MR1, there are no user profiles. Just return the global device
- // provisioned state.
- return Settings.Secure.getInt(
- context.getContentResolver(), SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0)
- == 1;
- }
+ return Settings.Secure.getInt(
+ context.getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0)
+ == 1;
}
/**
@@ -174,15 +164,9 @@
* @see #isUserSetupComplete(Context)
*/
public static boolean isDeviceProvisioned(Context context) {
- if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
- return Settings.Global.getInt(
- context.getContentResolver(), SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0)
- == 1;
- } else {
- return Settings.Secure.getInt(
- context.getContentResolver(), SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0)
- == 1;
- }
+ return Settings.Global.getInt(
+ context.getContentResolver(), SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0)
+ == 1;
}
/**
diff --git a/main/java/com/google/android/setupcompat/view/ButtonBarLayout.java b/main/java/com/google/android/setupcompat/view/ButtonBarLayout.java
index 1ef7b39..7500f26 100644
--- a/main/java/com/google/android/setupcompat/view/ButtonBarLayout.java
+++ b/main/java/com/google/android/setupcompat/view/ButtonBarLayout.java
@@ -70,7 +70,8 @@
super.onMeasure(initialWidthMeasureSpec, heightMeasureSpec);
- if (!isFooterButtonsEventlyWeighted(getContext()) && (getMeasuredWidth() > widthSize)) {
+ final boolean childrenLargerThanContainer = (widthSize > 0) && (getMeasuredWidth() > widthSize);
+ if (!isFooterButtonsEvenlyWeighted(getContext()) && childrenLargerThanContainer) {
setStacked(true);
// Measure again in the new orientation.
@@ -193,18 +194,18 @@
return childFooterButtons;
}
- private boolean isFooterButtonsEventlyWeighted(Context context) {
+ private boolean isFooterButtonsEvenlyWeighted(Context context) {
int childCount = getChildCount();
- int primayButtonCount = 0;
+ int primaryButtonCount = 0;
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child instanceof FooterActionButton) {
if (((FooterActionButton) child).isPrimaryButtonStyle()) {
- primayButtonCount += 1;
+ primaryButtonCount += 1;
}
}
}
- if (primayButtonCount != 2) {
+ if (primaryButtonCount != 2) {
return false;
}
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
index e08cfa6..e87d998 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
@@ -16,8 +16,12 @@
package com.google.android.setupcompat.partnerconfig;
+import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
@@ -34,12 +38,14 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import androidx.window.embedding.ActivityEmbeddingController;
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;
import java.util.List;
+import java.util.Objects;
/** The helper reads and caches the partner configurations from SUW. */
public class PartnerConfigHelper {
@@ -66,6 +72,9 @@
public static final String IS_DYNAMIC_COLOR_ENABLED_METHOD = "isDynamicColorEnabled";
@VisibleForTesting
+ public static final String IS_FULL_DYNAMIC_COLOR_ENABLED_METHOD = "isFullDynamicColorEnabled";
+
+ @VisibleForTesting
public static final String IS_NEUTRAL_BUTTON_STYLE_ENABLED_METHOD = "isNeutralButtonStyleEnabled";
@VisibleForTesting
@@ -91,6 +100,7 @@
@VisibleForTesting public static Bundle applyMaterialYouConfigBundle = null;
@VisibleForTesting public static Bundle applyDynamicColorBundle = null;
+ @VisibleForTesting public static Bundle applyFullDynamicColorBundle = null;
@VisibleForTesting public static Bundle applyNeutralButtonStyleBundle = null;
@@ -578,15 +588,40 @@
if (fallbackBundle != null) {
resourceEntryBundle.putBundle(KEY_FALLBACK_CONFIG, fallbackBundle.getBundle(resourceName));
}
- ResourceEntry adjustResourceEntry =
- adjustResourceEntryDefaultValue(
- context, ResourceEntry.fromBundle(context, resourceEntryBundle));
- ;
- if (BuildCompatUtils.isAtLeastU() && isEmbeddedActivityOnePaneEnabled(context)) {
- adjustResourceEntry = embeddedActivityResourceEntryDefaultValue(context, adjustResourceEntry);
+
+ ResourceEntry resourceEntry = ResourceEntry.fromBundle(context, resourceEntryBundle);
+
+ if (BuildCompatUtils.isAtLeastU() && isActivityEmbedded(context)) {
+ resourceEntry = adjustEmbeddedActivityResourceEntryDefaultValue(context, resourceEntry);
+ } else if (BuildCompatUtils.isAtLeastT() && shouldApplyMaterialYouStyle(context)) {
+ resourceEntry = adjustMaterialYouResourceEntryDefaultValue(context, resourceEntry);
}
- return adjustResourceEntryDayNightMode(context, adjustResourceEntry);
+ return adjustResourceEntryDayNightMode(context, resourceEntry);
+ }
+
+ @VisibleForTesting
+ boolean isActivityEmbedded(Context context) {
+ Activity activity;
+ try {
+ activity = lookupActivityFromContext(context);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Not a Activity instance in parent tree");
+ return false;
+ }
+
+ return isEmbeddedActivityOnePaneEnabled(context)
+ && ActivityEmbeddingController.getInstance(activity).isActivityEmbedded(activity);
+ }
+
+ public static Activity lookupActivityFromContext(Context context) {
+ if (context instanceof Activity) {
+ return (Activity) context;
+ } else if (context instanceof ContextWrapper) {
+ return lookupActivityFromContext(((ContextWrapper) context).getBaseContext());
+ } else {
+ throw new IllegalArgumentException("Cannot find instance of Activity in parent tree");
+ }
}
/**
@@ -614,68 +649,29 @@
// 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;
- }
-
- // Check the embedded activity flag and replace the inputResourceEntry.resourceName &
- // inputResourceEntry.resourceId after U.
- ResourceEntry embeddedActivityResourceEntryDefaultValue(
+ ResourceEntry adjustMaterialYouResourceEntryDefaultValue(
Context context, ResourceEntry inputResourceEntry) {
// If not overlay resource
try {
- if (SUW_PACKAGE_NAME.equals(inputResourceEntry.getPackageName())) {
+ if (Objects.equals(inputResourceEntry.getPackageName(), SUW_PACKAGE_NAME)) {
String resourceTypeName =
inputResourceEntry
.getResources()
.getResourceTypeName(inputResourceEntry.getResourceId());
// try to update resourceName & resourceId
- String embeddedActivityResourceName =
- inputResourceEntry.getResourceName().concat(EMBEDDED_ACTIVITY_RESOURCE_SUFFIX);
- int embeddedActivityResourceId =
+ String materialYouResourceName =
+ inputResourceEntry.getResourceName().concat(MATERIAL_YOU_RESOURCE_SUFFIX);
+ int materialYouResourceId =
inputResourceEntry
.getResources()
.getIdentifier(
- embeddedActivityResourceName,
- resourceTypeName,
- inputResourceEntry.getPackageName());
- if (embeddedActivityResourceId != 0) {
- Log.i(TAG, "use embedded activity resource:" + embeddedActivityResourceName);
+ materialYouResourceName, resourceTypeName, inputResourceEntry.getPackageName());
+ if (materialYouResourceId != 0) {
+ Log.i(TAG, "use material you resource:" + materialYouResourceName);
return new ResourceEntry(
inputResourceEntry.getPackageName(),
- embeddedActivityResourceName,
- embeddedActivityResourceId,
+ materialYouResourceName,
+ materialYouResourceId,
inputResourceEntry.getResources());
}
}
@@ -685,6 +681,55 @@
return inputResourceEntry;
}
+ // Check the embedded activity flag and replace the inputResourceEntry.resourceName &
+ // inputResourceEntry.resourceId, and try to find the embedded resource from the different
+ // package.
+ ResourceEntry adjustEmbeddedActivityResourceEntryDefaultValue(
+ Context context, ResourceEntry inputResourceEntry) {
+ // If not overlay resource
+ try {
+ String resourceTypeName =
+ inputResourceEntry.getResources().getResourceTypeName(inputResourceEntry.getResourceId());
+ // For the first time to get embedded activity resource id, it may get from setup wizard
+ // package or Overlay package.
+ String embeddedActivityResourceName =
+ inputResourceEntry.getResourceName().concat(EMBEDDED_ACTIVITY_RESOURCE_SUFFIX);
+ int embeddedActivityResourceId =
+ inputResourceEntry
+ .getResources()
+ .getIdentifier(
+ embeddedActivityResourceName,
+ resourceTypeName,
+ inputResourceEntry.getPackageName());
+ if (embeddedActivityResourceId != 0) {
+ Log.i(TAG, "use embedded activity resource:" + embeddedActivityResourceName);
+ return new ResourceEntry(
+ inputResourceEntry.getPackageName(),
+ embeddedActivityResourceName,
+ embeddedActivityResourceId,
+ inputResourceEntry.getResources());
+ } else {
+ // If resource id is not available from the Overlay package, try to get it from setup wizard
+ // package.
+ PackageManager manager = context.getPackageManager();
+ Resources resources = manager.getResourcesForApplication(SUW_PACKAGE_NAME);
+ embeddedActivityResourceId =
+ resources.getIdentifier(
+ embeddedActivityResourceName, resourceTypeName, SUW_PACKAGE_NAME);
+ if (embeddedActivityResourceId != 0) {
+ return new ResourceEntry(
+ SUW_PACKAGE_NAME,
+ embeddedActivityResourceName,
+ embeddedActivityResourceId,
+ resources);
+ }
+ }
+ } catch (NotFoundException | NameNotFoundException ex) {
+ // fall through
+ }
+ return inputResourceEntry;
+ }
+
@VisibleForTesting
public static synchronized void resetInstance() {
instance = null;
@@ -692,6 +737,7 @@
applyExtendedPartnerConfigBundle = null;
applyMaterialYouConfigBundle = null;
applyDynamicColorBundle = null;
+ applyFullDynamicColorBundle = null;
applyNeutralButtonStyleBundle = null;
applyEmbeddedActivityOnePaneBundle = null;
suwDefaultThemeBundle = null;
@@ -836,6 +882,29 @@
&& applyDynamicColorBundle.getBoolean(IS_DYNAMIC_COLOR_ENABLED_METHOD, false));
}
+ /** Returns {@code true} if the SetupWizard supports the full dynamic color during setup flow. */
+ public static boolean isSetupWizardFullDynamicColorEnabled(@NonNull Context context) {
+ if (applyFullDynamicColorBundle == null) {
+ try {
+ applyFullDynamicColorBundle =
+ context
+ .getContentResolver()
+ .call(
+ getContentUri(),
+ IS_FULL_DYNAMIC_COLOR_ENABLED_METHOD,
+ /* arg= */ null,
+ /* extras= */ null);
+ } catch (IllegalArgumentException | SecurityException exception) {
+ Log.w(TAG, "SetupWizard full dynamic color supporting status unknown; return as false.");
+ applyFullDynamicColorBundle = null;
+ return false;
+ }
+ }
+
+ return (applyFullDynamicColorBundle != null
+ && applyFullDynamicColorBundle.getBoolean(IS_FULL_DYNAMIC_COLOR_ENABLED_METHOD, false));
+ }
+
/** Returns true if the SetupWizard supports the one-pane embedded activity during setup flow. */
public static boolean isEmbeddedActivityOnePaneEnabled(@NonNull Context context) {
if (applyEmbeddedActivityOnePaneBundle == null) {
diff --git a/portal_extension/aidl/com/google/android/setupcompat/portal/ISetupNotificationServicePortalExtension.aidl b/portal_extension/aidl/com/google/android/setupcompat/portal/ISetupNotificationServicePortalExtension.aidl
index 2b83576..ec1da2e 100644
--- a/portal_extension/aidl/com/google/android/setupcompat/portal/ISetupNotificationServicePortalExtension.aidl
+++ b/portal_extension/aidl/com/google/android/setupcompat/portal/ISetupNotificationServicePortalExtension.aidl
@@ -21,6 +21,7 @@
/**
* Declares the interface for portal used by GmsCore.
+ * @deprecated, use {@link com.google.android.setupcompat.portal.v1_1.ISetupNotificationServicePortalExtension}.
*/
interface ISetupNotificationServicePortalExtension {
IPortalProgressCallback registerTask(in TaskComponent component) = 1;
diff --git a/portal_extension/aidl/com/google/android/setupcompat/portal/TaskComponent.aidl b/portal_extension/aidl/com/google/android/setupcompat/portal/TaskComponent.aidl
index 8a4dfb3..cdc0a9b 100644
--- a/portal_extension/aidl/com/google/android/setupcompat/portal/TaskComponent.aidl
+++ b/portal_extension/aidl/com/google/android/setupcompat/portal/TaskComponent.aidl
@@ -19,5 +19,6 @@
/**
* A class that represents how a persistent notification is to be presented to the user using the
* {@link com.google.android.setupcompat.portal.ISetupNotificationServicePortalExtension }.
+ * @Deprecated, use {@link com.google.android.setupcompat.portal.v1_1.TaskkComponent}.
*/
parcelable TaskComponent;
\ No newline at end of file
diff --git a/portal_extension/aidl/com/google/android/setupcompat/portal/v1_1/ISetupNotificationServicePortalExtension.aidl b/portal_extension/aidl/com/google/android/setupcompat/portal/v1_1/ISetupNotificationServicePortalExtension.aidl
new file mode 100644
index 0000000..c8b9dd4
--- /dev/null
+++ b/portal_extension/aidl/com/google/android/setupcompat/portal/v1_1/ISetupNotificationServicePortalExtension.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 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.v1_1;
+
+import com.google.android.setupcompat.portal.v1_1.IPortalProgressCallback;
+import com.google.android.setupcompat.portal.v1_1.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/v1_1/TaskComponent.aidl b/portal_extension/aidl/com/google/android/setupcompat/portal/v1_1/TaskComponent.aidl
new file mode 100644
index 0000000..c89173b
--- /dev/null
+++ b/portal_extension/aidl/com/google/android/setupcompat/portal/v1_1/TaskComponent.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 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.v1_1;
+
+/**
+ * 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
index 4f1b884..e0a51d2 100644
--- a/portal_extension/java/com/google/android/setupcompat/portal/PortalExtensionConstants.java
+++ b/portal_extension/java/com/google/android/setupcompat/portal/PortalExtensionConstants.java
@@ -18,9 +18,19 @@
/** Constant values used for PortalExtension */
public class PortalExtensionConstants {
+ /**
+ * Intent action to bind Portal Service.
+ * @deprecated, use {@code BIND_SERVICE_V_1_1_INTENT_ACTION}.
+ * */
+ @Deprecated
public static final String BIND_SERVICE_INTENT_ACTION =
"com.google.android.setupcompat.portal.SetupNotificationService.BIND_EXTENSION";
+ /**
+ * Intent action to bind Portal Service.
+ */
+ public static final String BIND_SERVICE_V_1_1_INTENT_ACTION =
+ "com.google.android.setupcompat.portal.SetupNotificationService.BIND_EXTENSION_V_1_1";
+
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
index 16d35a3..16289e0 100644
--- a/portal_extension/java/com/google/android/setupcompat/portal/TaskComponent.java
+++ b/portal_extension/java/com/google/android/setupcompat/portal/TaskComponent.java
@@ -27,7 +27,10 @@
/**
* A class that represents how a persistent notification is to be presented to the user using the
* {@link com.google.android.setupcompat.portal.ISetupNotificationServicePortalExtension }.
+ *
+ * @deprecated use {@link com.google.android.setupcompat.portal.v1_1.TaskComponent}.
*/
+@Deprecated
public class TaskComponent implements Parcelable {
private final String packageName;
private final String taskName;
diff --git a/portal_extension/java/com/google/android/setupcompat/portal/v1_1/TaskComponent.java b/portal_extension/java/com/google/android/setupcompat/portal/v1_1/TaskComponent.java
new file mode 100644
index 0000000..6cb9037
--- /dev/null
+++ b/portal_extension/java/com/google/android/setupcompat/portal/v1_1/TaskComponent.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2023 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.v1_1;
+
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+import androidx.annotation.NonNull;
+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;
+ private final String displayNameResName;
+ private final String displayIconResName;
+ private final Intent itemClickIntent;
+
+ private TaskComponent(
+ String packageName,
+ String taskName,
+ String displayNameResName,
+ String displayIconResName,
+ Intent itemClickIntent) {
+ this.packageName = packageName;
+ this.taskName = taskName;
+ this.displayNameResName = displayNameResName;
+ this.displayIconResName = displayIconResName;
+ 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 name of display name. */
+ @NonNull
+ public String getDisplayName() {
+ return displayNameResName;
+ }
+
+ /** Returns the drawable resource name of display icon. */
+ @NonNull
+ public String getDisplayIcon() {
+ return displayIconResName;
+ }
+
+ /** 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.writeString(getDisplayName());
+ dest.writeString(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.readString())
+ .setDisplayIcon(in.readString())
+ .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;
+ private String displayNameResName;
+ private String displayIconResName;
+ 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(String displayNameResName) {
+ this.displayNameResName = displayNameResName;
+ return this;
+ }
+
+ /** Sets the icon which is display on PortalActivity */
+ public Builder setDisplayIcon(String displayIconResName) {
+ this.displayIconResName = displayIconResName;
+ 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.checkNotNull(displayNameResName, "Display name cannot be null");
+ Preconditions.checkNotNull(displayIconResName, "Display icon cannot be null");
+
+ return new TaskComponent(
+ packageName, taskName, displayNameResName, displayIconResName, itemClickIntent);
+ }
+ }
+}