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.car.settings.users; 18 19 import android.car.user.CarUserManagerHelper; 20 import android.content.Context; 21 import android.content.pm.UserInfo; 22 import android.content.res.Resources; 23 import android.graphics.Bitmap; 24 import android.support.v4.graphics.drawable.RoundedBitmapDrawable; 25 import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; 26 import android.support.v7.widget.RecyclerView; 27 import android.util.AttributeSet; 28 import android.view.LayoutInflater; 29 import android.view.View; 30 import android.view.ViewGroup; 31 import android.widget.FrameLayout; 32 import android.widget.ImageView; 33 import android.widget.TextView; 34 35 import androidx.car.widget.PagedListView; 36 37 import com.android.car.settings.R; 38 import com.android.car.settings.common.BaseFragment; 39 import com.android.car.settings.users.ConfirmCreateNewUserDialog.CancelCreateNewUserListener; 40 import com.android.car.settings.users.ConfirmCreateNewUserDialog.ConfirmCreateNewUserListener; 41 import com.android.internal.util.UserIcons; 42 43 import java.util.ArrayList; 44 import java.util.List; 45 46 /** 47 * Displays a GridLayout with icons for the users in the system to allow switching between users. 48 * One of the uses of this is for the lock screen in auto. 49 */ 50 public class UserGridRecyclerView extends PagedListView implements 51 CarUserManagerHelper.OnUsersUpdateListener { 52 private UserAdapter mAdapter; 53 private CarUserManagerHelper mCarUserManagerHelper; 54 private Context mContext; 55 private BaseFragment mBaseFragment; 56 public AddNewUserTask mAddNewUserTask; 57 public boolean mEnableAddUserButton; 58 UserGridRecyclerView(Context context, AttributeSet attrs)59 public UserGridRecyclerView(Context context, AttributeSet attrs) { 60 super(context, attrs); 61 mContext = context; 62 mCarUserManagerHelper = new CarUserManagerHelper(mContext); 63 mEnableAddUserButton = true; 64 } 65 66 /** 67 * Register listener for any update to the users 68 */ 69 @Override onFinishInflate()70 public void onFinishInflate() { 71 super.onFinishInflate(); 72 mCarUserManagerHelper.registerOnUsersUpdateListener(this); 73 } 74 75 /** 76 * Unregisters listener checking for any change to the users 77 */ 78 @Override onDetachedFromWindow()79 public void onDetachedFromWindow() { 80 super.onDetachedFromWindow(); 81 mCarUserManagerHelper.unregisterOnUsersUpdateListener(); 82 if (mAddNewUserTask != null) { 83 mAddNewUserTask.cancel(/* mayInterruptIfRunning= */ false); 84 } 85 } 86 87 /** 88 * Initializes the adapter that populates the grid layout 89 */ buildAdapter()90 public void buildAdapter() { 91 List<UserRecord> userRecords = createUserRecords(mCarUserManagerHelper 92 .getAllUsers()); 93 mAdapter = new UserAdapter(mContext, userRecords); 94 super.setAdapter(mAdapter); 95 } 96 createUserRecords(List<UserInfo> userInfoList)97 private List<UserRecord> createUserRecords(List<UserInfo> userInfoList) { 98 List<UserRecord> userRecords = new ArrayList<>(); 99 for (UserInfo userInfo : userInfoList) { 100 if (userInfo.isGuest()) { 101 // Don't display guests in the switcher. 102 continue; 103 } 104 boolean isForeground = 105 mCarUserManagerHelper.getCurrentForegroundUserId() == userInfo.id; 106 UserRecord record = new UserRecord(userInfo, false /* isStartGuestSession */, 107 false /* isAddUser */, isForeground); 108 userRecords.add(record); 109 } 110 111 // Add guest user record if the foreground user is not a guest 112 if (!mCarUserManagerHelper.isForegroundUserGuest()) { 113 userRecords.add(addGuestUserRecord()); 114 } 115 116 // Add "add user" record if the foreground user can add users 117 if (mCarUserManagerHelper.canForegroundUserAddUsers()) { 118 userRecords.add(addUserRecord()); 119 } 120 121 return userRecords; 122 } 123 124 /** 125 * Show the "Add User" Button 126 */ enableAddUser()127 public void enableAddUser() { 128 mEnableAddUserButton = true; 129 onUsersUpdate(); 130 } 131 132 /** 133 * Hide the "Add User" Button 134 */ disableAddUser()135 public void disableAddUser() { 136 mEnableAddUserButton = false; 137 onUsersUpdate(); 138 } 139 140 /** 141 * Create guest user record 142 */ addGuestUserRecord()143 private UserRecord addGuestUserRecord() { 144 UserInfo userInfo = new UserInfo(); 145 userInfo.name = mContext.getString(R.string.user_guest); 146 return new UserRecord(userInfo, true /* isStartGuestSession */, 147 false /* isAddUser */, false /* isForeground */); 148 } 149 150 /** 151 * Create add user record 152 */ addUserRecord()153 private UserRecord addUserRecord() { 154 UserInfo userInfo = new UserInfo(); 155 userInfo.name = mContext.getString(R.string.user_add_user_menu); 156 return new UserRecord(userInfo, false /* isStartGuestSession */, 157 true /* isAddUser */, false /* isForeground */); 158 } 159 setFragment(BaseFragment fragment)160 public void setFragment(BaseFragment fragment) { 161 mBaseFragment = fragment; 162 } 163 164 @Override onUsersUpdate()165 public void onUsersUpdate() { 166 // If you can show the add user button, there is no restriction 167 mAdapter.setAddUserRestricted(mEnableAddUserButton ? false : true); 168 mAdapter.clearUsers(); 169 mAdapter.updateUsers(createUserRecords(mCarUserManagerHelper 170 .getAllUsers())); 171 mAdapter.notifyDataSetChanged(); 172 } 173 174 /** 175 * Adapter to populate the grid layout with the available user profiles 176 */ 177 public final class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserAdapterViewHolder> 178 implements ConfirmCreateNewUserListener, CancelCreateNewUserListener, 179 AddNewUserTask.AddNewUserListener { 180 181 private final Context mContext; 182 private List<UserRecord> mUsers; 183 private final Resources mRes; 184 private final String mGuestName; 185 private final String mNewUserName; 186 // View that holds the add user button. Used to enable/disable the view 187 private View mAddUserView; 188 private float mOpacityDisabled; 189 private float mOpacityEnabled; 190 private boolean mIsAddUserRestricted; 191 UserAdapter(Context context, List<UserRecord> users)192 public UserAdapter(Context context, List<UserRecord> users) { 193 mRes = context.getResources(); 194 mContext = context; 195 updateUsers(users); 196 mGuestName = mRes.getString(R.string.user_guest); 197 mNewUserName = mRes.getString(R.string.user_new_user_name); 198 mOpacityDisabled = mRes.getFloat(R.dimen.opacity_disabled); 199 mOpacityEnabled = mRes.getFloat(R.dimen.opacity_enabled); 200 } 201 202 /** 203 * Removes all the users from the User Grid. 204 */ clearUsers()205 public void clearUsers() { 206 mUsers.clear(); 207 } 208 209 /** 210 * Refreshes the User Grid with the new List of users. 211 * @param users 212 */ updateUsers(List<UserRecord> users)213 public void updateUsers(List<UserRecord> users) { 214 mUsers = users; 215 } 216 217 @Override onCreateViewHolder(ViewGroup parent, int viewType)218 public UserAdapterViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 219 View view = LayoutInflater.from(mContext) 220 .inflate(R.layout.car_user_switcher_pod, parent, false); 221 view.setAlpha(mOpacityEnabled); 222 view.bringToFront(); 223 return new UserAdapterViewHolder(view); 224 } 225 226 @Override onBindViewHolder(UserAdapterViewHolder holder, int position)227 public void onBindViewHolder(UserAdapterViewHolder holder, int position) { 228 UserRecord userRecord = mUsers.get(position); 229 RoundedBitmapDrawable circleIcon = RoundedBitmapDrawableFactory.create(mRes, 230 getUserRecordIcon(userRecord)); 231 circleIcon.setCircular(true); 232 holder.mUserAvatarImageView.setImageDrawable(circleIcon); 233 holder.mUserNameTextView.setText(userRecord.mInfo.name); 234 235 // Show the current user frame if current user 236 if (userRecord.mIsForeground) { 237 holder.mFrame.setBackgroundResource(R.drawable.car_user_avatar_bg_circle); 238 } else { 239 holder.mFrame.setBackgroundResource(0); 240 } 241 242 // If there are restrictions, show a 50% opaque "add user" view 243 if (userRecord.mIsAddUser && mIsAddUserRestricted) { 244 holder.mView.setAlpha(mOpacityDisabled); 245 } else { 246 holder.mView.setAlpha(mOpacityEnabled); 247 } 248 249 holder.mView.setOnClickListener(v -> { 250 if (userRecord == null) { 251 return; 252 } 253 254 // If the user selects Guest, start the guest session. 255 if (userRecord.mIsStartGuestSession) { 256 mCarUserManagerHelper.startNewGuestSession(mGuestName); 257 return; 258 } 259 260 // If the user wants to add a user, show dialog to confirm adding a user 261 if (userRecord.mIsAddUser) { 262 if (mIsAddUserRestricted) { 263 mBaseFragment.getFragmentController().showDOBlockingMessage(); 264 } else { 265 // Disable button so it cannot be clicked multiple times 266 mAddUserView = holder.mView; 267 mAddUserView.setEnabled(false); 268 269 ConfirmCreateNewUserDialog dialog = 270 new ConfirmCreateNewUserDialog(); 271 dialog.setConfirmCreateNewUserListener(this); 272 dialog.setCancelCreateNewUserListener(this); 273 if (mBaseFragment != null) { 274 dialog.show(mBaseFragment); 275 } 276 } 277 return; 278 } 279 // If the user doesn't want to be a guest or add a user, switch to the user selected 280 mCarUserManagerHelper.switchToUser(userRecord.mInfo); 281 }); 282 283 } 284 285 /** 286 * Specify if adding a user should be restricted. 287 * 288 * @param isAddUserRestricted should adding a user be restricted 289 * 290 */ setAddUserRestricted(boolean isAddUserRestricted)291 public void setAddUserRestricted(boolean isAddUserRestricted) { 292 mIsAddUserRestricted = isAddUserRestricted; 293 } 294 getUserRecordIcon(UserRecord userRecord)295 private Bitmap getUserRecordIcon(UserRecord userRecord) { 296 if (userRecord.mIsStartGuestSession) { 297 return mCarUserManagerHelper.getGuestDefaultIcon(); 298 } 299 300 if (userRecord.mIsAddUser) { 301 return UserIcons.convertToBitmap(mContext 302 .getDrawable(R.drawable.car_add_circle_round)); 303 } 304 305 return mCarUserManagerHelper.getUserIcon(userRecord.mInfo); 306 } 307 308 @Override onCreateNewUserConfirmed()309 public void onCreateNewUserConfirmed() { 310 mAddNewUserTask = 311 new AddNewUserTask(mCarUserManagerHelper, /* addNewUserListener= */ this); 312 mAddNewUserTask.execute(mNewUserName); 313 } 314 315 /** 316 * Enable the "add user" button if the user cancels adding an user 317 */ 318 @Override onCreateNewUserCancelled()319 public void onCreateNewUserCancelled() { 320 if (mAddUserView != null) { 321 mAddUserView.setEnabled(true); 322 } 323 } 324 325 @Override onUserAdded()326 public void onUserAdded() { 327 if (mAddUserView != null) { 328 mAddUserView.setEnabled(true); 329 } 330 } 331 332 @Override getItemCount()333 public int getItemCount() { 334 return mUsers.size(); 335 } 336 337 /** 338 * Layout for each individual pod in the Grid RecyclerView 339 */ 340 public class UserAdapterViewHolder extends RecyclerView.ViewHolder { 341 342 public ImageView mUserAvatarImageView; 343 public TextView mUserNameTextView; 344 public View mView; 345 public FrameLayout mFrame; 346 UserAdapterViewHolder(View view)347 public UserAdapterViewHolder(View view) { 348 super(view); 349 mView = view; 350 mUserAvatarImageView = (ImageView) view.findViewById(R.id.user_avatar); 351 mUserNameTextView = (TextView) view.findViewById(R.id.user_name); 352 mFrame = (FrameLayout) view.findViewById(R.id.current_user_frame); 353 } 354 } 355 } 356 357 /** 358 * Object wrapper class for the userInfo. Use it to distinguish if a profile is a 359 * guest profile, add user profile, or the foreground user. 360 */ 361 public static final class UserRecord { 362 363 public final UserInfo mInfo; 364 public final boolean mIsStartGuestSession; 365 public final boolean mIsAddUser; 366 public final boolean mIsForeground; 367 UserRecord(UserInfo userInfo, boolean isStartGuestSession, boolean isAddUser, boolean isForeground)368 public UserRecord(UserInfo userInfo, boolean isStartGuestSession, boolean isAddUser, 369 boolean isForeground) { 370 mInfo = userInfo; 371 mIsStartGuestSession = isStartGuestSession; 372 mIsAddUser = isAddUser; 373 mIsForeground = isForeground; 374 } 375 } 376 } 377