blob: 79f96f58d1841a2fcd875e27ffe1850bf2e742ee [file] [log] [blame]
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -07001/*
2 * Copyright (C) 2018 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
Brad Stenning8d1a51c2018-11-20 17:34:16 -080014 * limitations under the License.
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070015 */
16
17package com.android.systemui.statusbar.car;
18
Aarthi Balachandera7096002018-05-21 18:12:25 -070019import static android.content.DialogInterface.BUTTON_NEGATIVE;
Aarthi Balachander0e0e9212018-04-19 19:25:33 -070020import static android.content.DialogInterface.BUTTON_POSITIVE;
21
22import android.app.AlertDialog;
23import android.app.AlertDialog.Builder;
24import android.app.Dialog;
Ying Zhengad2773f2018-09-14 10:31:07 -070025import android.car.userlib.CarUserManagerHelper;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070026import android.content.Context;
Aarthi Balachander0e0e9212018-04-19 19:25:33 -070027import android.content.DialogInterface;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070028import android.content.pm.UserInfo;
29import android.content.res.Resources;
jovanake4ce3cc2018-04-19 12:17:12 -070030import android.graphics.Bitmap;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070031import android.os.AsyncTask;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070032import android.util.AttributeSet;
33import android.view.LayoutInflater;
34import android.view.View;
35import android.view.ViewGroup;
36import android.widget.ImageView;
37import android.widget.TextView;
38
Aarthi Balachander608b6e32018-04-11 18:41:52 -070039import androidx.car.widget.PagedListView;
Aarthi Balachandere3110e42018-04-30 18:16:26 -070040import androidx.core.graphics.drawable.RoundedBitmapDrawable;
41import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
Aarthi Balachandera7096002018-05-21 18:12:25 -070042import androidx.recyclerview.widget.RecyclerView;
43
jovanake4ce3cc2018-04-19 12:17:12 -070044import com.android.internal.util.UserIcons;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070045import com.android.systemui.R;
Aarthi Balachander0e0e9212018-04-19 19:25:33 -070046import com.android.systemui.statusbar.phone.SystemUIDialog;
Gus Prevasab336792018-11-14 13:52:20 -050047
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070048import java.util.ArrayList;
49import java.util.List;
50
51/**
52 * Displays a GridLayout with icons for the users in the system to allow switching between users.
53 * One of the uses of this is for the lock screen in auto.
54 */
Aarthi Balachander608b6e32018-04-11 18:41:52 -070055public class UserGridRecyclerView extends PagedListView implements
jovanak78cacc42018-08-06 18:38:03 -070056 CarUserManagerHelper.OnUsersUpdateListener {
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070057 private UserSelectionListener mUserSelectionListener;
58 private UserAdapter mAdapter;
jovanak78cacc42018-08-06 18:38:03 -070059 private CarUserManagerHelper mCarUserManagerHelper;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070060 private Context mContext;
61
62 public UserGridRecyclerView(Context context, AttributeSet attrs) {
63 super(context, attrs);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070064 mContext = context;
jovanak78cacc42018-08-06 18:38:03 -070065 mCarUserManagerHelper = new CarUserManagerHelper(mContext);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070066 }
67
68 /**
69 * Register listener for any update to the users
70 */
71 @Override
72 public void onFinishInflate() {
Aarthi Balachander608b6e32018-04-11 18:41:52 -070073 super.onFinishInflate();
jovanak78cacc42018-08-06 18:38:03 -070074 mCarUserManagerHelper.registerOnUsersUpdateListener(this);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070075 }
76
77 /**
78 * Unregisters listener checking for any change to the users
79 */
80 @Override
81 public void onDetachedFromWindow() {
Aarthi Balachander608b6e32018-04-11 18:41:52 -070082 super.onDetachedFromWindow();
jovanak78cacc42018-08-06 18:38:03 -070083 mCarUserManagerHelper.unregisterOnUsersUpdateListener(this);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070084 }
85
86 /**
87 * Initializes the adapter that populates the grid layout
88 *
89 * @return the adapter
90 */
91 public void buildAdapter() {
jovanak78cacc42018-08-06 18:38:03 -070092 List<UserRecord> userRecords = createUserRecords(mCarUserManagerHelper
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070093 .getAllUsers());
94 mAdapter = new UserAdapter(mContext, userRecords);
95 super.setAdapter(mAdapter);
96 }
97
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070098 private List<UserRecord> createUserRecords(List<UserInfo> userInfoList) {
99 List<UserRecord> userRecords = new ArrayList<>();
jovanak2baf8d62018-08-09 12:59:50 -0700100
101 // If the foreground user CANNOT switch to other users, only display the foreground user.
102 if (!mCarUserManagerHelper.canForegroundUserSwitchUsers()) {
103 userRecords.add(createForegroundUserRecord());
104 return userRecords;
105 }
106
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700107 for (UserInfo userInfo : userInfoList) {
jovanak80b48592018-04-11 17:09:53 -0700108 if (userInfo.isGuest()) {
109 // Don't display guests in the switcher.
110 continue;
111 }
jovanak78cacc42018-08-06 18:38:03 -0700112
113 boolean isForeground =
114 mCarUserManagerHelper.getCurrentForegroundUserId() == userInfo.id;
jovanak80b48592018-04-11 17:09:53 -0700115 UserRecord record = new UserRecord(userInfo, false /* isStartGuestSession */,
jovanak0535abc2018-04-10 15:14:50 -0700116 false /* isAddUser */, isForeground);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700117 userRecords.add(record);
118 }
119
jovanak2d26ae32018-07-31 10:44:41 -0700120 // Add button for starting guest session.
121 userRecords.add(createStartGuestUserRecord());
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700122
jovanak0535abc2018-04-10 15:14:50 -0700123 // Add add user record if the foreground user can add users
jovanak78cacc42018-08-06 18:38:03 -0700124 if (mCarUserManagerHelper.canForegroundUserAddUsers()) {
jovanak2d26ae32018-07-31 10:44:41 -0700125 userRecords.add(createAddUserRecord());
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700126 }
127
128 return userRecords;
129 }
130
jovanak2baf8d62018-08-09 12:59:50 -0700131 private UserRecord createForegroundUserRecord() {
132 return new UserRecord(mCarUserManagerHelper.getCurrentForegroundUserInfo(),
133 false /* isStartGuestSession */, false /* isAddUser */, true /* isForeground */);
134 }
135
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700136 /**
137 * Create guest user record
138 */
jovanak2d26ae32018-07-31 10:44:41 -0700139 private UserRecord createStartGuestUserRecord() {
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700140 UserInfo userInfo = new UserInfo();
jovanak2d26ae32018-07-31 10:44:41 -0700141 userInfo.name = mContext.getString(R.string.start_guest_session);
142 return new UserRecord(userInfo, true /* isStartGuestSession */, false /* isAddUser */,
143 false /* isForeground */);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700144 }
145
146 /**
147 * Create add user record
148 */
jovanak2d26ae32018-07-31 10:44:41 -0700149 private UserRecord createAddUserRecord() {
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700150 UserInfo userInfo = new UserInfo();
151 userInfo.name = mContext.getString(R.string.car_add_user);
jovanak80b48592018-04-11 17:09:53 -0700152 return new UserRecord(userInfo, false /* isStartGuestSession */,
jovanak0535abc2018-04-10 15:14:50 -0700153 true /* isAddUser */, false /* isForeground */);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700154 }
155
156 public void setUserSelectionListener(UserSelectionListener userSelectionListener) {
157 mUserSelectionListener = userSelectionListener;
158 }
159
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700160 @Override
161 public void onUsersUpdate() {
162 mAdapter.clearUsers();
jovanak78cacc42018-08-06 18:38:03 -0700163 mAdapter.updateUsers(createUserRecords(mCarUserManagerHelper.getAllUsers()));
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700164 mAdapter.notifyDataSetChanged();
165 }
166
167 /**
168 * Adapter to populate the grid layout with the available user profiles
169 */
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700170 public final class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserAdapterViewHolder>
Aarthi Balachanderc6d13662018-08-13 14:49:41 -0700171 implements Dialog.OnClickListener, Dialog.OnCancelListener {
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700172
173 private final Context mContext;
174 private List<UserRecord> mUsers;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700175 private final Resources mRes;
176 private final String mGuestName;
177 private final String mNewUserName;
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700178 // View that holds the add user button. Used to enable/disable the view
179 private View mAddUserView;
Aarthi Balachandera7096002018-05-21 18:12:25 -0700180 // User record for the add user. Need to call notifyUserSelected only if the user
181 // confirms adding a user
182 private UserRecord mAddUserRecord;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700183
184 public UserAdapter(Context context, List<UserRecord> users) {
185 mRes = context.getResources();
186 mContext = context;
187 updateUsers(users);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700188 mGuestName = mRes.getString(R.string.car_guest);
189 mNewUserName = mRes.getString(R.string.car_new_user);
190 }
191
192 public void clearUsers() {
193 mUsers.clear();
194 }
195
196 public void updateUsers(List<UserRecord> users) {
197 mUsers = users;
198 }
199
200 @Override
201 public UserAdapterViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
202 View view = LayoutInflater.from(mContext)
203 .inflate(R.layout.car_fullscreen_user_pod, parent, false);
204 view.setAlpha(1f);
205 view.bringToFront();
206 return new UserAdapterViewHolder(view);
207 }
208
209 @Override
210 public void onBindViewHolder(UserAdapterViewHolder holder, int position) {
211 UserRecord userRecord = mUsers.get(position);
Aarthi Balachandere3110e42018-04-30 18:16:26 -0700212 RoundedBitmapDrawable circleIcon = RoundedBitmapDrawableFactory.create(mRes,
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800213 getUserRecordIcon(userRecord));
Aarthi Balachandere3110e42018-04-30 18:16:26 -0700214 circleIcon.setCircular(true);
215 holder.mUserAvatarImageView.setImageDrawable(circleIcon);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700216 holder.mUserNameTextView.setText(userRecord.mInfo.name);
Aarthi Balachandera7096002018-05-21 18:12:25 -0700217
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700218 holder.mView.setOnClickListener(v -> {
219 if (userRecord == null) {
220 return;
221 }
222
jovanak80b48592018-04-11 17:09:53 -0700223 if (userRecord.mIsStartGuestSession) {
Aarthi Balachandera7096002018-05-21 18:12:25 -0700224 notifyUserSelected(userRecord);
jovanak2469ca72018-09-19 16:30:04 -0700225 mCarUserManagerHelper.startGuestSession(mGuestName);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700226 return;
227 }
228
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700229 // If the user wants to add a user, show dialog to confirm adding a user
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700230 if (userRecord.mIsAddUser) {
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700231 // Disable button so it cannot be clicked multiple times
232 mAddUserView = holder.mView;
233 mAddUserView.setEnabled(false);
Aarthi Balachandera7096002018-05-21 18:12:25 -0700234 mAddUserRecord = userRecord;
jovanaka17e1682018-08-09 15:02:13 -0700235
236 handleAddUserClicked();
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700237 return;
238 }
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700239 // If the user doesn't want to be a guest or add a user, switch to the user selected
Aarthi Balachandera7096002018-05-21 18:12:25 -0700240 notifyUserSelected(userRecord);
jovanak78cacc42018-08-06 18:38:03 -0700241 mCarUserManagerHelper.switchToUser(userRecord.mInfo);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700242 });
243
244 }
245
jovanaka17e1682018-08-09 15:02:13 -0700246 private void handleAddUserClicked() {
247 if (mCarUserManagerHelper.isUserLimitReached()) {
248 mAddUserView.setEnabled(true);
249 showMaxUserLimitReachedDialog();
250 } else {
251 showConfirmAddUserDialog();
252 }
253 }
254
255 private void showMaxUserLimitReachedDialog() {
Heemin Seog50b43b72019-03-17 23:54:23 -0700256 AlertDialog maxUsersDialog = new Builder(mContext,
257 com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert)
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800258 .setTitle(R.string.user_limit_reached_title)
259 .setMessage(getResources().getQuantityString(
260 R.plurals.user_limit_reached_message,
261 mCarUserManagerHelper.getMaxSupportedRealUsers(),
262 mCarUserManagerHelper.getMaxSupportedRealUsers()))
263 .setPositiveButton(android.R.string.ok, null)
264 .create();
jovanaka17e1682018-08-09 15:02:13 -0700265 // Sets window flags for the SysUI dialog
266 SystemUIDialog.applyFlags(maxUsersDialog);
267 maxUsersDialog.show();
268 }
269
270 private void showConfirmAddUserDialog() {
271 String message = mRes.getString(R.string.user_add_user_message_setup)
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800272 .concat(System.getProperty("line.separator"))
273 .concat(System.getProperty("line.separator"))
274 .concat(mRes.getString(R.string.user_add_user_message_update));
jovanaka17e1682018-08-09 15:02:13 -0700275
Heemin Seog50b43b72019-03-17 23:54:23 -0700276 AlertDialog addUserDialog = new Builder(mContext,
277 com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert)
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800278 .setTitle(R.string.user_add_user_title)
279 .setMessage(message)
280 .setNegativeButton(android.R.string.cancel, this)
281 .setPositiveButton(android.R.string.ok, this)
282 .setOnCancelListener(this)
283 .create();
jovanaka17e1682018-08-09 15:02:13 -0700284 // Sets window flags for the SysUI dialog
285 SystemUIDialog.applyFlags(addUserDialog);
286 addUserDialog.show();
287 }
288
Aarthi Balachandera7096002018-05-21 18:12:25 -0700289 private void notifyUserSelected(UserRecord userRecord) {
290 // Notify the listener which user was selected
291 if (mUserSelectionListener != null) {
292 mUserSelectionListener.onUserSelected(userRecord);
293 }
294 }
295
jovanake4ce3cc2018-04-19 12:17:12 -0700296 private Bitmap getUserRecordIcon(UserRecord userRecord) {
297 if (userRecord.mIsStartGuestSession) {
jovanak78cacc42018-08-06 18:38:03 -0700298 return mCarUserManagerHelper.getGuestDefaultIcon();
jovanake4ce3cc2018-04-19 12:17:12 -0700299 }
300
301 if (userRecord.mIsAddUser) {
302 return UserIcons.convertToBitmap(mContext
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800303 .getDrawable(R.drawable.car_add_circle_round));
jovanake4ce3cc2018-04-19 12:17:12 -0700304 }
305
jovanak78cacc42018-08-06 18:38:03 -0700306 return mCarUserManagerHelper.getUserIcon(userRecord.mInfo);
jovanake4ce3cc2018-04-19 12:17:12 -0700307 }
308
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700309 @Override
310 public void onClick(DialogInterface dialog, int which) {
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700311 if (which == BUTTON_POSITIVE) {
Aarthi Balachandera7096002018-05-21 18:12:25 -0700312 notifyUserSelected(mAddUserRecord);
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700313 new AddNewUserTask().execute(mNewUserName);
Aarthi Balachandera7096002018-05-21 18:12:25 -0700314 } else if (which == BUTTON_NEGATIVE) {
315 // Enable the add button only if cancel
316 if (mAddUserView != null) {
317 mAddUserView.setEnabled(true);
318 }
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700319 }
320 }
321
Aarthi Balachanderc6d13662018-08-13 14:49:41 -0700322 @Override
323 public void onCancel(DialogInterface dialog) {
324 // Enable the add button again if user cancels dialog by clicking outside the dialog
325 if (mAddUserView != null) {
326 mAddUserView.setEnabled(true);
327 }
328 }
329
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700330 private class AddNewUserTask extends AsyncTask<String, Void, UserInfo> {
331
332 @Override
333 protected UserInfo doInBackground(String... userNames) {
jovanak78cacc42018-08-06 18:38:03 -0700334 return mCarUserManagerHelper.createNewNonAdminUser(userNames[0]);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700335 }
336
337 @Override
338 protected void onPreExecute() {
339 }
340
341 @Override
342 protected void onPostExecute(UserInfo user) {
343 if (user != null) {
jovanak78cacc42018-08-06 18:38:03 -0700344 mCarUserManagerHelper.switchToUser(user);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700345 }
346 }
347 }
348
349 @Override
350 public int getItemCount() {
351 return mUsers.size();
352 }
353
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700354 public class UserAdapterViewHolder extends RecyclerView.ViewHolder {
355
356 public ImageView mUserAvatarImageView;
357 public TextView mUserNameTextView;
358 public View mView;
359
360 public UserAdapterViewHolder(View view) {
361 super(view);
362 mView = view;
363 mUserAvatarImageView = (ImageView) view.findViewById(R.id.user_avatar);
364 mUserNameTextView = (TextView) view.findViewById(R.id.user_name);
365 }
366 }
367 }
368
369 /**
370 * Object wrapper class for the userInfo. Use it to distinguish if a profile is a
jovanak0535abc2018-04-10 15:14:50 -0700371 * guest profile, add user profile, or the foreground user.
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700372 */
373 public static final class UserRecord {
374
375 public final UserInfo mInfo;
jovanak80b48592018-04-11 17:09:53 -0700376 public final boolean mIsStartGuestSession;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700377 public final boolean mIsAddUser;
jovanak0535abc2018-04-10 15:14:50 -0700378 public final boolean mIsForeground;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700379
jovanak80b48592018-04-11 17:09:53 -0700380 public UserRecord(UserInfo userInfo, boolean isStartGuestSession, boolean isAddUser,
jovanak0535abc2018-04-10 15:14:50 -0700381 boolean isForeground) {
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700382 mInfo = userInfo;
jovanak80b48592018-04-11 17:09:53 -0700383 mIsStartGuestSession = isStartGuestSession;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700384 mIsAddUser = isAddUser;
jovanak0535abc2018-04-10 15:14:50 -0700385 mIsForeground = isForeground;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700386 }
387 }
388
389 /**
390 * Listener used to notify when a user has been selected
391 */
392 interface UserSelectionListener {
393
394 void onUserSelected(UserRecord record);
395 }
396}