summaryrefslogtreecommitdiff
path: root/main/java/com/google/android/setupcompat
diff options
context:
space:
mode:
authorSetup Wizard Team <android-setup-team-eng@google.com>2019-04-01 10:27:47 +0800
committerpastychang <pastychang@google.com>2019-04-01 14:42:38 +0800
commitde9b52a147ec3aeb290d01d0eda1867aa3103acd (patch)
tree61ec9c94b17a8b3bac9064e687164d9b2d39725f /main/java/com/google/android/setupcompat
parent2c49b010bb28fcb7b9c666389308cee98db958a1 (diff)
downloadsetupcompat-de9b52a147ec3aeb290d01d0eda1867aa3103acd.tar.gz
Import updated Android SetupCompat Library 241248644
Copied from google3/third_party/java_src/android_libs/setupcompat Test: mm Included changes: - 241248644 Recycleview apply light theme. - 241246566 Replace isObjectEquals and hashCode to ObjectUtils#equals... - 240935991 Move SystemBarHelper from setupdesign to setupcompat. - 240910529 Fix lint errors and add #hashCode and #equals for CustomE... - 240908548 Force state refresh in button background before setting t... - 240263426 Add some code to avoid lint error for PartnerCustomizatio... - 240262587 Move the minSDK version declaration to BUILD file - 240190830 Don't suppress the Future return value error - 240189246 Move internal files to under internal package PiperOrigin-RevId: 241248644 Change-Id: I8d655b5abcb4899f5975fc75f24a256677f71a36
Diffstat (limited to 'main/java/com/google/android/setupcompat')
-rw-r--r--main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java91
-rw-r--r--main/java/com/google/android/setupcompat/internal/BuildCompat.java (renamed from main/java/com/google/android/setupcompat/util/BuildCompat.java)3
-rw-r--r--main/java/com/google/android/setupcompat/internal/FallbackThemeWrapper.java (renamed from main/java/com/google/android/setupcompat/util/FallbackThemeWrapper.java)2
-rw-r--r--main/java/com/google/android/setupcompat/internal/LifecycleFragment.java (renamed from main/java/com/google/android/setupcompat/lifecycle/LifecycleFragment.java)5
-rw-r--r--main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java3
-rw-r--r--main/java/com/google/android/setupcompat/internal/TemplateLayout.java9
-rw-r--r--main/java/com/google/android/setupcompat/logging/CustomEvent.java22
-rw-r--r--main/java/com/google/android/setupcompat/logging/MetricKey.java12
-rw-r--r--main/java/com/google/android/setupcompat/template/FooterBarMixin.java4
-rw-r--r--main/java/com/google/android/setupcompat/template/FooterButton.java5
-rw-r--r--main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java13
-rw-r--r--main/java/com/google/android/setupcompat/util/ObjectUtils.java35
-rw-r--r--main/java/com/google/android/setupcompat/util/SystemBarBaseHelper.java160
-rw-r--r--main/java/com/google/android/setupcompat/util/SystemBarHelper.java352
-rw-r--r--main/java/com/google/android/setupcompat/util/WizardManagerHelper.java1
15 files changed, 496 insertions, 221 deletions
diff --git a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
index 4a95727..0e85268 100644
--- a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
+++ b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
@@ -21,6 +21,7 @@ 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;
@@ -31,9 +32,10 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import com.google.android.setupcompat.internal.BuildCompat;
+import com.google.android.setupcompat.internal.LifecycleFragment;
import com.google.android.setupcompat.internal.PersistableBundles;
import com.google.android.setupcompat.internal.TemplateLayout;
-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;
@@ -43,7 +45,6 @@ import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupcompat.template.StatusBarMixin;
import com.google.android.setupcompat.template.SystemNavBarMixin;
-import com.google.android.setupcompat.util.BuildCompat;
import com.google.android.setupcompat.util.WizardManagerHelper;
/** A templatization layout with consistent style used in Setup Wizard or app itself. */
@@ -56,7 +57,7 @@ public class PartnerCustomizationLayout extends TemplateLayout {
* the {@code app:sucUsePartnerResource} XML attribute. Note that when running in setup wizard,
* this is always overridden to true.
*/
- private boolean usePartnerResourceAttr = true;
+ private boolean usePartnerResourceAttr;
private Activity activity;
@@ -85,9 +86,6 @@ public class PartnerCustomizationLayout extends TemplateLayout {
}
private void init(AttributeSet attrs, int defStyleAttr) {
- activity = lookupActivityFromContext(getContext());
-
- boolean isSetupFlow = WizardManagerHelper.isAnySetupWizard(activity.getIntent());
TypedArray a =
getContext()
@@ -97,32 +95,12 @@ public class PartnerCustomizationLayout extends TemplateLayout {
boolean layoutFullscreen =
a.getBoolean(R.styleable.SucPartnerCustomizationLayout_sucLayoutFullscreen, true);
- if (!a.hasValue(R.styleable.SucPartnerCustomizationLayout_sucUsePartnerResource)) {
- // TODO(b/128961334): Enable Log.WTF after other client already set sucUsePartnerResource.
- Log.e(TAG, "Attribute sucUsePartnerResource not found in " + activity.getComponentName());
- }
-
- usePartnerResourceAttr =
- isSetupFlow
- || a.getBoolean(R.styleable.SucPartnerCustomizationLayout_sucUsePartnerResource, true);
-
a.recycle();
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && layoutFullscreen) {
setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
- Log.i(
- TAG,
- "activity="
- + activity.getClass().getSimpleName()
- + " isSetupFlow="
- + isSetupFlow
- + " enablePartnerResourceLoading="
- + enablePartnerResourceLoading()
- + " usePartnerResourceAttr="
- + usePartnerResourceAttr);
-
registerMixin(
StatusBarMixin.class, new StatusBarMixin(this, activity.getWindow(), attrs, defStyleAttr));
registerMixin(SystemNavBarMixin.class, new SystemNavBarMixin(this, activity.getWindow()));
@@ -133,9 +111,11 @@ public class PartnerCustomizationLayout extends TemplateLayout {
// 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 (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ 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 (shouldApplyPartnerResource()) {
updateContentBackgroundColorWithPartnerConfig();
@@ -150,6 +130,55 @@ public class PartnerCustomizationLayout extends TemplateLayout {
return inflateTemplate(inflater, 0, template);
}
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This method sets all these flags before onTemplateInflated since it will be too late and get
+ * incorrect flag value on PartnerCustomizationLayout if sets them after onTemplateInflated.
+ */
+ @Override
+ protected void onBeforeTemplateInflated(AttributeSet attrs, int defStyleAttr) {
+
+ boolean isSetupFlow;
+
+ // Sets default value to true since this timing
+ // before PartnerCustomization members initialization
+ usePartnerResourceAttr = true;
+
+ activity = lookupActivityFromContext(getContext());
+
+ isSetupFlow = WizardManagerHelper.isAnySetupWizard(activity.getIntent());
+
+ TypedArray a =
+ getContext()
+ .obtainStyledAttributes(
+ attrs, R.styleable.SucPartnerCustomizationLayout, defStyleAttr, 0);
+
+ if (!a.hasValue(R.styleable.SucPartnerCustomizationLayout_sucUsePartnerResource)) {
+ // TODO(b/128961334): Enable Log.WTF after other client already set sucUsePartnerResource.
+ Log.e(TAG, "Attribute sucUsePartnerResource not found in " + activity.getComponentName());
+ }
+
+ usePartnerResourceAttr =
+ isSetupFlow
+ || a.getBoolean(R.styleable.SucPartnerCustomizationLayout_sucUsePartnerResource, true);
+
+ a.recycle();
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(
+ TAG,
+ "activity="
+ + activity.getClass().getSimpleName()
+ + " isSetupFlow="
+ + isSetupFlow
+ + " enablePartnerResourceLoading="
+ + enablePartnerResourceLoading()
+ + " usePartnerResourceAttr="
+ + usePartnerResourceAttr);
+ }
+ }
+
@Override
protected ViewGroup findContainer(int containerId) {
if (containerId == 0) {
@@ -168,7 +197,9 @@ public class PartnerCustomizationLayout extends TemplateLayout {
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- if (BuildCompat.isAtLeastQ() && WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
+ if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP
+ && BuildCompat.isAtLeastQ()
+ && WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
FooterBarMixin footerBarMixin = getMixin(FooterBarMixin.class);
footerBarMixin.onDetachedFromWindow();
FooterButton primaryButton = footerBarMixin.getPrimaryButton();
diff --git a/main/java/com/google/android/setupcompat/util/BuildCompat.java b/main/java/com/google/android/setupcompat/internal/BuildCompat.java
index b9d3a79..54c0911 100644
--- a/main/java/com/google/android/setupcompat/util/BuildCompat.java
+++ b/main/java/com/google/android/setupcompat/internal/BuildCompat.java
@@ -1,8 +1,9 @@
-package com.google.android.setupcompat.util;
+package com.google.android.setupcompat.internal;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
+/** Utility methods for detecting the build API version. */
public final class BuildCompat {
private BuildCompat() {}
diff --git a/main/java/com/google/android/setupcompat/util/FallbackThemeWrapper.java b/main/java/com/google/android/setupcompat/internal/FallbackThemeWrapper.java
index 917b9d7..af17a62 100644
--- a/main/java/com/google/android/setupcompat/util/FallbackThemeWrapper.java
+++ b/main/java/com/google/android/setupcompat/internal/FallbackThemeWrapper.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.android.setupcompat.util;
+package com.google.android.setupcompat.internal;
import android.content.Context;
import android.content.res.Resources.Theme;
diff --git a/main/java/com/google/android/setupcompat/lifecycle/LifecycleFragment.java b/main/java/com/google/android/setupcompat/internal/LifecycleFragment.java
index 7eec881..269dd6d 100644
--- a/main/java/com/google/android/setupcompat/lifecycle/LifecycleFragment.java
+++ b/main/java/com/google/android/setupcompat/internal/LifecycleFragment.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.android.setupcompat.lifecycle;
+package com.google.android.setupcompat.internal;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
@@ -25,9 +25,6 @@ import android.content.Context;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.Log;
-import com.google.android.setupcompat.internal.ClockProvider;
-import com.google.android.setupcompat.internal.LayoutBindBackHelper;
-import com.google.android.setupcompat.internal.SetupCompatServiceInvoker;
import com.google.android.setupcompat.logging.MetricKey;
import com.google.android.setupcompat.logging.SetupMetricsLogger;
import com.google.android.setupcompat.util.WizardManagerHelper;
diff --git a/main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java b/main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java
index 0ed1d9f..a1ca156 100644
--- a/main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java
+++ b/main/java/com/google/android/setupcompat/internal/SetupCompatServiceInvoker.java
@@ -41,10 +41,9 @@ import java.util.concurrent.TimeoutException;
*/
public class SetupCompatServiceInvoker {
- @SuppressWarnings("FutureReturnValueIgnored")
public void logMetricEvent(@MetricType int metricType, Bundle args) {
try {
- loggingExecutor.submit(() -> invokeLogMetric(metricType, args));
+ loggingExecutor.execute(() -> invokeLogMetric(metricType, args));
} catch (RejectedExecutionException e) {
Log.e(TAG, String.format("Metric of type %d dropped since queue is full.", metricType), e);
}
diff --git a/main/java/com/google/android/setupcompat/internal/TemplateLayout.java b/main/java/com/google/android/setupcompat/internal/TemplateLayout.java
index 222d482..c6eba37 100644
--- a/main/java/com/google/android/setupcompat/internal/TemplateLayout.java
+++ b/main/java/com/google/android/setupcompat/internal/TemplateLayout.java
@@ -31,7 +31,6 @@ import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import com.google.android.setupcompat.R;
import com.google.android.setupcompat.template.Mixin;
-import com.google.android.setupcompat.util.FallbackThemeWrapper;
import java.util.HashMap;
import java.util.Map;
@@ -80,6 +79,7 @@ public class TemplateLayout extends FrameLayout {
if (containerId == 0) {
containerId = a.getResourceId(R.styleable.SucTemplateLayout_sucContainer, 0);
}
+ onBeforeTemplateInflated(attrs, defStyleAttr);
inflateTemplate(template, containerId);
a.recycle();
@@ -208,6 +208,13 @@ public class TemplateLayout extends FrameLayout {
protected void onTemplateInflated() {}
/**
+ * This is called before the template has been inflated and added to the view hierarchy.
+ * Subclasses can implement this method to modify the template as necessary, such as something
+ * need to be done before onTemplateInflated which is called while still in the constructor.
+ */
+ protected void onBeforeTemplateInflated(AttributeSet attrs, int defStyleAttr) {}
+
+ /**
* @return ID of the default container for this layout. This will be used to find the container
* ViewGroup, which all children views of this layout will be placed in.
* @deprecated Override {@link #findContainer(int)} instead.
diff --git a/main/java/com/google/android/setupcompat/logging/CustomEvent.java b/main/java/com/google/android/setupcompat/logging/CustomEvent.java
index 98f8d91..18d8f3c 100644
--- a/main/java/com/google/android/setupcompat/logging/CustomEvent.java
+++ b/main/java/com/google/android/setupcompat/logging/CustomEvent.java
@@ -18,13 +18,16 @@ package com.google.android.setupcompat.logging;
import static com.google.android.setupcompat.internal.Validations.assertLengthInRange;
+import android.annotation.TargetApi;
+import android.os.Build.VERSION_CODES;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import androidx.annotation.VisibleForTesting;
+import com.google.android.setupcompat.internal.BuildCompat;
import com.google.android.setupcompat.internal.ClockProvider;
import com.google.android.setupcompat.internal.Preconditions;
-import java.util.Objects;
+import com.google.android.setupcompat.util.ObjectUtils;
/**
* This class represents a interesting event at a particular point in time. The event is identified
@@ -32,16 +35,21 @@ import java.util.Objects;
* providing more attributes associated with the given event. Only primitive values are supported
* for now (int, long, double, float, String).
*/
+@TargetApi(VERSION_CODES.Q)
public final class CustomEvent implements Parcelable {
/** Creates a new instance of {@code CustomEvent}. Null arguments are not allowed. */
public static CustomEvent create(
MetricKey metricKey, PersistableBundle bundle, PersistableBundle piiValues) {
+ Preconditions.checkArgument(
+ BuildCompat.isAtLeastQ(), "The constructor only support on sdk Q or higher");
return new CustomEvent(ClockProvider.timeInMillis(), metricKey, bundle, piiValues);
}
/** Creates a new instance of {@code CustomEvent}. Null arguments are not allowed. */
public static CustomEvent create(MetricKey metricKey, PersistableBundle bundle) {
+ Preconditions.checkArgument(
+ BuildCompat.isAtLeastQ(), "The constructor only support on sdk Q or higher.");
return create(metricKey, bundle, PersistableBundle.EMPTY);
}
@@ -52,8 +60,8 @@ public final class CustomEvent implements Parcelable {
return new CustomEvent(
in.readLong(),
in.readParcelable(MetricKey.class.getClassLoader()),
- in.readPersistableBundle(),
- in.readPersistableBundle());
+ in.readPersistableBundle(MetricKey.class.getClassLoader()),
+ in.readPersistableBundle(MetricKey.class.getClassLoader()));
}
@Override
@@ -108,14 +116,14 @@ public final class CustomEvent implements Parcelable {
}
CustomEvent that = (CustomEvent) o;
return timestampMillis == that.timestampMillis
- && Objects.equals(metricKey, that.metricKey)
- && Objects.equals(persistableBundle, that.persistableBundle)
- && Objects.equals(piiValues, that.piiValues);
+ && ObjectUtils.equals(metricKey, that.metricKey)
+ && ObjectUtils.equals(persistableBundle, that.persistableBundle)
+ && ObjectUtils.equals(piiValues, that.piiValues);
}
@Override
public int hashCode() {
- return Objects.hash(timestampMillis, metricKey, persistableBundle, piiValues);
+ return ObjectUtils.hashCode(timestampMillis, metricKey, persistableBundle, piiValues);
}
private CustomEvent(
diff --git a/main/java/com/google/android/setupcompat/logging/MetricKey.java b/main/java/com/google/android/setupcompat/logging/MetricKey.java
index b369b9f..8d3692c 100644
--- a/main/java/com/google/android/setupcompat/logging/MetricKey.java
+++ b/main/java/com/google/android/setupcompat/logging/MetricKey.java
@@ -22,7 +22,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import com.google.android.setupcompat.internal.Preconditions;
-import java.util.Arrays;
+import com.google.android.setupcompat.util.ObjectUtils;
import java.util.regex.Pattern;
/**
@@ -99,13 +99,13 @@ public final class MetricKey implements Parcelable {
return false;
}
MetricKey metricKey = (MetricKey) o;
- return isObjectEquals(name, metricKey.name) && isObjectEquals(screenName, metricKey.screenName);
+ return ObjectUtils.equals(name, metricKey.name)
+ && ObjectUtils.equals(screenName, metricKey.screenName);
}
@Override
public int hashCode() {
- Object[] o = {name, screenName};
- return Arrays.hashCode(o);
+ return ObjectUtils.hashCode(name, screenName);
}
private MetricKey(String name, String screenName) {
@@ -113,10 +113,6 @@ public final class MetricKey implements Parcelable {
this.screenName = screenName;
}
- private boolean isObjectEquals(Object a, Object b) {
- return (a == b) || (a != null && a.equals(b));
- }
-
private final String name;
private final String screenName;
diff --git a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
index 4a52414..005471d 100644
--- a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
@@ -612,6 +612,7 @@ public class FooterBarMixin implements Mixin {
private static PartnerConfig getDrawablePartnerConfig(@ButtonType int buttonType) {
PartnerConfig result;
+ // LINT.IfChange
switch (buttonType) {
case ButtonType.ADD_ANOTHER:
result = PartnerConfig.CONFIG_FOOTER_BUTTON_ICON_ADD_ANOTHER;
@@ -642,6 +643,9 @@ public class FooterBarMixin implements Mixin {
result = null;
break;
}
+ // LINT.ThenChange(
+ // //depot/google3/third_party/java_src/android_libs/setupcompat/main/java/com/google/android/setupcompat/template/FooterButton.java,
+ // //depot/google3/third_party/java_src/android_libs/setupcompat/main/res/values/attrs.xml)
return result;
}
diff --git a/main/java/com/google/android/setupcompat/template/FooterButton.java b/main/java/com/google/android/setupcompat/template/FooterButton.java
index 457a116..35ea02c 100644
--- a/main/java/com/google/android/setupcompat/template/FooterButton.java
+++ b/main/java/com/google/android/setupcompat/template/FooterButton.java
@@ -212,6 +212,7 @@ public final class FooterButton implements OnClickListener {
this.theme = theme;
}
+ // LINT.IfChange
/** Maximum valid value of ButtonType */
private static final int MAX_BUTTON_TYPE = 8;
@@ -265,6 +266,7 @@ public final class FooterButton implements OnClickListener {
}
private String getButtonTypeName() {
+ // LINT.IfChange
switch (buttonType) {
case ButtonType.ADD_ANOTHER:
return "ADD_ANOTHER";
@@ -286,6 +288,9 @@ public final class FooterButton implements OnClickListener {
default:
return "OTHER";
}
+ // LINT.ThenChange(
+ // //depot/google3/third_party/java_src/android_libs/setupcompat/main/java/com/google/android/setupcompat/template/FooterBarMixin.java,
+ // //depot/google3/third_party/java_src/android_libs/setupcompat/main/res/values/attrs.xml)
}
/**
diff --git a/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java b/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java
index c273b43..a3ce567 100644
--- a/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java
@@ -36,7 +36,7 @@ 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;
+import com.google.android.setupcompat.util.SystemBarHelper;
/**
* A {@link Mixin} for setting and getting background color and window compatible with light theme
@@ -163,9 +163,8 @@ public class SystemNavBarMixin implements Mixin {
*/
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);
+ SystemBarHelper.addVisibilityFlag(window, SystemBarHelper.DEFAULT_IMMERSIVE_FLAGS);
+ SystemBarHelper.addImmersiveFlagsToDecorView(window, SystemBarHelper.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.
@@ -183,9 +182,9 @@ public class SystemNavBarMixin implements Mixin {
*/
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);
+ SystemBarHelper.removeVisibilityFlag(window, SystemBarHelper.DEFAULT_IMMERSIVE_FLAGS);
+ SystemBarHelper.removeImmersiveFlagsFromDecorView(
+ window, SystemBarHelper.DEFAULT_IMMERSIVE_FLAGS);
if (context != null) {
if (applyPartnerResources) {
diff --git a/main/java/com/google/android/setupcompat/util/ObjectUtils.java b/main/java/com/google/android/setupcompat/util/ObjectUtils.java
new file mode 100644
index 0000000..cd5299d
--- /dev/null
+++ b/main/java/com/google/android/setupcompat/util/ObjectUtils.java
@@ -0,0 +1,35 @@
+/*
+ * 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 java.util.Arrays;
+
+/** The util for {@link java.util.Objects} method to suitable in all sdk version. */
+public final class ObjectUtils {
+
+ private ObjectUtils() {}
+
+ /** Copied from {@link java.util.Objects#hash(Object...)} */
+ public static int hashCode(Object... args) {
+ return Arrays.hashCode(args);
+ }
+
+ /** Copied from {@link java.util.Objects#equals(Object, Object)} */
+ public static boolean equals(Object a, Object b) {
+ return (a == b) || (a != null && a.equals(b));
+ }
+}
diff --git a/main/java/com/google/android/setupcompat/util/SystemBarBaseHelper.java b/main/java/com/google/android/setupcompat/util/SystemBarBaseHelper.java
deleted file mode 100644
index a1c5f65..0000000
--- a/main/java/com/google/android/setupcompat/util/SystemBarBaseHelper.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * 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);
- }
-}
diff --git a/main/java/com/google/android/setupcompat/util/SystemBarHelper.java b/main/java/com/google/android/setupcompat/util/SystemBarHelper.java
new file mode 100644
index 0000000..60d5283
--- /dev/null
+++ b/main/java/com/google/android/setupcompat/util/SystemBarHelper.java
@@ -0,0 +1,352 @@
+/*
+ * 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.annotation.TargetApi;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.os.Handler;
+import androidx.annotation.RequiresPermission;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+
+/**
+ * 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 final class SystemBarHelper {
+
+ private static final String TAG = "SystemBarHelper";
+
+ /** Needs to be equal to View.STATUS_BAR_DISABLE_BACK */
+ private static final int STATUS_BAR_DISABLE_BACK = 0x00400000;
+
+ @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);
+ }
+ });
+ }
+
+ /**
+ * Hide the navigation bar for a dialog.
+ *
+ * <p>This will only take effect in versions Lollipop or above. Otherwise this is a no-op.
+ *
+ * @deprecated If the layout is instance of TemplateLayout, please use
+ * SystemNavBarMixin.hideSystemBars.
+ */
+ @Deprecated
+ public static void hideSystemBars(final Dialog dialog) {
+ if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ final Window window = dialog.getWindow();
+ temporarilyDisableDialogFocus(window);
+ SystemBarHelper.addVisibilityFlag(window, SystemBarHelper.DIALOG_IMMERSIVE_FLAGS);
+ SystemBarHelper.addImmersiveFlagsToDecorView(window, SystemBarHelper.DIALOG_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(0);
+ window.setStatusBarColor(0);
+ }
+ }
+
+ /**
+ * Hide 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.
+ *
+ * @deprecated If the layout instance of TemplateLayout, please use
+ * SystemNavBarMixin.hideSystemBars.
+ */
+ @Deprecated
+ public static void hideSystemBars(final Window window) {
+ if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ SystemBarHelper.addVisibilityFlag(window, SystemBarHelper.DEFAULT_IMMERSIVE_FLAGS);
+ SystemBarHelper.addImmersiveFlagsToDecorView(window, SystemBarHelper.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(0);
+ window.setStatusBarColor(0);
+ }
+ }
+
+ /**
+ * Revert the actions of hideSystemBars. Note that this will remove the system UI visibility flags
+ * regardless of whether it is originally present. You should also manually reset the navigation
+ * bar and status bar colors, as this method doesn't know what value to revert it to.
+ *
+ * @deprecated If the layout is instance of TemplateLayout, please use
+ * SystemNavBarMixin.showSystemBars.
+ */
+ @Deprecated
+ public static void showSystemBars(final Window window, final Context context) {
+ if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ SystemBarHelper.removeVisibilityFlag(window, SystemBarHelper.DEFAULT_IMMERSIVE_FLAGS);
+ SystemBarHelper.removeImmersiveFlagsFromDecorView(
+ window, SystemBarHelper.DEFAULT_IMMERSIVE_FLAGS);
+
+ if (context != null) {
+ //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();
+ }
+ }
+ }
+
+ /**
+ * Sets whether the back button on the software navigation bar is visible. This only works if you
+ * have the STATUS_BAR permission. Otherwise framework will filter out this flag and this method
+ * call will not have any effect.
+ *
+ * <p>IMPORTANT: Do not assume that users have no way to go back when the back button is hidden.
+ * Many devices have physical back buttons, and accessibility services like TalkBack may have
+ * gestures mapped to back. Please use onBackPressed, onKeyDown, or other similar ways to make
+ * sure back button events are still handled (or ignored) properly.
+ */
+ @RequiresPermission("android.permission.STATUS_BAR")
+ public static void setBackButtonVisible(final Window window, final boolean visible) {
+ if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
+ if (visible) {
+ SystemBarHelper.removeVisibilityFlag(window, STATUS_BAR_DISABLE_BACK);
+ SystemBarHelper.removeImmersiveFlagsFromDecorView(window, STATUS_BAR_DISABLE_BACK);
+ } else {
+ SystemBarHelper.addVisibilityFlag(window, STATUS_BAR_DISABLE_BACK);
+ SystemBarHelper.addImmersiveFlagsToDecorView(window, STATUS_BAR_DISABLE_BACK);
+ }
+ }
+ }
+
+ /**
+ * Set a view to be resized when the keyboard is shown. This will set the bottom margin of the
+ * view to be immediately above the keyboard, and assumes that the view sits immediately above the
+ * navigation bar.
+ *
+ * <p>Note that you must set {@link android.R.attr#windowSoftInputMode} to {@code adjustResize}
+ * for this class to work. Otherwise window insets are not dispatched and this method will have no
+ * effect.
+ *
+ * <p>This will only take effect in versions Lollipop or above. Otherwise this is a no-op.
+ *
+ * @param view The view to be resized when the keyboard is shown.
+ */
+ public static void setImeInsetView(final View view) {
+ if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ view.setOnApplyWindowInsetsListener(new WindowInsetsListener());
+ }
+ }
+
+ /**
+ * Apply a hack to temporarily set the window to not focusable, so that the navigation bar will
+ * not show up during the transition.
+ */
+ private static void temporarilyDisableDialogFocus(final Window window) {
+ window.setFlags(
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+ // Add the SOFT_INPUT_IS_FORWARD_NAVIGATION_FLAG. This is normally done by the system when
+ // FLAG_NOT_FOCUSABLE is not set. Setting this flag allows IME to be shown automatically
+ // if the dialog has editable text fields.
+ window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION);
+ new Handler()
+ .post(
+ new Runnable() {
+ @Override
+ public void run() {
+ window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+ }
+ });
+ }
+
+ @TargetApi(VERSION_CODES.LOLLIPOP)
+ private static class WindowInsetsListener implements View.OnApplyWindowInsetsListener {
+ private int bottomOffset;
+ private boolean hasCalculatedBottomOffset = false;
+
+ @Override
+ public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) {
+ if (!hasCalculatedBottomOffset) {
+ bottomOffset = getBottomDistance(view);
+ hasCalculatedBottomOffset = true;
+ }
+
+ int bottomInset = insets.getSystemWindowInsetBottom();
+
+ final int bottomMargin = Math.max(insets.getSystemWindowInsetBottom() - bottomOffset, 0);
+
+ final ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
+ // Check that we have enough space to apply the bottom margins before applying it.
+ // Otherwise the framework may think that the view is empty and exclude it from layout.
+ if (bottomMargin < lp.bottomMargin + view.getHeight()) {
+ lp.setMargins(lp.leftMargin, lp.topMargin, lp.rightMargin, bottomMargin);
+ view.setLayoutParams(lp);
+ bottomInset = 0;
+ }
+
+ return insets.replaceSystemWindowInsets(
+ insets.getSystemWindowInsetLeft(),
+ insets.getSystemWindowInsetTop(),
+ insets.getSystemWindowInsetRight(),
+ bottomInset);
+ }
+ }
+
+ private static int getBottomDistance(View view) {
+ int[] coords = new int[2];
+ view.getLocationInWindow(coords);
+ return view.getRootView().getHeight() - coords[1] - view.getHeight();
+ }
+
+ 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);
+ }
+}
diff --git a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
index 082bcca..d071dbf 100644
--- a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
+++ b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
@@ -23,6 +23,7 @@ import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
+import com.google.android.setupcompat.internal.BuildCompat;
import java.util.Arrays;
/**