1 /* 2 * Copyright (C) 2020 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.ActionButtonsPreference.ActionButtons; 22 import static com.android.car.settings.enterprise.EnterpriseUtils.hasUserRestrictionByDpm; 23 import static com.android.car.settings.enterprise.EnterpriseUtils.hasUserRestrictionByUm; 24 25 import android.car.drivingstate.CarUxRestrictions; 26 import android.content.Context; 27 import android.content.pm.UserInfo; 28 import android.os.UserManager; 29 30 import androidx.annotation.VisibleForTesting; 31 32 import com.android.car.settings.R; 33 import com.android.car.settings.common.ActionButtonInfo; 34 import com.android.car.settings.common.ActionButtonsPreference; 35 import com.android.car.settings.common.ConfirmationDialogFragment; 36 import com.android.car.settings.common.FragmentController; 37 38 /** 39 * Displays the action buttons for profile details. 40 * 41 * <p>The actions shown depends on the current and selected profile. 42 * <ol> 43 * <li>Rename: shown if selected profile is the current profile 44 * <li>Make admin: shown if current profile is an admin and the selected profile is not 45 * <li>Manage other profiles: shown if selected profile is the current profile and 46 * there are other profiles 47 * <li>Add a profile: shown if selected profile is the current profile and 48 * there are no other profiles 49 * <li> Exit demo: shown if selected profile is the current profile and is a demo 50 * <li>Delete: shown if the current profile is allowed to remove profiles, is not a demo 51 * profile, and selected profile is not the current profile 52 * </ol> 53 */ 54 public final class ProfileDetailsActionButtonsPreferenceController 55 extends ProfileDetailsBasePreferenceController<ActionButtonsPreference> { 56 57 @VisibleForTesting 58 static final String MAKE_ADMIN_DIALOG_TAG = "MakeAdminDialogFragment"; 59 60 private final ProfileHelper mProfileHelper; 61 private final UserManager mUserManager; 62 private DemoProfileDialogHandler mDemoProfileDialogHandler; 63 private AddProfileHandler mAddProfileHandler; 64 65 @VisibleForTesting 66 final ConfirmationDialogFragment.ConfirmListener mMakeAdminConfirmListener = 67 arguments -> { 68 UserInfo profileToMakeAdmin = 69 (UserInfo) arguments.get(ProfilesDialogProvider.KEY_PROFILE_TO_MAKE_ADMIN); 70 com.android.car.internal.user.UserHelper.grantAdminPermissions(getContext(), 71 profileToMakeAdmin.getUserHandle()); 72 getFragmentController().goBack(); 73 }; 74 75 private final RemoveProfileHandler mRemoveProfileHandler; 76 ProfileDetailsActionButtonsPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)77 public ProfileDetailsActionButtonsPreferenceController(Context context, 78 String preferenceKey, FragmentController fragmentController, 79 CarUxRestrictions uxRestrictions) { 80 this(context, preferenceKey, fragmentController, uxRestrictions, 81 ProfileHelper.getInstance(context), UserManager.get(context), 82 new RemoveProfileHandler(context, ProfileHelper.getInstance(context), 83 UserManager.get(context), fragmentController)); 84 } 85 86 @VisibleForTesting ProfileDetailsActionButtonsPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions, ProfileHelper profileHelper, UserManager userManager, RemoveProfileHandler removeProfileHandler)87 ProfileDetailsActionButtonsPreferenceController(Context context, 88 String preferenceKey, FragmentController fragmentController, 89 CarUxRestrictions uxRestrictions, ProfileHelper profileHelper, UserManager userManager, 90 RemoveProfileHandler removeProfileHandler) { 91 super(context, preferenceKey, fragmentController, uxRestrictions); 92 mProfileHelper = profileHelper; 93 mUserManager = userManager; 94 mRemoveProfileHandler = removeProfileHandler; 95 mDemoProfileDialogHandler = new DemoProfileDialogHandler(context, fragmentController); 96 mAddProfileHandler = new AddProfileHandler(context, fragmentController, this); 97 } 98 99 @Override getPreferenceType()100 protected Class<ActionButtonsPreference> getPreferenceType() { 101 return ActionButtonsPreference.class; 102 } 103 104 @Override onCreateInternal()105 protected void onCreateInternal() { 106 super.onCreateInternal(); 107 108 mDemoProfileDialogHandler.onCreateInternal(); 109 mAddProfileHandler.onCreateInternal(); 110 111 ConfirmationDialogFragment makeAdminDialog = 112 (ConfirmationDialogFragment) getFragmentController().findDialogByTag( 113 MAKE_ADMIN_DIALOG_TAG); 114 ConfirmationDialogFragment.resetListeners( 115 makeAdminDialog, 116 mMakeAdminConfirmListener, 117 /* rejectListener= */ null, 118 /* neutralListener= */ null); 119 120 mRemoveProfileHandler.resetListeners(); 121 } 122 123 @Override onStartInternal()124 protected void onStartInternal() { 125 super.onStartInternal(); 126 127 ActionButtonInfo renameButton = getPreference().getButton(ActionButtons.BUTTON1); 128 ActionButtonInfo makeAdminButton = getPreference().getButton(ActionButtons.BUTTON2); 129 ActionButtonInfo profilesButton = getPreference().getButton(ActionButtons.BUTTON3); 130 ActionButtonInfo deleteButton = getPreference().getButton(ActionButtons.BUTTON4); 131 132 boolean isDemoProfile = mUserManager.isDemoUser(); 133 // When DISALLOW_ADD_USER is set by device or profile owner, the button should still be 134 // visible but disabled 135 boolean shouldShowAddProfile = !(mUserManager.isAdminUser() && areThereOtherProfiles()) 136 && mProfileHelper.isCurrentProcessUser(getUserInfo()) 137 && !hasUserRestrictionByUm(getContext(), DISALLOW_ADD_USER); 138 boolean shouldEnableAddProfile = shouldShowAddProfile 139 && !hasUserRestrictionByDpm(getContext(), DISALLOW_ADD_USER); 140 boolean shouldShowProfilesButton = isDemoProfile || shouldShowAddProfile 141 || mUserManager.isAdminUser() && mProfileHelper.isCurrentProcessUser(getUserInfo()) 142 && areThereOtherProfiles(); 143 144 int removeProfileAvailabilityStatus = mRemoveProfileHandler.getAvailabilityStatus( 145 getContext(), getUserInfo(), /* availableForCurrentProcessUser= */ false); 146 boolean shouldShowDeleteProfile = removeProfileAvailabilityStatus != DISABLED_FOR_PROFILE; 147 boolean shouldEnableDeleteProfile = removeProfileAvailabilityStatus == AVAILABLE; 148 149 int profileButtonText; 150 if (shouldShowAddProfile && isDemoProfile) { 151 profileButtonText = R.string.exit_retail_button_text; 152 } else if (shouldShowAddProfile) { 153 profileButtonText = R.string.add_a_profile_button_text; 154 } else { 155 profileButtonText = R.string.manage_other_profiles_button_text; 156 } 157 158 renameButton 159 .setText(R.string.bluetooth_rename_button) 160 .setIcon(R.drawable.ic_edit) 161 .setVisible(mProfileHelper.isCurrentProcessUser(getUserInfo())) 162 .setOnClickListener(v -> getFragmentController().launchFragment( 163 EditProfileNameFragment.newInstance(getUserInfo()))); 164 165 makeAdminButton 166 .setText(R.string.grant_admin_permissions_button_text) 167 .setIcon(R.drawable.ic_person) 168 .setVisible(ProfileUtils.isAdminViewingNonAdmin(mUserManager, getUserInfo())) 169 .setOnClickListener(v -> showConfirmMakeAdminDialog()); 170 171 profilesButton 172 .setText(profileButtonText) 173 .setVisible(shouldShowProfilesButton) 174 .setOnClickListener(v -> { 175 if (shouldShowAddProfile && isDemoProfile) { 176 mDemoProfileDialogHandler.showExitRetailDialog(); 177 } else if (shouldShowAddProfile && !shouldEnableAddProfile) { 178 mAddProfileHandler.runClickableWhileDisabled(); 179 } else if (shouldShowAddProfile) { 180 mAddProfileHandler.showAddProfileDialog(); 181 } else { 182 getFragmentController().launchFragment( 183 new ProfilesListFragment()); 184 } 185 }); 186 187 if (!isDemoProfile && shouldShowAddProfile) { 188 profilesButton.setIcon(R.drawable.ic_add); 189 } else if (!isDemoProfile && shouldShowProfilesButton) { 190 profilesButton.setIcon(R.drawable.ic_people); 191 } 192 193 // Do not show delete button if the current profile can't remove the selected profile 194 deleteButton 195 .setText(R.string.delete_button) 196 .setIcon(R.drawable.ic_delete) 197 .setVisible(shouldShowDeleteProfile) 198 .setOnClickListener(v -> { 199 if (shouldEnableDeleteProfile) { 200 mRemoveProfileHandler.showConfirmRemoveProfileDialog(); 201 } else { 202 mRemoveProfileHandler.runClickableWhileDisabled(); 203 } 204 }); 205 } 206 207 @Override setUserInfo(UserInfo userInfo)208 public void setUserInfo(UserInfo userInfo) { 209 super.setUserInfo(userInfo); 210 mRemoveProfileHandler.setUserInfo(userInfo); 211 } 212 213 @Override onStopInternal()214 protected void onStopInternal() { 215 super.onStopInternal(); 216 mAddProfileHandler.onStopInternal(); 217 } 218 219 @Override onDestroyInternal()220 protected void onDestroyInternal() { 221 super.onDestroyInternal(); 222 mAddProfileHandler.onDestroyInternal(); 223 } 224 225 @Override updateState(ActionButtonsPreference preference)226 protected void updateState(ActionButtonsPreference preference) { 227 mAddProfileHandler.updateState(preference); 228 } 229 showConfirmMakeAdminDialog()230 private void showConfirmMakeAdminDialog() { 231 ConfirmationDialogFragment dialogFragment = 232 ProfilesDialogProvider.getConfirmGrantAdminDialogFragment(getContext(), 233 mMakeAdminConfirmListener, /* rejectListener= */ null, getUserInfo()); 234 235 getFragmentController().showDialog(dialogFragment, MAKE_ADMIN_DIALOG_TAG); 236 } 237 areThereOtherProfiles()238 private boolean areThereOtherProfiles() { 239 UserInfo currUserInfo = mProfileHelper.getCurrentProcessUserInfo(); 240 return !mProfileHelper.getAllLivingProfiles( 241 userInfo -> !userInfo.isGuest() && userInfo.id != currUserInfo.id).isEmpty(); 242 } 243 } 244