blob: ee10d34b7d34f78b9a2aed6cb0c1893887e003c3 [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;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070025import android.content.Context;
Aarthi Balachander0e0e9212018-04-19 19:25:33 -070026import android.content.DialogInterface;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070027import android.content.pm.UserInfo;
28import android.content.res.Resources;
jovanake4ce3cc2018-04-19 12:17:12 -070029import android.graphics.Bitmap;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070030import android.os.AsyncTask;
jovanake4ce3cc2018-04-19 12:17:12 -070031import android.os.UserHandle;
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.settingslib.users.UserManagerHelper;
46import 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
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070057 UserManagerHelper.OnUsersUpdateListener {
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070058 private UserSelectionListener mUserSelectionListener;
59 private UserAdapter mAdapter;
60 private UserManagerHelper mUserManagerHelper;
61 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;
66 mUserManagerHelper = new UserManagerHelper(mContext);
67 }
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();
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070075 mUserManagerHelper.registerOnUsersUpdateListener(this);
76 }
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();
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070084 mUserManagerHelper.unregisterOnUsersUpdateListener();
85 }
86
87 /**
88 * Initializes the adapter that populates the grid layout
89 *
90 * @return the adapter
91 */
92 public void buildAdapter() {
93 List<UserRecord> userRecords = createUserRecords(mUserManagerHelper
94 .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<>();
101 for (UserInfo userInfo : userInfoList) {
jovanak80b48592018-04-11 17:09:53 -0700102 if (userInfo.isGuest()) {
103 // Don't display guests in the switcher.
104 continue;
105 }
jovanak0535abc2018-04-10 15:14:50 -0700106 boolean isForeground = mUserManagerHelper.getForegroundUserId() == userInfo.id;
jovanak80b48592018-04-11 17:09:53 -0700107 UserRecord record = new UserRecord(userInfo, false /* isStartGuestSession */,
jovanak0535abc2018-04-10 15:14:50 -0700108 false /* isAddUser */, isForeground);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700109 userRecords.add(record);
110 }
111
jovanak2d26ae32018-07-31 10:44:41 -0700112 // Add button for starting guest session.
113 userRecords.add(createStartGuestUserRecord());
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700114
jovanak0535abc2018-04-10 15:14:50 -0700115 // Add add user record if the foreground user can add users
jovanak82029ae2018-04-02 16:40:15 -0700116 if (mUserManagerHelper.foregroundUserCanAddUsers()) {
jovanak2d26ae32018-07-31 10:44:41 -0700117 userRecords.add(createAddUserRecord());
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700118 }
119
120 return userRecords;
121 }
122
123 /**
124 * Create guest user record
125 */
jovanak2d26ae32018-07-31 10:44:41 -0700126 private UserRecord createStartGuestUserRecord() {
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700127 UserInfo userInfo = new UserInfo();
jovanak2d26ae32018-07-31 10:44:41 -0700128 userInfo.name = mContext.getString(R.string.start_guest_session);
129 return new UserRecord(userInfo, true /* isStartGuestSession */, false /* isAddUser */,
130 false /* isForeground */);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700131 }
132
133 /**
134 * Create add user record
135 */
jovanak2d26ae32018-07-31 10:44:41 -0700136 private UserRecord createAddUserRecord() {
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700137 UserInfo userInfo = new UserInfo();
138 userInfo.name = mContext.getString(R.string.car_add_user);
jovanak80b48592018-04-11 17:09:53 -0700139 return new UserRecord(userInfo, false /* isStartGuestSession */,
jovanak0535abc2018-04-10 15:14:50 -0700140 true /* isAddUser */, false /* isForeground */);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700141 }
142
143 public void setUserSelectionListener(UserSelectionListener userSelectionListener) {
144 mUserSelectionListener = userSelectionListener;
145 }
146
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700147 @Override
148 public void onUsersUpdate() {
149 mAdapter.clearUsers();
150 mAdapter.updateUsers(createUserRecords(mUserManagerHelper.getAllUsers()));
151 mAdapter.notifyDataSetChanged();
152 }
153
154 /**
155 * Adapter to populate the grid layout with the available user profiles
156 */
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700157 public final class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserAdapterViewHolder>
Aarthi Balachanderc6d13662018-08-13 14:49:41 -0700158 implements Dialog.OnClickListener, Dialog.OnCancelListener {
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700159
160 private final Context mContext;
161 private List<UserRecord> mUsers;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700162 private final Resources mRes;
163 private final String mGuestName;
164 private final String mNewUserName;
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700165 private AlertDialog mDialog;
166 // View that holds the add user button. Used to enable/disable the view
167 private View mAddUserView;
Aarthi Balachandera7096002018-05-21 18:12:25 -0700168 // User record for the add user. Need to call notifyUserSelected only if the user
169 // confirms adding a user
170 private UserRecord mAddUserRecord;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700171
172 public UserAdapter(Context context, List<UserRecord> users) {
173 mRes = context.getResources();
174 mContext = context;
175 updateUsers(users);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700176 mGuestName = mRes.getString(R.string.car_guest);
177 mNewUserName = mRes.getString(R.string.car_new_user);
178 }
179
180 public void clearUsers() {
181 mUsers.clear();
182 }
183
184 public void updateUsers(List<UserRecord> users) {
185 mUsers = users;
186 }
187
188 @Override
189 public UserAdapterViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
190 View view = LayoutInflater.from(mContext)
191 .inflate(R.layout.car_fullscreen_user_pod, parent, false);
192 view.setAlpha(1f);
193 view.bringToFront();
194 return new UserAdapterViewHolder(view);
195 }
196
197 @Override
198 public void onBindViewHolder(UserAdapterViewHolder holder, int position) {
199 UserRecord userRecord = mUsers.get(position);
Aarthi Balachandere3110e42018-04-30 18:16:26 -0700200 RoundedBitmapDrawable circleIcon = RoundedBitmapDrawableFactory.create(mRes,
201 getUserRecordIcon(userRecord));
202 circleIcon.setCircular(true);
203 holder.mUserAvatarImageView.setImageDrawable(circleIcon);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700204 holder.mUserNameTextView.setText(userRecord.mInfo.name);
Aarthi Balachandera7096002018-05-21 18:12:25 -0700205
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700206 holder.mView.setOnClickListener(v -> {
207 if (userRecord == null) {
208 return;
209 }
210
jovanak80b48592018-04-11 17:09:53 -0700211 if (userRecord.mIsStartGuestSession) {
Aarthi Balachandera7096002018-05-21 18:12:25 -0700212 notifyUserSelected(userRecord);
jovanak6639c4d2018-04-25 14:45:18 -0700213 mUserManagerHelper.startNewGuestSession(mGuestName);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700214 return;
215 }
216
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700217 // If the user wants to add a user, show dialog to confirm adding a user
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700218 if (userRecord.mIsAddUser) {
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700219 // Disable button so it cannot be clicked multiple times
220 mAddUserView = holder.mView;
221 mAddUserView.setEnabled(false);
222
223 String message = mRes.getString(R.string.user_add_user_message_setup)
224 .concat(System.getProperty("line.separator"))
225 .concat(System.getProperty("line.separator"))
226 .concat(mRes.getString(R.string.user_add_user_message_update));
227
Aarthi Balachandera7096002018-05-21 18:12:25 -0700228 mAddUserRecord = userRecord;
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700229 mDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
230 .setTitle(R.string.user_add_user_title)
231 .setMessage(message)
232 .setNegativeButton(android.R.string.cancel, this)
233 .setPositiveButton(android.R.string.ok, this)
Aarthi Balachanderc6d13662018-08-13 14:49:41 -0700234 .setOnCancelListener(this)
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700235 .create();
236 // Sets window flags for the SysUI dialog
237 SystemUIDialog.applyFlags(mDialog);
238 mDialog.show();
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700239 return;
240 }
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700241 // 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 -0700242 notifyUserSelected(userRecord);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700243 mUserManagerHelper.switchToUser(userRecord.mInfo);
244 });
245
246 }
247
Aarthi Balachandera7096002018-05-21 18:12:25 -0700248 private void notifyUserSelected(UserRecord userRecord) {
249 // Notify the listener which user was selected
250 if (mUserSelectionListener != null) {
251 mUserSelectionListener.onUserSelected(userRecord);
252 }
253 }
254
jovanake4ce3cc2018-04-19 12:17:12 -0700255 private Bitmap getUserRecordIcon(UserRecord userRecord) {
256 if (userRecord.mIsStartGuestSession) {
jovanak6639c4d2018-04-25 14:45:18 -0700257 return mUserManagerHelper.getGuestDefaultIcon();
jovanake4ce3cc2018-04-19 12:17:12 -0700258 }
259
260 if (userRecord.mIsAddUser) {
261 return UserIcons.convertToBitmap(mContext
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700262 .getDrawable(R.drawable.car_add_circle_round));
jovanake4ce3cc2018-04-19 12:17:12 -0700263 }
264
265 return mUserManagerHelper.getUserIcon(userRecord.mInfo);
266 }
267
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700268 @Override
269 public void onClick(DialogInterface dialog, int which) {
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700270 if (which == BUTTON_POSITIVE) {
Aarthi Balachandera7096002018-05-21 18:12:25 -0700271 notifyUserSelected(mAddUserRecord);
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700272 new AddNewUserTask().execute(mNewUserName);
Aarthi Balachandera7096002018-05-21 18:12:25 -0700273 } else if (which == BUTTON_NEGATIVE) {
274 // Enable the add button only if cancel
275 if (mAddUserView != null) {
276 mAddUserView.setEnabled(true);
277 }
Aarthi Balachander0e0e9212018-04-19 19:25:33 -0700278 }
279 }
280
Aarthi Balachanderc6d13662018-08-13 14:49:41 -0700281 @Override
282 public void onCancel(DialogInterface dialog) {
283 // Enable the add button again if user cancels dialog by clicking outside the dialog
284 if (mAddUserView != null) {
285 mAddUserView.setEnabled(true);
286 }
287 }
288
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700289 private class AddNewUserTask extends AsyncTask<String, Void, UserInfo> {
290
291 @Override
292 protected UserInfo doInBackground(String... userNames) {
293 return mUserManagerHelper.createNewUser(userNames[0]);
294 }
295
296 @Override
297 protected void onPreExecute() {
298 }
299
300 @Override
301 protected void onPostExecute(UserInfo user) {
302 if (user != null) {
303 mUserManagerHelper.switchToUser(user);
304 }
305 }
306 }
307
308 @Override
309 public int getItemCount() {
310 return mUsers.size();
311 }
312
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700313 public class UserAdapterViewHolder extends RecyclerView.ViewHolder {
314
315 public ImageView mUserAvatarImageView;
316 public TextView mUserNameTextView;
317 public View mView;
318
319 public UserAdapterViewHolder(View view) {
320 super(view);
321 mView = view;
322 mUserAvatarImageView = (ImageView) view.findViewById(R.id.user_avatar);
323 mUserNameTextView = (TextView) view.findViewById(R.id.user_name);
324 }
325 }
326 }
327
328 /**
329 * Object wrapper class for the userInfo. Use it to distinguish if a profile is a
jovanak0535abc2018-04-10 15:14:50 -0700330 * guest profile, add user profile, or the foreground user.
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700331 */
332 public static final class UserRecord {
333
334 public final UserInfo mInfo;
jovanak80b48592018-04-11 17:09:53 -0700335 public final boolean mIsStartGuestSession;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700336 public final boolean mIsAddUser;
jovanak0535abc2018-04-10 15:14:50 -0700337 public final boolean mIsForeground;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700338
jovanak80b48592018-04-11 17:09:53 -0700339 public UserRecord(UserInfo userInfo, boolean isStartGuestSession, boolean isAddUser,
jovanak0535abc2018-04-10 15:14:50 -0700340 boolean isForeground) {
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700341 mInfo = userInfo;
jovanak80b48592018-04-11 17:09:53 -0700342 mIsStartGuestSession = isStartGuestSession;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700343 mIsAddUser = isAddUser;
jovanak0535abc2018-04-10 15:14:50 -0700344 mIsForeground = isForeground;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -0700345 }
346 }
347
348 /**
349 * Listener used to notify when a user has been selected
350 */
351 interface UserSelectionListener {
352
353 void onUserSelected(UserRecord record);
354 }
355}