| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2018 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package com.google.android.setupcompat; |
| 18 | |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 19 | import android.app.Activity; |
| 20 | import android.content.Context; |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 21 | import android.content.res.TypedArray; |
| Setup Wizard Team | de9b52a | 2019-04-01 10:27:47 +0800 | [diff] [blame] | 22 | import android.os.Build; |
| Setup Wizard Team | 1ed3073 | 2019-03-14 10:00:10 +0800 | [diff] [blame] | 23 | import android.os.Build.VERSION; |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 24 | import android.os.Build.VERSION_CODES; |
| Setup Wizard Team | 1d79d00 | 2018-12-13 14:30:13 +0800 | [diff] [blame] | 25 | import android.os.PersistableBundle; |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 26 | import android.util.AttributeSet; |
| 27 | import android.view.LayoutInflater; |
| 28 | import android.view.View; |
| 29 | import android.view.ViewGroup; |
| Setup Wizard Team | 55addcd | 2022-01-06 23:50:39 +0800 | [diff] [blame] | 30 | import android.view.ViewTreeObserver; |
| Pasty Chang | f3ba27a | 2025-02-23 23:52:07 -0800 | [diff] [blame^] | 31 | import android.view.WindowInsets; |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 32 | import android.view.WindowManager; |
| Pasty Chang | f3ba27a | 2025-02-23 23:52:07 -0800 | [diff] [blame^] | 33 | import android.widget.LinearLayout; |
| Setup Wizard Team | 55addcd | 2022-01-06 23:50:39 +0800 | [diff] [blame] | 34 | import androidx.annotation.VisibleForTesting; |
| 35 | import com.google.android.setupcompat.internal.FocusChangedMetricHelper; |
| Setup Wizard Team | de9b52a | 2019-04-01 10:27:47 +0800 | [diff] [blame] | 36 | import com.google.android.setupcompat.internal.LifecycleFragment; |
| Setup Wizard Team | d6fc4af | 2019-12-31 20:58:11 +0800 | [diff] [blame] | 37 | import com.google.android.setupcompat.internal.PersistableBundles; |
| Setup Wizard Team | 55addcd | 2022-01-06 23:50:39 +0800 | [diff] [blame] | 38 | import com.google.android.setupcompat.internal.SetupCompatServiceInvoker; |
| Maurice Lam | c452932 | 2019-02-14 22:04:56 +0000 | [diff] [blame] | 39 | import com.google.android.setupcompat.internal.TemplateLayout; |
| Setup Wizard Team | 1d79d00 | 2018-12-13 14:30:13 +0800 | [diff] [blame] | 40 | import com.google.android.setupcompat.logging.CustomEvent; |
| Setup Wizard Team | 4f5bbf4 | 2023-12-20 03:05:46 +0000 | [diff] [blame] | 41 | import com.google.android.setupcompat.logging.LoggingObserver; |
| 42 | import com.google.android.setupcompat.logging.LoggingObserver.SetupCompatUiEvent.LayoutInflatedEvent; |
| Setup Wizard Team | 1d79d00 | 2018-12-13 14:30:13 +0800 | [diff] [blame] | 43 | import com.google.android.setupcompat.logging.MetricKey; |
| 44 | import com.google.android.setupcompat.logging.SetupMetricsLogger; |
| Pasty Chang | f3ba27a | 2025-02-23 23:52:07 -0800 | [diff] [blame^] | 45 | import com.google.android.setupcompat.partnerconfig.PartnerConfig; |
| Setup Wizard Team | 1ed3073 | 2019-03-14 10:00:10 +0800 | [diff] [blame] | 46 | import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper; |
| Setup Wizard Team | d41e3be | 2019-01-17 16:07:58 +0800 | [diff] [blame] | 47 | import com.google.android.setupcompat.template.FooterBarMixin; |
| 48 | import com.google.android.setupcompat.template.FooterButton; |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 49 | import com.google.android.setupcompat.template.StatusBarMixin; |
| 50 | import com.google.android.setupcompat.template.SystemNavBarMixin; |
| Setup Wizard Team | 8a37aa8 | 2021-04-26 10:01:19 +0800 | [diff] [blame] | 51 | import com.google.android.setupcompat.util.BuildCompatUtils; |
| Setup Wizard Team | f513dd2 | 2021-06-10 15:49:36 +0800 | [diff] [blame] | 52 | import com.google.android.setupcompat.util.Logger; |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 53 | import com.google.android.setupcompat.util.WizardManagerHelper; |
| Setup Wizard Team | 0d3126a | 2022-04-27 09:03:55 +0800 | [diff] [blame] | 54 | import com.google.errorprone.annotations.CanIgnoreReturnValue; |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 55 | |
| 56 | /** A templatization layout with consistent style used in Setup Wizard or app itself. */ |
| 57 | public class PartnerCustomizationLayout extends TemplateLayout { |
| Setup Wizard Team | f513dd2 | 2021-06-10 15:49:36 +0800 | [diff] [blame] | 58 | |
| 59 | private static final Logger LOG = new Logger("PartnerCustomizationLayout"); |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 60 | |
| Setup Wizard Team | 67e7fef | 2019-03-23 08:06:48 +0800 | [diff] [blame] | 61 | /** |
| 62 | * Attribute indicating whether usage of partner theme resources is allowed. This corresponds to |
| 63 | * the {@code app:sucUsePartnerResource} XML attribute. Note that when running in setup wizard, |
| 64 | * this is always overridden to true. |
| 65 | */ |
| Setup Wizard Team | de9b52a | 2019-04-01 10:27:47 +0800 | [diff] [blame] | 66 | private boolean usePartnerResourceAttr; |
| Setup Wizard Team | 1ed3073 | 2019-03-14 10:00:10 +0800 | [diff] [blame] | 67 | |
| Setup Wizard Team | 8a37aa8 | 2021-04-26 10:01:19 +0800 | [diff] [blame] | 68 | /** |
| 69 | * Attribute indicating whether using full dynamic colors or not. This corresponds to the {@code |
| 70 | * app:sucFullDynamicColor} XML attribute. |
| 71 | */ |
| 72 | private boolean useFullDynamicColorAttr; |
| 73 | |
| 74 | /** |
| 75 | * Attribute indicating whether usage of dynamic is allowed. This corresponds to the existence of |
| 76 | * {@code app:sucFullDynamicColor} XML attribute. |
| 77 | */ |
| 78 | private boolean useDynamicColor; |
| 79 | |
| Pasty Chang | c2366eb | 2025-01-14 18:11:56 -0800 | [diff] [blame] | 80 | protected Activity activity; |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 81 | |
| David Liu | 534db6a | 2023-02-15 06:55:44 +0000 | [diff] [blame] | 82 | private PersistableBundle layoutTypeBundle; |
| 83 | |
| Pasty Chang | f3ba27a | 2025-02-23 23:52:07 -0800 | [diff] [blame^] | 84 | private int footerBarPaddingBottom; |
| 85 | |
| Setup Wizard Team | 0d3126a | 2022-04-27 09:03:55 +0800 | [diff] [blame] | 86 | @CanIgnoreReturnValue |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 87 | public PartnerCustomizationLayout(Context context) { |
| 88 | this(context, 0, 0); |
| 89 | } |
| 90 | |
| Setup Wizard Team | 0d3126a | 2022-04-27 09:03:55 +0800 | [diff] [blame] | 91 | @CanIgnoreReturnValue |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 92 | public PartnerCustomizationLayout(Context context, int template) { |
| 93 | this(context, template, 0); |
| 94 | } |
| 95 | |
| Setup Wizard Team | 0d3126a | 2022-04-27 09:03:55 +0800 | [diff] [blame] | 96 | @CanIgnoreReturnValue |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 97 | public PartnerCustomizationLayout(Context context, int template, int containerId) { |
| 98 | super(context, template, containerId); |
| 99 | init(null, R.attr.sucLayoutTheme); |
| 100 | } |
| 101 | |
| Setup Wizard Team | 0d3126a | 2022-04-27 09:03:55 +0800 | [diff] [blame] | 102 | @CanIgnoreReturnValue |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 103 | public PartnerCustomizationLayout(Context context, AttributeSet attrs) { |
| 104 | super(context, attrs); |
| 105 | init(attrs, R.attr.sucLayoutTheme); |
| 106 | } |
| 107 | |
| Setup Wizard Team | 0d3126a | 2022-04-27 09:03:55 +0800 | [diff] [blame] | 108 | @CanIgnoreReturnValue |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 109 | public PartnerCustomizationLayout(Context context, AttributeSet attrs, int defStyleAttr) { |
| 110 | super(context, attrs, defStyleAttr); |
| 111 | init(attrs, defStyleAttr); |
| 112 | } |
| 113 | |
| David Liu | 534db6a | 2023-02-15 06:55:44 +0000 | [diff] [blame] | 114 | @VisibleForTesting |
| 115 | final ViewTreeObserver.OnWindowFocusChangeListener windowFocusChangeListener = |
| 116 | this::onFocusChanged; |
| 117 | |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 118 | private void init(AttributeSet attrs, int defStyleAttr) { |
| Setup Wizard Team | 261f8b4 | 2021-03-04 09:18:37 +0800 | [diff] [blame] | 119 | if (isInEditMode()) { |
| 120 | return; |
| 121 | } |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 122 | |
| 123 | TypedArray a = |
| 124 | getContext() |
| 125 | .obtainStyledAttributes( |
| 126 | attrs, R.styleable.SucPartnerCustomizationLayout, defStyleAttr, 0); |
| 127 | |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 128 | boolean layoutFullscreen = |
| 129 | a.getBoolean(R.styleable.SucPartnerCustomizationLayout_sucLayoutFullscreen, true); |
| Setup Wizard Team | ff4fecb | 2018-12-19 11:31:37 +0800 | [diff] [blame] | 130 | |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 131 | a.recycle(); |
| 132 | |
| Pasty Chang | f3ba27a | 2025-02-23 23:52:07 -0800 | [diff] [blame^] | 133 | // Get the footer bar default padding bottom value. |
| 134 | TypedArray footerBarMixinAttrs = |
| 135 | getContext().obtainStyledAttributes(attrs, R.styleable.SucFooterBarMixin, defStyleAttr, 0); |
| 136 | int defaultPadding = |
| 137 | footerBarMixinAttrs.getDimensionPixelSize( |
| 138 | R.styleable.SucFooterBarMixin_sucFooterBarPaddingVertical, 0); |
| 139 | footerBarPaddingBottom = |
| 140 | footerBarMixinAttrs.getDimensionPixelSize( |
| 141 | R.styleable.SucFooterBarMixin_sucFooterBarPaddingBottom, defaultPadding); |
| 142 | |
| 143 | footerBarMixinAttrs.recycle(); |
| 144 | |
| Setup Wizard Team | 1ed3073 | 2019-03-14 10:00:10 +0800 | [diff] [blame] | 145 | if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && layoutFullscreen) { |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 146 | setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); |
| 147 | } |
| 148 | |
| Setup Wizard Team | ff4fecb | 2018-12-19 11:31:37 +0800 | [diff] [blame] | 149 | registerMixin( |
| Setup Wizard Team | 67e7fef | 2019-03-23 08:06:48 +0800 | [diff] [blame] | 150 | StatusBarMixin.class, new StatusBarMixin(this, activity.getWindow(), attrs, defStyleAttr)); |
| 151 | registerMixin(SystemNavBarMixin.class, new SystemNavBarMixin(this, activity.getWindow())); |
| 152 | registerMixin(FooterBarMixin.class, new FooterBarMixin(this, attrs, defStyleAttr)); |
| Setup Wizard Team | ff4fecb | 2018-12-19 11:31:37 +0800 | [diff] [blame] | 153 | |
| Setup Wizard Team | 1ed3073 | 2019-03-14 10:00:10 +0800 | [diff] [blame] | 154 | getMixin(SystemNavBarMixin.class).applyPartnerCustomizations(attrs, defStyleAttr); |
| 155 | |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 156 | // Override the FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, FLAG_TRANSLUCENT_STATUS, |
| 157 | // FLAG_TRANSLUCENT_NAVIGATION and SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN attributes of window forces |
| 158 | // showing status bar and navigation bar. |
| Setup Wizard Team | de9b52a | 2019-04-01 10:27:47 +0800 | [diff] [blame] | 159 | if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { |
| 160 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); |
| 161 | activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); |
| 162 | activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); |
| 163 | } |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 164 | } |
| 165 | |
| 166 | @Override |
| 167 | protected View onInflateTemplate(LayoutInflater inflater, int template) { |
| 168 | if (template == 0) { |
| 169 | template = R.layout.partner_customization_layout; |
| 170 | } |
| 171 | return inflateTemplate(inflater, 0, template); |
| 172 | } |
| 173 | |
| Setup Wizard Team | de9b52a | 2019-04-01 10:27:47 +0800 | [diff] [blame] | 174 | /** |
| 175 | * {@inheritDoc} |
| 176 | * |
| 177 | * <p>This method sets all these flags before onTemplateInflated since it will be too late and get |
| 178 | * incorrect flag value on PartnerCustomizationLayout if sets them after onTemplateInflated. |
| 179 | */ |
| 180 | @Override |
| 181 | protected void onBeforeTemplateInflated(AttributeSet attrs, int defStyleAttr) { |
| 182 | |
| Setup Wizard Team | de9b52a | 2019-04-01 10:27:47 +0800 | [diff] [blame] | 183 | // Sets default value to true since this timing |
| 184 | // before PartnerCustomization members initialization |
| 185 | usePartnerResourceAttr = true; |
| 186 | |
| 187 | activity = lookupActivityFromContext(getContext()); |
| 188 | |
| Setup Wizard Team | 75de590 | 2020-09-22 20:34:53 +0800 | [diff] [blame] | 189 | boolean isSetupFlow = WizardManagerHelper.isAnySetupWizard(activity.getIntent()); |
| Setup Wizard Team | de9b52a | 2019-04-01 10:27:47 +0800 | [diff] [blame] | 190 | |
| 191 | TypedArray a = |
| 192 | getContext() |
| 193 | .obtainStyledAttributes( |
| 194 | attrs, R.styleable.SucPartnerCustomizationLayout, defStyleAttr, 0); |
| 195 | |
| 196 | if (!a.hasValue(R.styleable.SucPartnerCustomizationLayout_sucUsePartnerResource)) { |
| Setup Wizard Team | 5b6b1b3 | 2019-04-02 13:42:22 +0800 | [diff] [blame] | 197 | // TODO: Enable Log.WTF after other client already set sucUsePartnerResource. |
| Setup Wizard Team | f513dd2 | 2021-06-10 15:49:36 +0800 | [diff] [blame] | 198 | LOG.e("Attribute sucUsePartnerResource not found in " + activity.getComponentName()); |
| Setup Wizard Team | de9b52a | 2019-04-01 10:27:47 +0800 | [diff] [blame] | 199 | } |
| 200 | |
| 201 | usePartnerResourceAttr = |
| 202 | isSetupFlow |
| 203 | || a.getBoolean(R.styleable.SucPartnerCustomizationLayout_sucUsePartnerResource, true); |
| 204 | |
| Setup Wizard Team | 8a37aa8 | 2021-04-26 10:01:19 +0800 | [diff] [blame] | 205 | useDynamicColor = a.hasValue(R.styleable.SucPartnerCustomizationLayout_sucFullDynamicColor); |
| 206 | useFullDynamicColorAttr = |
| 207 | a.getBoolean(R.styleable.SucPartnerCustomizationLayout_sucFullDynamicColor, false); |
| 208 | |
| Setup Wizard Team | de9b52a | 2019-04-01 10:27:47 +0800 | [diff] [blame] | 209 | a.recycle(); |
| 210 | |
| Setup Wizard Team | f513dd2 | 2021-06-10 15:49:36 +0800 | [diff] [blame] | 211 | LOG.atDebug( |
| 212 | "activity=" |
| 213 | + activity.getClass().getSimpleName() |
| 214 | + " isSetupFlow=" |
| 215 | + isSetupFlow |
| 216 | + " enablePartnerResourceLoading=" |
| 217 | + enablePartnerResourceLoading() |
| 218 | + " usePartnerResourceAttr=" |
| 219 | + usePartnerResourceAttr |
| 220 | + " useDynamicColor=" |
| 221 | + useDynamicColor |
| 222 | + " useFullDynamicColorAttr=" |
| 223 | + useFullDynamicColorAttr); |
| Setup Wizard Team | de9b52a | 2019-04-01 10:27:47 +0800 | [diff] [blame] | 224 | } |
| 225 | |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 226 | @Override |
| 227 | protected ViewGroup findContainer(int containerId) { |
| 228 | if (containerId == 0) { |
| 229 | containerId = R.id.suc_layout_content; |
| 230 | } |
| 231 | return super.findContainer(containerId); |
| 232 | } |
| 233 | |
| 234 | @Override |
| 235 | protected void onAttachedToWindow() { |
| 236 | super.onAttachedToWindow(); |
| Pasty Chang | cec90d0 | 2025-02-19 17:38:43 -0800 | [diff] [blame] | 237 | LifecycleFragment lifecycleFragment = |
| 238 | LifecycleFragment.attachNow(activity, this::logFooterButtonMetrics); |
| Pasty Chang | 1ca08d8 | 2025-02-16 18:10:28 -0800 | [diff] [blame] | 239 | if (lifecycleFragment == null) { |
| 240 | LOG.atDebug( |
| 241 | "Unable to attach lifecycle fragment to the host activity. Activity=" |
| 242 | + ((activity != null) ? activity.getClass().getSimpleName() : "null")); |
| 243 | } |
| 244 | |
| Setup Wizard Team | aa4a4b7 | 2023-10-16 04:39:29 +0000 | [diff] [blame] | 245 | if (WizardManagerHelper.isAnySetupWizard(activity.getIntent())) { |
| Setup Wizard Team | 55addcd | 2022-01-06 23:50:39 +0800 | [diff] [blame] | 246 | getViewTreeObserver().addOnWindowFocusChangeListener(windowFocusChangeListener); |
| 247 | } |
| Setup Wizard Team | d41e3be | 2019-01-17 16:07:58 +0800 | [diff] [blame] | 248 | getMixin(FooterBarMixin.class).onAttachedToWindow(); |
| Setup Wizard Team | 1d79d00 | 2018-12-13 14:30:13 +0800 | [diff] [blame] | 249 | } |
| 250 | |
| 251 | @Override |
| 252 | protected void onDetachedFromWindow() { |
| 253 | super.onDetachedFromWindow(); |
| Setup Wizard Team | 55addcd | 2022-01-06 23:50:39 +0800 | [diff] [blame] | 254 | if (VERSION.SDK_INT >= Build.VERSION_CODES.Q |
| Setup Wizard Team | de9b52a | 2019-04-01 10:27:47 +0800 | [diff] [blame] | 255 | && WizardManagerHelper.isAnySetupWizard(activity.getIntent())) { |
| Setup Wizard Team | d41e3be | 2019-01-17 16:07:58 +0800 | [diff] [blame] | 256 | FooterBarMixin footerBarMixin = getMixin(FooterBarMixin.class); |
| 257 | footerBarMixin.onDetachedFromWindow(); |
| 258 | FooterButton primaryButton = footerBarMixin.getPrimaryButton(); |
| 259 | FooterButton secondaryButton = footerBarMixin.getSecondaryButton(); |
| 260 | PersistableBundle primaryButtonMetrics = |
| 261 | primaryButton != null |
| 262 | ? primaryButton.getMetrics("PrimaryFooterButton") |
| 263 | : PersistableBundle.EMPTY; |
| 264 | PersistableBundle secondaryButtonMetrics = |
| 265 | secondaryButton != null |
| 266 | ? secondaryButton.getMetrics("SecondaryFooterButton") |
| 267 | : PersistableBundle.EMPTY; |
| 268 | |
| David Liu | 534db6a | 2023-02-15 06:55:44 +0000 | [diff] [blame] | 269 | PersistableBundle layoutTypeMetrics = |
| 270 | (layoutTypeBundle != null) ? layoutTypeBundle : PersistableBundle.EMPTY; |
| 271 | |
| Setup Wizard Team | d6fc4af | 2019-12-31 20:58:11 +0800 | [diff] [blame] | 272 | PersistableBundle persistableBundle = |
| 273 | PersistableBundles.mergeBundles( |
| David Liu | 534db6a | 2023-02-15 06:55:44 +0000 | [diff] [blame] | 274 | footerBarMixin.getLoggingMetrics(), |
| 275 | primaryButtonMetrics, |
| 276 | secondaryButtonMetrics, |
| 277 | layoutTypeMetrics); |
| Setup Wizard Team | d41e3be | 2019-01-17 16:07:58 +0800 | [diff] [blame] | 278 | |
| Setup Wizard Team | 1d79d00 | 2018-12-13 14:30:13 +0800 | [diff] [blame] | 279 | SetupMetricsLogger.logCustomEvent( |
| 280 | getContext(), |
| Setup Wizard Team | 24bb1e2 | 2019-11-08 15:22:51 +0800 | [diff] [blame] | 281 | CustomEvent.create(MetricKey.get("SetupCompatMetrics", activity), persistableBundle)); |
| Setup Wizard Team | 1d79d00 | 2018-12-13 14:30:13 +0800 | [diff] [blame] | 282 | } |
| Setup Wizard Team | aa4a4b7 | 2023-10-16 04:39:29 +0000 | [diff] [blame] | 283 | getViewTreeObserver().removeOnWindowFocusChangeListener(windowFocusChangeListener); |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 284 | } |
| 285 | |
| Pasty Chang | cec90d0 | 2025-02-19 17:38:43 -0800 | [diff] [blame] | 286 | private void logFooterButtonMetrics() { |
| 287 | if (VERSION.SDK_INT >= Build.VERSION_CODES.Q |
| 288 | && activity != null |
| 289 | && WizardManagerHelper.isAnySetupWizard(activity.getIntent()) |
| 290 | && PartnerConfigHelper.isEnhancedSetupDesignMetricsEnabled(getContext())) { |
| 291 | FooterBarMixin footerBarMixin = getMixin(FooterBarMixin.class); |
| 292 | |
| 293 | if (footerBarMixin == null |
| 294 | || (footerBarMixin.getPrimaryButton() == null |
| 295 | && footerBarMixin.getSecondaryButton() == null)) { |
| 296 | LOG.atDebug("Skip footer button logging because no footer buttons."); |
| 297 | return; |
| 298 | } |
| 299 | |
| 300 | footerBarMixin.onDetachedFromWindow(); |
| 301 | FooterButton primaryButton = footerBarMixin.getPrimaryButton(); |
| 302 | FooterButton secondaryButton = footerBarMixin.getSecondaryButton(); |
| 303 | PersistableBundle primaryButtonMetrics = |
| 304 | primaryButton != null |
| 305 | ? primaryButton.getMetrics("PrimaryFooterButton") |
| 306 | : PersistableBundle.EMPTY; |
| 307 | PersistableBundle secondaryButtonMetrics = |
| 308 | secondaryButton != null |
| 309 | ? secondaryButton.getMetrics("SecondaryFooterButton") |
| 310 | : PersistableBundle.EMPTY; |
| 311 | |
| 312 | PersistableBundle persistableBundle = |
| 313 | PersistableBundles.mergeBundles( |
| 314 | footerBarMixin.getLoggingMetrics(), primaryButtonMetrics, secondaryButtonMetrics); |
| 315 | |
| 316 | SetupMetricsLogger.logCustomEvent( |
| 317 | getContext(), |
| 318 | CustomEvent.create(MetricKey.get("FooterButtonMetrics", activity), persistableBundle)); |
| 319 | } |
| 320 | } |
| 321 | |
| David Liu | 534db6a | 2023-02-15 06:55:44 +0000 | [diff] [blame] | 322 | /** |
| Setup Wizard Team | 4f5bbf4 | 2023-12-20 03:05:46 +0000 | [diff] [blame] | 323 | * PartnerCustomizationLayout is a template layout for different type of GlifLayout. This method |
| 324 | * allows each type of layout to report its "GlifLayoutType". |
| David Liu | 534db6a | 2023-02-15 06:55:44 +0000 | [diff] [blame] | 325 | */ |
| 326 | public void setLayoutTypeMetrics(PersistableBundle bundle) { |
| 327 | this.layoutTypeBundle = bundle; |
| 328 | } |
| 329 | |
| 330 | /** Returns a {@link PersistableBundle} contains key "GlifLayoutType". */ |
| 331 | @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) |
| 332 | public PersistableBundle getLayoutTypeMetrics() { |
| 333 | return this.layoutTypeBundle; |
| 334 | } |
| 335 | |
| Setup Wizard Team | 642e096 | 2020-12-30 19:23:06 +0800 | [diff] [blame] | 336 | public static Activity lookupActivityFromContext(Context context) { |
| Setup Wizard Team | aa4a4b7 | 2023-10-16 04:39:29 +0000 | [diff] [blame] | 337 | return PartnerConfigHelper.lookupActivityFromContext(context); |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 338 | } |
| 339 | |
| 340 | /** |
| Setup Wizard Team | 67e7fef | 2019-03-23 08:06:48 +0800 | [diff] [blame] | 341 | * Returns true if partner resource loading is enabled. If true, and other necessary conditions |
| 342 | * for loading theme attributes are met, this layout will use customized theme attributes from OEM |
| 343 | * overlays. This is intended to be used with flag-based development, to allow a flag to control |
| 344 | * the rollout of partner resource loading. |
| Setup Wizard Team | d41e3be | 2019-01-17 16:07:58 +0800 | [diff] [blame] | 345 | */ |
| Setup Wizard Team | 67e7fef | 2019-03-23 08:06:48 +0800 | [diff] [blame] | 346 | protected boolean enablePartnerResourceLoading() { |
| Setup Wizard Team | d41e3be | 2019-01-17 16:07:58 +0800 | [diff] [blame] | 347 | return true; |
| 348 | } |
| 349 | |
| Setup Wizard Team | 1ed3073 | 2019-03-14 10:00:10 +0800 | [diff] [blame] | 350 | /** Returns if the current layout/activity applies partner customized configurations or not. */ |
| Setup Wizard Team | 67e7fef | 2019-03-23 08:06:48 +0800 | [diff] [blame] | 351 | public boolean shouldApplyPartnerResource() { |
| 352 | if (!enablePartnerResourceLoading()) { |
| 353 | return false; |
| 354 | } |
| 355 | if (!usePartnerResourceAttr) { |
| 356 | return false; |
| 357 | } |
| Setup Wizard Team | 3266cd8 | 2019-05-09 19:31:02 +0800 | [diff] [blame] | 358 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { |
| Setup Wizard Team | 67e7fef | 2019-03-23 08:06:48 +0800 | [diff] [blame] | 359 | return false; |
| 360 | } |
| 361 | if (!PartnerConfigHelper.get(getContext()).isAvailable()) { |
| 362 | return false; |
| 363 | } |
| 364 | return true; |
| Setup Wizard Team | 1ed3073 | 2019-03-14 10:00:10 +0800 | [diff] [blame] | 365 | } |
| Setup Wizard Team | 8a37aa8 | 2021-04-26 10:01:19 +0800 | [diff] [blame] | 366 | |
| 367 | /** |
| 368 | * Returns {@code true} if the current layout/activity applies dynamic color. Otherwise, returns |
| 369 | * {@code false}. |
| 370 | */ |
| 371 | public boolean shouldApplyDynamicColor() { |
| Setup Wizard Team | 8a37aa8 | 2021-04-26 10:01:19 +0800 | [diff] [blame] | 372 | if (!BuildCompatUtils.isAtLeastS()) { |
| 373 | return false; |
| 374 | } |
| Pasty Chang | 0a43918 | 2024-11-18 03:02:38 +0000 | [diff] [blame] | 375 | |
| Setup Wizard Team | 8a37aa8 | 2021-04-26 10:01:19 +0800 | [diff] [blame] | 376 | if (!PartnerConfigHelper.get(getContext()).isAvailable()) { |
| 377 | return false; |
| 378 | } |
| Pasty Chang | 0a43918 | 2024-11-18 03:02:38 +0000 | [diff] [blame] | 379 | |
| 380 | // If the dynamic theme is applied, useDynamicColor would be true and shouldApplyDynamicColor |
| 381 | // would return true. |
| 382 | if (useDynamicColor) { |
| 383 | return true; |
| 384 | } |
| 385 | if (!PartnerConfigHelper.isSetupWizardDynamicColorEnabled(getContext())) { |
| 386 | return false; |
| 387 | } |
| Setup Wizard Team | 8a37aa8 | 2021-04-26 10:01:19 +0800 | [diff] [blame] | 388 | return true; |
| 389 | } |
| 390 | |
| 391 | /** |
| 392 | * Returns {@code true} if the current layout/activity applies full dynamic color. Otherwise, |
| Pasty Chang | fc90bfe | 2024-10-07 12:01:17 +0000 | [diff] [blame] | 393 | * returns {@code false}. This method combines the result of {@link #shouldApplyDynamicColor()}, |
| 394 | * the value of the {@code app:sucFullDynamicColor}, and the result of {@link |
| 395 | * PartnerConfigHelper#isSetupWizardFullDynamicColorEnabled(Context)}. |
| Setup Wizard Team | 8a37aa8 | 2021-04-26 10:01:19 +0800 | [diff] [blame] | 396 | */ |
| 397 | public boolean useFullDynamicColor() { |
| Pasty Chang | fc90bfe | 2024-10-07 12:01:17 +0000 | [diff] [blame] | 398 | return shouldApplyDynamicColor() |
| 399 | && (useFullDynamicColorAttr |
| 400 | || PartnerConfigHelper.isSetupWizardFullDynamicColorEnabled(getContext())); |
| Setup Wizard Team | 8a37aa8 | 2021-04-26 10:01:19 +0800 | [diff] [blame] | 401 | } |
| Setup Wizard Team | 55addcd | 2022-01-06 23:50:39 +0800 | [diff] [blame] | 402 | |
| 403 | /** |
| Pasty Chang | fc90bfe | 2024-10-07 12:01:17 +0000 | [diff] [blame] | 404 | * Sets a logging observer for {@link FooterBarMixin}. The logging observer is used to log UI |
| 405 | * events (e.g. page impressions and button clicks) on the layout and footer bar buttons. |
| Setup Wizard Team | 4f5bbf4 | 2023-12-20 03:05:46 +0000 | [diff] [blame] | 406 | */ |
| 407 | public void setLoggingObserver(LoggingObserver loggingObserver) { |
| Setup Wizard Team | 4f5bbf4 | 2023-12-20 03:05:46 +0000 | [diff] [blame] | 408 | loggingObserver.log(new LayoutInflatedEvent(this)); |
| Pasty Chang | fc90bfe | 2024-10-07 12:01:17 +0000 | [diff] [blame] | 409 | getMixin(FooterBarMixin.class).setLoggingObserver(loggingObserver); |
| Setup Wizard Team | 4f5bbf4 | 2023-12-20 03:05:46 +0000 | [diff] [blame] | 410 | } |
| 411 | |
| 412 | /** |
| Setup Wizard Team | 55addcd | 2022-01-06 23:50:39 +0800 | [diff] [blame] | 413 | * Invoke the method onFocusStatusChanged when onWindowFocusChangeListener receive onFocusChanged. |
| 414 | */ |
| 415 | private void onFocusChanged(boolean hasFocus) { |
| 416 | SetupCompatServiceInvoker.get(getContext()) |
| 417 | .onFocusStatusChanged( |
| 418 | FocusChangedMetricHelper.getScreenName(activity), |
| 419 | FocusChangedMetricHelper.getExtraBundle( |
| 420 | activity, PartnerCustomizationLayout.this, hasFocus)); |
| 421 | } |
| Pasty Chang | f3ba27a | 2025-02-23 23:52:07 -0800 | [diff] [blame^] | 422 | |
| 423 | @Override |
| 424 | public WindowInsets onApplyWindowInsets(WindowInsets insets) { |
| 425 | // TODO: b/398407478 - Add test case for edge to edge to layout from library. |
| 426 | if (PartnerConfigHelper.isGlifExpressiveEnabled(getContext())) { |
| 427 | // Edge to edge extend the footer bar padding bottom to the navigation bar height. |
| 428 | if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && insets.getSystemWindowInsetBottom() > 0) { |
| 429 | LOG.atDebug("NavigationBarHeight: " + insets.getSystemWindowInsetBottom()); |
| 430 | FooterBarMixin footerBarMixin = getMixin(FooterBarMixin.class); |
| 431 | LinearLayout buttonContainer = footerBarMixin.getButtonContainer(); |
| 432 | if (footerBarMixin != null && footerBarMixin.getButtonContainer() != null) { |
| 433 | if (PartnerConfigHelper.get(getContext()) |
| 434 | .isPartnerConfigAvailable(PartnerConfig.CONFIG_FOOTER_BUTTON_PADDING_BOTTOM)) { |
| 435 | footerBarPaddingBottom = |
| 436 | (int) |
| 437 | PartnerConfigHelper.get(getContext()) |
| 438 | .getDimension( |
| 439 | getContext(), PartnerConfig.CONFIG_FOOTER_BUTTON_PADDING_BOTTOM); |
| 440 | } |
| 441 | // Adjust footer bar padding to account for the navigation bar, ensuring |
| 442 | // it extends to the bottom of the screen and with proper bottom padding. |
| 443 | buttonContainer.setPadding( |
| 444 | buttonContainer.getPaddingLeft(), |
| 445 | buttonContainer.getPaddingTop(), |
| 446 | buttonContainer.getPaddingRight(), |
| 447 | footerBarPaddingBottom + insets.getSystemWindowInsetBottom()); |
| 448 | } |
| 449 | } |
| 450 | } |
| 451 | return super.onApplyWindowInsets(insets); |
| 452 | } |
| Setup Wizard Team | 8ccc9e6 | 2018-11-28 13:33:48 +0800 | [diff] [blame] | 453 | } |