Add pagination dots and fix animation of user switcher.

- Adds pagination dots to user switcher dropdown.
- Adds device name text view for when devices are associated with users.
- Fixes the layout of the user grid to handle changing height for
  proper animation.

Bug: 65423882
Tested on Mojave with longer animation times to ensure correctness.

Change-Id: I54ae5cc93f9caa9d6c4a5c2c7e9e2488a6257001
(cherry picked from commit 0de35b5386ad7e00f13a7611fce36ca990b201e0)
This commit is contained in:
Bryan Eyler
2017-10-10 11:56:21 -07:00
parent 9c8295273a
commit 715bdbcd42
11 changed files with 169 additions and 122 deletions

View File

@@ -0,0 +1,20 @@
<!-- 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.
-->
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="133"
android:valueType="intType"
android:valueFrom="@dimen/car_user_switcher_container_height"
android:valueTo="0"
android:interpolator="@android:interpolator/fast_out_slow_in" />

View File

@@ -0,0 +1,20 @@
<!-- 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.
-->
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
android:valueType="intType"
android:valueFrom="0"
android:valueTo="@dimen/car_user_switcher_container_height"
android:interpolator="@android:interpolator/fast_out_slow_in" />

View File

@@ -15,27 +15,38 @@
limitations under the License.
-->
<LinearLayout
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center" >
android:layout_height="@dimen/car_fullscreen_user_pod_height"
android:layout_gravity="center_horizontal|bottom" >
<ImageView android:id="@+id/user_avatar"
android:layout_gravity="center"
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_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_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_text_size"
android:textSize="@dimen/car_fullscreen_user_pod_name_text_size"
android:textColor="@color/qs_user_detail_name"
android:ellipsize="end"
android:singleLine="true"
android:gravity="center_horizontal"
android:layout_gravity="center_horizontal" />
</LinearLayout>
android:layout_above="@id/device_name" />
<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>

View File

@@ -18,8 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_gravity="center" >
android:gravity="center" >
<!-- car_fullscreen_user_pods will be dynamically added here. -->
</LinearLayout>

View File

@@ -54,13 +54,13 @@
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/car_margin"
android:layout_marginRight="@dimen/car_margin"
android:layout_marginBottom="@dimen/car_user_grid_margin_bottom"
android:layout_centerInParent="true" />
<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_marginTop="@dimen/car_page_indicator_margin_top"
android:layout_below="@+id/user_grid" />
<Button

View File

@@ -26,10 +26,29 @@
<include layout="@layout/car_status_bar_header"/>
<include layout="@layout/car_qs_footer"/>
<com.android.systemui.statusbar.car.UserGridView
android:id="@+id/user_grid"
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/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_height="@dimen/car_user_switcher_container_height"
android:layout_gravity="center_horizontal" >
<com.android.systemui.statusbar.car.UserGridView
android:id="@+id/user_grid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/car_margin"
android:layout_marginRight="@dimen/car_margin"
android:layout_marginBottom="@dimen/car_user_grid_margin_bottom"
android:layout_above="@id/user_switcher_page_indicator" />
<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:layout_alignParentBottom="true" />
</RelativeLayout>
</LinearLayout>

View File

@@ -18,20 +18,23 @@
<resources>
<dimen name="car_margin">148dp</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">64dp</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>
<dimen name="car_fullscreen_user_pod_text_size">40sp</dimen> <!-- B1 -->
<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_navigation_button_width">64dp</dimen>
<dimen name="car_navigation_bar_width">760dp</dimen>
<dimen name="car_page_indicator_dot_diameter">12dp</dimen>
<dimen name="car_page_indicator_margin_top">32dp</dimen>
<dimen name="car_page_indicator_margin_bottom">24dp</dimen>
<dimen name="car_user_switcher_progress_bar_height">6dp</dimen>
<dimen name="car_user_switcher_progress_bar_margin_top">@dimen/status_bar_height</dimen>
@@ -50,5 +53,8 @@
<dimen name="car_qs_footer_user_switch_margin_right">46dp</dimen>
<dimen name="car_qs_footer_user_name_text_size">@dimen/car_body2_size</dimen>
<dimen name="car_user_switcher_container_height">420dp</dimen>
<dimen name="car_user_grid_margin_bottom">28dp</dimen>
<dimen name="car_body2_size">26sp</dimen>
</resources>

View File

@@ -47,7 +47,7 @@ public class CarQSFooter extends RelativeLayout implements QSFooter,
private MultiUserSwitch mMultiUserSwitch;
private TextView mUserName;
private ImageView mMultiUserAvatar;
private UserGridView mUserGridView;
private CarQSFragment.UserSwitchCallback mUserSwitchCallback;
public CarQSFooter(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -63,15 +63,15 @@ public class CarQSFooter extends RelativeLayout implements QSFooter,
mUserInfoController = Dependency.get(UserInfoController.class);
mMultiUserSwitch.setOnClickListener(v -> {
if (mUserGridView == null) {
if (mUserSwitchCallback == null) {
Log.e(TAG, "CarQSFooter not properly set up; cannot display user switcher.");
return;
}
if (!mUserGridView.isShowing()) {
mUserGridView.show();
if (!mUserSwitchCallback.isShowing()) {
mUserSwitchCallback.show();
} else {
mUserGridView.hide();
mUserSwitchCallback.hide();
}
});
@@ -102,8 +102,8 @@ public class CarQSFooter extends RelativeLayout implements QSFooter,
}
}
public void setUserGridView(UserGridView view) {
mUserGridView = view;
public void setUserSwitchCallback(CarQSFragment.UserSwitchCallback callback) {
mUserSwitchCallback = callback;
}
@Override

View File

@@ -13,6 +13,10 @@
*/
package com.android.systemui.qs.car;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
@@ -26,6 +30,7 @@ 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;
@@ -36,8 +41,11 @@ import com.android.systemui.statusbar.policy.UserSwitcherController;
*/
public class CarQSFragment extends Fragment implements QS {
private View mHeader;
private View mUserSwitcherContainer;
private CarQSFooter mFooter;
private UserGridView mUserGridView;
private PageIndicator mPageIndicator;
private ValueAnimator mHeightAnimator;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -51,11 +59,17 @@ public class CarQSFragment extends Fragment implements QS {
mHeader = view.findViewById(R.id.header);
mFooter = view.findViewById(R.id.qs_footer);
mUserGridView = view.findViewById(R.id.user_grid);
mUserGridView.init(null, Dependency.get(UserSwitcherController.class),
false /* showInitially */);
mUserSwitcherContainer = view.findViewById(R.id.user_switcher_container);
mFooter.setUserGridView(mUserGridView);
updateUserSwitcherHeight(0);
mUserGridView = view.findViewById(R.id.user_grid);
mUserGridView.init(null, Dependency.get(UserSwitcherController.class));
mPageIndicator = view.findViewById(R.id.user_switcher_page_indicator);
mPageIndicator.setupWithViewPager(mUserGridView);
mFooter.setUserSwitchCallback(new UserSwitchCallback());
}
@Override
@@ -171,4 +185,49 @@ public class CarQSFragment extends Fragment implements QS {
public void setExpandClickListener(OnClickListener onClickListener) {
// No ability to expand the quick settings.
}
public class UserSwitchCallback {
private boolean mShowing;
public boolean isShowing() {
return mShowing;
}
public void show() {
mShowing = true;
animateHeightChange(true /* opening */);
}
public void hide() {
mShowing = false;
animateHeightChange(false /* opening */);
}
}
private void updateUserSwitcherHeight(int height) {
ViewGroup.LayoutParams layoutParams = mUserSwitcherContainer.getLayoutParams();
layoutParams.height = height;
mUserSwitcherContainer.requestLayout();
}
private void animateHeightChange(boolean opening) {
// Animation in progress; cancel it to avoid contention.
if (mHeightAnimator != null){
mHeightAnimator.cancel();
}
mHeightAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(getContext(),
opening ? R.anim.car_user_switcher_open_animation
: R.anim.car_user_switcher_close_animation);
mHeightAnimator.addUpdateListener(valueAnimator -> {
updateUserSwitcherHeight((Integer)valueAnimator.getAnimatedValue());
});
mHeightAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mHeightAnimator = null;
}
});
mHeightAnimator.start();
}
}

View File

@@ -53,7 +53,7 @@ public class FullscreenUserSwitcher {
mParent = containerStub.inflate();
mContainer = mParent.findViewById(R.id.container);
mUserGridView = mContainer.findViewById(R.id.user_grid);
mUserGridView.init(statusBar, mUserSwitcherController, true /* showInitially */);
mUserGridView.init(statusBar, mUserSwitcherController);
mUserGridView.setUserSelectionListener(record -> {
if (!record.isCurrent) {
toggleSwitchInProgress(true);

View File

@@ -16,9 +16,6 @@
package com.android.systemui.statusbar.car;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -29,13 +26,11 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.util.AttributeSet;
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.systemui.R;
@@ -47,44 +42,21 @@ import com.android.systemui.statusbar.policy.UserSwitcherController;
* One of the uses of this is for the lock screen in auto.
*/
public class UserGridView extends ViewPager {
private static final int EXPAND_ANIMATION_TIME_MS = 200;
private static final int HIDE_ANIMATION_TIME_MS = 133;
private StatusBar mStatusBar;
private UserSwitcherController mUserSwitcherController;
private Adapter mAdapter;
private UserSelectionListener mUserSelectionListener;
private ValueAnimator mHeightAnimator;
private int mTargetHeight;
private int mHeightChildren;
private boolean mShowing;
public UserGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void init(StatusBar statusBar, UserSwitcherController userSwitcherController,
boolean showInitially) {
public void init(StatusBar statusBar, UserSwitcherController userSwitcherController) {
mStatusBar = statusBar;
mUserSwitcherController = userSwitcherController;
mAdapter = new Adapter(mUserSwitcherController);
addOnLayoutChangeListener(mAdapter);
setAdapter(mAdapter);
mShowing = showInitially;
}
public boolean isShowing() {
return mShowing;
}
public void show() {
mShowing = true;
animateHeightChange(getMeasuredHeight(), mHeightChildren);
}
public void hide() {
mShowing = false;
animateHeightChange(getMeasuredHeight(), 0);
}
public void onUserSwitched(int newUserId) {
@@ -115,13 +87,6 @@ public class UserGridView extends ViewPager {
height = Math.max(child.getMeasuredHeight(), height);
}
mHeightChildren = height;
// Override the height if it's not showing.
if (!mShowing) {
height = 0;
}
// Respect the AT_MOST request from parent.
if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
height = Math.min(MeasureSpec.getSize(heightMeasureSpec), height);
@@ -132,58 +97,6 @@ public class UserGridView extends ViewPager {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private void animateHeightChange(int oldHeight, int newHeight) {
// If there is no change in height or an animation is already in progress towards the
// desired height, then there's no need to make any changes.
if (oldHeight == newHeight || newHeight == mTargetHeight) {
return;
}
// Animation in progress is not going towards the new target, so cancel it.
if (mHeightAnimator != null){
mHeightAnimator.cancel();
}
mTargetHeight = newHeight;
mHeightAnimator = ValueAnimator.ofInt(oldHeight, mTargetHeight);
mHeightAnimator.addUpdateListener(valueAnimator -> {
ViewGroup.LayoutParams layoutParams = getLayoutParams();
layoutParams.height = (Integer) valueAnimator.getAnimatedValue();
requestLayout();
});
mHeightAnimator.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {}
@Override
public void onAnimationEnd(Animator animator) {
// ValueAnimator does not guarantee that the update listener will get an update
// to the final value, so here, the final value is set. Though the final calculated
// height (mTargetHeight) could be set, WRAP_CONTENT is more appropriate.
ViewGroup.LayoutParams layoutParams = getLayoutParams();
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
requestLayout();
mHeightAnimator = null;
}
@Override
public void onAnimationCancel(Animator animator) {}
@Override
public void onAnimationRepeat(Animator animator) {}
});
mHeightAnimator.setInterpolator(new FastOutSlowInInterpolator());
if (oldHeight < newHeight) {
// Expanding
mHeightAnimator.setDuration(EXPAND_ANIMATION_TIME_MS);
} else {
// Hiding
mHeightAnimator.setDuration(HIDE_ANIMATION_TIME_MS);
}
mHeightAnimator.start();
}
/**
* This is a ViewPager.PagerAdapter which deletegates the work to a
* UserSwitcherController.BaseUserAdapter. Java doesn't support multiple inheritance so we have
@@ -245,8 +158,8 @@ public class UserGridView extends ViewPager {
// 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) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
ViewGroup.MarginLayoutParams params =
(ViewGroup.MarginLayoutParams) v.getLayoutParams();
params.setMargins(0, 0, mPodMarginBetween, 0);
v.setLayoutParams(params);
}