| /* |
| * Copyright (C) 2015 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.content.Context; |
| import android.graphics.Bitmap; |
| import android.graphics.Canvas; |
| import android.graphics.Color; |
| import android.graphics.Paint; |
| import android.graphics.Paint.Align; |
| import android.graphics.drawable.Drawable; |
| import android.graphics.drawable.GradientDrawable; |
| import android.os.UserHandle; |
| import android.support.v4.view.PagerAdapter; |
| import android.support.v4.view.ViewPager; |
| import android.util.AttributeSet; |
| import android.view.Gravity; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.ImageView; |
| import android.widget.LinearLayout; |
| import android.widget.TextView; |
| |
| import com.android.internal.util.UserIcons; |
| import com.android.systemui.R; |
| import com.android.systemui.statusbar.UserUtil; |
| import com.android.systemui.statusbar.phone.StatusBar; |
| import com.android.systemui.statusbar.policy.UserSwitcherController; |
| |
| /** |
| * Displays a ViewPager 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 UserGridView extends ViewPager { |
| private StatusBar mStatusBar; |
| private UserSwitcherController mUserSwitcherController; |
| private Adapter mAdapter; |
| private UserSelectionListener mUserSelectionListener; |
| |
| public UserGridView(Context context, AttributeSet attrs) { |
| super(context, attrs); |
| } |
| |
| public void init(StatusBar statusBar, UserSwitcherController userSwitcherController) { |
| mStatusBar = statusBar; |
| mUserSwitcherController = userSwitcherController; |
| mAdapter = new Adapter(mUserSwitcherController); |
| addOnLayoutChangeListener(mAdapter); |
| setAdapter(mAdapter); |
| } |
| |
| public void onUserSwitched(int newUserId) { |
| // Bring up security view after user switch is completed. |
| post(this::showOfflineAuthUi); |
| } |
| |
| public void setUserSelectionListener(UserSelectionListener userSelectionListener) { |
| mUserSelectionListener = userSelectionListener; |
| } |
| |
| void showOfflineAuthUi() { |
| // TODO: Show keyguard UI in-place. |
| mStatusBar.executeRunnableDismissingKeyguard(null, null, true, true, true); |
| } |
| |
| @Override |
| protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
| // Wrap content doesn't work in ViewPagers, so simulate the behavior in code. |
| int height = 0; |
| for(int i = 0; i < getChildCount(); i++) { |
| View child = getChildAt(i); |
| child.measure(widthMeasureSpec, |
| MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); |
| height = Math.max(child.getMeasuredHeight(), height); |
| } |
| heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); |
| super.onMeasure(widthMeasureSpec, heightMeasureSpec); |
| } |
| |
| /** |
| * This is a ViewPager.PagerAdapter which deletegates the work to a |
| * UserSwitcherController.BaseUserAdapter. Java doesn't support multiple inheritance so we have |
| * to use composition instead to achieve the same goal since both the base classes are abstract |
| * classes and not interfaces. |
| */ |
| private final class Adapter extends PagerAdapter implements View.OnLayoutChangeListener { |
| private final int mPodWidth; |
| private final int mPodMargin; |
| |
| private final WrappedBaseUserAdapter mUserAdapter; |
| private int mContainerWidth; |
| |
| public Adapter(UserSwitcherController controller) { |
| super(); |
| mUserAdapter = new WrappedBaseUserAdapter(controller, this); |
| mPodWidth = getResources().getDimensionPixelSize( |
| R.dimen.car_fullscreen_user_pod_image_avatar_width); |
| mPodMargin = getResources().getDimensionPixelSize( |
| R.dimen.car_fullscreen_user_pod_margin_side); |
| } |
| |
| @Override |
| public void destroyItem(ViewGroup container, int position, Object object) { |
| container.removeView((View) object); |
| } |
| |
| private int getIconsPerPage() { |
| // We need to know how many pods we need in this page. Each pod has its own width and |
| // margins on both sides. We can then divide the measured width of the parent by the |
| // sum of pod width and margin to get the number of pods that will completely fit. |
| return mContainerWidth / (mPodWidth + mPodMargin * 2); |
| } |
| |
| @Override |
| public Object instantiateItem(ViewGroup container, int position) { |
| Context context = getContext(); |
| LayoutInflater inflater = LayoutInflater.from(context); |
| |
| ViewGroup pods = (ViewGroup) inflater.inflate( |
| R.layout.car_fullscreen_user_pod_container, null); |
| |
| int iconsPerPage = getIconsPerPage(); |
| int limit = Math.min(mUserAdapter.getCount(), (position + 1) * iconsPerPage); |
| for (int i = position * iconsPerPage; i < limit; i++) { |
| pods.addView(makeUserPod(inflater, context, i, pods)); |
| } |
| container.addView(pods); |
| return pods; |
| } |
| |
| /** |
| * 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 userName the username of the user for which the icon is to be created |
| */ |
| private Bitmap getDefaultUserIcon(CharSequence userName) { |
| CharSequence displayText = userName.subSequence(0, 1); |
| Bitmap out = Bitmap.createBitmap(mPodWidth, mPodWidth, 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(getContext().getColor(R.color.car_user_switcher_no_user_image_bgcolor)); |
| shape.setBounds(0, 0, mPodWidth, mPodWidth); |
| shape.draw(canvas); |
| |
| // Draw the letter in the center. |
| Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); |
| paint.setColor(getContext().getColor(R.color.car_user_switcher_no_user_image_fgcolor)); |
| paint.setTextAlign(Align.CENTER); |
| paint.setTextSize(getResources().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(), |
| mPodWidth / 2, (mPodWidth - (metrics.bottom + metrics.top)) / 2, paint); |
| |
| return out; |
| } |
| |
| private View makeUserPod(LayoutInflater inflater, Context context, |
| int position, ViewGroup parent) { |
| final UserSwitcherController.UserRecord record = mUserAdapter.getItem(position); |
| View view = inflater.inflate(R.layout.car_fullscreen_user_pod, parent, false); |
| |
| TextView nameView = view.findViewById(R.id.user_name); |
| if (record != null) { |
| nameView.setText(mUserAdapter.getName(context, record)); |
| view.setActivated(record.isCurrent); |
| } else { |
| nameView.setText(context.getString(R.string.unknown_user_label)); |
| } |
| |
| ImageView iconView = (ImageView) view.findViewById(R.id.user_avatar); |
| if (record == null || (record.picture == null && !record.isAddUser)) { |
| iconView.setImageBitmap(getDefaultUserIcon(nameView.getText())); |
| } else if (record.isAddUser) { |
| Drawable icon = context.getDrawable(R.drawable.ic_add_circle_qs); |
| icon.setTint(context.getColor(R.color.car_user_switcher_no_user_image_bgcolor)); |
| iconView.setImageDrawable(icon); |
| } else { |
| iconView.setImageBitmap(record.picture); |
| } |
| |
| iconView.setOnClickListener(v -> { |
| if (record == null) { |
| return; |
| } |
| |
| if (mUserSelectionListener != null) { |
| mUserSelectionListener.onUserSelected(record); |
| } |
| |
| if (record.isCurrent) { |
| showOfflineAuthUi(); |
| } else { |
| mUserSwitcherController.switchTo(record); |
| } |
| }); |
| |
| return view; |
| } |
| |
| @Override |
| public int getCount() { |
| int iconsPerPage = getIconsPerPage(); |
| if (iconsPerPage == 0) { |
| return 0; |
| } |
| return (int) Math.ceil((double) mUserAdapter.getCount() / getIconsPerPage()); |
| } |
| |
| public void refresh() { |
| mUserAdapter.refresh(); |
| } |
| |
| @Override |
| public boolean isViewFromObject(View view, Object object) { |
| return view == object; |
| } |
| |
| @Override |
| public void onLayoutChange(View v, int left, int top, int right, int bottom, |
| int oldLeft, int oldTop, int oldRight, int oldBottom) { |
| mContainerWidth = Math.max(left - right, right - left); |
| notifyDataSetChanged(); |
| } |
| } |
| |
| private final class WrappedBaseUserAdapter extends UserSwitcherController.BaseUserAdapter { |
| private Adapter mContainer; |
| |
| public WrappedBaseUserAdapter(UserSwitcherController controller, Adapter container) { |
| super(controller); |
| mContainer = container; |
| } |
| |
| @Override |
| public View getView(int position, View convertView, ViewGroup parent) { |
| throw new UnsupportedOperationException("unused"); |
| } |
| |
| @Override |
| public void notifyDataSetChanged() { |
| super.notifyDataSetChanged(); |
| mContainer.notifyDataSetChanged(); |
| } |
| } |
| |
| interface UserSelectionListener { |
| void onUserSelected(UserSwitcherController.UserRecord record); |
| }; |
| } |