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

Commit 4335c709 authored by James O'Leary's avatar James O'Leary
Browse files

Adjustments to handles visuals and behavior

- Fix display on devices with larger corners by resizing the handle
views as the corner radius changes.
- Fix home x assist handle color matching by ensuring `isListening` is
set to after color matching start
- Allow UiController to suppress handles showing up during assist
sessions by calling ScreenDecorations.setAssistHintBlocked
- Add animation curve from spec to handle scale animation
- Fix handle rounded ends
- Make handle dimensions customizable

jamesoleary 2 CLs - ag/7727795, ag/7727797 (fix test failure)
mrcasey 2 CLs - ag/7704742, ag/7713549

Test: Verify WAI
Bug: 130642504
Change-Id: I9563d7f437064c8c63c988a9fda41be906064219
parent ab09bd96
Loading
Loading
Loading
Loading
+0 −25
Original line number Diff line number Diff line
<!--
    Copyright (C) 2019 The Android Open Source Project

    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:height="12dp"
    android:width="12dp"
    android:viewportWidth="12"
    android:viewportHeight="12">

    <path android:fillColor="#00000000"
          android:pathData="M 1.18 10.65 C 1.18 5.58 5.41 1.18 10.65 1.18"
          android:strokeColor="#000"
          android:strokeLineCap="round"
          android:strokeWidth="1.3" />
</vector>
+14 −20
Original line number Diff line number Diff line
@@ -18,6 +18,18 @@
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.android.systemui.CornerHandleView
        android:id="@+id/assist_hint_left"
        android:layout_width="36dp"
        android:layout_height="36dp"
        android:layout_gravity="left|top"
        android:visibility="gone"/>
    <com.android.systemui.CornerHandleView
        android:id="@+id/assist_hint_right"
        android:layout_width="36dp"
        android:layout_height="36dp"
        android:layout_gravity="right|bottom"
        android:visibility="gone"/>
    <ImageView
        android:id="@+id/left"
        android:layout_width="12dp"
@@ -32,22 +44,4 @@
        android:tint="#ff000000"
        android:layout_gravity="right|bottom"
        android:src="@drawable/rounded"/>
    <ImageView
        android:id="@+id/assist_hint_left"
        android:layout_width="32dp"
        android:layout_height="32dp"
        android:padding="6dp"
        android:layout_gravity="left|top"
        android:src="@drawable/corner_gesture_hint"
        android:tint="#ffffffff"
        android:visibility="gone" />
    <ImageView
        android:id="@+id/assist_hint_right"
        android:layout_width="32dp"
        android:layout_height="32dp"
        android:padding="6dp"
        android:layout_gravity="right|bottom"
        android:src="@drawable/corner_gesture_hint"
        android:tint="#ffffffff"
        android:visibility="gone" />
</com.android.systemui.RegionInterceptingFrameLayout>
+143 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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;

import android.animation.ArgbEvaluator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.SystemProperties;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.ContextThemeWrapper;
import android.view.View;

import com.android.settingslib.Utils;

/**
 * CornerHandleView draws an inset arc intended to be displayed within the screen decoration
 * corners.
 */
public class CornerHandleView extends View {
    private static final boolean ALLOW_TUNING = false;
    private static final int ANGLE_DEGREES = 50;
    public static final int MARGIN_DP = 11;
    public static final int RADIUS_DP = 37;
    public static final float STROKE_DP = 2.5f;

    private Paint mPaint;
    private int mLightColor;
    private int mDarkColor;
    private RectF mOval;


    public CornerHandleView(Context context, AttributeSet attrs) {
        super(context, attrs);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(getStrokePx());

        final int dualToneDarkTheme = Utils.getThemeAttr(mContext,
                R.attr.darkIconTheme);
        final int dualToneLightTheme = Utils.getThemeAttr(mContext,
                R.attr.lightIconTheme);
        Context lightContext = new ContextThemeWrapper(mContext, dualToneLightTheme);
        Context darkContext = new ContextThemeWrapper(mContext, dualToneDarkTheme);
        mLightColor = Utils.getColorAttrDefaultColor(lightContext,
                R.attr.singleToneColor);
        mDarkColor = Utils.getColorAttrDefaultColor(darkContext,
                R.attr.singleToneColor);

        updateOval();
    }

    /**
     * Receives an intensity from 0 (lightest) to 1 (darkest) and sets the handle color
     * approriately. Intention is to match the home handle color.
     */
    public void updateDarkness(float darkIntensity) {
        mPaint.setColor((int) ArgbEvaluator.getInstance().evaluate(darkIntensity,
                mLightColor,
                mDarkColor));
        invalidate();
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (ALLOW_TUNING) {
            mPaint.setStrokeWidth(getStrokePx());
            updateOval();
        }

        canvas.drawArc(mOval, 180 + ((90 - getAngle()) / 2), getAngle(), false,
                mPaint);
    }

    // TODO(b/133834204): Remove tweaking of corner handles
    private static float convertDpToPixel(float dp, Context context) {
        return dp * ((float) context.getResources().getDisplayMetrics().densityDpi
                / DisplayMetrics.DENSITY_DEFAULT);
    }

    private void updateOval() {
        mOval = new RectF(getMarginPx() - (getStrokePx() / 2.f),
                getMarginPx() - (getStrokePx() / 2.f),
                getMarginPx() + (2 * (getRadiusPx()) + (getStrokePx() / 2.f)),
                getMarginPx() + 2 * getRadiusPx() + (getStrokePx() / 2.f));
    }

    private int getAngle() {
        if (ALLOW_TUNING) {
            return SystemProperties.getInt("CORNER_HANDLE_ANGLE_DEGREES", ANGLE_DEGREES);
        } else {
            return ANGLE_DEGREES;
        }
    }

    private int getMarginPx() {
        if (ALLOW_TUNING) {
            return SystemProperties.getInt("CORNER_HANDLE_MARGIN_PX",
                    (int) convertDpToPixel(MARGIN_DP, getContext()));
        } else {
            return (int) convertDpToPixel(MARGIN_DP, getContext());
        }
    }

    private int getRadiusPx() {
        if (ALLOW_TUNING) {
            return SystemProperties.getInt("CORNER_HANDLE_RADIUS_PX",
                    (int) convertDpToPixel(RADIUS_DP, getContext()));
        } else {
            return (int) convertDpToPixel(RADIUS_DP, getContext());
        }
    }

    private int getStrokePx() {
        if (ALLOW_TUNING) {
            return SystemProperties.getInt("CORNER_HANDLE_STROKE_PX",
                    (int) convertDpToPixel(STROKE_DP, getContext()));
        } else {
            return (int) convertDpToPixel(STROKE_DP, getContext());
        }
    }
}
+104 −24
Original line number Diff line number Diff line
@@ -60,6 +60,12 @@ import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.OvershootInterpolator;
import android.view.animation.PathInterpolator;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import android.widget.ImageView;

@@ -72,6 +78,7 @@ import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.SecureSetting;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.NavigationBarTransitions;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.tuner.TunablePadding;
import com.android.systemui.tuner.TunerService;
@@ -85,7 +92,8 @@ import java.util.List;
 * An overlay that draws screen decorations in software (e.g for rounded corners or display cutout)
 * for antialiasing and emulation purposes.
 */
public class ScreenDecorations extends SystemUI implements Tunable {
public class ScreenDecorations extends SystemUI implements Tunable,
        NavigationBarTransitions.DarkIntensityListener {
    private static final boolean DEBUG = false;
    private static final String TAG = "ScreenDecorations";

@@ -93,13 +101,17 @@ public class ScreenDecorations extends SystemUI implements Tunable {
    public static final String PADDING = "sysui_rounded_content_padding";
    private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS =
            SystemProperties.getBoolean("debug.screenshot_rounded_corners", false);
    private static final boolean VERBOSE = false;

    private DisplayManager mDisplayManager;
    private DisplayManager.DisplayListener mDisplayListener;

    @VisibleForTesting protected int mRoundedDefault;
    @VisibleForTesting protected int mRoundedDefaultTop;
    @VisibleForTesting protected int mRoundedDefaultBottom;
    @VisibleForTesting
    protected int mRoundedDefault;
    @VisibleForTesting
    protected int mRoundedDefaultTop;
    @VisibleForTesting
    protected int mRoundedDefaultBottom;
    private View mOverlay;
    private View mBottomOverlay;
    private float mDensity;
@@ -111,6 +123,8 @@ public class ScreenDecorations extends SystemUI implements Tunable {
    private SecureSetting mColorInversionSetting;
    private boolean mPendingRotationChange;
    private Handler mHandler;
    private boolean mAssistHintBlocked = false;
    private boolean mIsReceivingNavBarColor = false;

    /**
     * Converts a set of {@link Rect}s into a {@link Region}
@@ -137,15 +151,32 @@ public class ScreenDecorations extends SystemUI implements Tunable {
        putComponent(ScreenDecorations.class, this);
    }

    private void fade(View view, boolean fadeIn) {
    private void fade(View view, boolean fadeIn, boolean isLeft) {
        if (fadeIn) {
            view.animate().cancel();
            view.setAlpha(0f);
            view.setAlpha(1f);
            view.setVisibility(View.VISIBLE);
            view.animate().alpha(1f);

            AnimationSet anim = new AnimationSet(true);
            anim.setDuration(900);

            Animation scaleAnimation = new ScaleAnimation(2f, 1f, 2f, 1f,
                    ScaleAnimation.RELATIVE_TO_SELF, 0.5f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
            anim.addAnimation(scaleAnimation);
            anim.setInterpolator(new PathInterpolator(0.02f, 0.44f, 0.67f, 1.00f));

            Animation translateAnimation = new TranslateAnimation(
                    TranslateAnimation.RELATIVE_TO_SELF, isLeft ? -0.2f : 0.2f,
                    TranslateAnimation.RELATIVE_TO_SELF,
                    0f,
                    TranslateAnimation.RELATIVE_TO_SELF, 0.2f, TranslateAnimation.RELATIVE_TO_SELF,
                    0f);
            anim.addAnimation(translateAnimation);
            anim.setInterpolator(new OvershootInterpolator());
            view.startAnimation(anim);
        } else {
            view.animate().cancel();
            view.animate().alpha(0f).withEndAction(() -> view.setVisibility(View.INVISIBLE));
            view.animate().setDuration(400).alpha(0f);
        }

    }
@@ -161,35 +192,59 @@ public class ScreenDecorations extends SystemUI implements Tunable {
            return;
        }

        if (mAssistHintBlocked && visible) {
            if (VERBOSE) {
                Log.v(TAG, "Assist hint blocked, cannot make it visible");
            }
            return;
        }

        if (mAssistHintVisible != visible) {
            mAssistHintVisible = visible;

            View assistHintTopLeft = mOverlay.findViewById(R.id.assist_hint_left);
            View assistHintTopRight = mOverlay.findViewById(R.id.assist_hint_right);
            View assistHintBottomLeft = mBottomOverlay.findViewById(R.id.assist_hint_left);
            View assistHintBottomRight = mBottomOverlay.findViewById(R.id.assist_hint_right);
            CornerHandleView assistHintTopLeft = mOverlay.findViewById(R.id.assist_hint_left);
            CornerHandleView assistHintTopRight = mOverlay.findViewById(R.id.assist_hint_right);
            CornerHandleView assistHintBottomLeft = mBottomOverlay.findViewById(
                    R.id.assist_hint_left);
            CornerHandleView assistHintBottomRight = mBottomOverlay.findViewById(
                    R.id.assist_hint_right);

            switch (mRotation) {
                case RotationUtils.ROTATION_NONE:
                    fade(assistHintBottomLeft, mAssistHintVisible);
                    fade(assistHintBottomRight, mAssistHintVisible);
                    fade(assistHintBottomLeft, mAssistHintVisible, /* isLeft = */ true);
                    fade(assistHintBottomRight, mAssistHintVisible, /* isLeft = */ false);
                    break;
                case RotationUtils.ROTATION_LANDSCAPE:
                    fade(assistHintTopRight, mAssistHintVisible);
                    fade(assistHintBottomRight, mAssistHintVisible);
                    fade(assistHintTopRight, mAssistHintVisible, /* isLeft = */ true);
                    fade(assistHintBottomRight, mAssistHintVisible, /* isLeft = */ false);
                    break;
                case RotationUtils.ROTATION_SEASCAPE:
                    fade(assistHintTopLeft, mAssistHintVisible);
                    fade(assistHintBottomLeft, mAssistHintVisible);
                    fade(assistHintTopLeft, mAssistHintVisible, /* isLeft = */ false);
                    fade(assistHintBottomLeft, mAssistHintVisible,  /* isLeft = */ true);
                    break;
                case RotationUtils.ROTATION_UPSIDE_DOWN:
                    fade(assistHintTopLeft, mAssistHintVisible);
                    fade(assistHintTopRight, mAssistHintVisible);
                    fade(assistHintTopLeft, mAssistHintVisible, /* isLeft = */ false);
                    fade(assistHintTopRight, mAssistHintVisible, /* isLeft = */ true);
                    break;
            }
        }
    }

    /**
     * Prevents the assist hint from becoming visible even if `mAssistHintVisible` is true.
     */
    public void setAssistHintBlocked(boolean blocked) {
        if (!mHandler.getLooper().isCurrentThread()) {
            mHandler.post(() -> setAssistHintBlocked(blocked));
            return;
        }

        mAssistHintBlocked = blocked;
        if (mAssistHintVisible && mAssistHintBlocked) {
            setAssistHintVisible(false);
        }
    }

    @VisibleForTesting
    Handler startHandlerThread() {
        HandlerThread thread = new HandlerThread("ScreenDecorations");
@@ -416,7 +471,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
        } else if (mRotation == RotationUtils.ROTATION_LANDSCAPE) {
            updateView(topLeft, Gravity.TOP | Gravity.LEFT, 0);
            updateView(topRight, Gravity.BOTTOM | Gravity.LEFT, 270);
            updateView(bottomLeft, Gravity.TOP | Gravity.RIGHT, 90);;
            updateView(bottomLeft, Gravity.TOP | Gravity.RIGHT, 90);
            updateView(bottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
        } else if (mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) {
            updateView(topLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
@@ -613,6 +668,10 @@ public class ScreenDecorations extends SystemUI implements Tunable {
                setSize(mOverlay.findViewById(R.id.right), sizeTop);
                setSize(mBottomOverlay.findViewById(R.id.left), sizeBottom);
                setSize(mBottomOverlay.findViewById(R.id.right), sizeBottom);
                setSize(mOverlay.findViewById(R.id.assist_hint_left), sizeTop * 2);
                setSize(mOverlay.findViewById(R.id.assist_hint_right), sizeTop * 2);
                setSize(mBottomOverlay.findViewById(R.id.assist_hint_left), sizeBottom * 2);
                setSize(mBottomOverlay.findViewById(R.id.assist_hint_right), sizeBottom * 2);
            }
        });
    }
@@ -624,6 +683,27 @@ public class ScreenDecorations extends SystemUI implements Tunable {
        view.setLayoutParams(params);
    }

    @Override
    public void onDarkIntensity(float darkIntensity) {
        if (mOverlay != null) {
            CornerHandleView assistHintTopLeft = mOverlay.findViewById(R.id.assist_hint_left);
            CornerHandleView assistHintTopRight = mOverlay.findViewById(R.id.assist_hint_right);

            assistHintTopLeft.updateDarkness(darkIntensity);
            assistHintTopRight.updateDarkness(darkIntensity);
        }

        if (mBottomOverlay != null) {
            CornerHandleView assistHintBottomLeft = mBottomOverlay.findViewById(
                    R.id.assist_hint_left);
            CornerHandleView assistHintBottomRight = mBottomOverlay.findViewById(
                    R.id.assist_hint_right);

            assistHintBottomLeft.updateDarkness(darkIntensity);
            assistHintBottomRight.updateDarkness(darkIntensity);
        }
    }

    @VisibleForTesting
    static class TunablePaddingTagListener implements FragmentListener {

+8 −1
Original line number Diff line number Diff line
@@ -86,6 +86,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.ScreenDecorations;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.fragments.FragmentHostManager;
@@ -170,6 +171,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
    public int mDisplayId;
    private boolean mIsOnDefaultDisplay;
    public boolean mHomeBlockedThisTouch;
    private ScreenDecorations mScreenDecorations;

    private Handler mHandler = Dependency.get(Dependency.MAIN_HANDLER);

@@ -348,12 +350,17 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
            mDisabledFlags2 |= StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS;
        }
        setDisabled2Flags(mDisabledFlags2);

        mScreenDecorations = SysUiServiceProvider.getComponent(getContext(),
                ScreenDecorations.class);
        getBarTransitions().addDarkIntensityListener(mScreenDecorations);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        if (mNavigationBarView != null) {
            mNavigationBarView.getBarTransitions().removeDarkIntensityListener(mScreenDecorations);
            mNavigationBarView.getBarTransitions().destroy();
            mNavigationBarView.getLightTransitionsController().destroy(getContext());
        }
@@ -1020,7 +1027,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
        getBarTransitions().transitionTo(barMode, animate);
    }

    private BarTransitions getBarTransitions() {
    public NavigationBarTransitions getBarTransitions() {
        return mNavigationBarView.getBarTransitions();
    }

Loading