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