| /* |
| * 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 android.app.ActivityManager; |
| import android.content.Context; |
| import android.content.pm.UserInfo; |
| import android.content.res.Resources; |
| import android.graphics.Bitmap; |
| import android.graphics.Canvas; |
| import android.graphics.Paint; |
| import android.graphics.Paint.Align; |
| import android.graphics.drawable.GradientDrawable; |
| import android.os.AsyncTask; |
| import android.support.annotation.Nullable; |
| import android.support.v7.widget.RecyclerView; |
| import android.support.v7.widget.RecyclerView.ViewHolder; |
| 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 com.android.settingslib.users.UserManagerHelper; |
| import com.android.systemui.R; |
| |
| 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 RecyclerView implements |
| UserManagerHelper.OnUsersUpdateListener { |
| private UserSelectionListener mUserSelectionListener; |
| private UserAdapter mAdapter; |
| private UserManagerHelper mUserManagerHelper; |
| private Context mContext; |
| |
| public UserGridRecyclerView(Context context, AttributeSet attrs) { |
| super(context, attrs); |
| super.setHasFixedSize(true); |
| mContext = context; |
| mUserManagerHelper = new UserManagerHelper(mContext); |
| } |
| |
| /** |
| * Register listener for any update to the users |
| */ |
| @Override |
| public void onFinishInflate() { |
| mUserManagerHelper.registerOnUsersUpdateListener(this); |
| } |
| |
| /** |
| * Unregisters listener checking for any change to the users |
| */ |
| @Override |
| public void onDetachedFromWindow() { |
| mUserManagerHelper.unregisterOnUsersUpdateListener(); |
| } |
| |
| /** |
| * Initializes the adapter that populates the grid layout |
| * |
| * @return the adapter |
| */ |
| public void buildAdapter() { |
| List<UserRecord> userRecords = createUserRecords(mUserManagerHelper |
| .getAllUsers()); |
| mAdapter = new UserAdapter(mContext, userRecords); |
| super.setAdapter(mAdapter); |
| } |
| |
| private List<UserRecord> createUserRecords(List<UserInfo> userInfoList) { |
| List<UserRecord> userRecords = new ArrayList<>(); |
| for (UserInfo userInfo : userInfoList) { |
| boolean isForeground = mUserManagerHelper.getForegroundUserId() == userInfo.id; |
| UserRecord record = new UserRecord(userInfo, false /* isGuest */, |
| false /* isAddUser */, isForeground); |
| userRecords.add(record); |
| } |
| |
| // Add guest user record if the foreground user is not a guest |
| if (!mUserManagerHelper.foregroundUserIsGuestUser()) { |
| userRecords.add(addGuestUserRecord()); |
| } |
| |
| // Add add user record if the foreground user can add users |
| if (mUserManagerHelper.foregroundUserCanAddUsers()) { |
| userRecords.add(addUserRecord()); |
| } |
| |
| return userRecords; |
| } |
| |
| /** |
| * Create guest user record |
| */ |
| private UserRecord addGuestUserRecord() { |
| UserInfo userInfo = new UserInfo(); |
| userInfo.name = mContext.getString(R.string.car_guest); |
| return new UserRecord(userInfo, true /* isGuest */, |
| false /* isAddUser */, false /* isForeground */); |
| } |
| |
| /** |
| * Create add user record |
| */ |
| private UserRecord addUserRecord() { |
| UserInfo userInfo = new UserInfo(); |
| userInfo.name = mContext.getString(R.string.car_add_user); |
| return new UserRecord(userInfo, false /* isGuest */, |
| true /* isAddUser */, false /* isForeground */); |
| } |
| |
| public void setUserSelectionListener(UserSelectionListener userSelectionListener) { |
| mUserSelectionListener = userSelectionListener; |
| } |
| |
| @Override |
| public void onUsersUpdate() { |
| mAdapter.clearUsers(); |
| mAdapter.updateUsers(createUserRecords(mUserManagerHelper.getAllUsers())); |
| mAdapter.notifyDataSetChanged(); |
| } |
| |
| /** |
| * Adapter to populate the grid layout with the available user profiles |
| */ |
| public final class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserAdapterViewHolder> { |
| |
| private final Context mContext; |
| private List<UserRecord> mUsers; |
| private final int mPodImageAvatarWidth; |
| private final int mPodImageAvatarHeight; |
| private final Resources mRes; |
| private final String mGuestName; |
| private final String mNewUserName; |
| |
| public UserAdapter(Context context, List<UserRecord> users) { |
| mRes = context.getResources(); |
| mContext = context; |
| updateUsers(users); |
| mPodImageAvatarWidth = mRes.getDimensionPixelSize( |
| R.dimen.car_fullscreen_user_pod_image_avatar_width); |
| mPodImageAvatarHeight = mRes.getDimensionPixelSize( |
| R.dimen.car_fullscreen_user_pod_image_avatar_height); |
| 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); |
| holder.mUserAvatarImageView.setImageBitmap(getDefaultUserIcon(userRecord)); |
| holder.mUserNameTextView.setText(userRecord.mInfo.name); |
| holder.mView.setOnClickListener(v -> { |
| if (userRecord == null) { |
| return; |
| } |
| |
| // Notify the listener which user was selected |
| if (mUserSelectionListener != null) { |
| mUserSelectionListener.onUserSelected(userRecord); |
| } |
| |
| // If the user selects Guest, switch to Guest profile |
| if (userRecord.mIsGuest) { |
| mUserManagerHelper.switchToGuest(mGuestName); |
| return; |
| } |
| |
| // If the user wants to add a user, start task to add new user |
| if (userRecord.mIsAddUser) { |
| new AddNewUserTask().execute(mNewUserName); |
| return; |
| } |
| |
| // If the user doesn't want to be a guest or add a user, switch to the user selected |
| mUserManagerHelper.switchToUser(userRecord.mInfo); |
| }); |
| |
| } |
| |
| private class AddNewUserTask extends AsyncTask<String, Void, UserInfo> { |
| |
| @Override |
| protected UserInfo doInBackground(String... userNames) { |
| return mUserManagerHelper.createNewUser(userNames[0]); |
| } |
| |
| @Override |
| protected void onPreExecute() { |
| } |
| |
| @Override |
| protected void onPostExecute(UserInfo user) { |
| if (user != null) { |
| mUserManagerHelper.switchToUser(user); |
| } |
| } |
| } |
| |
| @Override |
| public int getItemCount() { |
| return mUsers.size(); |
| } |
| |
| /** |
| * Returns the default user icon. This icon is a circle with a letter in it. The letter is |
| * the first character in the username. |
| * |
| * @param record the profile of the user for which the icon should be created |
| */ |
| private Bitmap getDefaultUserIcon(UserRecord record) { |
| CharSequence displayText; |
| boolean isAddUserText = false; |
| if (record.mIsAddUser) { |
| displayText = "+"; |
| isAddUserText = true; |
| } else { |
| displayText = record.mInfo.name.subSequence(0, 1); |
| } |
| Bitmap out = Bitmap.createBitmap(mPodImageAvatarWidth, mPodImageAvatarHeight, |
| Bitmap.Config.ARGB_8888); |
| Canvas canvas = new Canvas(out); |
| |
| // Draw the circle background. |
| GradientDrawable shape = new GradientDrawable(); |
| shape.setShape(GradientDrawable.RADIAL_GRADIENT); |
| shape.setGradientRadius(1.0f); |
| shape.setColor(mContext.getColor(R.color.car_user_switcher_no_user_image_bgcolor)); |
| shape.setBounds(0, 0, mPodImageAvatarWidth, mPodImageAvatarHeight); |
| shape.draw(canvas); |
| |
| // Draw the letter in the center. |
| Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); |
| paint.setColor(mContext.getColor(R.color.car_user_switcher_no_user_image_fgcolor)); |
| paint.setTextAlign(Align.CENTER); |
| if (isAddUserText) { |
| paint.setTextSize(mRes.getDimensionPixelSize( |
| R.dimen.car_touch_target_size)); |
| } else { |
| paint.setTextSize(mRes.getDimensionPixelSize( |
| R.dimen.car_fullscreen_user_pod_icon_text_size)); |
| } |
| |
| Paint.FontMetricsInt metrics = paint.getFontMetricsInt(); |
| // The Y coordinate is measured by taking half the height of the pod, but that would |
| // draw the character putting the bottom of the font in the middle of the pod. To |
| // correct this, half the difference between the top and bottom distance metrics of the |
| // font gives the offset of the font. Bottom is a positive value, top is negative, so |
| // the different is actually a sum. The "half" operation is then factored out. |
| canvas.drawText(displayText.toString(), mPodImageAvatarWidth / 2, |
| (mPodImageAvatarHeight - (metrics.bottom + metrics.top)) / 2, paint); |
| |
| return out; |
| } |
| |
| 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 mIsGuest; |
| public final boolean mIsAddUser; |
| public final boolean mIsForeground; |
| |
| public UserRecord(UserInfo userInfo, boolean isGuest, boolean isAddUser, |
| boolean isForeground) { |
| mInfo = userInfo; |
| mIsGuest = isGuest; |
| mIsAddUser = isAddUser; |
| mIsForeground = isForeground; |
| } |
| } |
| |
| /** |
| * Listener used to notify when a user has been selected |
| */ |
| interface UserSelectionListener { |
| |
| void onUserSelected(UserRecord record); |
| } |
| } |