summaryrefslogtreecommitdiff
path: root/main/java/com/google/android/setupcompat
diff options
context:
space:
mode:
authorSetup Wizard Team <android-setup-team-eng@google.com>2019-03-14 10:00:10 +0800
committerpastychang <pastychang@google.com>2019-03-15 10:37:13 +0800
commit1ed3073417396ad4d3bec9d6a5722b490a49e47e (patch)
tree6be9a1c1b6f1d6bbba588ccc23cbd069bad56622 /main/java/com/google/android/setupcompat
parentb5345f3d3b611fcf3513c613d1663f424414a12f (diff)
downloadsetupcompat-1ed3073417396ad4d3bec9d6a5722b490a49e47e.tar.gz
Import updated Android SetupCompat Library 238357591
Test: mm PiperOrigin-RevId: 238357591 Change-Id: Ifa32c7db8d5383f076582aeb7b44f42499a1217a
Diffstat (limited to 'main/java/com/google/android/setupcompat')
-rw-r--r--main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java49
-rw-r--r--main/java/com/google/android/setupcompat/lifecycle/LifecycleFragment.java4
-rw-r--r--main/java/com/google/android/setupcompat/logging/internal/FooterBarMixinMetrics.java29
-rw-r--r--main/java/com/google/android/setupcompat/template/FooterBarMixin.java35
-rw-r--r--main/java/com/google/android/setupcompat/template/FooterButton.java3
-rw-r--r--main/java/com/google/android/setupcompat/template/HeaderMixin.java118
-rw-r--r--main/java/com/google/android/setupcompat/template/IconMixin.java109
-rw-r--r--main/java/com/google/android/setupcompat/template/PartnerConfig.java137
-rw-r--r--main/java/com/google/android/setupcompat/template/PartnerConfigHelper.java327
-rw-r--r--main/java/com/google/android/setupcompat/template/PartnerConfigKey.java125
-rw-r--r--main/java/com/google/android/setupcompat/template/StatusBarMixin.java2
-rw-r--r--main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java137
-rw-r--r--main/java/com/google/android/setupcompat/util/ResourceEntry.java86
-rw-r--r--main/java/com/google/android/setupcompat/util/SystemBarBaseHelper.java160
14 files changed, 348 insertions, 973 deletions
diff --git a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
index cef0aee..faeda2c 100644
--- a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
+++ b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
@@ -21,9 +21,10 @@ 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;
import android.os.Build.VERSION_CODES;
import android.os.PersistableBundle;
+import androidx.annotation.ColorInt;
import androidx.annotation.LayoutRes;
import android.util.AttributeSet;
import android.util.Log;
@@ -38,6 +39,8 @@ import com.google.android.setupcompat.lifecycle.LifecycleFragment;
import com.google.android.setupcompat.logging.CustomEvent;
import com.google.android.setupcompat.logging.MetricKey;
import com.google.android.setupcompat.logging.SetupMetricsLogger;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupcompat.template.StatusBarMixin;
@@ -49,7 +52,9 @@ public class PartnerCustomizationLayout extends TemplateLayout {
// Log tags can have at most 23 characters on N or before.
private static final String TAG = "PartnerCustomizedLayout";
- private final boolean suwVersionSupportPartnerResource = Build.VERSION.SDK_INT > VERSION_CODES.P;
+ private final boolean suwVersionSupportPartnerResource = isAtLeastQ();
+
+ private boolean applyPartnerResource;
private Activity activity;
public PartnerCustomizationLayout(Context context) {
@@ -93,7 +98,7 @@ public class PartnerCustomizationLayout extends TemplateLayout {
a.getBoolean(R.styleable.SucPartnerCustomizationLayout_sucUsePartnerResource, true);
a.recycle();
- if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && layoutFullscreen) {
+ if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && layoutFullscreen) {
setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
@@ -109,13 +114,10 @@ public class PartnerCustomizationLayout extends TemplateLayout {
+ usePartnerResource);
if (suwVersionSupportPartnerResource && isSetupFlow && !applyPartnerResource()) {
- Log.w(
- TAG,
- "This is inavlid usage that applyPartnerResource() should returns true"
- + " during setup wizard flow");
+ Log.w(TAG, "applyPartnerResource() should return true during setup wizard flow");
}
- boolean applyPartnerResource =
+ applyPartnerResource =
suwVersionSupportPartnerResource
&& applyPartnerResource()
&& (isSetupFlow || usePartnerResource);
@@ -124,17 +126,22 @@ public class PartnerCustomizationLayout extends TemplateLayout {
new StatusBarMixin(this, activity.getWindow(), attrs, defStyleAttr, applyPartnerResource));
registerMixin(
SystemNavBarMixin.class,
- new SystemNavBarMixin(
- this, activity.getWindow(), attrs, defStyleAttr, applyPartnerResource));
+ new SystemNavBarMixin(this, activity.getWindow(), applyPartnerResource));
registerMixin(
FooterBarMixin.class, new FooterBarMixin(this, attrs, defStyleAttr, applyPartnerResource));
+ getMixin(SystemNavBarMixin.class).applyPartnerCustomizations(attrs, defStyleAttr);
+
// Override the FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, FLAG_TRANSLUCENT_STATUS,
// FLAG_TRANSLUCENT_NAVIGATION and SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN attributes of window forces
// showing status bar and navigation bar.
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
+
+ if (applyPartnerResource) {
+ updateContentBackgroundColorWithPartnerConfig();
+ }
}
@Override
@@ -200,6 +207,14 @@ public class PartnerCustomizationLayout extends TemplateLayout {
}
}
+ // TODO(b/127925696): remove the code for pre-release version of Android Q
+ private static boolean isAtLeastQ() {
+ return (VERSION.SDK_INT > VERSION_CODES.P)
+ || (VERSION.CODENAME.length() == 1
+ && VERSION.CODENAME.charAt(0) >= 'Q'
+ && VERSION.CODENAME.charAt(0) <= 'Z');
+ }
+
/**
* This method determines applying the partner resource regardless inside setup wizard flow or
* not. It always returns true indicating applying partner resource inside setup wizard flow and
@@ -222,4 +237,18 @@ public class PartnerCustomizationLayout extends TemplateLayout {
footerStub.setLayoutResource(footer);
return footerStub.inflate();
}
+
+ /** Returns if the current layout/activity applies partner customized configurations or not. */
+ protected boolean shouldApplyPartnerResource() {
+ return applyPartnerResource;
+ }
+
+ /** Updates the background color of this layout with the partner-customizable background color. */
+ private void updateContentBackgroundColorWithPartnerConfig() {
+ @ColorInt
+ int color =
+ PartnerConfigHelper.get(getContext())
+ .getColor(getContext(), PartnerConfig.CONFIG_LAYOUT_BACKGROUND_COLOR);
+ this.getRootView().setBackgroundColor(color);
+ }
}
diff --git a/main/java/com/google/android/setupcompat/lifecycle/LifecycleFragment.java b/main/java/com/google/android/setupcompat/lifecycle/LifecycleFragment.java
index a617fd7..71aa898 100644
--- a/main/java/com/google/android/setupcompat/lifecycle/LifecycleFragment.java
+++ b/main/java/com/google/android/setupcompat/lifecycle/LifecycleFragment.java
@@ -16,6 +16,8 @@
package com.google.android.setupcompat.lifecycle;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
@@ -94,7 +96,7 @@ public class LifecycleFragment extends Fragment {
@Override
public void onDetach() {
super.onDetach();
- SetupMetricsLogger.logDuration(getContext(), metricKey, durationInNanos);
+ SetupMetricsLogger.logDuration(getContext(), metricKey, NANOSECONDS.toMillis(durationInNanos));
}
@Override
diff --git a/main/java/com/google/android/setupcompat/logging/internal/FooterBarMixinMetrics.java b/main/java/com/google/android/setupcompat/logging/internal/FooterBarMixinMetrics.java
index 8886f51..007aff9 100644
--- a/main/java/com/google/android/setupcompat/logging/internal/FooterBarMixinMetrics.java
+++ b/main/java/com/google/android/setupcompat/logging/internal/FooterBarMixinMetrics.java
@@ -18,6 +18,8 @@ package com.google.android.setupcompat.logging.internal;
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.annotation.TargetApi;
+import android.os.Build.VERSION_CODES;
import android.os.PersistableBundle;
import androidx.annotation.StringDef;
import androidx.annotation.VisibleForTesting;
@@ -44,9 +46,9 @@ public class FooterBarMixinMetrics {
@VisibleForTesting
public @interface FooterButtonVisibility {
String UNKNOWN = "Unknown";
- String VISIBLE_USING_XML = "VisibileUsingXml";
+ String VISIBLE_USING_XML = "VisibleUsingXml";
String VISIBLE = "Visible";
- String VISIBLE_USING_XML_TO_INVISIBLE = "VisibileUsingXml_to_Invisible";
+ String VISIBLE_USING_XML_TO_INVISIBLE = "VisibleUsingXml_to_Invisible";
String VISIBLE_TO_INVISIBLE = "Visible_to_Invisible";
String INVISIBLE_TO_VISIBLE = "Invisible_to_Visible";
String INVISIBLE = "Invisible";
@@ -92,35 +94,36 @@ public class FooterBarMixinMetrics {
/** Saves footer button visibility when finish state */
public void updateButtonVisibility(
- boolean isPrimaryButtonVisiable, boolean isSecondaryButtonVisible) {
+ boolean isPrimaryButtonVisible, boolean isSecondaryButtonVisible) {
primaryButtonVisibility =
- updateButtonVisibilityState(primaryButtonVisibility, isPrimaryButtonVisiable);
+ updateButtonVisibilityState(primaryButtonVisibility, isPrimaryButtonVisible);
secondaryButtonVisibility =
updateButtonVisibilityState(secondaryButtonVisibility, isSecondaryButtonVisible);
}
@FooterButtonVisibility
static String updateButtonVisibilityState(
- @FooterButtonVisibility String origionalVisibility, boolean isVisible) {
- if (!origionalVisibility.equals(FooterButtonVisibility.VISIBLE_USING_XML)
- && !origionalVisibility.equals(FooterButtonVisibility.VISIBLE)
- && !origionalVisibility.equals(FooterButtonVisibility.INVISIBLE)) {
- throw new IllegalStateException("Illegal visibility state:" + origionalVisibility);
+ @FooterButtonVisibility String originalVisibility, boolean isVisible) {
+ if (!FooterButtonVisibility.VISIBLE_USING_XML.equals(originalVisibility)
+ && !FooterButtonVisibility.VISIBLE.equals(originalVisibility)
+ && !FooterButtonVisibility.INVISIBLE.equals(originalVisibility)) {
+ throw new IllegalStateException("Illegal visibility state: " + originalVisibility);
}
- if (isVisible && origionalVisibility.equals(FooterButtonVisibility.INVISIBLE)) {
+ if (isVisible && FooterButtonVisibility.INVISIBLE.equals(originalVisibility)) {
return FooterButtonVisibility.INVISIBLE_TO_VISIBLE;
} else if (!isVisible) {
- if (origionalVisibility.equals(FooterButtonVisibility.VISIBLE_USING_XML)) {
+ if (FooterButtonVisibility.VISIBLE_USING_XML.equals(originalVisibility)) {
return FooterButtonVisibility.VISIBLE_USING_XML_TO_INVISIBLE;
- } else if (origionalVisibility.equals(FooterButtonVisibility.VISIBLE)) {
+ } else if (FooterButtonVisibility.VISIBLE.equals(originalVisibility)) {
return FooterButtonVisibility.VISIBLE_TO_INVISIBLE;
}
}
- return origionalVisibility;
+ return originalVisibility;
}
/** Returns metrics data for logging */
+ @TargetApi(VERSION_CODES.Q)
public PersistableBundle getMetrics() {
PersistableBundle persistableBundle = new PersistableBundle();
persistableBundle.putString(EXTRA_PRIMARY_BUTTON_VISIBILITY, primaryButtonVisibility);
diff --git a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
index 25fe418..4284ac9 100644
--- a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
@@ -17,6 +17,7 @@
package com.google.android.setupcompat.template;
import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
@@ -52,6 +53,8 @@ import android.widget.LinearLayout.LayoutParams;
import com.google.android.setupcompat.R;
import com.google.android.setupcompat.internal.TemplateLayout;
import com.google.android.setupcompat.logging.internal.FooterBarMixinMetrics;
+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.concurrent.atomic.AtomicInteger;
@@ -89,7 +92,6 @@ public class FooterBarMixin implements Mixin {
return new FooterButton.OnButtonEventListener() {
-
@Override
public void onEnabledChanged(boolean enabled) {
if (buttonContainer != null) {
@@ -196,7 +198,14 @@ public class FooterBarMixin implements Mixin {
} else {
buttonContainer.setId(generateViewId());
}
- updateBottomBarPadding();
+ updateFooterBarPadding();
+ if (applyPartnerResources) {
+ @ColorInt
+ int color =
+ PartnerConfigHelper.get(context)
+ .getColor(context, PartnerConfig.CONFIG_FOOTER_BAR_BG_COLOR);
+ buttonContainer.setBackgroundColor(color);
+ }
}
return buttonContainer;
}
@@ -416,7 +425,7 @@ public class FooterBarMixin implements Mixin {
private void updateButtonAttrsWithPartnerConfig(
Button button, boolean isPrimaryButton, @ButtonType int buttonType) {
updateButtonTextColorWithPartnerConfig(button, isPrimaryButton);
- updateButtonTextSizeWithPartnerConfig(button, isPrimaryButton);
+ updateButtonTextSizeWithPartnerConfig(button);
updateButtonTypeFaceWithPartnerConfig(button);
updateButtonBackgroundWithPartnerConfig(button, isPrimaryButton);
updateButtonRadiusWithPartnerConfig(button);
@@ -438,18 +447,13 @@ public class FooterBarMixin implements Mixin {
button.setTextColor(color);
}
- private void updateButtonTextSizeWithPartnerConfig(Button button, boolean isPrimaryButton) {
- float size;
- if (isPrimaryButton) {
- size =
- PartnerConfigHelper.get(context)
- .getDimension(context, PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_SIZE);
- } else {
- size =
- PartnerConfigHelper.get(context)
- .getDimension(context, PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_SIZE);
+ private void updateButtonTextSizeWithPartnerConfig(Button button) {
+ float size =
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_SIZE);
+ if (size > 0) {
+ button.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
}
- button.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
}
private void updateButtonTypeFaceWithPartnerConfig(Button button) {
@@ -639,7 +643,7 @@ public class FooterBarMixin implements Mixin {
return footerStub.inflate();
}
- private void updateBottomBarPadding() {
+ private void updateFooterBarPadding() {
if (buttonContainer == null) {
// Ignore action since buttonContainer is null
return;
@@ -692,6 +696,7 @@ public class FooterBarMixin implements Mixin {
/**
* Assigns logging metrics to bundle for PartnerCustomizationLayout to log metrics to SetupWizard.
*/
+ @TargetApi(VERSION_CODES.Q)
public PersistableBundle getLoggingMetrics() {
return metrics.getMetrics();
}
diff --git a/main/java/com/google/android/setupcompat/template/FooterButton.java b/main/java/com/google/android/setupcompat/template/FooterButton.java
index f913c0b..e2ef6c6 100644
--- a/main/java/com/google/android/setupcompat/template/FooterButton.java
+++ b/main/java/com/google/android/setupcompat/template/FooterButton.java
@@ -18,8 +18,10 @@ package com.google.android.setupcompat.template;
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
+import android.os.Build.VERSION_CODES;
import android.os.PersistableBundle;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
@@ -320,6 +322,7 @@ public final class FooterButton implements OnClickListener {
* Returns footer button related metrics bundle for PartnerCustomizationLayout to log to
* SetupWizard.
*/
+ @TargetApi(VERSION_CODES.Q)
public PersistableBundle getMetrics(String buttonName) {
PersistableBundle bundle = new PersistableBundle();
bundle.putString(buttonName + KEY_BUTTON_TEXT, getText().toString());
diff --git a/main/java/com/google/android/setupcompat/template/HeaderMixin.java b/main/java/com/google/android/setupcompat/template/HeaderMixin.java
deleted file mode 100644
index f6ae697..0000000
--- a/main/java/com/google/android/setupcompat/template/HeaderMixin.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2017 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.template;
-
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import androidx.annotation.AttrRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import android.util.AttributeSet;
-import android.widget.TextView;
-import com.google.android.setupcompat.R;
-import com.google.android.setupcompat.internal.TemplateLayout;
-
-/**
- * A {@link com.google.android.setupcompat.template.Mixin} for setting and getting the header text.
- */
-public class HeaderMixin implements Mixin {
-
- private final TemplateLayout templateLayout;
-
- /**
- * @param layout The layout this Mixin belongs to.
- * @param attrs XML attributes given to the layout.
- * @param defStyleAttr The default style attribute as given to the constructor of the layout.
- */
- public HeaderMixin(
- @NonNull TemplateLayout layout, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
- templateLayout = layout;
-
- final TypedArray a =
- layout
- .getContext()
- .obtainStyledAttributes(attrs, R.styleable.SucHeaderMixin, defStyleAttr, 0);
-
- // Set the header text
- final CharSequence headerText = a.getText(R.styleable.SucHeaderMixin_sucHeaderText);
- if (headerText != null) {
- setText(headerText);
- }
- // Set the header text color
- final ColorStateList headerTextColor =
- a.getColorStateList(R.styleable.SucHeaderMixin_sucHeaderTextColor);
- if (headerTextColor != null) {
- setTextColor(headerTextColor);
- }
-
- a.recycle();
- }
-
- /** @return The TextView displaying the header. */
- public TextView getTextView() {
- return (TextView) templateLayout.findManagedViewById(R.id.suc_layout_title);
- }
-
- /**
- * Sets the header text. This can also be set via the XML attribute {@code app:sucHeaderText}.
- *
- * @param title The resource ID of the text to be set as header.
- */
- public void setText(int title) {
- final TextView titleView = getTextView();
- if (titleView != null) {
- titleView.setText(title);
- }
- }
-
- /**
- * Sets the header text. This can also be set via the XML attribute {@code app:sucHeaderText}.
- *
- * @param title The text to be set as header.
- */
- public void setText(CharSequence title) {
- final TextView titleView = getTextView();
- if (titleView != null) {
- titleView.setText(title);
- }
- }
-
- /** @return The current header text. */
- public CharSequence getText() {
- final TextView titleView = getTextView();
- return titleView != null ? titleView.getText() : null;
- }
-
- /**
- * Sets the color of the header text. This can also be set via XML using {@code
- * app:sucHeaderTextColor}.
- *
- * @param color The text color of the header.
- */
- public void setTextColor(ColorStateList color) {
- final TextView titleView = getTextView();
- if (titleView != null) {
- titleView.setTextColor(color);
- }
- }
-
- /** Returns the current text color of the header. */
- public ColorStateList getTextColor() {
- final TextView titleView = getTextView();
- return titleView != null ? titleView.getTextColors() : null;
- }
-}
diff --git a/main/java/com/google/android/setupcompat/template/IconMixin.java b/main/java/com/google/android/setupcompat/template/IconMixin.java
deleted file mode 100644
index c2002e2..0000000
--- a/main/java/com/google/android/setupcompat/template/IconMixin.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2017 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.template;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import androidx.annotation.DrawableRes;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageView;
-import com.google.android.setupcompat.R;
-import com.google.android.setupcompat.internal.TemplateLayout;
-
-/**
- * A {@link com.google.android.setupcompat.template.Mixin} for setting an icon on the template
- * layout.
- */
-public class IconMixin implements Mixin {
-
- private final TemplateLayout templateLayout;
-
- /**
- * @param layout The template layout that this Mixin is a part of.
- * @param attrs XML attributes given to the layout.
- * @param defStyleAttr The default style attribute as given to the constructor of the layout.
- */
- public IconMixin(TemplateLayout layout, AttributeSet attrs, int defStyleAttr) {
- templateLayout = layout;
- final Context context = layout.getContext();
-
- final TypedArray a =
- context.obtainStyledAttributes(attrs, R.styleable.SucIconMixin, defStyleAttr, 0);
-
- final @DrawableRes int icon = a.getResourceId(R.styleable.SucIconMixin_android_icon, 0);
- if (icon != 0) {
- setIcon(icon);
- }
-
- a.recycle();
- }
-
- /**
- * Sets the icon on this layout. The icon can also be set in XML using {@code android:icon}.
- *
- * @param icon A drawable icon.
- */
- public void setIcon(Drawable icon) {
- final ImageView iconView = getView();
- if (iconView != null) {
- iconView.setImageDrawable(icon);
- iconView.setVisibility(icon != null ? View.VISIBLE : View.GONE);
- }
- }
-
- /**
- * Sets the icon on this layout. The icon can also be set in XML using {@code android:icon}.
- *
- * @param icon A drawable icon resource.
- */
- public void setIcon(@DrawableRes int icon) {
- final ImageView iconView = getView();
- if (iconView != null) {
- // Note: setImageResource on the ImageView is overridden in AppCompatImageView for
- // support lib users, which enables vector drawable compat to work on versions pre-L.
- iconView.setImageResource(icon);
- iconView.setVisibility(icon != 0 ? View.VISIBLE : View.GONE);
- }
- }
-
- /** @return The icon previously set in {@link #setIcon(Drawable)} or {@code android:icon} */
- public Drawable getIcon() {
- final ImageView iconView = getView();
- return iconView != null ? iconView.getDrawable() : null;
- }
-
- /** Sets the content description of the icon view */
- public void setContentDescription(CharSequence description) {
- final ImageView iconView = getView();
- if (iconView != null) {
- iconView.setContentDescription(description);
- }
- }
-
- /** @return The content description of the icon view */
- public CharSequence getContentDescription() {
- final ImageView iconView = getView();
- return iconView != null ? iconView.getContentDescription() : null;
- }
-
- /** @return The ImageView responsible for displaying the icon. */
- protected ImageView getView() {
- return (ImageView) templateLayout.findManagedViewById(R.id.suc_layout_icon);
- }
-}
diff --git a/main/java/com/google/android/setupcompat/template/PartnerConfig.java b/main/java/com/google/android/setupcompat/template/PartnerConfig.java
deleted file mode 100644
index 00a36b7..0000000
--- a/main/java/com/google/android/setupcompat/template/PartnerConfig.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.setupcompat.template;
-
-// TODO(b/121371322): optimize the enum
-/** Resources that can be customized by partner overlay APK. */
-enum PartnerConfig {
-
- // Status bar background color or illustration.
- CONFIG_STATUS_BAR_BACKGROUND(PartnerConfigKey.KEY_STATUS_BAR_BACKGROUND, ResourceType.DRAWABLE),
-
- // The same as "WindowLightStatusBar". If set true, the status bar icons will be drawn such
- // that it is compatible with a light status bar background
- CONFIG_LIGHT_STATUS_BAR(PartnerConfigKey.KEY_LIGHT_STATUS_BAR, ResourceType.BOOL),
-
- // Navigation bar background color
- CONFIG_NAVIGATION_BAR_BG_COLOR(PartnerConfigKey.KEY_NAVIGATION_BAR_BG_COLOR, ResourceType.COLOR),
-
- // The same as "windowLightNavigationBar". If set true, the navigation bar icons will be drawn
- // such that it is compatible with a light navigation bar background.
- CONFIG_LIGHT_NAVIGATION_BAR(PartnerConfigKey.KEY_LIGHT_NAVIGATION_BAR, ResourceType.BOOL),
-
- // The font face used in footer buttons. This must be a string reference to a font that is
- // available in the system. Font references (@font or @xml) are not allowed.
- CONFIG_FOOTER_BUTTON_FONT_FAMILY(
- PartnerConfigKey.KEY_FOOTER_BUTTON_FONT_FAMILY, ResourceType.STRING),
-
- // The icon for "add another" action. Can be "@null" for no icon.
- CONFIG_FOOTER_BUTTON_ICON_ADD_ANOTHER(
- PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_ADD_ANOTHER, ResourceType.DRAWABLE),
-
- // The icon for "cancel" action. Can be "@null" for no icon.
- CONFIG_FOOTER_BUTTON_ICON_CANCEL(
- PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_CANCEL, ResourceType.DRAWABLE),
-
- // The icon for "clear" action. Can be "@null" for no icon.
- CONFIG_FOOTER_BUTTON_ICON_CLEAR(
- PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_CLEAR, ResourceType.DRAWABLE),
-
- // The icon for "done" action. Can be "@null" for no icon.
- CONFIG_FOOTER_BUTTON_ICON_DONE(
- PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_DONE, ResourceType.DRAWABLE),
-
- // The icon for "next" action. Can be "@null" for no icon.
- CONFIG_FOOTER_BUTTON_ICON_NEXT(
- PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_NEXT, ResourceType.DRAWABLE),
-
- // The icon for "opt-in" action. Can be "@null" for no icon.
- CONFIG_FOOTER_BUTTON_ICON_OPT_IN(
- PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_OPT_IN, ResourceType.DRAWABLE),
-
- // The icon for "skip" action. Can be "@null" for no icon.
- CONFIG_FOOTER_BUTTON_ICON_SKIP(
- PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_SKIP, ResourceType.DRAWABLE),
-
- // The icon for "stop" action. Can be "@null" for no icon.
- CONFIG_FOOTER_BUTTON_ICON_STOP(
- PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_STOP, ResourceType.DRAWABLE),
-
- // Top padding of the footer buttons
- CONFIG_FOOTER_BUTTON_PADDING_TOP(
- PartnerConfigKey.KEY_FOOTER_BUTTON_PADDING_TOP, ResourceType.DIMENSION),
-
- // Bottom padding of the footer buttons
- CONFIG_FOOTER_BUTTON_PADDING_BOTTOM(
- PartnerConfigKey.KEY_FOOTER_BUTTON_PADDING_BOTTOM, ResourceType.DIMENSION),
-
- // Corner radius of the footer buttons
- CONFIG_FOOTER_BUTTON_RADIUS(PartnerConfigKey.KEY_FOOTER_BUTTON_RADIUS, ResourceType.DIMENSION),
-
- // Ripple color alpha the footer buttons
- CONFIG_FOOTER_BUTTON_RIPPLE_COLOR_ALPHA(
- PartnerConfigKey.KEY_FOOTER_BUTTON_RIPPLE_ALPHA, ResourceType.FRACTION),
-
- // Background color of the primary footer button
- CONFIG_FOOTER_PRIMARY_BUTTON_BG_COLOR(
- PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_BG_COLOR, ResourceType.COLOR),
-
- // Text color of the primary footer button
- CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_COLOR(
- PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_TEXT_COLOR, ResourceType.COLOR),
-
- // Text size of the primary footer button
- CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_SIZE(
- PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_TEXT_SIZE, ResourceType.DIMENSION),
-
- // Background color of the secondary footer button
- CONFIG_FOOTER_SECONDARY_BUTTON_BG_COLOR(
- PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_BG_COLOR, ResourceType.COLOR),
-
- // Text color of the secondary footer button
- CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR(
- PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_TEXT_COLOR, ResourceType.COLOR),
-
- // Text size of the secondary footer button
- CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_SIZE(
- PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_TEXT_SIZE, ResourceType.DIMENSION);
-
- public enum ResourceType {
- BOOL,
- COLOR,
- DRAWABLE,
- STRING,
- DIMENSION,
- FRACTION;
- }
-
- private final String resourceName;
- private final ResourceType resourceType;
-
- public ResourceType getResourceType() {
- return resourceType;
- }
-
- public String getResourceName() {
- return resourceName;
- }
-
- PartnerConfig(@PartnerConfigKey String resourceName, ResourceType type) {
- this.resourceName = resourceName;
- this.resourceType = type;
- }
-}
diff --git a/main/java/com/google/android/setupcompat/template/PartnerConfigHelper.java b/main/java/com/google/android/setupcompat/template/PartnerConfigHelper.java
deleted file mode 100644
index 9c540dd..0000000
--- a/main/java/com/google/android/setupcompat/template/PartnerConfigHelper.java
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.setupcompat.template;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Build.VERSION_CODES;
-import android.os.Bundle;
-import androidx.annotation.ColorInt;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import android.util.Log;
-import android.util.TypedValue;
-import com.google.android.setupcompat.template.PartnerConfig.ResourceType;
-import com.google.android.setupcompat.util.ResourceEntry;
-import java.util.EnumMap;
-
-/** The helper reads and caches the partner configurations from SUW. */
-@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
-public class PartnerConfigHelper {
-
- private static final String TAG = PartnerConfigHelper.class.getSimpleName();
-
- @VisibleForTesting
- public static final String SUW_AUTHORITY = "com.google.android.setupwizard.partner";
-
- @VisibleForTesting public static final String SUW_GET_PARTNER_CONFIG_METHOD = "getOverlayConfig";
- private static PartnerConfigHelper instance = null;
-
- @VisibleForTesting Bundle resultBundle = null;
-
- @VisibleForTesting
- final EnumMap<PartnerConfig, Object> partnerResourceCache = new EnumMap<>(PartnerConfig.class);
-
- public static synchronized PartnerConfigHelper get(@NonNull Context context) {
- if (instance == null) {
- instance = new PartnerConfigHelper(context);
- }
- return instance;
- }
-
- private PartnerConfigHelper(Context context) {
- getPartnerConfigBundle(context);
- }
-
- /**
- * Returns the color of given {@code resourceConfig}, or 0 if the given {@code resourceConfig} is
- * not found. If the {@code ResourceType} of the given {@code resourceConfig} is not color,
- * IllegalArgumentException will be thrown.
- *
- * @param context The context of client activity
- * @param resourceConfig The {@code PartnerConfig} of target resource
- */
- @ColorInt
- public int getColor(@NonNull Context context, PartnerConfig resourceConfig) {
- if (resourceConfig.getResourceType() != ResourceType.COLOR) {
- throw new IllegalArgumentException("Not a color resource");
- }
-
- if (partnerResourceCache.containsKey(resourceConfig)) {
- return (int) partnerResourceCache.get(resourceConfig);
- }
-
- int result = 0;
- try {
- String resourceName = resourceConfig.getResourceName();
- ResourceEntry resourceEntry = getResourceEntryFromKey(resourceName);
- Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName());
- if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
- result = resource.getColor(resourceEntry.getResourceId(), null);
- } else {
- result = resource.getColor(resourceEntry.getResourceId());
- }
- partnerResourceCache.put(resourceConfig, result);
- } catch (PackageManager.NameNotFoundException | NullPointerException exception) {
- // fall through
- }
- return result;
- }
-
- /**
- * Returns the {@code Drawable} of given {@code resourceConfig}, or {@code null} if the given
- * {@code resourceConfig} is not found. If the {@code ResourceType} of the given {@code
- * resourceConfig} is not drawable, IllegalArgumentException will be thrown.
- *
- * @param context The context of client activity
- * @param resourceConfig The {@code PartnerConfig} of target resource
- */
- @Nullable
- public Drawable getDrawable(@NonNull Context context, PartnerConfig resourceConfig) {
- if (resourceConfig.getResourceType() != ResourceType.DRAWABLE) {
- throw new IllegalArgumentException("Not a drawable resource");
- }
-
- if (partnerResourceCache.containsKey(resourceConfig)) {
- return (Drawable) partnerResourceCache.get(resourceConfig);
- }
-
- Drawable result = null;
- try {
- ResourceEntry resourceEntry = getResourceEntryFromKey(resourceConfig.getResourceName());
- Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName());
-
- // for @null
- TypedValue outValue = new TypedValue();
- resource.getValue(resourceEntry.getResourceId(), outValue, true);
- if (outValue.type == TypedValue.TYPE_REFERENCE && outValue.data == 0) {
- return result;
- }
-
- if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
- result = resource.getDrawable(resourceEntry.getResourceId(), null);
- } else {
- result = resource.getDrawable(resourceEntry.getResourceId());
- }
- partnerResourceCache.put(resourceConfig, result);
- } catch (PackageManager.NameNotFoundException
- | NullPointerException
- | NotFoundException exception) {
- // fall through
- }
- return result;
- }
-
- /**
- * Returns the string of the given {@code resourceConfig}, or {@code null} if the given {@code
- * resourceConfig} is not found. If the {@code ResourceType} of the given {@code resourceConfig}
- * is not string, IllegalArgumentException will be thrown.
- *
- * @param context The context of client activity
- * @param resourceConfig The {@code PartnerConfig} of target resource
- */
- @Nullable
- public String getString(@NonNull Context context, PartnerConfig resourceConfig) {
- if (resourceConfig.getResourceType() != ResourceType.STRING) {
- throw new IllegalArgumentException("Not a string resource");
- }
-
- if (partnerResourceCache.containsKey(resourceConfig)) {
- return (String) partnerResourceCache.get(resourceConfig);
- }
-
- String result = null;
- try {
- ResourceEntry resourceEntry = getResourceEntryFromKey(resourceConfig.getResourceName());
- Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName());
- result = resource.getString(resourceEntry.getResourceId());
- partnerResourceCache.put(resourceConfig, result);
- } catch (PackageManager.NameNotFoundException | NullPointerException exception) {
- // fall through
- }
- return result;
- }
-
- /**
- * Returns the boolean of given {@code resourceConfig}, or {@code defaultValue} if the given
- * {@code resourceName} is not found. If the {@code ResourceType} of the given {@code
- * resourceConfig} is not boolean, IllegalArgumentException will be thrown.
- *
- * @param context The context of client activity
- * @param resourceConfig The {@code PartnerConfig} of target resource
- * @param defaultValue The default value
- */
- public boolean getBoolean(
- @NonNull Context context, PartnerConfig resourceConfig, boolean defaultValue) {
- if (resourceConfig.getResourceType() != ResourceType.BOOL) {
- throw new IllegalArgumentException("Not a bool resource");
- }
-
- if (partnerResourceCache.containsKey(resourceConfig)) {
- return (boolean) partnerResourceCache.get(resourceConfig);
- }
-
- boolean result = defaultValue;
- try {
- ResourceEntry resourceEntry = getResourceEntryFromKey(resourceConfig.getResourceName());
- Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName());
- result = resource.getBoolean(resourceEntry.getResourceId());
- partnerResourceCache.put(resourceConfig, result);
- } catch (PackageManager.NameNotFoundException | NullPointerException exception) {
- // fall through
- }
- return result;
- }
-
- /**
- * Returns the dimension of given {@code resourceConfig}. The default return value is 0.
- *
- * @param context The context of client activity
- * @param resourceConfig The {@code PartnerConfig} of target resource
- */
- public float getDimension(@NonNull Context context, PartnerConfig resourceConfig) {
- return getDimension(context, resourceConfig, 0);
- }
-
- /**
- * Returns the dimension of given {@code resourceConfig}. If the given {@code resourceConfig} not
- * found, will return {@code defaultValue}. If the {@code ResourceType} of given {@code
- * resourceConfig} is not dimension, will throw IllegalArgumentException.
- *
- * @param context The context of client activity
- * @param resourceConfig The {@code PartnerConfig} of target resource
- * @param defaultValue The default value
- */
- public float getDimension(
- @NonNull Context context, PartnerConfig resourceConfig, float defaultValue) {
- if (resourceConfig.getResourceType() != ResourceType.DIMENSION) {
- throw new IllegalArgumentException("Not a dimension resource");
- }
-
- if (partnerResourceCache.containsKey(resourceConfig)) {
- return (float) partnerResourceCache.get(resourceConfig);
- }
-
- float result = defaultValue;
- try {
- ResourceEntry resourceEntry = getResourceEntryFromKey(resourceConfig.getResourceName());
- Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName());
- result = resource.getDimension(resourceEntry.getResourceId());
- partnerResourceCache.put(resourceConfig, result);
- } catch (PackageManager.NameNotFoundException | NullPointerException exception) {
- // fall through
- }
- return result;
- }
-
- /**
- * Returns the float of given {@code resourceConfig}. The default return value is 0.
- *
- * @param context The context of client activity
- * @param resourceConfig The {@code PartnerConfig} of target resource
- */
- public float getFraction(@NonNull Context context, PartnerConfig resourceConfig) {
- return getFraction(context, resourceConfig, 0.0f);
- }
-
- /**
- * Returns the float of given {@code resourceConfig}. If the given {@code resourceConfig} not
- * found, will return {@code defaultValue}. If the {@code ResourceType} of given {@code
- * resourceConfig} is not fraction, will throw IllegalArgumentException.
- *
- * @param context The context of client activity
- * @param resourceConfig The {@code PartnerConfig} of target resource
- * @param defaultValue The default value
- */
- public float getFraction(
- @NonNull Context context, PartnerConfig resourceConfig, float defaultValue) {
- if (resourceConfig.getResourceType() != ResourceType.FRACTION) {
- throw new IllegalArgumentException("Not a fraction resource");
- }
-
- if (partnerResourceCache.containsKey(resourceConfig)) {
- return (float) partnerResourceCache.get(resourceConfig);
- }
-
- float result = defaultValue;
- try {
- ResourceEntry resourceEntry = getResourceEntryFromKey(resourceConfig.getResourceName());
- Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName());
- result = resource.getFraction(resourceEntry.getResourceId(), 1, 1);
- partnerResourceCache.put(resourceConfig, result);
- } catch (PackageManager.NameNotFoundException | NullPointerException exception) {
- // fall through
- }
- return result;
- }
-
- private void getPartnerConfigBundle(Context context) {
- if (resultBundle == null || resultBundle.isEmpty()) {
- try {
- Uri contentUri =
- new Uri.Builder()
- .scheme(ContentResolver.SCHEME_CONTENT)
- .authority(SUW_AUTHORITY)
- .appendPath(SUW_GET_PARTNER_CONFIG_METHOD)
- .build();
- resultBundle =
- context
- .getContentResolver()
- .call(
- contentUri, SUW_GET_PARTNER_CONFIG_METHOD, /* arg= */ null, /* extras= */ null);
- partnerResourceCache.clear();
- } catch (IllegalArgumentException exception) {
- Log.w(TAG, "Fail to get config from suw provider");
- }
- }
- }
-
- private Resources getResourcesByPackageName(Context context, String packageName)
- throws PackageManager.NameNotFoundException {
- PackageManager manager = context.getPackageManager();
- return manager.getResourcesForApplication(packageName);
- }
-
- private ResourceEntry getResourceEntryFromKey(String resourceName) {
- if (resultBundle == null) {
- return null;
- }
- return ResourceEntry.fromBundle(resultBundle.getBundle(resourceName));
- }
-
- @VisibleForTesting
- public static synchronized void resetForTesting() {
- instance = null;
- }
-}
diff --git a/main/java/com/google/android/setupcompat/template/PartnerConfigKey.java b/main/java/com/google/android/setupcompat/template/PartnerConfigKey.java
deleted file mode 100644
index d79c152..0000000
--- a/main/java/com/google/android/setupcompat/template/PartnerConfigKey.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.setupcompat.template;
-
-import androidx.annotation.StringDef;
-import androidx.annotation.VisibleForTesting;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/** Resource names that can be customized by partner overlay APK. */
-@Retention(RetentionPolicy.SOURCE)
-@StringDef({
- PartnerConfigKey.KEY_STATUS_BAR_BACKGROUND,
- PartnerConfigKey.KEY_LIGHT_STATUS_BAR,
- PartnerConfigKey.KEY_NAVIGATION_BAR_BG_COLOR,
- PartnerConfigKey.KEY_LIGHT_NAVIGATION_BAR,
- PartnerConfigKey.KEY_FOOTER_BUTTON_FONT_FAMILY,
- PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_ADD_ANOTHER,
- PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_CANCEL,
- PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_CLEAR,
- PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_DONE,
- PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_NEXT,
- PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_OPT_IN,
- PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_SKIP,
- PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_STOP,
- PartnerConfigKey.KEY_FOOTER_BUTTON_PADDING_TOP,
- PartnerConfigKey.KEY_FOOTER_BUTTON_PADDING_BOTTOM,
- PartnerConfigKey.KEY_FOOTER_BUTTON_RADIUS,
- PartnerConfigKey.KEY_FOOTER_BUTTON_RIPPLE_ALPHA,
- PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_BG_COLOR,
- PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_TEXT_COLOR,
- PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_TEXT_SIZE,
- PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_BG_COLOR,
- PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_TEXT_COLOR,
- PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_TEXT_SIZE,
-})
-// TODO(121371322): can be removed and always reference PartnerConfig.getResourceName()?
-@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
-public @interface PartnerConfigKey {
- // Status bar background color or illustration.
- String KEY_STATUS_BAR_BACKGROUND = "setup_compat_status_bar_background";
-
- // The same as "WindowLightStatusBar". If set true, the status bar icons will be drawn such
- // that it is compatible with a light status bar background
- String KEY_LIGHT_STATUS_BAR = "setup_compat_light_status_bar";
-
- // Navigation bar background color
- String KEY_NAVIGATION_BAR_BG_COLOR = "setup_compat_navigation_bar_bg_color";
-
- // The same as "windowLightNavigationBar". If set true, the navigation bar icons will be drawn
- // such that it is compatible with a light navigation bar background.
- String KEY_LIGHT_NAVIGATION_BAR = "setup_compat_light_navigation_bar";
-
- // The font face used in footer buttons. This must be a string reference to a font that is
- // available in the system. Font references (@font or @xml) are not allowed.
- String KEY_FOOTER_BUTTON_FONT_FAMILY = "setup_compat_footer_button_font_family";
-
- // The icon for "add another" action. Can be "@null" for no icon.
- String KEY_FOOTER_BUTTON_ICON_ADD_ANOTHER = "setup_compat_footer_button_icon_add_another";
-
- // The icon for "cancel" action. Can be "@null" for no icon.
- String KEY_FOOTER_BUTTON_ICON_CANCEL = "setup_compat_footer_button_icon_cancel";
-
- // The icon for "clear" action. Can be "@null" for no icon.
- String KEY_FOOTER_BUTTON_ICON_CLEAR = "setup_compat_footer_button_icon_clear";
-
- // The icon for "done" action. Can be "@null" for no icon.
- String KEY_FOOTER_BUTTON_ICON_DONE = "setup_compat_footer_button_icon_done";
-
- // The icon for "next" action. Can be "@null" for no icon.
- String KEY_FOOTER_BUTTON_ICON_NEXT = "setup_compat_footer_button_icon_next";
-
- // The icon for "opt-in" action. Can be "@null" for no icon.
- String KEY_FOOTER_BUTTON_ICON_OPT_IN = "setup_compat_footer_button_icon_opt_in";
-
- // The icon for "skip" action. Can be "@null" for no icon.
- String KEY_FOOTER_BUTTON_ICON_SKIP = "setup_compat_footer_button_icon_skip";
-
- // The icon for "stop" action. Can be "@null" for no icon.
- String KEY_FOOTER_BUTTON_ICON_STOP = "setup_compat_footer_button_icon_stop";
-
- // Top padding of the footer buttons
- String KEY_FOOTER_BUTTON_PADDING_TOP = "setup_compat_footer_button_padding_top";
-
- // Bottom padding of the footer buttons
- String KEY_FOOTER_BUTTON_PADDING_BOTTOM = "setup_compat_footer_button_padding_bottom";
-
- // Corner radius of the footer buttons
- String KEY_FOOTER_BUTTON_RADIUS = "setup_compat_footer_button_radius";
-
- // Ripple color alpha of the footer button
- String KEY_FOOTER_BUTTON_RIPPLE_ALPHA = "setup_compat_footer_button_ripple_alpha";
-
- // Background color of the primary footer button
- String KEY_FOOTER_PRIMARY_BUTTON_BG_COLOR = "setup_compat_footer_primary_button_bg_color";
-
- // Text color of the primary footer button
- String KEY_FOOTER_PRIMARY_BUTTON_TEXT_COLOR = "setup_compat_footer_primary_button_text_color";
-
- // Text size of the primary footer button
- String KEY_FOOTER_PRIMARY_BUTTON_TEXT_SIZE = "setup_compat_footer_primary_button_text_size";
-
- // Background color of the secondary footer button
- String KEY_FOOTER_SECONDARY_BUTTON_BG_COLOR = "setup_compat_footer_secondary_button_bg_color";
-
- // Text color of the secondary footer button
- String KEY_FOOTER_SECONDARY_BUTTON_TEXT_COLOR = "setup_compat_footer_secondary_button_text_color";
-
- // Text size of the secondary footer button
- String KEY_FOOTER_SECONDARY_BUTTON_TEXT_SIZE = "setup_compat_footer_secondary_button_text_size";
-}
diff --git a/main/java/com/google/android/setupcompat/template/StatusBarMixin.java b/main/java/com/google/android/setupcompat/template/StatusBarMixin.java
index 38e903b..2aa37dc 100644
--- a/main/java/com/google/android/setupcompat/template/StatusBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/StatusBarMixin.java
@@ -35,6 +35,8 @@ import android.view.Window;
import android.widget.LinearLayout;
import com.google.android.setupcompat.PartnerCustomizationLayout;
import com.google.android.setupcompat.R;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
import com.google.android.setupcompat.view.StatusBarBackgroundLayout;
/**
diff --git a/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java b/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java
index f137e7f..eae876c 100644
--- a/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Build;
+import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
@@ -32,6 +33,10 @@ import android.view.View;
import android.view.Window;
import com.google.android.setupcompat.PartnerCustomizationLayout;
import com.google.android.setupcompat.R;
+import com.google.android.setupcompat.internal.TemplateLayout;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupcompat.util.SystemBarBaseHelper;
/**
* A {@link Mixin} for setting and getting background color and window compatible with light theme
@@ -39,38 +44,39 @@ import com.google.android.setupcompat.R;
*/
public class SystemNavBarMixin implements Mixin {
- private final PartnerCustomizationLayout partnerCustomizationLayout;
- private final Window windowOfActivity;
- private final View decorView;
+ private final TemplateLayout templateLayout;
+ @Nullable private final Window windowOfActivity;
@VisibleForTesting final boolean applyPartnerResources;
+ private int sucSystemNavBarBackgroundColor = 0;
/**
- * Creates a mixin for managing system navigation bar.
+ * Creates a mixin for managing the system navigation bar.
*
* @param layout The layout this Mixin belongs to.
- * @param window The window this activity of Mixin belongs to.
- * @param attrs XML attributes given to the layout.
- * @param defStyleAttr The default style attribute as given to the constructor of the layout.
- * @param applyPartnerResources determine applies partner resources or not.
+ * @param window The window this activity of Mixin belongs to.*
+ * @param applyPartnerResources whether to apply partner resources or not.
*/
public SystemNavBarMixin(
- @NonNull PartnerCustomizationLayout layout,
- @NonNull Window window,
- @Nullable AttributeSet attrs,
- @AttrRes int defStyleAttr,
- boolean applyPartnerResources) {
- partnerCustomizationLayout = layout;
- windowOfActivity = window;
- decorView = window.getDecorView();
+ @NonNull TemplateLayout layout, @Nullable Window window, boolean applyPartnerResources) {
+ this.templateLayout = layout;
+ this.windowOfActivity = window;
this.applyPartnerResources = applyPartnerResources;
+ }
+ /**
+ * Creates a mixin for managing the system navigation bar.
+ *
+ * @param attrs XML attributes given to the layout.
+ * @param defStyleAttr The default style attribute as given to the constructor of the layout.
+ */
+ public void applyPartnerCustomizations(@Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
TypedArray a =
- partnerCustomizationLayout
+ templateLayout
.getContext()
.obtainStyledAttributes(attrs, R.styleable.SucSystemNavBarMixin, defStyleAttr, 0);
- int navigationBarBackground =
+ sucSystemNavBarBackgroundColor =
a.getColor(R.styleable.SucSystemNavBarMixin_sucSystemNavBarBackgroundColor, 0);
- setSystemNavBarBackground(navigationBarBackground);
+ setSystemNavBarBackground(sucSystemNavBarBackgroundColor);
setLightSystemNavBar(
a.getBoolean(R.styleable.SucSystemNavBarMixin_sucLightSystemNavBar, isLightSystemNavBar()));
a.recycle();
@@ -83,21 +89,20 @@ public class SystemNavBarMixin implements Mixin {
* @param color The background color of navigation bar.
*/
public void setSystemNavBarBackground(int color) {
- if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && windowOfActivity != null) {
if (applyPartnerResources) {
- Context context = partnerCustomizationLayout.getContext();
+ Context context = templateLayout.getContext();
color =
PartnerConfigHelper.get(context)
.getColor(context, PartnerConfig.CONFIG_NAVIGATION_BAR_BG_COLOR);
}
-
windowOfActivity.setNavigationBarColor(color);
}
}
/** Returns the background color of navigation bar. */
public int getSystemNavBarBackground() {
- if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && windowOfActivity != null) {
return windowOfActivity.getNavigationBarColor();
}
return Color.BLACK;
@@ -111,20 +116,25 @@ public class SystemNavBarMixin implements Mixin {
* @param isLight true means compatible with light theme, otherwise compatible with dark theme
*/
public void setLightSystemNavBar(boolean isLight) {
- if (Build.VERSION.SDK_INT >= VERSION_CODES.O) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.O && windowOfActivity != null) {
if (applyPartnerResources) {
- Context context = partnerCustomizationLayout.getContext();
+ Context context = templateLayout.getContext();
isLight =
PartnerConfigHelper.get(context)
.getBoolean(context, PartnerConfig.CONFIG_LIGHT_NAVIGATION_BAR, false);
}
-
if (isLight) {
- decorView.setSystemUiVisibility(
- decorView.getSystemUiVisibility() | SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+ windowOfActivity
+ .getDecorView()
+ .setSystemUiVisibility(
+ windowOfActivity.getDecorView().getSystemUiVisibility()
+ | SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
} else {
- decorView.setSystemUiVisibility(
- decorView.getSystemUiVisibility() & ~SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+ windowOfActivity
+ .getDecorView()
+ .setSystemUiVisibility(
+ windowOfActivity.getDecorView().getSystemUiVisibility()
+ & ~SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
}
}
}
@@ -134,10 +144,73 @@ public class SystemNavBarMixin implements Mixin {
* should be drawn light-on-dark.
*/
public boolean isLightSystemNavBar() {
- if (Build.VERSION.SDK_INT >= VERSION_CODES.O) {
- return (decorView.getSystemUiVisibility() & SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR)
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.O && windowOfActivity != null) {
+ return (windowOfActivity.getDecorView().getSystemUiVisibility()
+ & SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR)
== SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
}
return true;
}
+
+ /**
+ * Hides the navigation bar, make the color of the status and navigation bars transparent, and
+ * specify {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} flag so that the content is laid-out
+ * behind the transparent status bar. This is commonly used with {@link
+ * android.app.Activity#getWindow()} to make the navigation and status bars follow the Setup
+ * Wizard style.
+ *
+ * <p>This will only take effect in versions Lollipop or above. Otherwise this is a no-op.
+ */
+ public void hideSystemBars(final Window window) {
+ if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ SystemBarBaseHelper.addVisibilityFlag(window, SystemBarBaseHelper.DEFAULT_IMMERSIVE_FLAGS);
+ SystemBarBaseHelper.addImmersiveFlagsToDecorView(
+ window, SystemBarBaseHelper.DEFAULT_IMMERSIVE_FLAGS);
+
+ // Also set the navigation bar and status bar to transparent color. Note that this
+ // doesn't work if android.R.boolean.config_enableTranslucentDecor is false.
+ window.setNavigationBarColor(Color.TRANSPARENT);
+ window.setStatusBarColor(Color.TRANSPARENT);
+ }
+ }
+
+ /**
+ * Reverts the actions of hideSystemBars. Note that this will remove the system UI visibility
+ * flags regardless of whether it is originally present. The status bar color is reset to
+ * transparent, thus it will show the status bar color set by StatusBarMixin.
+ *
+ * <p>This will only take effect in versions Lollipop or above. Otherwise this is a no-op.
+ */
+ public void showSystemBars(final Window window, final Context context) {
+ if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ SystemBarBaseHelper.removeVisibilityFlag(window, SystemBarBaseHelper.DEFAULT_IMMERSIVE_FLAGS);
+ SystemBarBaseHelper.removeImmersiveFlagsFromDecorView(
+ window, SystemBarBaseHelper.DEFAULT_IMMERSIVE_FLAGS);
+
+ if (context != null) {
+ if (applyPartnerResources) {
+ int partnerNavigationBarColor =
+ PartnerConfigHelper.get(context)
+ .getColor(context, PartnerConfig.CONFIG_NAVIGATION_BAR_BG_COLOR);
+ window.setStatusBarColor(Color.TRANSPARENT);
+ window.setNavigationBarColor(partnerNavigationBarColor);
+ } else {
+ if (templateLayout instanceof PartnerCustomizationLayout) {
+ window.setStatusBarColor(Color.TRANSPARENT);
+ window.setNavigationBarColor(sucSystemNavBarBackgroundColor);
+ } else {
+ // noinspection AndroidLintInlinedApi
+ final TypedArray typedArray =
+ context.obtainStyledAttributes(
+ new int[] {android.R.attr.statusBarColor, android.R.attr.navigationBarColor});
+ final int statusBarColor = typedArray.getColor(0, 0);
+ final int navigationBarColor = typedArray.getColor(1, 0);
+ window.setStatusBarColor(statusBarColor);
+ window.setNavigationBarColor(navigationBarColor);
+ typedArray.recycle();
+ }
+ }
+ }
+ }
+ }
}
diff --git a/main/java/com/google/android/setupcompat/util/ResourceEntry.java b/main/java/com/google/android/setupcompat/util/ResourceEntry.java
deleted file mode 100644
index c1dee67..0000000
--- a/main/java/com/google/android/setupcompat/util/ResourceEntry.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.setupcompat.util;
-
-import android.os.Bundle;
-import androidx.annotation.VisibleForTesting;
-
-/**
- * A potentially cross-package resource entry, which can then be retrieved using {@link
- * PackageManager#getApplicationForResources}. This class can also be sent across to other packages
- * on IPC via the Bundle representation.
- */
-public final class ResourceEntry {
- @VisibleForTesting static final String KEY_PACKAGE_NAME = "packageName";
- @VisibleForTesting static final String KEY_RESOURCE_NAME = "resourceName";
- @VisibleForTesting static final String KEY_RESOURCE_ID = "resourceId";
-
- private final String packageName;
- private final String resourceName;
- private final int resourceId;
-
- /**
- * Creates a {@code ResourceEntry} object from a provided bundle.
- *
- * @param bundle the source bundle needs to have all the information for a {@code ResourceEntry}
- */
- public static ResourceEntry fromBundle(Bundle bundle) {
- String packageName;
- String resourceName;
- int resourceId;
- if (!bundle.containsKey(KEY_PACKAGE_NAME)
- || !bundle.containsKey(KEY_RESOURCE_NAME)
- || !bundle.containsKey(KEY_RESOURCE_ID)) {
- return null;
- }
- packageName = bundle.getString(KEY_PACKAGE_NAME);
- resourceName = bundle.getString(KEY_RESOURCE_NAME);
- resourceId = bundle.getInt(KEY_RESOURCE_ID);
- return new ResourceEntry(packageName, resourceName, resourceId);
- }
-
- public ResourceEntry(String packageName, String resourceName, int resourceId) {
- this.packageName = packageName;
- this.resourceName = resourceName;
- this.resourceId = resourceId;
- }
-
- public String getPackageName() {
- return this.packageName;
- }
-
- public String getResourceName() {
- return this.resourceName;
- }
-
- public int getResourceId() {
- return this.resourceId;
- }
-
- /**
- * Returns a bundle representation of this resource entry, which can then be sent over IPC.
- *
- * @see #fromBundle(Bundle)
- */
- public Bundle toBundle() {
- Bundle result = new Bundle();
- result.putString(KEY_PACKAGE_NAME, packageName);
- result.putString(KEY_RESOURCE_NAME, resourceName);
- result.putInt(KEY_RESOURCE_ID, resourceId);
- return result;
- }
-}
diff --git a/main/java/com/google/android/setupcompat/util/SystemBarBaseHelper.java b/main/java/com/google/android/setupcompat/util/SystemBarBaseHelper.java
new file mode 100644
index 0000000..a1c5f65
--- /dev/null
+++ b/main/java/com/google/android/setupcompat/util/SystemBarBaseHelper.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2019 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.util;
+
+import android.annotation.SuppressLint;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+
+/**
+ * A helper class to manage the system navigation bar and status bar. This will add various
+ * systemUiVisibility flags to the given Window or View to make them follow the Setup Wizard style.
+ *
+ * <p>When the useImmersiveMode intent extra is true, a screen in Setup Wizard should hide the
+ * system bars using methods from this class. For Lollipop, {@link
+ * #hideSystemBars(android.view.Window)} will completely hide the system navigation bar and change
+ * the status bar to transparent, and layout the screen contents (usually the illustration) behind
+ * it.
+ */
+public class SystemBarBaseHelper {
+
+ private static final String TAG = "SystemBarBaseHelper";
+
+ @SuppressLint("InlinedApi")
+ public static final int DEFAULT_IMMERSIVE_FLAGS =
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+
+ @SuppressLint("InlinedApi")
+ public static final int DIALOG_IMMERSIVE_FLAGS =
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+
+ /**
+ * The maximum number of retries when peeking the decor view. When polling for the decor view,
+ * waiting it to be installed, set a maximum number of retries.
+ */
+ private static final int PEEK_DECOR_VIEW_RETRIES = 3;
+
+ /** Convenience method to add a visibility flag in addition to the existing ones. */
+ public static void addVisibilityFlag(final View view, final int flag) {
+ final int vis = view.getSystemUiVisibility();
+ view.setSystemUiVisibility(vis | flag);
+ }
+
+ /** Convenience method to add a visibility flag in addition to the existing ones. */
+ public static void addVisibilityFlag(final Window window, final int flag) {
+ WindowManager.LayoutParams attrs = window.getAttributes();
+ attrs.systemUiVisibility |= flag;
+ window.setAttributes(attrs);
+ }
+
+ /**
+ * Convenience method to remove a visibility flag from the view, leaving other flags that are not
+ * specified intact.
+ */
+ public static void removeVisibilityFlag(final View view, final int flag) {
+ final int vis = view.getSystemUiVisibility();
+ view.setSystemUiVisibility(vis & ~flag);
+ }
+
+ /**
+ * Convenience method to remove a visibility flag from the window, leaving other flags that are
+ * not specified intact.
+ */
+ public static void removeVisibilityFlag(final Window window, final int flag) {
+ WindowManager.LayoutParams attrs = window.getAttributes();
+ attrs.systemUiVisibility &= ~flag;
+ window.setAttributes(attrs);
+ }
+
+ /**
+ * Add the specified immersive flags to the decor view of the window, because {@link
+ * View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} only takes effect when it is added to a view instead of
+ * the window.
+ */
+ public static void addImmersiveFlagsToDecorView(final Window window, final int vis) {
+ getDecorView(
+ window,
+ new OnDecorViewInstalledListener() {
+ @Override
+ public void onDecorViewInstalled(View decorView) {
+ addVisibilityFlag(decorView, vis);
+ }
+ });
+ }
+
+ public static void removeImmersiveFlagsFromDecorView(final Window window, final int vis) {
+ getDecorView(
+ window,
+ new OnDecorViewInstalledListener() {
+ @Override
+ public void onDecorViewInstalled(View decorView) {
+ removeVisibilityFlag(decorView, vis);
+ }
+ });
+ }
+
+ private static void getDecorView(Window window, OnDecorViewInstalledListener callback) {
+ new DecorViewFinder().getDecorView(window, callback, PEEK_DECOR_VIEW_RETRIES);
+ }
+
+ private static class DecorViewFinder {
+
+ private final Handler handler = new Handler();
+ private Window window;
+ private int retries;
+ private OnDecorViewInstalledListener callback;
+
+ private final Runnable checkDecorViewRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ // Use peekDecorView instead of getDecorView so that clients can still set window
+ // features after calling this method.
+ final View decorView = window.peekDecorView();
+ if (decorView != null) {
+ callback.onDecorViewInstalled(decorView);
+ } else {
+ retries--;
+ if (retries >= 0) {
+ // If the decor view is not installed yet, try again in the next loop.
+ handler.post(checkDecorViewRunnable);
+ } else {
+ Log.w(TAG, "Cannot get decor view of window: " + window);
+ }
+ }
+ }
+ };
+
+ public void getDecorView(Window window, OnDecorViewInstalledListener callback, int retries) {
+ this.window = window;
+ this.retries = retries;
+ this.callback = callback;
+ checkDecorViewRunnable.run();
+ }
+ }
+
+ private interface OnDecorViewInstalledListener {
+
+ void onDecorViewInstalled(View decorView);
+ }
+}