1 /* 2 * Copyright (C) 2021 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.android.car.settings.profiles; 18 19 import static android.os.UserManager.DISALLOW_ADD_USER; 20 21 import static com.android.car.settings.common.PreferenceController.AVAILABLE; 22 import static com.android.car.settings.common.PreferenceController.AVAILABLE_FOR_VIEWING; 23 import static com.android.car.settings.common.PreferenceController.DISABLED_FOR_PROFILE; 24 import static com.android.car.settings.enterprise.ActionDisabledByAdminDialogFragment.DISABLED_BY_ADMIN_CONFIRM_DIALOG_TAG; 25 import static com.android.car.settings.enterprise.EnterpriseUtils.hasUserRestrictionByDpm; 26 import static com.android.car.settings.enterprise.EnterpriseUtils.hasUserRestrictionByUm; 27 28 import android.car.Car; 29 import android.car.user.CarUserManager; 30 import android.content.Context; 31 import android.os.UserManager; 32 import android.widget.Toast; 33 34 import androidx.annotation.VisibleForTesting; 35 import androidx.preference.Preference; 36 37 import com.android.car.settings.R; 38 import com.android.car.settings.common.ConfirmationDialogFragment; 39 import com.android.car.settings.common.ErrorDialog; 40 import com.android.car.settings.common.FragmentController; 41 import com.android.car.settings.common.PreferenceController; 42 import com.android.car.settings.enterprise.EnterpriseUtils; 43 44 /** 45 * Consolidates adding profile logic into one handler so we can have consistent logic across various 46 * parts of the Settings app. 47 */ 48 public class AddProfileHandler implements AddNewProfileTask.AddNewProfileListener { 49 50 @VisibleForTesting 51 static final String CONFIRM_CREATE_NEW_PROFILE_DIALOG_TAG = 52 "com.android.car.settings.profiles.ConfirmCreateNewProfileDialog"; 53 @VisibleForTesting 54 static final String MAX_PROFILES_LIMIT_REACHED_DIALOG_TAG = 55 "com.android.car.settings.profiles.MaxProfilesLimitReachedDialog"; 56 57 @VisibleForTesting 58 AddNewProfileTask mAddNewProfileTask; 59 /** Indicates that a task is running. */ 60 private boolean mIsBusy; 61 62 private final Context mContext; 63 private final FragmentController mFragmentController; 64 private final PreferenceController mPreferenceController; 65 private Car mCar; 66 private CarUserManager mCarUserManager; 67 68 @VisibleForTesting 69 ConfirmationDialogFragment.ConfirmListener mConfirmCreateNewProfileListener; 70 private final Car.CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> { 71 if (ready) { 72 mCar = car; 73 mCarUserManager = mCar.getCarManager(CarUserManager.class); 74 } else { 75 mCar = null; 76 mCarUserManager = null; 77 } 78 }; 79 AddProfileHandler(Context context, FragmentController fragmentController, PreferenceController preferenceController)80 public AddProfileHandler(Context context, FragmentController fragmentController, 81 PreferenceController preferenceController) { 82 mContext = context; 83 mFragmentController = fragmentController; 84 mPreferenceController = preferenceController; 85 Car.createCar(context, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT, 86 mCarServiceLifecycleListener); 87 88 mConfirmCreateNewProfileListener = arguments -> { 89 mAddNewProfileTask = new AddNewProfileTask(mContext, 90 mCarUserManager, /* addNewProfileListener= */ this); 91 mAddNewProfileTask.execute(mContext.getString(R.string.user_new_user_name)); 92 93 mIsBusy = true; 94 mPreferenceController.refreshUi(); 95 }; 96 } 97 98 /** 99 * Handles operations that should happen in host's onCreateInternal(). 100 * Resets listeners as they can get unregistered with certain configuration changes. 101 */ onCreateInternal()102 public void onCreateInternal() { 103 ConfirmationDialogFragment.resetListeners( 104 (ConfirmationDialogFragment) mFragmentController.findDialogByTag( 105 CONFIRM_CREATE_NEW_PROFILE_DIALOG_TAG), 106 mConfirmCreateNewProfileListener, 107 /* rejectListener= */ null, 108 /* neutralListener= */ null); 109 } 110 111 /** 112 * Handles operations that should happen in host's onStopInternal(). 113 */ onStopInternal()114 public void onStopInternal() { 115 mFragmentController.showProgressBar(false); 116 } 117 118 /** 119 * Handles events that should happen in host's onDestroyInternal(). 120 */ onDestroyInternal()121 public void onDestroyInternal() { 122 if (mAddNewProfileTask != null) { 123 mAddNewProfileTask.cancel(/* mayInterruptIfRunning= */ false); 124 } 125 if (mCar != null) { 126 mCar.disconnect(); 127 } 128 } 129 130 /** 131 * Handles events that should happen in host's updateState() when there is task running. 132 */ updateState(Preference preference)133 public void updateState(Preference preference) { 134 preference.setEnabled(!mIsBusy); 135 mFragmentController.showProgressBar(mIsBusy); 136 } 137 138 @Override onProfileAddedSuccess()139 public void onProfileAddedSuccess() { 140 mIsBusy = false; 141 mPreferenceController.refreshUi(); 142 } 143 144 @Override onProfileAddedFailure()145 public void onProfileAddedFailure() { 146 mIsBusy = false; 147 mPreferenceController.refreshUi(); 148 // Display failure dialog. 149 mFragmentController.showDialog( 150 ErrorDialog.newInstance(R.string.add_user_error_title), /* tag= */ null); 151 } 152 153 /** 154 * Determines whether the user manager instance has the permission to add profiles 155 * 156 * @param userManager UserManager instance to evaluate 157 * @return whether the user has permissions to add profiles 158 */ canAddProfiles(UserManager userManager)159 public static boolean canAddProfiles(UserManager userManager) { 160 return !userManager.hasUserRestriction(DISALLOW_ADD_USER); 161 } 162 163 /** 164 * Returns {@code PreferenceController.AVAILABLE} when preference should be available, 165 * {@code PreferenceController.DISABLED_FOR_PROFILE} when preference should be unavailable, 166 * {@code PreferenceController.AVAILABLE_FOR_VIEWING} when preference is visible but 167 * disabled. 168 */ getAddProfilePreferenceAvailabilityStatus(Context context)169 public static int getAddProfilePreferenceAvailabilityStatus(Context context) { 170 UserManager um = getUserManager(context); 171 if (um.isDemoUser() || canAddProfiles(um)) { 172 return AVAILABLE; 173 } 174 if (hasUserRestrictionByUm(context, DISALLOW_ADD_USER)) { 175 return DISABLED_FOR_PROFILE; 176 } 177 return AVAILABLE_FOR_VIEWING; 178 } 179 180 /** 181 * Display dialog to add a profile 182 */ showAddProfileDialog()183 public void showAddProfileDialog() { 184 ConfirmationDialogFragment dialogFragment = 185 ProfilesDialogProvider.getConfirmCreateNewProfileDialogFragment( 186 mContext, mConfirmCreateNewProfileListener, null); 187 188 mFragmentController.showDialog(dialogFragment, CONFIRM_CREATE_NEW_PROFILE_DIALOG_TAG); 189 } 190 191 /** 192 * Shows a dialog or toast when the Preference is disabled while still visible. 193 */ runClickableWhileDisabled()194 public void runClickableWhileDisabled() { 195 if (hasUserRestrictionByDpm(mContext, DISALLOW_ADD_USER)) { 196 // Shows a dialog if this PreferenceController is disabled because there is 197 // restriction set from DevicePolicyManager 198 showActionDisabledByAdminDialog(); 199 } else if (!getUserManager(mContext).canAddMoreUsers()) { 200 // Shows a dialog if no more profiles can be added because the maximum allowed number 201 // is reached 202 ConfirmationDialogFragment dialogFragment = 203 ProfilesDialogProvider.getMaxProfilesLimitReachedDialogFragment(mContext, 204 ProfileHelper.getInstance(mContext).getMaxSupportedRealProfiles()); 205 mFragmentController.showDialog(dialogFragment, MAX_PROFILES_LIMIT_REACHED_DIALOG_TAG); 206 } else { 207 Toast.makeText(mContext, mContext.getString(R.string.action_unavailable), 208 Toast.LENGTH_LONG).show(); 209 } 210 } 211 showActionDisabledByAdminDialog()212 private void showActionDisabledByAdminDialog() { 213 mFragmentController.showDialog( 214 EnterpriseUtils.getActionDisabledByAdminDialog(mContext, DISALLOW_ADD_USER), 215 DISABLED_BY_ADMIN_CONFIRM_DIALOG_TAG); 216 } 217 getUserManager(Context context)218 private static UserManager getUserManager(Context context) { 219 return context.getSystemService(UserManager.class); 220 } 221 222 @VisibleForTesting setCarUserManager(CarUserManager carUserManager)223 void setCarUserManager(CarUserManager carUserManager) { 224 mCarUserManager = carUserManager; 225 } 226 } 227