Merge "UI modifications for user picker Test: tested on Mojave bug: 75023449" into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
fd76ec539e
@@ -15,40 +15,32 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:clipChildren="false"
|
||||
android:alpha="0"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/car_fullscreen_user_pod_height"
|
||||
android:layout_gravity="center_horizontal|bottom" >
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:gravity="fill_horizontal"
|
||||
android:layout_marginTop="@dimen/car_padding_5"
|
||||
android:layout_marginStart="@dimen/car_padding_4">
|
||||
|
||||
<ImageView android:id="@+id/user_avatar"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="@dimen/car_fullscreen_user_pod_margin_image_top"
|
||||
android:layout_width="@dimen/car_fullscreen_user_pod_image_avatar_width"
|
||||
android:layout_height="@dimen/car_fullscreen_user_pod_image_avatar_height"
|
||||
android:layout_above="@id/user_name" />
|
||||
/>
|
||||
|
||||
<TextView android:id="@+id/user_name"
|
||||
android:layout_width="@dimen/car_fullscreen_user_pod_width"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/car_fullscreen_user_pod_margin_name_top"
|
||||
android:layout_marginBottom="@dimen/car_fullscreen_user_pod_margin_name_bottom"
|
||||
android:textSize="@dimen/car_fullscreen_user_pod_name_text_size"
|
||||
android:layout_marginTop="@dimen/car_padding_4"
|
||||
android:textSize="@dimen/car_body1_size"
|
||||
android:textColor="@color/qs_user_detail_name"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_above="@id/device_name" />
|
||||
android:gravity="center"
|
||||
android:layout_below="@id/user_avatar"/>
|
||||
|
||||
<TextView android:id="@+id/device_name"
|
||||
android:layout_width="@dimen/car_fullscreen_user_pod_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/car_fullscreen_user_pod_device_text_size"
|
||||
android:textColor="@color/qs_user_detail_name"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_alignParentBottom="true" />
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (C) 2017 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.
|
||||
-->
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:clipChildren="false"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center" >
|
||||
|
||||
<!-- car_fullscreen_user_pods will be dynamically added here. -->
|
||||
</LinearLayout>
|
||||
@@ -39,27 +39,34 @@
|
||||
android:theme="@android:style/Theme"
|
||||
android:layout_alignParentTop="true"/>
|
||||
|
||||
<com.android.systemui.statusbar.car.UserGridView
|
||||
android:id="@+id/user_grid"
|
||||
<RelativeLayout
|
||||
android:id="@+id/fullscreen_user_switcher_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/car_margin"
|
||||
android:layout_marginRight="@dimen/car_margin"
|
||||
android:layout_centerInParent="true"/>
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/car_margin"
|
||||
android:layout_marginEnd="@dimen/car_margin">
|
||||
|
||||
<com.android.systemui.statusbar.car.PageIndicator
|
||||
android:id="@+id/user_switcher_page_indicator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/car_page_indicator_dot_diameter"
|
||||
android:layout_below="@+id/user_grid" />
|
||||
<RelativeLayout
|
||||
android:id="@+id/fullscreen_user_switcher_container_inner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginEnd="@dimen/car_padding_4">
|
||||
|
||||
<com.android.systemui.statusbar.car.UserGridRecyclerView
|
||||
android:id="@+id/user_grid"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/header"
|
||||
android:scrollbars="vertical"
|
||||
android:scrollbarFadeDuration="0"
|
||||
android:verticalScrollbarPosition="left"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentRight="true"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/start_driving"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/car_start_driving_height"
|
||||
android:text="@string/start_driving"
|
||||
style="@style/CarUserSwitcher.StartDrivingButton"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true" />
|
||||
</RelativeLayout>
|
||||
</FrameLayout>
|
||||
|
||||
@@ -30,27 +30,29 @@
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/user_switcher_container"
|
||||
android:layout_marginStart="@dimen/car_margin"
|
||||
android:layout_marginEnd="@dimen/car_margin"
|
||||
android:clipChildren="false"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/car_user_switcher_container_height"
|
||||
android:layout_gravity="center_horizontal" >
|
||||
android:layout_height="@dimen/car_user_switcher_container_height">
|
||||
|
||||
<com.android.systemui.statusbar.car.UserGridView
|
||||
android:id="@+id/user_grid"
|
||||
android:clipChildren="false"
|
||||
<RelativeLayout
|
||||
android:id="@+id/fullscreen_user_switcher_container_inner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/car_margin"
|
||||
android:layout_marginRight="@dimen/car_margin"
|
||||
android:layout_above="@id/user_switcher_page_indicator" />
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginEnd="@dimen/car_padding_4">
|
||||
|
||||
<com.android.systemui.statusbar.car.PageIndicator
|
||||
android:id="@+id/user_switcher_page_indicator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/car_page_indicator_dot_diameter"
|
||||
android:layout_marginBottom="@dimen/car_page_indicator_margin_bottom"
|
||||
android:alpha="0"
|
||||
android:layout_alignParentBottom="true" />
|
||||
<com.android.systemui.statusbar.car.UserGridRecyclerView
|
||||
android:id="@+id/user_grid"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollbars="vertical"
|
||||
android:verticalScrollbarPosition="left"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:scrollbarFadeDuration="0"/>
|
||||
</RelativeLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
@@ -17,18 +17,14 @@
|
||||
-->
|
||||
<resources>
|
||||
<dimen name="car_margin">148dp</dimen>
|
||||
<dimen name="car_margin_standard">112dp</dimen>
|
||||
|
||||
<dimen name="car_fullscreen_user_pod_margin_image_top">24dp</dimen>
|
||||
<dimen name="car_fullscreen_user_pod_margin_name_top">24dp</dimen>
|
||||
<dimen name="car_fullscreen_user_pod_margin_name_bottom">20dp</dimen>
|
||||
<dimen name="car_fullscreen_user_pod_margin_between">24dp</dimen>
|
||||
<dimen name="car_fullscreen_user_pod_icon_text_size">96dp</dimen>
|
||||
<dimen name="car_fullscreen_user_pod_image_avatar_width">192dp</dimen>
|
||||
<dimen name="car_fullscreen_user_pod_image_avatar_height">192dp</dimen>
|
||||
<dimen name="car_fullscreen_user_pod_width">264dp</dimen>
|
||||
<!-- TODO replace with car support lib sizes when available -->
|
||||
<dimen name="car_fullscreen_user_pod_icon_text_size">32sp</dimen>
|
||||
<dimen name="car_fullscreen_user_pod_width">243dp</dimen>
|
||||
<dimen name="car_fullscreen_user_pod_height">356dp</dimen>
|
||||
<dimen name="car_fullscreen_user_pod_name_text_size">40sp</dimen> <!-- B1 -->
|
||||
<dimen name="car_fullscreen_user_pod_device_text_size">@dimen/car_body2_size</dimen>
|
||||
<dimen name="car_fullscreen_user_pod_image_avatar_width">96dp</dimen>
|
||||
<dimen name="car_fullscreen_user_pod_image_avatar_height">96dp</dimen>
|
||||
|
||||
<dimen name="car_navigation_button_width">64dp</dimen>
|
||||
<dimen name="car_navigation_bar_width">760dp</dimen>
|
||||
|
||||
@@ -17,4 +17,6 @@
|
||||
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<integer name="car_user_switcher_anim_cascade_delay_ms">27</integer>
|
||||
<!-- Full screen user switcher column number TODO: move to support library-->
|
||||
<integer name="user_fullscreen_switcher_num_col">3</integer>
|
||||
</resources>
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
*/
|
||||
-->
|
||||
<resources>
|
||||
<string name="unknown_user_label">Unknown</string>
|
||||
<string name="start_driving">Start Driving</string>
|
||||
<!-- Name of Guest Profile. [CHAR LIMIT=30] -->
|
||||
<string name="car_guest">Guest</string>
|
||||
<!-- Name of Add User Profile. [CHAR LIMIT=30] -->
|
||||
<string name="car_add_user">Add User</string>
|
||||
<!-- Default name of the new user created. [CHAR LIMIT=30] -->
|
||||
<string name="car_new_user">New User</string>
|
||||
</resources>
|
||||
|
||||
@@ -29,7 +29,6 @@ import com.android.systemui.R;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
import com.android.systemui.qs.QSFooter;
|
||||
import com.android.systemui.qs.QSPanel;
|
||||
import com.android.systemui.statusbar.car.UserGridView;
|
||||
import com.android.systemui.statusbar.phone.MultiUserSwitch;
|
||||
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
|
||||
import com.android.systemui.statusbar.policy.UserInfoController;
|
||||
|
||||
@@ -20,21 +20,20 @@ import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.plugins.qs.QS;
|
||||
import com.android.systemui.qs.QSFooter;
|
||||
import com.android.systemui.statusbar.car.PageIndicator;
|
||||
import com.android.systemui.statusbar.car.UserGridView;
|
||||
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
||||
import com.android.systemui.statusbar.car.UserGridRecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -45,14 +44,12 @@ import java.util.List;
|
||||
* status bar, and a static row with access to the user switcher and settings.
|
||||
*/
|
||||
public class CarQSFragment extends Fragment implements QS {
|
||||
private ViewGroup mPanel;
|
||||
private View mHeader;
|
||||
private View mUserSwitcherContainer;
|
||||
private CarQSFooter mFooter;
|
||||
private View mFooterUserName;
|
||||
private View mFooterExpandIcon;
|
||||
private UserGridView mUserGridView;
|
||||
private PageIndicator mPageIndicator;
|
||||
private UserGridRecyclerView mUserGridView;
|
||||
private AnimatorSet mAnimatorSet;
|
||||
private UserSwitchCallback mUserSwitchCallback;
|
||||
|
||||
@@ -65,7 +62,6 @@ public class CarQSFragment extends Fragment implements QS {
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mPanel = (ViewGroup) view;
|
||||
mHeader = view.findViewById(R.id.header);
|
||||
mFooter = view.findViewById(R.id.qs_footer);
|
||||
mFooterUserName = mFooter.findViewById(R.id.user_name);
|
||||
@@ -75,16 +71,15 @@ public class CarQSFragment extends Fragment implements QS {
|
||||
|
||||
updateUserSwitcherHeight(0);
|
||||
|
||||
mUserGridView = view.findViewById(R.id.user_grid);
|
||||
mUserGridView.init(null, Dependency.get(UserSwitcherController.class),
|
||||
false /* overrideAlpha */);
|
||||
|
||||
mPageIndicator = view.findViewById(R.id.user_switcher_page_indicator);
|
||||
mPageIndicator.setupWithViewPager(mUserGridView);
|
||||
Context context = getContext();
|
||||
mUserGridView = mUserSwitcherContainer.findViewById(R.id.user_grid);
|
||||
GridLayoutManager layoutManager = new GridLayoutManager(context,
|
||||
context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
|
||||
mUserGridView.setLayoutManager(layoutManager);
|
||||
mUserGridView.buildAdapter();
|
||||
|
||||
mUserSwitchCallback = new UserSwitchCallback();
|
||||
mFooter.setUserSwitchCallback(mUserSwitchCallback);
|
||||
mUserGridView.setUserSwitchCallback(mUserSwitchCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -111,13 +106,11 @@ public class CarQSFragment extends Fragment implements QS {
|
||||
@Override
|
||||
public void setHeaderListening(boolean listening) {
|
||||
mFooter.setListening(listening);
|
||||
mUserGridView.setListening(listening);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListening(boolean listening) {
|
||||
mFooter.setListening(listening);
|
||||
mUserGridView.setListening(listening);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -219,24 +212,6 @@ public class CarQSFragment extends Fragment implements QS {
|
||||
mShowing = false;
|
||||
animateHeightChange(false /* opening */);
|
||||
}
|
||||
|
||||
public void resetShowing() {
|
||||
if (mShowing) {
|
||||
for (int i = 0; i < mUserGridView.getChildCount(); i++) {
|
||||
ViewGroup podContainer = (ViewGroup) mUserGridView.getChildAt(i);
|
||||
// Need to bring the last child to the front to maintain the order in the pod
|
||||
// container. Why? ¯\_(ツ)_/¯
|
||||
if (podContainer.getChildCount() > 0) {
|
||||
podContainer.getChildAt(podContainer.getChildCount() - 1).bringToFront();
|
||||
}
|
||||
// The alpha values are default to 0, so if the pods have been refreshed, they
|
||||
// need to be set to 1 when showing.
|
||||
for (int j = 0; j < podContainer.getChildCount(); j++) {
|
||||
podContainer.getChildAt(j).setAlpha(1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUserSwitcherHeight(int height) {
|
||||
@@ -260,27 +235,6 @@ public class CarQSFragment extends Fragment implements QS {
|
||||
});
|
||||
allAnimators.add(heightAnimator);
|
||||
|
||||
// The user grid contains pod containers that each contain a number of pods. Animate
|
||||
// all pods to avoid any discrepancy/race conditions with possible changes during the
|
||||
// animation.
|
||||
int cascadeDelay = getResources().getInteger(
|
||||
R.integer.car_user_switcher_anim_cascade_delay_ms);
|
||||
for (int i = 0; i < mUserGridView.getChildCount(); i++) {
|
||||
ViewGroup podContainer = (ViewGroup) mUserGridView.getChildAt(i);
|
||||
for (int j = 0; j < podContainer.getChildCount(); j++) {
|
||||
View pod = podContainer.getChildAt(j);
|
||||
Animator podAnimator = AnimatorInflater.loadAnimator(getContext(),
|
||||
opening ? R.anim.car_user_switcher_open_pod_animation
|
||||
: R.anim.car_user_switcher_close_pod_animation);
|
||||
// Add the cascading delay between pods
|
||||
if (opening) {
|
||||
podAnimator.setStartDelay(podAnimator.getStartDelay() + j * cascadeDelay);
|
||||
}
|
||||
podAnimator.setTarget(pod);
|
||||
allAnimators.add(podAnimator);
|
||||
}
|
||||
}
|
||||
|
||||
Animator nameAnimator = AnimatorInflater.loadAnimator(getContext(),
|
||||
opening ? R.anim.car_user_switcher_open_name_animation
|
||||
: R.anim.car_user_switcher_close_name_animation);
|
||||
@@ -293,12 +247,6 @@ public class CarQSFragment extends Fragment implements QS {
|
||||
iconAnimator.setTarget(mFooterExpandIcon);
|
||||
allAnimators.add(iconAnimator);
|
||||
|
||||
Animator pageAnimator = AnimatorInflater.loadAnimator(getContext(),
|
||||
opening ? R.anim.car_user_switcher_open_pages_animation
|
||||
: R.anim.car_user_switcher_close_pages_animation);
|
||||
pageAnimator.setTarget(mPageIndicator);
|
||||
allAnimators.add(pageAnimator);
|
||||
|
||||
mAnimatorSet = new AnimatorSet();
|
||||
mAnimatorSet.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
|
||||
@@ -418,8 +418,7 @@ public class CarStatusBar extends StatusBar implements
|
||||
Dependency.get(UserSwitcherController.class);
|
||||
if (userSwitcherController.useFullscreenUserSwitcher()) {
|
||||
mFullscreenUserSwitcher = new FullscreenUserSwitcher(this,
|
||||
userSwitcherController,
|
||||
mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub));
|
||||
mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), mContext);
|
||||
} else {
|
||||
super.createUserSwitcher();
|
||||
}
|
||||
|
||||
@@ -18,14 +18,15 @@ package com.android.systemui.statusbar.car;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.content.res.Resources;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewStub;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.phone.StatusBar;
|
||||
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
||||
|
||||
/**
|
||||
* Manages the fullscreen user switcher.
|
||||
@@ -33,36 +34,25 @@ import com.android.systemui.statusbar.policy.UserSwitcherController;
|
||||
public class FullscreenUserSwitcher {
|
||||
private final View mContainer;
|
||||
private final View mParent;
|
||||
private final UserGridView mUserGridView;
|
||||
private final UserSwitcherController mUserSwitcherController;
|
||||
private final UserGridRecyclerView mUserGridView;
|
||||
private final ProgressBar mSwitchingUsers;
|
||||
private final int mShortAnimDuration;
|
||||
|
||||
private boolean mShowing;
|
||||
|
||||
public FullscreenUserSwitcher(StatusBar statusBar,
|
||||
UserSwitcherController userSwitcherController,
|
||||
ViewStub containerStub) {
|
||||
mUserSwitcherController = userSwitcherController;
|
||||
public FullscreenUserSwitcher(StatusBar statusBar, ViewStub containerStub, Context context) {
|
||||
mParent = containerStub.inflate();
|
||||
mContainer = mParent.findViewById(R.id.container);
|
||||
mUserGridView = mContainer.findViewById(R.id.user_grid);
|
||||
mUserGridView.init(statusBar, mUserSwitcherController, true /* overrideAlpha */);
|
||||
mUserGridView.setUserSelectionListener(record -> {
|
||||
if (!record.isCurrent) {
|
||||
toggleSwitchInProgress(true);
|
||||
}
|
||||
});
|
||||
mUserGridView.setStatusBar(statusBar);
|
||||
GridLayoutManager layoutManager = new GridLayoutManager(context,
|
||||
context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
|
||||
mUserGridView.setLayoutManager(layoutManager);
|
||||
mUserGridView.buildAdapter();
|
||||
mUserGridView.setUserSelectionListener(record -> toggleSwitchInProgress(true));
|
||||
|
||||
PageIndicator pageIndicator = mContainer.findViewById(R.id.user_switcher_page_indicator);
|
||||
pageIndicator.setupWithViewPager(mUserGridView);
|
||||
|
||||
Resources res = mContainer.getResources();
|
||||
mShortAnimDuration = res.getInteger(android.R.integer.config_shortAnimTime);
|
||||
|
||||
mContainer.findViewById(R.id.start_driving).setOnClickListener(v -> {
|
||||
automaticallySelectUser();
|
||||
});
|
||||
mShortAnimDuration = mContainer.getResources()
|
||||
.getInteger(android.R.integer.config_shortAnimTime);
|
||||
|
||||
mSwitchingUsers = mParent.findViewById(R.id.switching_users);
|
||||
}
|
||||
@@ -115,10 +105,4 @@ public class FullscreenUserSwitcher {
|
||||
toggleSwitchInProgress(false);
|
||||
mParent.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void automaticallySelectUser() {
|
||||
// TODO: Switch according to some policy. This implementation just tries to drop the
|
||||
// keyguard for the current user.
|
||||
mUserGridView.showOfflineAuthUi();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* 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 com.android.systemui.qs.car.CarQSFragment;
|
||||
import com.android.systemui.statusbar.phone.StatusBar;
|
||||
|
||||
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 StatusBar mStatusBar;
|
||||
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);
|
||||
}
|
||||
|
||||
public void setStatusBar(@Nullable StatusBar statusBar) {
|
||||
mStatusBar = statusBar;
|
||||
}
|
||||
|
||||
private List<UserRecord> createUserRecords(List<UserInfo> userInfoList) {
|
||||
List<UserRecord> userRecords = new ArrayList<>();
|
||||
for (UserInfo userInfo : userInfoList) {
|
||||
boolean isCurrent = false;
|
||||
if (ActivityManager.getCurrentUser() == userInfo.id) {
|
||||
isCurrent = true;
|
||||
}
|
||||
UserRecord record = new UserRecord(userInfo, false /* isGuest */,
|
||||
false /* isAddUser */, isCurrent);
|
||||
userRecords.add(record);
|
||||
}
|
||||
|
||||
// Add guest user record if the current user is not a guest
|
||||
if (!mUserManagerHelper.isGuestUser()) {
|
||||
userRecords.add(addGuestUserRecord());
|
||||
}
|
||||
|
||||
// Add add user record if the current user can add users
|
||||
if (mUserManagerHelper.canAddUsers()) {
|
||||
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 /* isCurrent */);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 /* isCurrent */);
|
||||
}
|
||||
|
||||
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.
|
||||
if (mStatusBar != null) {
|
||||
mStatusBar.executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
|
||||
true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
|
||||
}
|
||||
}
|
||||
|
||||
@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 a current user.
|
||||
*/
|
||||
public static final class UserRecord {
|
||||
|
||||
public final UserInfo mInfo;
|
||||
public final boolean mIsGuest;
|
||||
public final boolean mIsAddUser;
|
||||
public final boolean mIsCurrent;
|
||||
|
||||
public UserRecord(UserInfo userInfo, boolean isGuest, boolean isAddUser,
|
||||
boolean isCurrent) {
|
||||
mInfo = userInfo;
|
||||
mIsGuest = isGuest;
|
||||
mIsAddUser = isAddUser;
|
||||
mIsCurrent = isCurrent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener used to notify when a user has been selected
|
||||
*/
|
||||
interface UserSelectionListener {
|
||||
|
||||
void onUserSelected(UserRecord record);
|
||||
}
|
||||
}
|
||||
@@ -1,364 +0,0 @@
|
||||
/*
|
||||
* 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.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Align;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.support.v4.view.PagerAdapter;
|
||||
import android.support.v4.view.ViewPager;
|
||||
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.systemui.Dependency;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.qs.car.CarQSFragment;
|
||||
import com.android.systemui.statusbar.phone.StatusBar;
|
||||
import com.android.systemui.statusbar.policy.UserInfoController;
|
||||
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* 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 implements
|
||||
UserInfoController.OnUserInfoChangedListener {
|
||||
private StatusBar mStatusBar;
|
||||
private UserSwitcherController mUserSwitcherController;
|
||||
private Adapter mAdapter;
|
||||
private UserSelectionListener mUserSelectionListener;
|
||||
private UserInfoController mUserInfoController;
|
||||
private Vector mUserContainers;
|
||||
private int mContainerWidth;
|
||||
private boolean mOverrideAlpha;
|
||||
private CarQSFragment.UserSwitchCallback mUserSwitchCallback;
|
||||
|
||||
public UserGridView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void init(StatusBar statusBar, UserSwitcherController userSwitcherController,
|
||||
boolean overrideAlpha) {
|
||||
mStatusBar = statusBar;
|
||||
mUserSwitcherController = userSwitcherController;
|
||||
mAdapter = new Adapter(mUserSwitcherController);
|
||||
mUserInfoController = Dependency.get(UserInfoController.class);
|
||||
mOverrideAlpha = overrideAlpha;
|
||||
// Whenever the container width changes, the containers must be refreshed. Instead of
|
||||
// doing an initial refreshContainers() to populate the containers, this listener will
|
||||
// refresh them on layout change because that affects how the users are split into
|
||||
// containers. Furthermore, at this point, the container width is unknown, so
|
||||
// refreshContainers() cannot populate any containers.
|
||||
addOnLayoutChangeListener(
|
||||
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
|
||||
int newWidth = Math.max(left - right, right - left);
|
||||
if (mContainerWidth != newWidth) {
|
||||
mContainerWidth = newWidth;
|
||||
refreshContainers();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void refreshContainers() {
|
||||
mUserContainers = new Vector();
|
||||
|
||||
Context context = getContext();
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
|
||||
for (int i = 0; i < mAdapter.getCount(); i++) {
|
||||
ViewGroup pods = (ViewGroup) inflater.inflate(
|
||||
R.layout.car_fullscreen_user_pod_container, null);
|
||||
|
||||
int iconsPerPage = mAdapter.getIconsPerPage();
|
||||
int limit = Math.min(mUserSwitcherController.getUsers().size(), (i + 1) * iconsPerPage);
|
||||
for (int j = i * iconsPerPage; j < limit; j++) {
|
||||
View v = mAdapter.makeUserPod(inflater, context, j, pods);
|
||||
if (mOverrideAlpha) {
|
||||
v.setAlpha(1f);
|
||||
}
|
||||
pods.addView(v);
|
||||
// This is hacky, but the dividers on the pod container LinearLayout don't seem
|
||||
// to work for whatever reason. Instead, set a right margin on the pod if it's not
|
||||
// the right-most pod and there is more than one pod in the container.
|
||||
if (i < limit - 1 && limit > 1) {
|
||||
ViewGroup.MarginLayoutParams params =
|
||||
(ViewGroup.MarginLayoutParams) v.getLayoutParams();
|
||||
params.setMargins(0, 0, getResources().getDimensionPixelSize(
|
||||
R.dimen.car_fullscreen_user_pod_margin_between), 0);
|
||||
v.setLayoutParams(params);
|
||||
}
|
||||
}
|
||||
mUserContainers.add(pods);
|
||||
}
|
||||
|
||||
mAdapter = new Adapter(mUserSwitcherController);
|
||||
setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
|
||||
refreshContainers();
|
||||
}
|
||||
|
||||
public void setUserSwitchCallback(CarQSFragment.UserSwitchCallback callback) {
|
||||
mUserSwitchCallback = callback;
|
||||
}
|
||||
|
||||
public void onUserSwitched(int newUserId) {
|
||||
// Bring up security view after user switch is completed.
|
||||
post(this::showOfflineAuthUi);
|
||||
}
|
||||
|
||||
public void setUserSelectionListener(UserSelectionListener userSelectionListener) {
|
||||
mUserSelectionListener = userSelectionListener;
|
||||
}
|
||||
|
||||
public void setListening(boolean listening) {
|
||||
if (listening) {
|
||||
mUserInfoController.addCallback(this);
|
||||
} else {
|
||||
mUserInfoController.removeCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
|
||||
height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
// Respect the AT_MOST request from parent.
|
||||
if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
|
||||
height = Math.min(MeasureSpec.getSize(heightMeasureSpec), 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 {
|
||||
private final int mPodWidth;
|
||||
private final int mPodMarginBetween;
|
||||
private final int mPodImageAvatarWidth;
|
||||
private final int mPodImageAvatarHeight;
|
||||
|
||||
private final WrappedBaseUserAdapter mUserAdapter;
|
||||
|
||||
public Adapter(UserSwitcherController controller) {
|
||||
super();
|
||||
mUserAdapter = new WrappedBaseUserAdapter(controller, this);
|
||||
|
||||
Resources res = getResources();
|
||||
mPodWidth = res.getDimensionPixelSize(R.dimen.car_fullscreen_user_pod_width);
|
||||
mPodMarginBetween = res.getDimensionPixelSize(
|
||||
R.dimen.car_fullscreen_user_pod_margin_between);
|
||||
mPodImageAvatarWidth = res.getDimensionPixelSize(
|
||||
R.dimen.car_fullscreen_user_pod_image_avatar_width);
|
||||
mPodImageAvatarHeight = res.getDimensionPixelSize(
|
||||
R.dimen.car_fullscreen_user_pod_image_avatar_height);
|
||||
}
|
||||
|
||||
@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
|
||||
// a margin between them. 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.
|
||||
// There is one less margin than the number of pods (eg. for 5 pods, there are 4
|
||||
// margins), so need to add the margin to the measured width to account for that.
|
||||
return (mContainerWidth + mPodMarginBetween) /
|
||||
(mPodWidth + mPodMarginBetween);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpdate(ViewGroup container) {
|
||||
if (mUserSwitchCallback != null) {
|
||||
mUserSwitchCallback.resetShowing();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(ViewGroup container, int position) {
|
||||
if (position < mUserContainers.size()) {
|
||||
container.addView((View) mUserContainers.get(position));
|
||||
return mUserContainers.get(position);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(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(getContext().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(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(), mPodImageAvatarWidth / 2,
|
||||
(mPodImageAvatarHeight - (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;
|
||||
}
|
||||
}
|
||||
|
||||
private final class WrappedBaseUserAdapter extends UserSwitcherController.BaseUserAdapter {
|
||||
private final 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);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user