Merge changes from topic "User Switcher CP to Master"

* changes:
  Fix users not showing up in fullscreen selection.
  Update user names on changes in settings/user.
  Add expand icon for user switcher.
  Cascade animation of users in switcher.
  Add pagination dots and fix animation of user switcher.
This commit is contained in:
Bryan Eyler
2018-01-23 18:38:18 +00:00
committed by Android (Google) Code Review
23 changed files with 601 additions and 152 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,24 @@
<!-- 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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="167"
android:propertyName="rotation"
android:valueType="floatType"
android:valueFrom="180"
android:valueTo="0"
android:interpolator="@android:interpolator/fast_out_slow_in" />
</set>

View File

@@ -0,0 +1,23 @@
<!-- 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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="83"
android:propertyName="alpha"
android:valueType="floatType"
android:valueFrom="0"
android:valueTo="1" />
</set>

View File

@@ -0,0 +1,23 @@
<!-- 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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="83"
android:propertyName="alpha"
android:valueType="floatType"
android:valueFrom="1"
android:valueTo="0" />
</set>

View File

@@ -0,0 +1,24 @@
<!-- 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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="together" >
<objectAnimator
android:duration="50"
android:propertyName="alpha"
android:valueType="floatType"
android:valueFrom="1"
android:valueTo="0" />
</set>

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

@@ -0,0 +1,24 @@
<!-- 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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="167"
android:propertyName="rotation"
android:valueType="floatType"
android:valueFrom="0"
android:valueTo="180"
android:interpolator="@android:interpolator/fast_out_slow_in" />
</set>

View File

@@ -0,0 +1,24 @@
<!-- 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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="83"
android:startOffset="83"
android:propertyName="alpha"
android:valueType="floatType"
android:valueFrom="1"
android:valueTo="0" />
</set>

View File

@@ -0,0 +1,24 @@
<!-- 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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="83"
android:startOffset="83"
android:propertyName="alpha"
android:valueType="floatType"
android:valueFrom="0"
android:valueTo="1" />
</set>

View File

@@ -0,0 +1,33 @@
<!-- 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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="together" >
<objectAnimator
android:duration="167"
android:startOffset="67"
android:propertyName="translationY"
android:valueType="floatType"
android:valueFrom="@dimen/car_user_switcher_container_anim_height"
android:valueTo="0"
android:interpolator="@android:interpolator/fast_out_slow_in" />
<objectAnimator
android:duration="83"
android:startOffset="117"
android:propertyName="alpha"
android:valueType="floatType"
android:valueFrom="0"
android:valueTo="1" />
</set>

View File

@@ -1,5 +1,5 @@
<!--
~ Copyright (C) 2015 The Android Open Source Project
~ 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.

View File

@@ -0,0 +1,24 @@
<!--
~ 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
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48.0dp"
android:height="48.0dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M14 28l10-10 10 10z"/>
</vector>

View File

@@ -15,27 +15,40 @@
limitations under the License.
-->
<LinearLayout
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:clipChildren="false"
android:alpha="0"
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

@@ -16,10 +16,10 @@
-->
<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"
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

@@ -35,7 +35,6 @@
android:layout_centerVertical="true"
android:layout_width="@dimen/car_qs_footer_icon_width"
android:layout_height="@dimen/car_qs_footer_icon_height"
android:layout_marginRight="@dimen/car_qs_footer_user_switch_margin_right"
android:background="@drawable/ripple_drawable"
android:focusable="true">
@@ -47,6 +46,18 @@
android:scaleType="fitCenter"/>
</com.android.systemui.statusbar.phone.MultiUserSwitch>
<ImageView
android:id="@+id/user_switch_expand_icon"
android:layout_height="match_parent"
android:layout_width="@dimen/car_qs_footer_user_switch_icon_width"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/multi_user_switch"
android:layout_marginLeft="@dimen/car_qs_footer_user_switch_icon_margin"
android:layout_marginRight="@dimen/car_qs_footer_user_switch_icon_margin"
android:src="@drawable/car_ic_arrow_drop_up"
android:scaleType="fitCenter">
</ImageView>
<TextView android:id="@+id/user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -54,7 +65,7 @@
android:textColor="@color/car_qs_footer_user_name_color"
android:gravity="start|center_vertical"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/multi_user_switch" />
android:layout_toEndOf="@id/user_switch_expand_icon" />
<com.android.systemui.statusbar.phone.SettingsButton
android:id="@+id/settings_button"

View File

@@ -16,6 +16,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/quick_settings_container"
android:clipChildren="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/car_qs_background_primary"
@@ -26,10 +27,32 @@
<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:clipChildren="false"
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:clipChildren="false"
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:alpha="0"
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>
@@ -47,8 +50,14 @@
<dimen name="car_qs_footer_padding_start">46dp</dimen>
<dimen name="car_qs_footer_icon_width">56dp</dimen>
<dimen name="car_qs_footer_icon_height">56dp</dimen>
<dimen name="car_qs_footer_user_switch_margin_right">46dp</dimen>
<dimen name="car_qs_footer_user_switch_icon_margin">5dp</dimen>
<dimen name="car_qs_footer_user_switch_icon_width">36dp</dimen>
<dimen name="car_qs_footer_user_name_text_size">@dimen/car_body2_size</dimen>
<dimen name="car_user_switcher_container_height">420dp</dimen>
<!-- This must be the negative of car_user_switcher_container_height for the animation. -->
<dimen name="car_user_switcher_container_anim_height">-420dp</dimen>
<dimen name="car_user_grid_margin_bottom">28dp</dimen>
<dimen name="car_body2_size">26sp</dimen>
</resources>

View File

@@ -19,4 +19,5 @@
<integer name="car_user_switcher_timeout_ms">15000</integer>
<!-- This values less than ProgressBar.PROGRESS_ANIM_DURATION for a smooth animation. -->
<integer name="car_user_switcher_anim_update_ms">60</integer>
<integer name="car_user_switcher_anim_cascade_delay_ms">27</integer>
</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,12 @@
*/
package com.android.systemui.qs.car;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
@@ -26,18 +32,29 @@ 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 java.util.ArrayList;
import java.util.List;
/**
* A quick settings fragment for the car. For auto, there is no row for quick settings or ability
* to expand the quick settings panel. Instead, the only thing is that displayed is the
* 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 AnimatorSet mAnimatorSet;
private UserSwitchCallback mUserSwitchCallback;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -48,14 +65,26 @@ 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);
mFooterExpandIcon = mFooter.findViewById(R.id.user_switch_expand_icon);
mUserSwitcherContainer = view.findViewById(R.id.user_switcher_container);
updateUserSwitcherHeight(0);
mUserGridView = view.findViewById(R.id.user_grid);
mUserGridView.init(null, Dependency.get(UserSwitcherController.class),
false /* showInitially */);
false /* overrideAlpha */);
mFooter.setUserGridView(mUserGridView);
mPageIndicator = view.findViewById(R.id.user_switcher_page_indicator);
mPageIndicator.setupWithViewPager(mUserGridView);
mUserSwitchCallback = new UserSwitchCallback();
mFooter.setUserSwitchCallback(mUserSwitchCallback);
mUserGridView.setUserSwitchCallback(mUserSwitchCallback);
}
@Override
@@ -82,11 +111,13 @@ 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
@@ -171,4 +202,126 @@ 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 */);
}
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) {
ViewGroup.LayoutParams layoutParams = mUserSwitcherContainer.getLayoutParams();
layoutParams.height = height;
mUserSwitcherContainer.requestLayout();
}
private void animateHeightChange(boolean opening) {
// Animation in progress; cancel it to avoid contention.
if (mAnimatorSet != null){
mAnimatorSet.cancel();
}
List<Animator> allAnimators = new ArrayList<>();
ValueAnimator heightAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(getContext(),
opening ? R.anim.car_user_switcher_open_animation
: R.anim.car_user_switcher_close_animation);
heightAnimator.addUpdateListener(valueAnimator -> {
updateUserSwitcherHeight((Integer) valueAnimator.getAnimatedValue());
});
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);
nameAnimator.setTarget(mFooterUserName);
allAnimators.add(nameAnimator);
Animator iconAnimator = AnimatorInflater.loadAnimator(getContext(),
opening ? R.anim.car_user_switcher_open_icon_animation
: R.anim.car_user_switcher_close_icon_animation);
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
public void onAnimationEnd(Animator animation) {
mAnimatorSet = null;
}
});
mAnimatorSet.playTogether(allAnimators.toArray(new Animator[0]));
// Setup all values to the start values in the animations, since there are delays, but need
// to have all values start at the beginning.
setupInitialValues(mAnimatorSet);
mAnimatorSet.start();
}
private void setupInitialValues(Animator anim) {
if (anim instanceof AnimatorSet) {
for (Animator a : ((AnimatorSet) anim).getChildAnimations()) {
setupInitialValues(a);
}
} else if (anim instanceof ObjectAnimator) {
((ObjectAnimator) anim).setCurrentFraction(0.0f);
}
}
}

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, true /* overrideAlpha */);
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,62 +26,110 @@ 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.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 {
private static final int EXPAND_ANIMATION_TIME_MS = 200;
private static final int HIDE_ANIMATION_TIME_MS = 133;
public class UserGridView extends ViewPager implements
UserInfoController.OnUserInfoChangedListener {
private StatusBar mStatusBar;
private UserSwitcherController mUserSwitcherController;
private Adapter mAdapter;
private UserSelectionListener mUserSelectionListener;
private ValueAnimator mHeightAnimator;
private int mTargetHeight;
private int mHeightChildren;
private boolean mShowing;
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 showInitially) {
boolean overrideAlpha) {
mStatusBar = statusBar;
mUserSwitcherController = userSwitcherController;
mAdapter = new Adapter(mUserSwitcherController);
addOnLayoutChangeListener(mAdapter);
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);
mShowing = showInitially;
}
public boolean isShowing() {
return mShowing;
@Override
public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
refreshContainers();
}
public void show() {
mShowing = true;
animateHeightChange(getMeasuredHeight(), mHeightChildren);
}
public void hide() {
mShowing = false;
animateHeightChange(getMeasuredHeight(), 0);
public void setUserSwitchCallback(CarQSFragment.UserSwitchCallback callback) {
mUserSwitchCallback = callback;
}
public void onUserSwitched(int newUserId) {
@@ -96,6 +141,14 @@ public class UserGridView extends ViewPager {
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);
@@ -115,13 +168,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,72 +178,19 @@ 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
* 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 class Adapter extends PagerAdapter {
private final int mPodWidth;
private final int mPodMarginBetween;
private final int mPodImageAvatarWidth;
private final int mPodImageAvatarHeight;
private final WrappedBaseUserAdapter mUserAdapter;
private int mContainerWidth;
public Adapter(UserSwitcherController controller) {
super();
@@ -229,30 +222,20 @@ public class UserGridView extends ViewPager {
}
@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++) {
View v = makeUserPod(inflater, context, i, pods);
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) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.setMargins(0, 0, mPodMarginBetween, 0);
v.setLayoutParams(params);
}
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;
}
container.addView(pods);
return pods;
}
/**
@@ -353,17 +336,10 @@ public class UserGridView extends ViewPager {
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;
private final Adapter mContainer;
public WrappedBaseUserAdapter(UserSwitcherController controller, Adapter container) {
super(controller);