Move car related code from SystemUI to CarSystemUI
Test: Emulator phone and Car
Change-Id: Ia64a23c1d3643899118e578b82c665c034af1c8e
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
new file mode 100644
index 0000000..fb2b57b
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.car;
+
+import static android.content.DialogInterface.BUTTON_NEGATIVE;
+import static android.content.DialogInterface.BUTTON_POSITIVE;
+
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
+import android.app.Dialog;
+import android.car.userlib.CarUserManagerHelper;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.car.widget.PagedListView;
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.internal.util.UserIcons;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Displays a GridLayout with icons for the users in the system to allow switching between users.
+ * One of the uses of this is for the lock screen in auto.
+ */
+public class UserGridRecyclerView extends PagedListView implements
+ CarUserManagerHelper.OnUsersUpdateListener {
+ private UserSelectionListener mUserSelectionListener;
+ private UserAdapter mAdapter;
+ private CarUserManagerHelper mCarUserManagerHelper;
+ private Context mContext;
+
+ public UserGridRecyclerView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ mCarUserManagerHelper = new CarUserManagerHelper(mContext);
+ }
+
+ /**
+ * Register listener for any update to the users
+ */
+ @Override
+ public void onFinishInflate() {
+ super.onFinishInflate();
+ mCarUserManagerHelper.registerOnUsersUpdateListener(this);
+ }
+
+ /**
+ * Unregisters listener checking for any change to the users
+ */
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mCarUserManagerHelper.unregisterOnUsersUpdateListener(this);
+ }
+
+ /**
+ * Initializes the adapter that populates the grid layout
+ *
+ * @return the adapter
+ */
+ public void buildAdapter() {
+ List<UserRecord> userRecords = createUserRecords(mCarUserManagerHelper
+ .getAllUsers());
+ mAdapter = new UserAdapter(mContext, userRecords);
+ super.setAdapter(mAdapter);
+ }
+
+ private List<UserRecord> createUserRecords(List<UserInfo> userInfoList) {
+ List<UserRecord> userRecords = new ArrayList<>();
+
+ // If the foreground user CANNOT switch to other users, only display the foreground user.
+ if (!mCarUserManagerHelper.canForegroundUserSwitchUsers()) {
+ userRecords.add(createForegroundUserRecord());
+ return userRecords;
+ }
+
+ for (UserInfo userInfo : userInfoList) {
+ if (userInfo.isGuest()) {
+ // Don't display guests in the switcher.
+ continue;
+ }
+
+ boolean isForeground =
+ mCarUserManagerHelper.getCurrentForegroundUserId() == userInfo.id;
+ UserRecord record = new UserRecord(userInfo, false /* isStartGuestSession */,
+ false /* isAddUser */, isForeground);
+ userRecords.add(record);
+ }
+
+ // Add button for starting guest session.
+ userRecords.add(createStartGuestUserRecord());
+
+ // Add add user record if the foreground user can add users
+ if (mCarUserManagerHelper.canForegroundUserAddUsers()) {
+ userRecords.add(createAddUserRecord());
+ }
+
+ return userRecords;
+ }
+
+ private UserRecord createForegroundUserRecord() {
+ return new UserRecord(mCarUserManagerHelper.getCurrentForegroundUserInfo(),
+ false /* isStartGuestSession */, false /* isAddUser */, true /* isForeground */);
+ }
+
+ /**
+ * Create guest user record
+ */
+ private UserRecord createStartGuestUserRecord() {
+ UserInfo userInfo = new UserInfo();
+ userInfo.name = mContext.getString(R.string.start_guest_session);
+ return new UserRecord(userInfo, true /* isStartGuestSession */, false /* isAddUser */,
+ false /* isForeground */);
+ }
+
+ /**
+ * Create add user record
+ */
+ private UserRecord createAddUserRecord() {
+ UserInfo userInfo = new UserInfo();
+ userInfo.name = mContext.getString(R.string.car_add_user);
+ return new UserRecord(userInfo, false /* isStartGuestSession */,
+ true /* isAddUser */, false /* isForeground */);
+ }
+
+ public void setUserSelectionListener(UserSelectionListener userSelectionListener) {
+ mUserSelectionListener = userSelectionListener;
+ }
+
+ @Override
+ public void onUsersUpdate() {
+ mAdapter.clearUsers();
+ mAdapter.updateUsers(createUserRecords(mCarUserManagerHelper.getAllUsers()));
+ mAdapter.notifyDataSetChanged();
+ }
+
+ /**
+ * Adapter to populate the grid layout with the available user profiles
+ */
+ public final class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserAdapterViewHolder>
+ implements Dialog.OnClickListener, Dialog.OnCancelListener {
+
+ private final Context mContext;
+ private List<UserRecord> mUsers;
+ private final Resources mRes;
+ private final String mGuestName;
+ private final String mNewUserName;
+ // View that holds the add user button. Used to enable/disable the view
+ private View mAddUserView;
+ // User record for the add user. Need to call notifyUserSelected only if the user
+ // confirms adding a user
+ private UserRecord mAddUserRecord;
+
+ public UserAdapter(Context context, List<UserRecord> users) {
+ mRes = context.getResources();
+ mContext = context;
+ updateUsers(users);
+ mGuestName = mRes.getString(R.string.car_guest);
+ mNewUserName = mRes.getString(R.string.car_new_user);
+ }
+
+ public void clearUsers() {
+ mUsers.clear();
+ }
+
+ public void updateUsers(List<UserRecord> users) {
+ mUsers = users;
+ }
+
+ @Override
+ public UserAdapterViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(mContext)
+ .inflate(R.layout.car_fullscreen_user_pod, parent, false);
+ view.setAlpha(1f);
+ view.bringToFront();
+ return new UserAdapterViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(UserAdapterViewHolder holder, int position) {
+ UserRecord userRecord = mUsers.get(position);
+ RoundedBitmapDrawable circleIcon = RoundedBitmapDrawableFactory.create(mRes,
+ getUserRecordIcon(userRecord));
+ circleIcon.setCircular(true);
+ holder.mUserAvatarImageView.setImageDrawable(circleIcon);
+ holder.mUserNameTextView.setText(userRecord.mInfo.name);
+
+ holder.mView.setOnClickListener(v -> {
+ if (userRecord == null) {
+ return;
+ }
+
+ if (userRecord.mIsStartGuestSession) {
+ notifyUserSelected(userRecord);
+ mCarUserManagerHelper.startGuestSession(mGuestName);
+ return;
+ }
+
+ // If the user wants to add a user, show dialog to confirm adding a user
+ if (userRecord.mIsAddUser) {
+ // Disable button so it cannot be clicked multiple times
+ mAddUserView = holder.mView;
+ mAddUserView.setEnabled(false);
+ mAddUserRecord = userRecord;
+
+ handleAddUserClicked();
+ return;
+ }
+ // If the user doesn't want to be a guest or add a user, switch to the user selected
+ notifyUserSelected(userRecord);
+ mCarUserManagerHelper.switchToUser(userRecord.mInfo);
+ });
+
+ }
+
+ private void handleAddUserClicked() {
+ if (mCarUserManagerHelper.isUserLimitReached()) {
+ mAddUserView.setEnabled(true);
+ showMaxUserLimitReachedDialog();
+ } else {
+ showConfirmAddUserDialog();
+ }
+ }
+
+ private void showMaxUserLimitReachedDialog() {
+ AlertDialog maxUsersDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
+ .setTitle(R.string.user_limit_reached_title)
+ .setMessage(getResources().getQuantityString(
+ R.plurals.user_limit_reached_message,
+ mCarUserManagerHelper.getMaxSupportedRealUsers(),
+ mCarUserManagerHelper.getMaxSupportedRealUsers()))
+ .setPositiveButton(android.R.string.ok, null)
+ .create();
+ // Sets window flags for the SysUI dialog
+ SystemUIDialog.applyFlags(maxUsersDialog);
+ maxUsersDialog.show();
+ }
+
+ private void showConfirmAddUserDialog() {
+ String message = mRes.getString(R.string.user_add_user_message_setup)
+ .concat(System.getProperty("line.separator"))
+ .concat(System.getProperty("line.separator"))
+ .concat(mRes.getString(R.string.user_add_user_message_update));
+
+ AlertDialog addUserDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
+ .setTitle(R.string.user_add_user_title)
+ .setMessage(message)
+ .setNegativeButton(android.R.string.cancel, this)
+ .setPositiveButton(android.R.string.ok, this)
+ .setOnCancelListener(this)
+ .create();
+ // Sets window flags for the SysUI dialog
+ SystemUIDialog.applyFlags(addUserDialog);
+ addUserDialog.show();
+ }
+
+ private void notifyUserSelected(UserRecord userRecord) {
+ // Notify the listener which user was selected
+ if (mUserSelectionListener != null) {
+ mUserSelectionListener.onUserSelected(userRecord);
+ }
+ }
+
+ private Bitmap getUserRecordIcon(UserRecord userRecord) {
+ if (userRecord.mIsStartGuestSession) {
+ return mCarUserManagerHelper.getGuestDefaultIcon();
+ }
+
+ if (userRecord.mIsAddUser) {
+ return UserIcons.convertToBitmap(mContext
+ .getDrawable(R.drawable.car_add_circle_round));
+ }
+
+ return mCarUserManagerHelper.getUserIcon(userRecord.mInfo);
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == BUTTON_POSITIVE) {
+ notifyUserSelected(mAddUserRecord);
+ new AddNewUserTask().execute(mNewUserName);
+ } else if (which == BUTTON_NEGATIVE) {
+ // Enable the add button only if cancel
+ if (mAddUserView != null) {
+ mAddUserView.setEnabled(true);
+ }
+ }
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ // Enable the add button again if user cancels dialog by clicking outside the dialog
+ if (mAddUserView != null) {
+ mAddUserView.setEnabled(true);
+ }
+ }
+
+ private class AddNewUserTask extends AsyncTask<String, Void, UserInfo> {
+
+ @Override
+ protected UserInfo doInBackground(String... userNames) {
+ return mCarUserManagerHelper.createNewNonAdminUser(userNames[0]);
+ }
+
+ @Override
+ protected void onPreExecute() {
+ }
+
+ @Override
+ protected void onPostExecute(UserInfo user) {
+ if (user != null) {
+ mCarUserManagerHelper.switchToUser(user);
+ }
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return mUsers.size();
+ }
+
+ public class UserAdapterViewHolder extends RecyclerView.ViewHolder {
+
+ public ImageView mUserAvatarImageView;
+ public TextView mUserNameTextView;
+ public View mView;
+
+ public UserAdapterViewHolder(View view) {
+ super(view);
+ mView = view;
+ mUserAvatarImageView = (ImageView) view.findViewById(R.id.user_avatar);
+ mUserNameTextView = (TextView) view.findViewById(R.id.user_name);
+ }
+ }
+ }
+
+ /**
+ * Object wrapper class for the userInfo. Use it to distinguish if a profile is a
+ * guest profile, add user profile, or the foreground user.
+ */
+ public static final class UserRecord {
+
+ public final UserInfo mInfo;
+ public final boolean mIsStartGuestSession;
+ public final boolean mIsAddUser;
+ public final boolean mIsForeground;
+
+ public UserRecord(UserInfo userInfo, boolean isStartGuestSession, boolean isAddUser,
+ boolean isForeground) {
+ mInfo = userInfo;
+ mIsStartGuestSession = isStartGuestSession;
+ mIsAddUser = isAddUser;
+ mIsForeground = isForeground;
+ }
+ }
+
+ /**
+ * Listener used to notify when a user has been selected
+ */
+ interface UserSelectionListener {
+
+ void onUserSelected(UserRecord record);
+ }
+}