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 class WizardManagerHelper { 36 37 private 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 @VisibleForTesting public static final String EXTRA_IS_FIRST_RUN = "firstRun"; 47 @VisibleForTesting static final String EXTRA_IS_DEFERRED_SETUP = "deferredSetup"; 48 @VisibleForTesting static final String EXTRA_IS_PRE_DEFERRED_SETUP = "preDeferredSetup"; 49 @VisibleForTesting public static final String EXTRA_IS_SETUP_FLOW = "isSetupFlow"; 50 51 public static final String EXTRA_THEME = "theme"; 52 public static final String EXTRA_USE_IMMERSIVE_MODE = "useImmersiveMode"; 53 54 public static final String SETTINGS_GLOBAL_DEVICE_PROVISIONED = "device_provisioned"; 55 public static final String SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete"; 56 57 /** 58 * Gets an intent that will invoke the next step of setup wizard. 59 * 60 * @param originalIntent The original intent that was used to start the step, usually via {@link 61 * Activity#getIntent()}. 62 * @param resultCode The result code of the step. See {@link ResultCodes}. 63 * @return A new intent that can be used with {@link Activity#startActivityForResult(Intent, int)} 64 * to start the next step of the setup flow. 65 */ 66 public static Intent getNextIntent(Intent originalIntent, int resultCode) { 67 return getNextIntent(originalIntent, resultCode, null); 68 } 69 70 /** 71 * Gets an intent that will invoke the next step of setup wizard. 72 * 73 * @param originalIntent The original intent that was used to start the step, usually via {@link 74 * Activity#getIntent()}. 75 * @param resultCode The result code of the step. See {@link ResultCodes}. 76 * @param data An intent containing extra result data. 77 * @return A new intent that can be used with {@link Activity#startActivityForResult(Intent, int)} 78 * to start the next step of the setup flow. 79 */ 80 public static Intent getNextIntent(Intent originalIntent, int resultCode, Intent data) { 81 Intent intent = new Intent(ACTION_NEXT); 82 copyWizardManagerExtras(originalIntent, intent); 83 intent.putExtra(EXTRA_RESULT_CODE, resultCode); 84 if (data != null && data.getExtras() != null) { 85 intent.putExtras(data.getExtras()); 86 } 87 intent.putExtra(EXTRA_THEME, originalIntent.getStringExtra(EXTRA_THEME)); 88 89 return intent; 90 } 91 92 /** 93 * Copies the internal extras used by setup wizard from one intent to another. For low-level use 94 * only, such as when using {@link Intent#FLAG_ACTIVITY_FORWARD_RESULT} to relay to another 95 * intent. 96 * 97 * @param srcIntent Intent to get the wizard manager extras from. 98 * @param dstIntent Intent to copy the wizard manager extras to. 99 */ 100 public static void copyWizardManagerExtras(Intent srcIntent, Intent dstIntent) { 101 dstIntent.putExtra(EXTRA_WIZARD_BUNDLE, srcIntent.getBundleExtra(EXTRA_WIZARD_BUNDLE)); 102 for (String key : 103 Arrays.asList( 104 EXTRA_IS_FIRST_RUN, 105 EXTRA_IS_DEFERRED_SETUP, 106 EXTRA_IS_PRE_DEFERRED_SETUP, 107 EXTRA_IS_SETUP_FLOW)) { 108 dstIntent.putExtra(key, srcIntent.getBooleanExtra(key, false)); 109 } 110 111 for (String key : Arrays.asList(EXTRA_THEME, EXTRA_SCRIPT_URI, EXTRA_ACTION_ID)) { 112 dstIntent.putExtra(key, srcIntent.getStringExtra(key)); 113 } 114 } 115 116 /** @deprecated Use {@link isInitialSetupWizard} instead. */ 117 @Deprecated 118 public static boolean isSetupWizardIntent(Intent intent) { 119 return intent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false); 120 } 121 122 /** 123 * Checks whether the current user has completed Setup Wizard. This is true if the current user 124 * has gone through Setup Wizard. The current user may or may not be the device owner and the 125 * device owner may have already completed setup wizard. 126 * 127 * @param context The context to retrieve the settings. 128 * @return true if the current user has completed Setup Wizard. 129 * @see #isDeviceProvisioned(Context) 130 */ 131 public static boolean isUserSetupComplete(Context context) { 132 if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) { 133 return Settings.Secure.getInt( 134 context.getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) 135 == 1; 136 } else { 137 // For versions below JB MR1, there are no user profiles. Just return the global device 138 // provisioned state. 139 return Settings.Secure.getInt( 140 context.getContentResolver(), SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0) 141 == 1; 142 } 143 } 144 145 /** 146 * Checks whether the device is provisioned. This means that the device has gone through Setup 147 * Wizard at least once. Note that the user can still be in Setup Wizard even if this is true, for 148 * a secondary user profile triggered through Settings > Add account. 149 * 150 * @param context The context to retrieve the settings. 151 * @return true if the device is provisioned. 152 * @see #isUserSetupComplete(Context) 153 */ 154 public static boolean isDeviceProvisioned(Context context) { 155 if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) { 156 return Settings.Global.getInt( 157 context.getContentResolver(), SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0) 158 == 1; 159 } else { 160 return Settings.Secure.getInt( 161 context.getContentResolver(), SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0) 162 == 1; 163 } 164 } 165 166 /** 167 * Checks whether an intent is running in the deferred setup wizard flow. 168 * 169 * @param originalIntent The original intent that was used to start the step, usually via {@link 170 * Activity#getIntent()}. 171 * @return true if the intent passed in was running in deferred setup wizard. 172 */ 173 public static boolean isDeferredSetupWizard(Intent originalIntent) { 174 return originalIntent != null && originalIntent.getBooleanExtra(EXTRA_IS_DEFERRED_SETUP, false); 175 } 176 177 /** 178 * Checks whether an intent is running in "pre-deferred" setup wizard flow. 179 * 180 * @param originalIntent The original intent that was used to start the step, usually via {@link 181 * Activity#getIntent()}. 182 * @return true if the intent passed in was running in "pre-deferred" setup wizard. 183 */ 184 public static boolean isPreDeferredSetupWizard(Intent originalIntent) { 185 return originalIntent != null 186 && originalIntent.getBooleanExtra(EXTRA_IS_PRE_DEFERRED_SETUP, false); 187 } 188 189 /** 190 * Checks whether an intent is is running in the initial setup wizard flow. 191 * 192 * @param intent The intent to be checked, usually from {@link Activity#getIntent()}. 193 * @return true if the intent passed in was intended to be used with setup wizard. 194 */ 195 public static boolean isInitialSetupWizard(Intent intent) { 196 return intent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false); 197 } 198 199 /** 200 * Returns true if the intent passed in indicates that it is running in any setup wizard flow, 201 * including initial setup and deferred setup etc. 202 * 203 * @param originalIntent The original intent that was used to start the step, usually via {@link 204 * Activity#getIntent()}. 205 */ 206 public static boolean isAnySetupWizard(@Nullable Intent originalIntent) { 207 if (originalIntent == null) { 208 return false; 209 } 210 211 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 212 return originalIntent.getBooleanExtra(EXTRA_IS_SETUP_FLOW, false); 213 } else { 214 return isInitialSetupWizard(originalIntent) 215 || isPreDeferredSetupWizard(originalIntent) 216 || isDeferredSetupWizard(originalIntent); 217 } 218 } 219 } 220