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 android.car.userlib.UserHelper.grantAdminPermissions(getContext(), 71 profileToMakeAdmin); 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() 136 && mProfileHelper.isCurrentProcessUser(getUserInfo()) 137 && !hasUserRestrictionByUm(getContext(), DISALLOW_ADD_USER) 138 && !areThereOtherProfiles(); 139 boolean shouldEnableAddProfile = shouldShowAddProfile 140 && !hasUserRestrictionByDpm(getContext(), DISALLOW_ADD_USER); 141 boolean shouldShowProfilesButton = isDemoProfile || shouldShowAddProfile 142 || mUserManager.isAdminUser() && mProfileHelper.isCurrentProcessUser(getUserInfo()) 143 && areThereOtherProfiles(); 144 145 int removeProfileAvailabilityStatus = mRemoveProfileHandler.getAvailabilityStatus( 146 getContext(), getUserInfo(), /* availableForCurrentProcessUser= */ false); 147 boolean shouldShowDeleteProfile = removeProfileAvailabilityStatus != DISABLED_FOR_PROFILE; 148 boolean shouldEnableDeleteProfile = removeProfileAvailabilityStatus == AVAILABLE; 149 150 int profileButtonText; 151 if (shouldShowAddProfile && isDemoProfile) { 152 profileButtonText = R.string.exit_retail_button_text; 153 } else if (shouldShowAddProfile) { 154 profileButtonText = R.string.add_a_profile_button_text; 155 } else { 156 profileButtonText = R.string.manage_other_profiles_button_text; 157 } 158 159 renameButton 160 .setText(R.string.bluetooth_rename_button) 161 .setIcon(R.drawable.ic_edit) 162 .setVisible(mProfileHelper.isCurrentProcessUser(getUserInfo())) 163 .setOnClickListener(v -> getFragmentController().launchFragment( 164 EditProfileNameFragment.newInstance(getUserInfo()))); 165 166 makeAdminButton 167 .setText(R.string.grant_admin_permissions_button_text) 168 .setIcon(R.drawable.ic_person) 169 .setVisible(ProfileUtils.isAdminViewingNonAdmin(mUserManager, getUserInfo())) 170 .setOnClickListener(v -> showConfirmMakeAdminDialog()); 171 172 profilesButton 173 .setText(profileButtonText) 174 .setVisible(shouldShowProfilesButton) 175 .setOnClickListener(v -> { 176 if (shouldShowAddProfile && isDemoProfile) { 177 mDemoProfileDialogHandler.showExitRetailDialog(); 178 } else if (shouldShowAddProfile && !shouldEnableAddProfile) { 179 mAddProfileHandler.runClickableWhileDisabled(); 180 } else if (shouldShowAddProfile) { 181 mAddProfileHandler.showAddProfileDialog(); 182 } else { 183 getFragmentController().launchFragment( 184 new ProfilesListFragment()); 185 } 186 }); 187 188 if (!isDemoProfile && shouldShowAddProfile) { 189 profilesButton.setIcon(R.drawable.ic_add); 190 } else if (!isDemoProfile && shouldShowProfilesButton) { 191 profilesButton.setIcon(R.drawable.ic_people); 192 } 193 194 // Do not show delete button if the current profile can't remove the selected profile 195 deleteButton 196 .setText(R.string.delete_button) 197 .setIcon(R.drawable.ic_delete) 198 .setVisible(shouldShowDeleteProfile) 199 .setOnClickListener(v -> { 200 if (shouldEnableDeleteProfile) { 201 mRemoveProfileHandler.showConfirmRemoveProfileDialog(); 202 } else { 203 mRemoveProfileHandler.runClickableWhileDisabled(); 204 } 205 }); 206 } 207 208 @Override setUserInfo(UserInfo userInfo)209 public void setUserInfo(UserInfo userInfo) { 210 super.setUserInfo(userInfo); 211 mRemoveProfileHandler.setUserInfo(userInfo); 212 } 213 214 @Override onStopInternal()215 protected void onStopInternal() { 216 super.onStopInternal(); 217 mAddProfileHandler.onStopInternal(); 218 } 219 220 @Override onDestroyInternal()221 protected void onDestroyInternal() { 222 super.onDestroyInternal(); 223 mAddProfileHandler.onDestroyInternal(); 224 } 225 226 @Override updateState(ActionButtonsPreference preference)227 protected void updateState(ActionButtonsPreference preference) { 228 mAddProfileHandler.updateState(preference); 229 } 230 showConfirmMakeAdminDialog()231 private void showConfirmMakeAdminDialog() { 232 ConfirmationDialogFragment dialogFragment = 233 ProfilesDialogProvider.getConfirmGrantAdminDialogFragment(getContext(), 234 mMakeAdminConfirmListener, /* rejectListener= */ null, getUserInfo()); 235 236 getFragmentController().showDialog(dialogFragment, MAKE_ADMIN_DIALOG_TAG); 237 } 238 areThereOtherProfiles()239 private boolean areThereOtherProfiles() { 240 UserInfo currUserInfo = mProfileHelper.getCurrentProcessUserInfo(); 241 return !mProfileHelper.getAllLivingProfiles( 242 userInfo -> !userInfo.isGuest() && userInfo.id != currUserInfo.id).isEmpty(); 243 } 244 } 245