1 /* 2 * Copyright (C) 2015 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.util; 18 19 import android.app.Activity; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.os.Build; 23 import android.os.Build.VERSION; 24 import android.os.Build.VERSION_CODES; 25 import android.provider.Settings; 26 import androidx.annotation.Nullable; 27 import androidx.annotation.VisibleForTesting; 28 import java.util.Arrays; 29 30 /** 31 * Helper to interact with Wizard Manager in setup wizard, which should be used when a screen is 32 * shown inside the setup flow. This includes things like parsing extras passed by Wizard Manager, 33 * and invoking Wizard Manager to start the next action. 34 */ 35 public final class WizardManagerHelper { 36 37 @VisibleForTesting public static final String ACTION_NEXT = "com.android.wizard.NEXT"; 38 39 // EXTRA_SCRIPT_URI and EXTRA_ACTION_ID are used in setup wizard in versions before M and are 40 // kept for backwards compatibility. 41 @VisibleForTesting static final String EXTRA_SCRIPT_URI = "scriptUri"; 42 @VisibleForTesting static final String EXTRA_ACTION_ID = "actionId"; 43 44 @VisibleForTesting static final String EXTRA_WIZARD_BUNDLE = "wizardBundle"; 45 private static final String EXTRA_RESULT_CODE = "com.android.setupwizard.ResultCode"; 46 47 /** Extra for notifying an Activity that it is inside the first SetupWizard flow or not. */ 48 public static final String EXTRA_IS_FIRST_RUN = "firstRun"; 49 50 /** Extra for notifying an Activity that it is inside the Deferred SetupWizard flow or not. */ 51 public static final String EXTRA_IS_DEFERRED_SETUP = "deferredSetup"; 52 53 /** Extra for notifying an Activity that it is inside the "Pre-Deferred Setup" flow. */ 54 public static final String EXTRA_IS_PRE_DEFERRED_SETUP = "preDeferredSetup"; 55 56 /** Extra for notifying an Activity that it is inside the "Portal Setup" flow. */ 57 public static final String EXTRA_IS_PORTAL_SETUP = "portalSetup"; 58 59 /** 60 * Extra for notifying an Activity that it is inside the any setup flow. 61 * 62 * <p>Apps that target API levels below {@link android.os.Build.VERSION_CODES#Q} is able to 63 * determine whether Activity is inside the any setup flow by one of {@link #EXTRA_IS_FIRST_RUN}, 64 * {@link #EXTRA_IS_DEFERRED_SETUP}, and {@link #EXTRA_IS_PRE_DEFERRED_SETUP} is true. 65 */ 66 public static final String EXTRA_IS_SETUP_FLOW = "isSetupFlow"; 67 68 /** Extra for notifying an activity that was called from suggested action activity. */ 69 public static final String EXTRA_IS_SUW_SUGGESTED_ACTION_FLOW = "isSuwSuggestedActionFlow"; 70 71 public static final String EXTRA_THEME = "theme"; 72 public static final String EXTRA_USE_IMMERSIVE_MODE = "useImmersiveMode"; 73 74 public static final String SETTINGS_GLOBAL_DEVICE_PROVISIONED = "device_provisioned"; 75 public static final String SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete"; 76 77 /** 78 * Gets an intent that will invoke the next step of setup wizard. 79 * 80 * @param originalIntent The original intent that was used to start the step, usually via {@link 81 * Activity#getIntent()}. 82 * @param resultCode The result code of the step. See {@link ResultCodes}. 83 * @return A new intent that can be used with {@link Activity#startActivityForResult(Intent, int)} 84 * to start the next step of the setup flow. 85 */ getNextIntent(Intent originalIntent, int resultCode)86 public static Intent getNextIntent(Intent originalIntent, int resultCode) { 87 return getNextIntent(originalIntent, resultCode, null); 88 } 89 90 /** 91 * Gets an intent that will invoke the next step of setup wizard. 92 * 93 * @param originalIntent The original intent that was used to start the step, usually via {@link 94 * Activity#getIntent()}. 95 * @param resultCode The result code of the step. See {@link ResultCodes}. 96 * @param data An intent containing extra result data. 97 * @return A new intent that can be used with {@link Activity#startActivityForResult(Intent, int)} 98 * to start the next step of the setup flow. 99 */ getNextIntent(Intent originalIntent, int resultCode, Intent data)100 public static Intent getNextIntent(Intent originalIntent, int resultCode, Intent data) { 101 Intent intent = new Intent(ACTION_NEXT); 102 copyWizardManagerExtras(originalIntent, intent); 103 intent.putExtra(EXTRA_RESULT_CODE, resultCode); 104 if (data != null && data.getExtras() != null) { 105 intent.putExtras(data.getExtras()); 106 } 107 intent.putExtra(EXTRA_THEME, originalIntent.getStringExtra(EXTRA_THEME)); 108 109 return intent; 110 } 111 112 /** 113 * Copies the internal extras used by setup wizard from one intent to another. For low-level use 114 * only, such as when using {@link Intent#FLAG_ACTIVITY_FORWARD_RESULT} to relay to another 115 * intent. 116 * 117 * @param srcIntent Intent to get the wizard manager extras from. 118 * @param dstIntent Intent to copy the wizard manager extras to. 119 */ copyWizardManagerExtras(Intent srcIntent, Intent dstIntent)120 public static void copyWizardManagerExtras(Intent srcIntent, Intent dstIntent) { 121 dstIntent.putExtra(EXTRA_WIZARD_BUNDLE, srcIntent.getBundleExtra(EXTRA_WIZARD_BUNDLE)); 122 for (String key : 123 Arrays.asList( 124 EXTRA_IS_FIRST_RUN, 125 EXTRA_IS_DEFERRED_SETUP, 126 EXTRA_IS_PRE_DEFERRED_SETUP, 127 EXTRA_IS_PORTAL_SETUP, 128 EXTRA_IS_SETUP_FLOW, 129 EXTRA_IS_SUW_SUGGESTED_ACTION_FLOW)) { 130 dstIntent.putExtra(key, srcIntent.getBooleanExtra(key, false)); 131 } 132 133 for (String key : Arrays.asList(EXTRA_THEME, EXTRA_SCRIPT_URI, EXTRA_ACTION_ID)) { 134 dstIntent.putExtra(key, srcIntent.getStringExtra(key)); 135 } 136 } 137 138 /** @deprecated Use {@link isInitialSetupWizard} instead. */ 139 @Deprecated isSetupWizardIntent(Intent intent)140 public static boolean isSetupWizardIntent(Intent intent) { 141 return intent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false); 142 } 143 144 /** 145 * Checks whether the current user has completed Setup Wizard. This is true if the current user 146 * has gone through Setup Wizard. The current user may or may not be the device owner and the 147 * device owner may have already completed setup wizard. 148 * 149 * @param context The context to retrieve the settings. 150 * @return true if the current user has completed Setup Wizard. 151 * @see #isDeviceProvisioned(Context) 152 */ isUserSetupComplete(Context context)153 public static boolean isUserSetupComplete(Context context) { 154 if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) { 155 return Settings.Secure.getInt( 156 context.getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) 157 == 1; 158 } else { 159 // For versions below JB MR1, there are no user profiles. Just return the global device 160 // provisioned state. 161 return Settings.Secure.getInt( 162 context.getContentResolver(), SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0) 163 == 1; 164 } 165 } 166 167 /** 168 * Checks whether the device is provisioned. This means that the device has gone through Setup 169 * Wizard at least once. Note that the user can still be in Setup Wizard even if this is true, for 170 * a secondary user profile triggered through Settings > Add account. 171 * 172 * @param context The context to retrieve the settings. 173 * @return true if the device is provisioned. 174 * @see #isUserSetupComplete(Context) 175 */ isDeviceProvisioned(Context context)176 public static boolean isDeviceProvisioned(Context context) { 177 if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) { 178 return Settings.Global.getInt( 179 context.getContentResolver(), SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0) 180 == 1; 181 } else { 182 return Settings.Secure.getInt( 183 context.getContentResolver(), SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0) 184 == 1; 185 } 186 } 187 188 /** 189 * Checks whether an intent is running in the portal setup wizard flow. This API is supported 190 * since S. 191 * 192 * @param originalIntent The original intent that was used to start the step, usually via {@link 193 * Activity#getIntent()}. 194 * @return true if the intent passed in was running in portal setup wizard. 195 */ isPortalSetupWizard(Intent originalIntent)196 public static boolean isPortalSetupWizard(Intent originalIntent) { 197 return originalIntent != null && originalIntent.getBooleanExtra(EXTRA_IS_PORTAL_SETUP, false); 198 } 199 200 /** 201 * Checks whether an intent is running in the deferred setup wizard flow. 202 * 203 * @param originalIntent The original intent that was used to start the step, usually via {@link 204 * Activity#getIntent()}. 205 * @return true if the intent passed in was running in deferred setup wizard. 206 */ isDeferredSetupWizard(Intent originalIntent)207 public static boolean isDeferredSetupWizard(Intent originalIntent) { 208 return originalIntent != null && originalIntent.getBooleanExtra(EXTRA_IS_DEFERRED_SETUP, false); 209 } 210 211 /** 212 * Checks whether an intent is running in "pre-deferred" setup wizard flow. 213 * 214 * @param originalIntent The original intent that was used to start the step, usually via {@link 215 * Activity#getIntent()}. 216 * @return true if the intent passed in was running in "pre-deferred" setup wizard. 217 */ isPreDeferredSetupWizard(Intent originalIntent)218 public static boolean isPreDeferredSetupWizard(Intent originalIntent) { 219 return originalIntent != null 220 && originalIntent.getBooleanExtra(EXTRA_IS_PRE_DEFERRED_SETUP, false); 221 } 222 223 /** 224 * Checks whether an intent is is running in the initial setup wizard flow. 225 * 226 * @param intent The intent to be checked, usually from {@link Activity#getIntent()}. 227 * @return true if the intent passed in was intended to be used with setup wizard. 228 */ isInitialSetupWizard(Intent intent)229 public static boolean isInitialSetupWizard(Intent intent) { 230 return intent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false); 231 } 232 233 /** 234 * Since Q, returns true if the intent passed in indicates that it is running in setup wizard 235 * flows, including initial, predeferred, deferred. Since S, it also supports portal setup. 236 * 237 * <p>Pre-Q, it is running in three setup wizard flows, including initial, predeferred, deferred 238 * setup. 239 * 240 * @param originalIntent The original intent that was used to start the step, usually via {@link 241 * Activity#getIntent()}. 242 */ isAnySetupWizard(@ullable Intent originalIntent)243 public static boolean isAnySetupWizard(@Nullable Intent originalIntent) { 244 if (originalIntent == null) { 245 return false; 246 } 247 248 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 249 return originalIntent.getBooleanExtra(EXTRA_IS_SETUP_FLOW, false); 250 } else { 251 return isInitialSetupWizard(originalIntent) 252 || isPreDeferredSetupWizard(originalIntent) 253 || isDeferredSetupWizard(originalIntent); 254 } 255 } 256 WizardManagerHelper()257 private WizardManagerHelper() {} 258 } 259