blob: 67e512cf23f91c4427629e63b97f9bf442ee06d4 [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
14 * limitations under the License
15 */
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;
jovanak78cacc42018-08-06 18:38:03 -070025import android.car.user.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;
jovanake4ce3cc2018-04-19 12:17:12 -070032import android.os.UserHandle;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070033import android.util.AttributeSet;
34import android.view.LayoutInflater;
35import android.view.View;
36import android.view.ViewGroup;
37import android.widget.ImageView;
38import android.widget.TextView;
39
Aarthi Balachander608b6e32018-04-11 18:41:52 -070040import androidx.car.widget.PagedListView;
Aarthi Balachandere3110e42018-04-30 18:16:26 -070041import androidx.core.graphics.drawable.RoundedBitmapDrawable;
42import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
Aarthi Balachandera7096002018-05-21 18:12:25 -070043import androidx.recyclerview.widget.RecyclerView;
44
jovanake4ce3cc2018-04-19 12:17:12 -070045import com.android.internal.util.UserIcons;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070046import com.android.systemui.R;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070047
Aarthi Balachander0e0e9212018-04-19 19:25:33 -070048import com.android.systemui.statusbar.phone.SystemUIDialog;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070049import java.util.ArrayList;
50import java.util.List;
51
52/**
53 * Displays a GridLayout with icons for the users in the system to allow switching between users.
54 * One of the uses of this is for the lock screen in auto.
55 */
Aarthi Balachander608b6e32018-04-11 18:41:52 -070056public class UserGridRecyclerView extends PagedListView implements
jovanak78cacc42018-08-06 18:38:03 -070057 CarUserManagerHelper.OnUsersUpdateListener {
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070058 private UserSelectionListener mUserSelectionListener;
59 private UserAdapter mAdapter;
jovanak78cacc42018-08-06 18:38:03 -070060 private CarUserManagerHelper mCarUserManagerHelper;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070061 private Context mContext;
62
63 public UserGridRecyclerView(Context context, AttributeSet attrs) {
64 super(context, attrs);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070065 mContext = context;
jovanak78cacc42018-08-06 18:38:03 -070066 mCarUserManagerHelper = new CarUserManagerHelper(mContext);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070067 }
68
69 /**
70 * Register listener for any update to the users
71 */
72 @Override
73 public void onFinishInflate() {
Aarthi Balachander608b6e32018-04-11 18:41:52 -070074 super.onFinishInflate();
jovanak78cacc42018-08-06 18:38:03 -070075 mCarUserManagerHelper.registerOnUsersUpdateListener(this);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070076 }
77
78 /**
79 * Unregisters listener checking for any change to the users
80 */
81 @Override
82 public void onDetachedFromWindow() {
Aarthi Balachander608b6e32018-04-11 18:41:52 -070083 super.onDetachedFromWindow();
jovanak78cacc42018-08-06 18:38:03 -070084 mCarUserManagerHelper.unregisterOnUsersUpdateListener(this);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070085 }
86
87 /**
88 * Initializes the adapter that populates the grid layout
89 *
90 * @return the adapter
91 */
92 public void buildAdapter() {
jovanak78cacc42018-08-06 18:38:03 -070093 List<UserRecord> userRecords = createUserRecords(mCarUserManagerHelper
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070094 .getAllUsers());
95 mAdapter = new UserAdapter(mContext, userRecords);
96 super.setAdapter(mAdapter);
97 }
98
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070099 private List<UserRecord> createUserRecords(List<UserInfo> userInfoList) {
100 List<UserRecord> userRecords = new ArrayList<>();
jovanak2baf8d62018-08-09 12:59:50 -0700101
102 // If the foreground user CANNOT switch to other users, only display the foreground user.
103 if (!mCarUserManagerHelper.canForegroundUserSwitchUsers()) {
104 userRecords.add(createForegroundUserRecord());
105 return userRecords;
106 }
107
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700108 for (UserInfo userInfo : userInfoList) {
jovanak80b48592018-04-11 17:09:53 -0700109 if (userInfo.isGuest()) {
110 // Don't display guests in the switcher.
111 continue;
112 }
jovanak78cacc42018-08-06 18:38:03 -0700113
114 boolean isForeground =
115 mCarUserManagerHelper.getCurrentForegroundUserId() == userInfo.id;
jovanak80b48592018-04-11 17:09:53 -0700116 UserRecord record = new UserRecord(userInfo, false /* isStartGuestSession */,
jovanak0535abc2018-04-10 15:14:50 -0700117 false /* isAddUser */, isForeground);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700118 userRecords.add(record);
119 }
120
jovanak2d26ae32018-07-31 10:44:41 -0700121 // Add button for starting guest session.
122 userRecords.add(createStartGuestUserRecord());
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700123
jovanak0535abc2018-04-10 15:14:50 -0700124 // Add add user record if the foreground user can add users
jovanak78cacc42018-08-06 18:38:03 -0700125 if (mCarUserManagerHelper.canForegroundUserAddUsers()) {
jovanak2d26ae32018-07-31 10:44:41 -0700126 userRecords.add(createAddUserRecord());
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700127 }
128
129 return userRecords;
130 }
131
jovanak2baf8d62018-08-09 12:59:50 -0700132 private UserRecord createForegroundUserRecord() {
133 return new UserRecord(mCarUserManagerHelper.getCurrentForegroundUserInfo(),
134 false /* isStartGuestSession */, false /* isAddUser */, true /* isForeground */);
135 }
136
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700137 /**
138 * Create guest user record
139 */
jovanak2d26ae32018-07-31 10:44:41 -0700140 private UserRecord createStartGuestUserRecord() {
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700141 UserInfo userInfo = new UserInfo();
jovanak2d26ae32018-07-31 10:44:41 -0700142 userInfo.name = mContext.getString(R.string.start_guest_session);
143 return new UserRecord(userInfo, true /* isStartGuestSession */, false /* isAddUser */,
144 false /* isForeground */);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700145 }
146
147 /**
148 * Create add user record
149 */
jovanak2d26ae32018-07-31 10:44:41 -0700150 private UserRecord createAddUserRecord() {
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700151 UserInfo userInfo = new UserInfo();
152 userInfo.name = mContext.getString(R.string.car_add_user);
jovanak80b48592018-04-11 17:09:53 -0700153 return new UserRecord(userInfo, false /* isStartGuestSession */,
jovanak0535abc2018-04-10 15:14:50 -0700154 true /* isAddUser */, false /* isForeground */);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700155 }
156
157 public void setUserSelectionListener(UserSelectionListener userSelectionListener) {
158 mUserSelectionListener = userSelectionListener;
159 }
160
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700161 @Override
162 public void onUsersUpdate() {
163 mAdapter.clearUsers();
jovanak78cacc42018-08-06 18:38:03 -0700164 mAdapter.updateUsers(createUserRecords(mCarUserManagerHelper.getAllUsers()));
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700165 mAdapter.notifyDataSetChanged();
166 }
167
168 /**
169 * Adapter to populate the grid layout with the available user profiles
170 */
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700171 public final class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserAdapterViewHolder>
Aarthi Balachanderc6d13662018-08-13 14:49:41 -0700172 implements Dialog.OnClickListener, Dialog.OnCancelListener {
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700173
174 private final Context mContext;
175 private List<UserRecord> mUsers;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700176 private final Resources mRes;
177 private final String mGuestName;
178 private final String mNewUserName;
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700179 // View that holds the add user button. Used to enable/disable the view
180 private View mAddUserView;
Aarthi Balachandera7096002018-05-21 18:12:25 -0700181 // User record for the add user. Need to call notifyUserSelected only if the user
182 // confirms adding a user
183 private UserRecord mAddUserRecord;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700184
185 public UserAdapter(Context context, List<UserRecord> users) {
186 mRes = context.getResources();
187 mContext = context;
188 updateUsers(users);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700189 mGuestName = mRes.getString(R.string.car_guest);
190 mNewUserName = mRes.getString(R.string.car_new_user);
191 }
192
193 public void clearUsers() {
194 mUsers.clear();
195 }
196
197 public void updateUsers(List<UserRecord> users) {
198 mUsers = users;
199 }
200
201 @Override
202 public UserAdapterViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
203 View view = LayoutInflater.from(mContext)
204 .inflate(R.layout.car_fullscreen_user_pod, parent, false);
205 view.setAlpha(1f);
206 view.bringToFront();
207 return new UserAdapterViewHolder(view);
208 }
209
210 @Override
211 public void onBindViewHolder(UserAdapterViewHolder holder, int position) {
212 UserRecord userRecord = mUsers.get(position);
Aarthi Balachandere3110e42018-04-30 18:16:26 -0700213 RoundedBitmapDrawable circleIcon = RoundedBitmapDrawableFactory.create(mRes,
214 getUserRecordIcon(userRecord));
215 circleIcon.setCircular(true);
216 holder.mUserAvatarImageView.setImageDrawable(circleIcon);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700217 holder.mUserNameTextView.setText(userRecord.mInfo.name);
Aarthi Balachandera7096002018-05-21 18:12:25 -0700218
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700219 holder.mView.setOnClickListener(v -> {
220 if (userRecord == null) {
221 return;
222 }
223
jovanak80b48592018-04-11 17:09:53 -0700224 if (userRecord.mIsStartGuestSession) {
Aarthi Balachandera7096002018-05-21 18:12:25 -0700225 notifyUserSelected(userRecord);
jovanak78cacc42018-08-06 18:38:03 -0700226 mCarUserManagerHelper.startNewGuestSession(mGuestName);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700227 return;
228 }
229
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700230 // If the user wants to add a user, show dialog to confirm adding a user
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700231 if (userRecord.mIsAddUser) {
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700232 // Disable button so it cannot be clicked multiple times
233 mAddUserView = holder.mView;
234 mAddUserView.setEnabled(false);
Aarthi Balachandera7096002018-05-21 18:12:25 -0700235 mAddUserRecord = userRecord;
jovanaka17e1682018-08-09 15:02:13 -0700236
237 handleAddUserClicked();
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700238 return;
239 }
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700240 // 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 -0700241 notifyUserSelected(userRecord);
jovanak78cacc42018-08-06 18:38:03 -0700242 mCarUserManagerHelper.switchToUser(userRecord.mInfo);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700243 });
244
245 }
246
jovanaka17e1682018-08-09 15:02:13 -0700247 private void handleAddUserClicked() {
248 if (mCarUserManagerHelper.isUserLimitReached()) {
249 mAddUserView.setEnabled(true);
250 showMaxUserLimitReachedDialog();
251 } else {
252 showConfirmAddUserDialog();
253 }
254 }
255
256 private void showMaxUserLimitReachedDialog() {
257 AlertDialog maxUsersDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
258 .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();
265 // 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)
272 .concat(System.getProperty("line.separator"))
273 .concat(System.getProperty("line.separator"))
274 .concat(mRes.getString(R.string.user_add_user_message_update));
275
276 AlertDialog addUserDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
277 .setTitle(R.string.user_add_user_title)
278 .setMessage(message)
279 .setNegativeButton(android.R.string.cancel, this)
280 .setPositiveButton(android.R.string.ok, this)
281 .setOnCancelListener(this)
282 .create();
283 // Sets window flags for the SysUI dialog
284 SystemUIDialog.applyFlags(addUserDialog);
285 addUserDialog.show();
286 }
287
Aarthi Balachandera7096002018-05-21 18:12:25 -0700288 private void notifyUserSelected(UserRecord userRecord) {
289 // Notify the listener which user was selected
290 if (mUserSelectionListener != null) {
291 mUserSelectionListener.onUserSelected(userRecord);
292 }
293 }
294
jovanake4ce3cc2018-04-19 12:17:12 -0700295 private Bitmap getUserRecordIcon(UserRecord userRecord) {
296 if (userRecord.mIsStartGuestSession) {
jovanak78cacc42018-08-06 18:38:03 -0700297 return mCarUserManagerHelper.getGuestDefaultIcon();
jovanake4ce3cc2018-04-19 12:17:12 -0700298 }
299
300 if (userRecord.mIsAddUser) {
301 return UserIcons.convertToBitmap(mContext
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700302 .getDrawable(R.drawable.car_add_circle_round));
jovanake4ce3cc2018-04-19 12:17:12 -0700303 }
304
jovanak78cacc42018-08-06 18:38:03 -0700305 return mCarUserManagerHelper.getUserIcon(userRecord.mInfo);
jovanake4ce3cc2018-04-19 12:17:12 -0700306 }
307
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700308 @Override
309 public void onClick(DialogInterface dialog, int which) {
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700310 if (which == BUTTON_POSITIVE) {
Aarthi Balachandera7096002018-05-21 18:12:25 -0700311 notifyUserSelected(mAddUserRecord);
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700312 new AddNewUserTask().execute(mNewUserName);
Aarthi Balachandera7096002018-05-21 18:12:25 -0700313 } else if (which == BUTTON_NEGATIVE) {
314 // Enable the add button only if cancel
315 if (mAddUserView != null) {
316 mAddUserView.setEnabled(true);
317 }
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700318 }
319 }
320
Aarthi Balachanderc6d13662018-08-13 14:49:41 -0700321 @Override
322 public void onCancel(DialogInterface dialog) {
323 // Enable the add button again if user cancels dialog by clicking outside the dialog
324 if (mAddUserView != null) {
325 mAddUserView.setEnabled(true);
326 }
327 }
328
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700329 private class AddNewUserTask extends AsyncTask<String, Void, UserInfo> {
330
331 @Override
332 protected UserInfo doInBackground(String... userNames) {
jovanak78cacc42018-08-06 18:38:03 -0700333 return mCarUserManagerHelper.createNewNonAdminUser(userNames[0]);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700334 }
335
336 @Override
337 protected void onPreExecute() {
338 }
339
340 @Override
341 protected void onPostExecute(UserInfo user) {
342 if (user != null) {
jovanak78cacc42018-08-06 18:38:03 -0700343 mCarUserManagerHelper.switchToUser(user);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700344 }
345 }
346 }
347
348 @Override
349 public int getItemCount() {
350 return mUsers.size();
351 }
352
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700353 public class UserAdapterViewHolder extends RecyclerView.ViewHolder {
354
355 public ImageView mUserAvatarImageView;
356 public TextView mUserNameTextView;
357 public View mView;
358
359 public UserAdapterViewHolder(View view) {
360 super(view);
361 mView = view;
362 mUserAvatarImageView = (ImageView) view.findViewById(R.id.user_avatar);
363 mUserNameTextView = (TextView) view.findViewById(R.id.user_name);
364 }
365 }
366 }
367
368 /**
369 * Object wrapper class for the userInfo. Use it to distinguish if a profile is a
jovanak0535abc2018-04-10 15:14:50 -0700370 * guest profile, add user profile, or the foreground user.
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700371 */
372 public static final class UserRecord {
373
374 public final UserInfo mInfo;
jovanak80b48592018-04-11 17:09:53 -0700375 public final boolean mIsStartGuestSession;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700376 public final boolean mIsAddUser;
jovanak0535abc2018-04-10 15:14:50 -0700377 public final boolean mIsForeground;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700378
jovanak80b48592018-04-11 17:09:53 -0700379 public UserRecord(UserInfo userInfo, boolean isStartGuestSession, boolean isAddUser,
jovanak0535abc2018-04-10 15:14:50 -0700380 boolean isForeground) {
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700381 mInfo = userInfo;
jovanak80b48592018-04-11 17:09:53 -0700382 mIsStartGuestSession = isStartGuestSession;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700383 mIsAddUser = isAddUser;
jovanak0535abc2018-04-10 15:14:50 -0700384 mIsForeground = isForeground;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700385 }
386 }
387
388 /**
389 * Listener used to notify when a user has been selected
390 */
391 interface UserSelectionListener {
392
393 void onUserSelected(UserRecord record);
394 }
395}