[automerger skipped] Merge Android 12L am: 524748a371 -s ours am: abdf9ae1d1 -s ours am: d49534499b -s ours
am skip reason: Merged-In I41b0f264d344daeb60050231160084c448083f16 with SHA-1 2bac0b4304 is already in history
Original change: https://android-review.googlesource.com/c/platform/external/setupcompat/+/2011692
Change-Id: Ie32a488eae88268af2b5b9c61e9aa8c8db029c9a
diff --git a/main/aidl/com/google/android/setupcompat/ISetupCompatService.aidl b/main/aidl/com/google/android/setupcompat/ISetupCompatService.aidl
index 09f7c9a..e8cb7e5 100644
--- a/main/aidl/com/google/android/setupcompat/ISetupCompatService.aidl
+++ b/main/aidl/com/google/android/setupcompat/ISetupCompatService.aidl
@@ -26,4 +26,6 @@
oneway void validateActivity(String screenName, in Bundle arguments) = 0;
oneway void logMetric(int metricType, in Bundle arguments, in Bundle extras) = 1;
+
+ oneway void onFocusStatusChanged(in Bundle bundle) = 2;
}
diff --git a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
index e5ba0c5..4a88f5b 100644
--- a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
+++ b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
@@ -29,9 +29,13 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.view.WindowManager;
+import androidx.annotation.VisibleForTesting;
+import com.google.android.setupcompat.internal.FocusChangedMetricHelper;
import com.google.android.setupcompat.internal.LifecycleFragment;
import com.google.android.setupcompat.internal.PersistableBundles;
+import com.google.android.setupcompat.internal.SetupCompatServiceInvoker;
import com.google.android.setupcompat.internal.TemplateLayout;
import com.google.android.setupcompat.logging.CustomEvent;
import com.google.android.setupcompat.logging.MetricKey;
@@ -84,6 +88,10 @@
init(null, R.attr.sucLayoutTheme);
}
+ @VisibleForTesting
+ final ViewTreeObserver.OnWindowFocusChangeListener windowFocusChangeListener =
+ this::onFocusChanged;
+
public PartnerCustomizationLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, R.attr.sucLayoutTheme);
@@ -203,14 +211,17 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
LifecycleFragment.attachNow(activity);
+ if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2
+ && WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
+ getViewTreeObserver().addOnWindowFocusChangeListener(windowFocusChangeListener);
+ }
getMixin(FooterBarMixin.class).onAttachedToWindow();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP
- && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
+ if (VERSION.SDK_INT >= Build.VERSION_CODES.Q
&& WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
FooterBarMixin footerBarMixin = getMixin(FooterBarMixin.class);
footerBarMixin.onDetachedFromWindow();
@@ -233,6 +244,10 @@
getContext(),
CustomEvent.create(MetricKey.get("SetupCompatMetrics", activity), persistableBundle));
}
+
+ if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
+ getViewTreeObserver().removeOnWindowFocusChangeListener(windowFocusChangeListener);
+ }
}
public static Activity lookupActivityFromContext(Context context) {
@@ -297,4 +312,16 @@
public boolean useFullDynamicColor() {
return shouldApplyDynamicColor() && useFullDynamicColorAttr;
}
+
+ /**
+ * Invoke the method onFocusStatusChanged when onWindowFocusChangeListener receive onFocusChanged.
+ */
+ private void onFocusChanged(boolean hasFocus) {
+ SetupCompatServiceInvoker.get(getContext())
+ .onFocusStatusChanged(
+ FocusChangedMetricHelper.getScreenName(activity),
+ FocusChangedMetricHelper.getExtraBundle(
+ activity, PartnerCustomizationLayout.this, hasFocus));
+ }
}
+
diff --git a/main/java/com/google/android/setupcompat/internal/ExecutorProvider.java b/main/java/com/google/android/setupcompat/internal/ExecutorProvider.java
index 3c707ae..28ced66 100644
--- a/main/java/com/google/android/setupcompat/internal/ExecutorProvider.java
+++ b/main/java/com/google/android/setupcompat/internal/ExecutorProvider.java
@@ -33,7 +33,6 @@
public final class ExecutorProvider<T extends Executor> {
private static final int SETUP_METRICS_LOGGER_MAX_QUEUED = 50;
- private static final int SETUP_COMPAT_BINDBACK_MAX_QUEUED = 1;
/**
* Creates a single threaded {@link ExecutorService} with a maximum pool size {@code maxSize}.
* Jobs submitted when the pool is full causes {@link
@@ -43,11 +42,6 @@
new ExecutorProvider<>(
createSizeBoundedExecutor("SetupCompatServiceInvoker", SETUP_METRICS_LOGGER_MAX_QUEUED));
- public static final ExecutorProvider<ExecutorService> setupCompatExecutor =
- new ExecutorProvider<>(
- createSizeBoundedExecutor(
- "SetupBindbackServiceExecutor", SETUP_COMPAT_BINDBACK_MAX_QUEUED));
-
private final T executor;
@Nullable private T injectedExecutor;
diff --git a/main/java/com/google/android/setupcompat/internal/FocusChangedMetricHelper.java b/main/java/com/google/android/setupcompat/internal/FocusChangedMetricHelper.java
new file mode 100644
index 0000000..39ade19
--- /dev/null
+++ b/main/java/com/google/android/setupcompat/internal/FocusChangedMetricHelper.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.setupcompat.internal;
+
+import android.app.Activity;
+import android.os.Bundle;
+import androidx.annotation.StringDef;
+import com.google.android.setupcompat.internal.FocusChangedMetricHelper.Constants.ExtraKey;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A help class response to generate extra bundle and capture screen name for interruption metric.
+ */
+public class FocusChangedMetricHelper {
+ private FocusChangedMetricHelper() {}
+
+ public static final String getScreenName(Activity activity) {
+ return activity.getComponentName().toShortString();
+ }
+
+ public static final Bundle getExtraBundle(
+ Activity activity, TemplateLayout layout, boolean hasFocus) {
+ Bundle bundle = new Bundle();
+
+ bundle.putString(ExtraKey.PACKAGE_NAME, activity.getComponentName().getPackageName());
+ bundle.putString(ExtraKey.SCREEN_NAME, activity.getComponentName().getShortClassName());
+ bundle.putInt(ExtraKey.HASH_CODE, layout.hashCode());
+ bundle.putBoolean(ExtraKey.HAS_FOCUS, hasFocus);
+ bundle.putLong(ExtraKey.TIME_IN_MILLIS, System.currentTimeMillis());
+
+ return bundle;
+ }
+
+ /**
+ * Constant values used by {@link
+ * com.google.android.setupcompat.internal.FocusChangedMetricHelper}.
+ */
+ public static final class Constants {
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ ExtraKey.PACKAGE_NAME,
+ ExtraKey.SCREEN_NAME,
+ ExtraKey.HASH_CODE,
+ ExtraKey.HAS_FOCUS,
+ ExtraKey.TIME_IN_MILLIS
+ })
+ public @interface ExtraKey {
+
+ /** This key will be used to save the package name. */
+ String PACKAGE_NAME = "packageName";
+
+ /** This key will be used to save the activity name. */
+ String SCREEN_NAME = "screenName";
+
+ /**
+ * This key will be used to save the has code of {@link
+ * com.google.android.setupcompat.PartnerCustomizationLayout}.
+ */
+ String HASH_CODE = "hash";
+
+ /**
+ * This key will be used to save whether the window which is including the {@link
+ * com.google.android.setupcompat.PartnerCustomizationLayout}. has focus or not.
+ */
+ String HAS_FOCUS = "focus";
+
+ /** This key will be use to save the time stamp in milliseconds. */
+ String TIME_IN_MILLIS = "timeInMillis";
+ }
+
+ private Constants() {}
+ }
+}
diff --git a/main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java b/main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java
index 149da54..ed9c0e3 100644
--- a/main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java
+++ b/main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java
@@ -54,12 +54,23 @@
public void bindBack(String screenName, Bundle bundle) {
try {
- setupCompatExecutor.execute(() -> invokeBindBack(screenName, bundle));
+ loggingExecutor.execute(() -> invokeBindBack(screenName, bundle));
} catch (RejectedExecutionException e) {
LOG.e(String.format("Screen %s bind back fail.", screenName), e);
}
}
+ /**
+ * Help invoke the {@link ISetupCompatService#onFocusStatusChanged} using {@code loggingExecutor}.
+ */
+ public void onFocusStatusChanged(String screenName, Bundle bundle) {
+ try {
+ loggingExecutor.execute(() -> invokeOnWindowFocusChanged(screenName, bundle));
+ } catch (RejectedExecutionException e) {
+ LOG.e(String.format("Screen %s report focus changed failed.", screenName), e);
+ }
+ }
+
private void invokeLogMetric(
@MetricType int metricType, @SuppressWarnings("unused") Bundle args) {
try {
@@ -76,6 +87,29 @@
}
}
+ private void invokeOnWindowFocusChanged(String screenName, Bundle bundle) {
+ try {
+ ISetupCompatService setupCompatService =
+ SetupCompatServiceProvider.get(
+ context, waitTimeInMillisForServiceConnection, TimeUnit.MILLISECONDS);
+ if (setupCompatService != null) {
+ setupCompatService.onFocusStatusChanged(bundle);
+ } else {
+ LOG.w(
+ "Report focusChange failed since service reference is null. Are the permission valid?");
+ }
+ } catch (InterruptedException
+ | TimeoutException
+ | RemoteException
+ | UnsupportedOperationException e) {
+ LOG.e(
+ String.format(
+ "Exception occurred while %s trying report windowFocusChange to SetupWizard.",
+ screenName),
+ e);
+ }
+ }
+
private void invokeBindBack(String screenName, Bundle bundle) {
try {
ISetupCompatService setupCompatService =
@@ -96,14 +130,12 @@
private SetupCompatServiceInvoker(Context context) {
this.context = context;
this.loggingExecutor = ExecutorProvider.setupCompatServiceInvoker.get();
- this.setupCompatExecutor = ExecutorProvider.setupCompatExecutor.get();
this.waitTimeInMillisForServiceConnection = MAX_WAIT_TIME_FOR_CONNECTION_MS;
}
private final Context context;
private final ExecutorService loggingExecutor;
- private final ExecutorService setupCompatExecutor;
private final long waitTimeInMillisForServiceConnection;
public static synchronized SetupCompatServiceInvoker get(Context context) {
diff --git a/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java b/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java
index ef2aa6b..476d45b 100644
--- a/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java
+++ b/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java
@@ -35,7 +35,6 @@
import android.view.ViewGroup;
import android.widget.Button;
import androidx.annotation.ColorInt;
-import androidx.annotation.VisibleForTesting;
import com.google.android.setupcompat.R;
import com.google.android.setupcompat.internal.FooterButtonPartnerConfig;
import com.google.android.setupcompat.internal.Preconditions;
@@ -429,7 +428,7 @@
defaultTextColor.clear();
}
- @VisibleForTesting
+ /** Gets {@code GradientDrawable} from given {@code button}. */
public static GradientDrawable getGradientDrawable(Button button) {
// RippleDrawable is available after sdk 21, InsetDrawable#getDrawable is available after
// sdk 19. So check the sdk is higher than sdk 21 and since Stencil customization provider only
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
index 3525fa1..3c35d29 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
@@ -60,6 +60,9 @@
"isExtendedPartnerConfigEnabled";
@VisibleForTesting
+ public static final String IS_MATERIAL_YOU_STYLE_ENABLED_METHOD = "IsMaterialYouStyleEnabled";
+
+ @VisibleForTesting
public static final String IS_DYNAMIC_COLOR_ENABLED_METHOD = "isDynamicColorEnabled";
@VisibleForTesting
@@ -69,6 +72,8 @@
@VisibleForTesting public static Bundle applyExtendedPartnerConfigBundle = null;
+ @VisibleForTesting public static Bundle applyMaterialYouConfigBundle = null;
+
@VisibleForTesting public static Bundle applyDynamicColorBundle = null;
@VisibleForTesting public static Bundle applyNeutralButtonStyleBundle = null;
@@ -570,6 +575,7 @@
instance = null;
suwDayNightEnabledBundle = null;
applyExtendedPartnerConfigBundle = null;
+ applyMaterialYouConfigBundle = null;
applyDynamicColorBundle = null;
applyNeutralButtonStyleBundle = null;
}
@@ -628,6 +634,29 @@
IS_EXTENDED_PARTNER_CONFIG_ENABLED_METHOD, false));
}
+ /** Returns true if the SetupWizard is flow enabled "Material You(Glifv4)" style. */
+ public static boolean shouldApplyMaterialYouStyle(@NonNull Context context) {
+ if (applyMaterialYouConfigBundle == null) {
+ try {
+ applyMaterialYouConfigBundle =
+ context
+ .getContentResolver()
+ .call(
+ getContentUri(),
+ IS_MATERIAL_YOU_STYLE_ENABLED_METHOD,
+ /* arg= */ null,
+ /* extras= */ null);
+ } catch (IllegalArgumentException | SecurityException exception) {
+ Log.w(TAG, "SetupWizard Material You configs supporting status unknown; return as false.");
+ applyMaterialYouConfigBundle = null;
+ return false;
+ }
+ }
+
+ return (applyMaterialYouConfigBundle != null
+ && applyMaterialYouConfigBundle.getBoolean(IS_MATERIAL_YOU_STYLE_ENABLED_METHOD, false));
+ }
+
/** Returns true if the SetupWizard supports the dynamic color during setup flow. */
public static boolean isSetupWizardDynamicColorEnabled(@NonNull Context context) {
if (applyDynamicColorBundle == null) {