Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2ff95846 authored by Bryan Eyler's avatar Bryan Eyler
Browse files

Add user selection to car status bar and animate.

Adds the UserGridView to the car status bar and adds an animation when
expanding/hiding.

Bug: 63593747
Tested on Mojave with the fullscreen user switcher and status bar.

Change-Id: If6ddbd4da896c5eb661393dcc35ab299361754e9
parent 1b08a4a8
Loading
Loading
Loading
Loading
+7 −0
Original line number Original line Diff line number Diff line
@@ -24,4 +24,11 @@


    <include layout="@layout/car_status_bar_header" />
    <include layout="@layout/car_status_bar_header" />
    <include layout="@layout/car_qs_footer" />
    <include layout="@layout/car_qs_footer" />

    <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" />
</LinearLayout>
</LinearLayout>
+22 −0
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@ import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.View;
import android.widget.ImageView;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout;
@@ -27,6 +28,7 @@ import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSFooter;
import com.android.systemui.qs.QSFooter;
import com.android.systemui.qs.QSPanel;
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.phone.MultiUserSwitch;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserInfoController;
@@ -37,10 +39,13 @@ import com.android.systemui.statusbar.policy.UserInfoController;
 */
 */
public class CarQSFooter extends RelativeLayout implements QSFooter,
public class CarQSFooter extends RelativeLayout implements QSFooter,
        UserInfoController.OnUserInfoChangedListener {
        UserInfoController.OnUserInfoChangedListener {
    private static final String TAG = "CarQSFooter";

    private UserInfoController mUserInfoController;
    private UserInfoController mUserInfoController;


    private MultiUserSwitch mMultiUserSwitch;
    private MultiUserSwitch mMultiUserSwitch;
    private ImageView mMultiUserAvatar;
    private ImageView mMultiUserAvatar;
    private UserGridView mUserGridView;


    public CarQSFooter(Context context, AttributeSet attrs) {
    public CarQSFooter(Context context, AttributeSet attrs) {
        super(context, attrs);
        super(context, attrs);
@@ -54,6 +59,19 @@ public class CarQSFooter extends RelativeLayout implements QSFooter,


        mUserInfoController = Dependency.get(UserInfoController.class);
        mUserInfoController = Dependency.get(UserInfoController.class);


        mMultiUserSwitch.setOnClickListener(v -> {
            if (mUserGridView == null) {
                Log.e(TAG, "CarQSFooter not properly set up; cannot display user switcher.");
                return;
            }

            if (!mUserGridView.isShowing()) {
                mUserGridView.show();
            } else {
                mUserGridView.hide();
            }
        });

        findViewById(R.id.settings_button).setOnClickListener(v -> {
        findViewById(R.id.settings_button).setOnClickListener(v -> {
            ActivityStarter activityStarter = Dependency.get(ActivityStarter.class);
            ActivityStarter activityStarter = Dependency.get(ActivityStarter.class);


@@ -80,6 +98,10 @@ public class CarQSFooter extends RelativeLayout implements QSFooter,
        }
        }
    }
    }


    public void setUserGridView(UserGridView view) {
        mUserGridView = view;
    }

    @Override
    @Override
    public void setListening(boolean listening) {
    public void setListening(boolean listening) {
        if (listening) {
        if (listening) {
+11 −1
Original line number Original line Diff line number Diff line
@@ -22,9 +22,12 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup;


import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.QSFooter;
import com.android.systemui.qs.QSFooter;
import com.android.systemui.statusbar.car.UserGridView;
import com.android.systemui.statusbar.policy.UserSwitcherController;


/**
/**
 * A quick settings fragment for the car. For auto, there is no row for quick settings or ability
 * A quick settings fragment for the car. For auto, there is no row for quick settings or ability
@@ -33,7 +36,8 @@ import com.android.systemui.qs.QSFooter;
 */
 */
public class CarQSFragment extends Fragment implements QS {
public class CarQSFragment extends Fragment implements QS {
    private View mHeader;
    private View mHeader;
    private QSFooter mFooter;
    private CarQSFooter mFooter;
    private UserGridView mUserGridView;


    @Override
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -46,6 +50,12 @@ public class CarQSFragment extends Fragment implements QS {
        super.onViewCreated(view, savedInstanceState);
        super.onViewCreated(view, savedInstanceState);
        mHeader = view.findViewById(R.id.header);
        mHeader = view.findViewById(R.id.header);
        mFooter = view.findViewById(R.id.qs_footer);
        mFooter = view.findViewById(R.id.qs_footer);

        mUserGridView = view.findViewById(R.id.user_grid);
        mUserGridView.init(null, Dependency.get(UserSwitcherController.class),
                false /* showInitially */);

        mFooter.setUserGridView(mUserGridView);
    }
    }


    @Override
    @Override
+1 −1
Original line number Original line Diff line number Diff line
@@ -53,7 +53,7 @@ public class FullscreenUserSwitcher {
        mParent = containerStub.inflate();
        mParent = containerStub.inflate();
        mContainer = mParent.findViewById(R.id.container);
        mContainer = mParent.findViewById(R.id.container);
        mUserGridView = mContainer.findViewById(R.id.user_grid);
        mUserGridView = mContainer.findViewById(R.id.user_grid);
        mUserGridView.init(statusBar, mUserSwitcherController);
        mUserGridView.init(statusBar, mUserSwitcherController, true /* showInitially */);
        mUserGridView.setUserSelectionListener(record -> {
        mUserGridView.setUserSelectionListener(record -> {
            if (!record.isCurrent) {
            if (!record.isCurrent) {
                toggleSwitchInProgress(true);
                toggleSwitchInProgress(true);
+102 −11
Original line number Original line Diff line number Diff line
@@ -16,20 +16,21 @@


package com.android.systemui.statusbar.car;
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.Context;
import android.content.res.Resources;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Align;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.GradientDrawable;
import android.os.UserHandle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.util.AttributeSet;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup;
@@ -37,9 +38,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.TextView;


import com.android.internal.util.UserIcons;
import com.android.systemui.R;
import com.android.systemui.R;
import com.android.systemui.statusbar.UserUtil;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.UserSwitcherController;


@@ -48,21 +47,44 @@ import com.android.systemui.statusbar.policy.UserSwitcherController;
 * One of the uses of this is for the lock screen in auto.
 * One of the uses of this is for the lock screen in auto.
 */
 */
public class UserGridView extends ViewPager {
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 StatusBar mStatusBar;
    private UserSwitcherController mUserSwitcherController;
    private UserSwitcherController mUserSwitcherController;
    private Adapter mAdapter;
    private Adapter mAdapter;
    private UserSelectionListener mUserSelectionListener;
    private UserSelectionListener mUserSelectionListener;
    private ValueAnimator mHeightAnimator;
    private int mTargetHeight;
    private int mHeightChildren;
    private boolean mShowing;


    public UserGridView(Context context, AttributeSet attrs) {
    public UserGridView(Context context, AttributeSet attrs) {
        super(context, attrs);
        super(context, attrs);
    }
    }


    public void init(StatusBar statusBar, UserSwitcherController userSwitcherController) {
    public void init(StatusBar statusBar, UserSwitcherController userSwitcherController,
            boolean showInitially) {
        mStatusBar = statusBar;
        mStatusBar = statusBar;
        mUserSwitcherController = userSwitcherController;
        mUserSwitcherController = userSwitcherController;
        mAdapter = new Adapter(mUserSwitcherController);
        mAdapter = new Adapter(mUserSwitcherController);
        addOnLayoutChangeListener(mAdapter);
        addOnLayoutChangeListener(mAdapter);
        setAdapter(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) {
    public void onUserSwitched(int newUserId) {
@@ -83,16 +105,85 @@ public class UserGridView extends ViewPager {
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // Wrap content doesn't work in ViewPagers, so simulate the behavior in code.
        // Wrap content doesn't work in ViewPagers, so simulate the behavior in code.
        int height = 0;
        int height = 0;
        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
            height = MeasureSpec.getSize(heightMeasureSpec);
        } else {
            for (int i = 0; i < getChildCount(); i++) {
            for (int i = 0; i < getChildCount(); i++) {
                View child = getChildAt(i);
                View child = getChildAt(i);
                child.measure(widthMeasureSpec,
                child.measure(widthMeasureSpec,
                        MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
                        MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
                height = Math.max(child.getMeasuredHeight(), height);
                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);
            }
        }
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        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
     * This is a ViewPager.PagerAdapter which deletegates the work to a
     * UserSwitcherController.BaseUserAdapter. Java doesn't support multiple inheritance so we have
     * UserSwitcherController.BaseUserAdapter. Java doesn't support multiple inheritance so we have