1 /* 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 17 package com.android.systemui.statusbar.car; 18 19 import static android.content.DialogInterface.BUTTON_NEGATIVE; 20 import static android.content.DialogInterface.BUTTON_POSITIVE; 21 22 import android.app.AlertDialog; 23 import android.app.AlertDialog.Builder; 24 import android.app.Dialog; 25 import android.content.Context; 26 import android.content.DialogInterface; 27 import android.content.pm.UserInfo; 28 import android.content.res.Resources; 29 import android.graphics.Bitmap; 30 import android.os.AsyncTask; 31 import android.support.v4.graphics.drawable.RoundedBitmapDrawable; 32 import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; 33 import android.support.v7.widget.RecyclerView; 34 import android.util.AttributeSet; 35 import android.view.LayoutInflater; 36 import android.view.View; 37 import android.view.ViewGroup; 38 import android.widget.ImageView; 39 import android.widget.TextView; 40 41 import androidx.car.widget.PagedListView; 42 43 import com.android.internal.util.UserIcons; 44 import com.android.settingslib.users.UserManagerHelper; 45 import com.android.systemui.R; 46 47 import com.android.systemui.statusbar.phone.SystemUIDialog; 48 import java.util.ArrayList; 49 import 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 */ 55 public class UserGridRecyclerView extends PagedListView implements 56 UserManagerHelper.OnUsersUpdateListener { 57 private UserSelectionListener mUserSelectionListener; 58 private UserAdapter mAdapter; 59 private UserManagerHelper mUserManagerHelper; 60 private Context mContext; 61 UserGridRecyclerView(Context context, AttributeSet attrs)62 public UserGridRecyclerView(Context context, AttributeSet attrs) { 63 super(context, attrs); 64 mContext = context; 65 mUserManagerHelper = new UserManagerHelper(mContext); 66 } 67 68 /** 69 * Register listener for any update to the users 70 */ 71 @Override onFinishInflate()72 public void onFinishInflate() { 73 super.onFinishInflate(); 74 mUserManagerHelper.registerOnUsersUpdateListener(this); 75 } 76 77 /** 78 * Unregisters listener checking for any change to the users 79 */ 80 @Override onDetachedFromWindow()81 public void onDetachedFromWindow() { 82 super.onDetachedFromWindow(); 83 mUserManagerHelper.unregisterOnUsersUpdateListener(); 84 } 85 86 /** 87 * Initializes the adapter that populates the grid layout 88 * 89 * @return the adapter 90 */ buildAdapter()91 public void buildAdapter() { 92 List<UserRecord> userRecords = createUserRecords(mUserManagerHelper 93 .getAllUsers()); 94 mAdapter = new UserAdapter(mContext, userRecords); 95 super.setAdapter(mAdapter); 96 } 97 createUserRecords(List<UserInfo> userInfoList)98 private List<UserRecord> createUserRecords(List<UserInfo> userInfoList) { 99 List<UserRecord> userRecords = new ArrayList<>(); 100 for (UserInfo userInfo : userInfoList) { 101 if (userInfo.isGuest()) { 102 // Don't display guests in the switcher. 103 continue; 104 } 105 boolean isForeground = 106 mUserManagerHelper.getForegroundUserId() == userInfo.id; 107 UserRecord record = new UserRecord(userInfo, false /* isStartGuestSession */, 108 false /* isAddUser */, isForeground); 109 userRecords.add(record); 110 } 111 112 // Add guest user record if the foreground user is not a guest 113 if (!mUserManagerHelper.foregroundUserIsGuestUser()) { 114 userRecords.add(addGuestUserRecord()); 115 } 116 117 // Add add user record if the foreground user can add users 118 if (mUserManagerHelper.foregroundUserCanAddUsers()) { 119 userRecords.add(addUserRecord()); 120 } 121 122 return userRecords; 123 } 124 125 /** 126 * Create guest user record 127 */ addGuestUserRecord()128 private UserRecord addGuestUserRecord() { 129 UserInfo userInfo = new UserInfo(); 130 userInfo.name = mContext.getString(R.string.car_guest); 131 return new UserRecord(userInfo, true /* isStartGuestSession */, 132 false /* isAddUser */, false /* isForeground */); 133 } 134 135 /** 136 * Create add user record 137 */ addUserRecord()138 private UserRecord addUserRecord() { 139 UserInfo userInfo = new UserInfo(); 140 userInfo.name = mContext.getString(R.string.car_add_user); 141 return new UserRecord(userInfo, false /* isStartGuestSession */, 142 true /* isAddUser */, false /* isForeground */); 143 } 144 setUserSelectionListener(UserSelectionListener userSelectionListener)145 public void setUserSelectionListener(UserSelectionListener userSelectionListener) { 146 mUserSelectionListener = userSelectionListener; 147 } 148 149 @Override onUsersUpdate()150 public void onUsersUpdate() { 151 mAdapter.clearUsers(); 152 mAdapter.updateUsers(createUserRecords(mUserManagerHelper.getAllUsers())); 153 mAdapter.notifyDataSetChanged(); 154 } 155 156 /** 157 * Adapter to populate the grid layout with the available user profiles 158 */ 159 public final class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserAdapterViewHolder> 160 implements Dialog.OnClickListener { 161 162 private final Context mContext; 163 private List<UserRecord> mUsers; 164 private final Resources mRes; 165 private final String mGuestName; 166 private final String mNewUserName; 167 private AlertDialog mDialog; 168 // View that holds the add user button. Used to enable/disable the view 169 private View mAddUserView; 170 // User record for the add user. Need to call notifyUserSelected only if the user 171 // confirms adding a user 172 private UserRecord mAddUserRecord; 173 UserAdapter(Context context, List<UserRecord> users)174 public UserAdapter(Context context, List<UserRecord> users) { 175 mRes = context.getResources(); 176 mContext = context; 177 updateUsers(users); 178 mGuestName = mRes.getString(R.string.car_guest); 179 mNewUserName = mRes.getString(R.string.car_new_user); 180 } 181 clearUsers()182 public void clearUsers() { 183 mUsers.clear(); 184 } 185 updateUsers(List<UserRecord> users)186 public void updateUsers(List<UserRecord> users) { 187 mUsers = users; 188 } 189 190 @Override onCreateViewHolder(ViewGroup parent, int viewType)191 public UserAdapterViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 192 View view = LayoutInflater.from(mContext) 193 .inflate(R.layout.car_fullscreen_user_pod, parent, false); 194 view.setAlpha(1f); 195 view.bringToFront(); 196 return new UserAdapterViewHolder(view); 197 } 198 199 @Override onBindViewHolder(UserAdapterViewHolder holder, int position)200 public void onBindViewHolder(UserAdapterViewHolder holder, int position) { 201 UserRecord userRecord = mUsers.get(position); 202 RoundedBitmapDrawable circleIcon = RoundedBitmapDrawableFactory.create(mRes, 203 getUserRecordIcon(userRecord)); 204 circleIcon.setCircular(true); 205 holder.mUserAvatarImageView.setImageDrawable(circleIcon); 206 holder.mUserNameTextView.setText(userRecord.mInfo.name); 207 208 holder.mView.setOnClickListener(v -> { 209 if (userRecord == null) { 210 return; 211 } 212 213 214 // If the user selects Guest, start the guest session. 215 if (userRecord.mIsStartGuestSession) { 216 notifyUserSelected(userRecord); 217 mUserManagerHelper.startNewGuestSession(mGuestName); 218 return; 219 } 220 221 // If the user wants to add a user, show dialog to confirm adding a user 222 if (userRecord.mIsAddUser) { 223 // Disable button so it cannot be clicked multiple times 224 mAddUserView = holder.mView; 225 mAddUserView.setEnabled(false); 226 227 String message = mRes.getString(R.string.user_add_user_message_setup) 228 .concat(System.getProperty("line.separator")) 229 .concat(System.getProperty("line.separator")) 230 .concat(mRes.getString(R.string.user_add_user_message_update)); 231 232 mAddUserRecord = userRecord; 233 mDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert) 234 .setTitle(R.string.user_add_user_title) 235 .setMessage(message) 236 .setNegativeButton(android.R.string.cancel, this) 237 .setPositiveButton(android.R.string.ok, this) 238 .create(); 239 // Sets window flags for the SysUI dialog 240 SystemUIDialog.applyFlags(mDialog); 241 mDialog.show(); 242 return; 243 } 244 // If the user doesn't want to be a guest or add a user, switch to the user selected 245 notifyUserSelected(userRecord); 246 mUserManagerHelper.switchToUser(userRecord.mInfo); 247 }); 248 249 } 250 notifyUserSelected(UserRecord userRecord)251 private void notifyUserSelected(UserRecord userRecord) { 252 // Notify the listener which user was selected 253 if (mUserSelectionListener != null) { 254 mUserSelectionListener.onUserSelected(userRecord); 255 } 256 } 257 getUserRecordIcon(UserRecord userRecord)258 private Bitmap getUserRecordIcon(UserRecord userRecord) { 259 if (userRecord.mIsStartGuestSession) { 260 return mUserManagerHelper.getGuestDefaultIcon(); 261 } 262 263 if (userRecord.mIsAddUser) { 264 return UserIcons.convertToBitmap(mContext 265 .getDrawable(R.drawable.car_add_circle_round)); 266 } 267 268 return mUserManagerHelper.getUserIcon(userRecord.mInfo); 269 } 270 271 @Override onClick(DialogInterface dialog, int which)272 public void onClick(DialogInterface dialog, int which) { 273 if (which == BUTTON_POSITIVE) { 274 notifyUserSelected(mAddUserRecord); 275 new AddNewUserTask().execute(mNewUserName); 276 } else if (which == BUTTON_NEGATIVE) { 277 // Enable the add button only if cancel 278 if (mAddUserView != null) { 279 mAddUserView.setEnabled(true); 280 } 281 } 282 } 283 284 private class AddNewUserTask extends AsyncTask<String, Void, UserInfo> { 285 286 @Override doInBackground(String... userNames)287 protected UserInfo doInBackground(String... userNames) { 288 return mUserManagerHelper.createNewUser(userNames[0]); 289 } 290 291 @Override onPreExecute()292 protected void onPreExecute() { 293 } 294 295 @Override onPostExecute(UserInfo user)296 protected void onPostExecute(UserInfo user) { 297 if (user != null) { 298 mUserManagerHelper.switchToUser(user); 299 } 300 } 301 } 302 303 @Override getItemCount()304 public int getItemCount() { 305 return mUsers.size(); 306 } 307 308 public class UserAdapterViewHolder extends RecyclerView.ViewHolder { 309 310 public ImageView mUserAvatarImageView; 311 public TextView mUserNameTextView; 312 public View mView; 313 UserAdapterViewHolder(View view)314 public UserAdapterViewHolder(View view) { 315 super(view); 316 mView = view; 317 mUserAvatarImageView = (ImageView) view.findViewById(R.id.user_avatar); 318 mUserNameTextView = (TextView) view.findViewById(R.id.user_name); 319 } 320 } 321 } 322 323 /** 324 * Object wrapper class for the userInfo. Use it to distinguish if a profile is a 325 * guest profile, add user profile, or the foreground user. 326 */ 327 public static final class UserRecord { 328 329 public final UserInfo mInfo; 330 public final boolean mIsStartGuestSession; 331 public final boolean mIsAddUser; 332 public final boolean mIsForeground; 333 UserRecord(UserInfo userInfo, boolean isStartGuestSession, boolean isAddUser, boolean isForeground)334 public UserRecord(UserInfo userInfo, boolean isStartGuestSession, boolean isAddUser, 335 boolean isForeground) { 336 mInfo = userInfo; 337 mIsStartGuestSession = isStartGuestSession; 338 mIsAddUser = isAddUser; 339 mIsForeground = isForeground; 340 } 341 } 342 343 /** 344 * Listener used to notify when a user has been selected 345 */ 346 interface UserSelectionListener { 347 onUserSelected(UserRecord record)348 void onUserSelected(UserRecord record); 349 } 350 } 351