diff options
author | Xin Li <delphij@google.com> | 2024-03-06 09:29:56 -0800 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2024-03-06 09:29:56 -0800 |
commit | 2ce41c8f4de550b5186233cec0a722dd0ffd9a84 (patch) | |
tree | 6ab948737b4b7e25788b41a782f81baa3161073b | |
parent | 6f1b1d3013fad63f768850953b971579061fca1b (diff) | |
parent | a04310d7b5d43e4b83c08de855ae588e465445d1 (diff) | |
download | setupcompat-2ce41c8f4de550b5186233cec0a722dd0ffd9a84.tar.gz |
Bug: 319669529
Merged-In: I3acad682b4cf6c6ad9bcb2ce91d845b21a9f133a
Change-Id: I20f9bc8a7528573995368985977110f0385e9e9d
20 files changed, 491 insertions, 148 deletions
@@ -96,9 +96,10 @@ android_library { ], 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"], @@ -123,9 +124,10 @@ android_library { ], 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/build.gradle b/build.gradle index 7398305..a52cb6d 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ android { // Not specifying compileSdkVersion here so clients can specify it; must be at least Q defaultConfig { - minSdkVersion 14 + minSdkVersion 19 targetSdkVersion 28 } diff --git a/exempting_lint_checks.txt b/exempting_lint_checks.txt index 4129b20..08ba928 100644 --- a/exempting_lint_checks.txt +++ b/exempting_lint_checks.txt @@ -15,3 +15,14 @@ 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/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) +third_party/java_src/android_libs/setupcompat/AndroidManifest.xml: ExpiringTargetSdkVersion: <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="31" /> +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/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 @@ public class PartnerCustomizationLayout extends TemplateLayout { } @CanIgnoreReturnValue - @TargetApi(VERSION_CODES.HONEYCOMB) public PartnerCustomizationLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs, defStyleAttr); @@ -219,8 +216,7 @@ 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())) { + if (WizardManagerHelper.isAnySetupWizard(activity.getIntent())) { getViewTreeObserver().addOnWindowFocusChangeListener(windowFocusChangeListener); } getMixin(FooterBarMixin.class).onAttachedToWindow(); @@ -258,10 +254,7 @@ 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); - } + getViewTreeObserver().removeOnWindowFocusChangeListener(windowFocusChangeListener); } /** @@ -279,13 +272,7 @@ public class PartnerCustomizationLayout extends TemplateLayout { } 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.Context; 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.PartnerConfig; import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper; import com.google.android.setupcompat.template.FooterButton.ButtonType; import java.util.Locale; -import java.util.concurrent.atomic.AtomicInteger; /** * A {@link Mixin} for managing buttons. By default, the button bar expects that buttons on the @@ -93,8 +91,6 @@ public class FooterBarMixin implements Mixin { 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 @@ public class FooterBarMixin implements Mixin { } @Override - @TargetApi(VERSION_CODES.JELLY_BEAN_MR1) public void onLocaleChanged(Locale locale) { if (buttonContainer != null) { Button button = buttonContainer.findViewById(id); @@ -155,7 +150,6 @@ public class FooterBarMixin implements Mixin { } @Override - @TargetApi(VERSION_CODES.JELLY_BEAN_MR1) public void onDirectionChanged(int direction) { if (buttonContainer != null && direction != -1) { buttonContainer.setLayoutDirection(direction); @@ -281,11 +275,7 @@ public class FooterBarMixin implements Mixin { // 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 @@ public class FooterBarMixin implements Mixin { 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 @@ public class FooterBarMixin implements Mixin { 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 @@ public class FooterBarMixin implements Mixin { && 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 @@ public class FooterBarMixin implements Mixin { } 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 @@ public class FooterButtonStyleUtils { } 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 final class BuildCompatUtils { */ 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 @@ public final class BuildCompatUtils { } 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.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Build; -import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; import android.provider.Settings; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; @@ -151,17 +149,9 @@ public final class WizardManagerHelper { * @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 @@ public final class WizardManagerHelper { * @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 @@ public class ButtonBarLayout extends LinearLayout { 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 @@ public class ButtonBarLayout extends LinearLayout { 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/PartnerConfig.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java index 4f752d7..03e9bb5 100644 --- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java +++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java @@ -206,6 +206,10 @@ public enum PartnerConfig { // Size of the icon CONFIG_ICON_SIZE(PartnerConfigKey.KEY_ICON_SIZE, ResourceType.DIMENSION), + // The max width of the illustration + CONFIG_ILLUSTRATION_MAX_WIDTH( + PartnerConfigKey.KEY_ILLUSTRATION_MAX_WIDTH, ResourceType.DIMENSION), + // Background color of the header area CONFIG_HEADER_AREA_BACKGROUND_COLOR( PartnerConfigKey.KEY_HEADER_AREA_BACKGROUND_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 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.ColorInt; 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 class PartnerConfigHelper { 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 @@ public class PartnerConfigHelper { @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 @@ public class PartnerConfigHelper { 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, 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 adjustResourceEntryDayNightMode(context, adjustResourceEntry); + 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 @@ public class PartnerConfigHelper { // 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 @@ public class PartnerConfigHelper { 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 @@ public class PartnerConfigHelper { applyExtendedPartnerConfigBundle = null; applyMaterialYouConfigBundle = null; applyDynamicColorBundle = null; + applyFullDynamicColorBundle = null; applyNeutralButtonStyleBundle = null; applyEmbeddedActivityOnePaneBundle = null; suwDefaultThemeBundle = null; @@ -836,6 +882,29 @@ public class PartnerConfigHelper { && 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/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java index 1dca0cd..1dac81f 100644 --- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java +++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java @@ -81,6 +81,7 @@ import java.lang.annotation.RetentionPolicy; PartnerConfigKey.KEY_LAYOUT_GRAVITY, PartnerConfigKey.KEY_ICON_MARGIN_TOP, PartnerConfigKey.KEY_ICON_SIZE, + PartnerConfigKey.KEY_ILLUSTRATION_MAX_WIDTH, PartnerConfigKey.KEY_DESCRIPTION_TEXT_SIZE, PartnerConfigKey.KEY_DESCRIPTION_TEXT_COLOR, PartnerConfigKey.KEY_DESCRIPTION_LINK_TEXT_COLOR, @@ -307,6 +308,8 @@ public @interface PartnerConfigKey { // Size of the icon String KEY_ICON_SIZE = "setup_design_icon_size"; + String KEY_ILLUSTRATION_MAX_WIDTH = "setup_design_illustration_max_width"; + // Background color of the header area String KEY_HEADER_AREA_BACKGROUND_COLOR = "setup_design_header_area_background_color"; 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 @@ import com.google.android.setupcompat.portal.TaskComponent; /** * 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 @@ 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 }. + * @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 @@ package com.google.android.setupcompat.portal; /** 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 @@ 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 }. + * + * @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); + } + } +} |