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

Commit f47630e7 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Prevent clipping clock during shrink, bold transition" into qt-dev

parents 94a58df8 7cfa9ddc
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -48,16 +48,16 @@
             android:id="@+id/default_clock_view_bold"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_gravity="center_horizontal"
             android:layout_gravity="bottom|center_horizontal"
             android:gravity="center_horizontal"
             android:letterSpacing="0.03"
             android:textColor="?attr/wallpaperTextColor"
             android:singleLine="true"
             style="@style/widget_big_bold"
             style="@style/widget_title_bold"
             android:format12Hour="@string/keyguard_widget_12_hours_format"
             android:format24Hour="@string/keyguard_widget_24_hours_format"
             android:elegantTextHeight="false"
             android:visibility="gone"
             android:visibility="invisible"
             />
    </FrameLayout>
    <include layout="@layout/keyguard_status_area"
+4 −4
Original line number Diff line number Diff line
@@ -66,16 +66,16 @@
        <item name="android:fontFeatureSettings">@*android:string/config_headlineFontFeatureSettings</item>
        <item name="android:ellipsize">none</item>
    </style>
    <style name="widget_big_bold">
    <style name="widget_title_bold">
        <item name="android:textStyle">bold</item>
        <item name="android:textSize">@dimen/widget_big_font_size</item>
        <item name="android:paddingBottom">@dimen/bottom_text_spacing_digital</item>
        <item name="android:textSize">@dimen/widget_title_font_size</item>
        <item name="android:paddingBottom">@dimen/widget_vertical_padding_clock</item>
        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
        <item name="android:ellipsize">none</item>
    </style>
    <style name="widget_small_bold">
        <item name="android:textStyle">bold</item>
        <item name="android:textSize">@dimen/widget_title_font_size</item>
        <item name="android:textSize">@dimen/widget_small_font_size</item>
        <item name="android:paddingBottom">@dimen/bottom_text_spacing_digital</item>
        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
        <item name="android:ellipsize">none</item>
+142 −87
Original line number Diff line number Diff line
@@ -3,22 +3,20 @@ package com.android.keyguard;
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.os.Build;
import android.transition.ChangeBounds;
import android.transition.Transition;
import android.transition.TransitionListenerAdapter;
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.transition.TransitionValues;
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -51,6 +49,11 @@ public class KeyguardClockSwitch extends RelativeLayout {

    private static final String TAG = "KeyguardClockSwitch";

    /**
     * Animation fraction when text is transitioned to/from bold.
     */
    private static final float TO_BOLD_TRANSITION_FRACTION = 0.7f;

    /**
     * Controller used to track StatusBar state to know when to show the big_clock_container.
     */
@@ -71,10 +74,8 @@ public class KeyguardClockSwitch extends RelativeLayout {
     */
    private final Transition mTransition;

    /**
     * Listener for layout transitions.
     */
    private final Transition.TransitionListener mTransitionListener;
    private final ClockVisibilityTransition mClockTransition;
    private final ClockVisibilityTransition mBoldClockTransition;

    /**
     * Optional/alternative clock injected via plugin.
@@ -156,8 +157,19 @@ public class KeyguardClockSwitch extends RelativeLayout {
        mStatusBarState = mStatusBarStateController.getState();
        mSysuiColorExtractor = colorExtractor;
        mClockManager = clockManager;
        mTransition = new ClockBoundsTransition();
        mTransitionListener = new ClockBoundsTransitionListener();

        mClockTransition = new ClockVisibilityTransition().setCutoff(
                1 - TO_BOLD_TRANSITION_FRACTION);
        mClockTransition.addTarget(R.id.default_clock_view);
        mBoldClockTransition = new ClockVisibilityTransition().setCutoff(
                TO_BOLD_TRANSITION_FRACTION);
        mBoldClockTransition.addTarget(R.id.default_clock_view_bold);
        mTransition = new TransitionSet()
                .setOrdering(TransitionSet.ORDERING_TOGETHER)
                .addTransition(mClockTransition)
                .addTransition(mBoldClockTransition)
                .setDuration(KeyguardSliceView.DEFAULT_ANIM_DURATION / 2)
                .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
    }

    /**
@@ -182,7 +194,6 @@ public class KeyguardClockSwitch extends RelativeLayout {
        mClockManager.addOnClockChangedListener(mClockChangedListener);
        mStatusBarStateController.addCallback(mStateListener);
        mSysuiColorExtractor.addOnColorsChangedListener(mColorsListener);
        mTransition.addListener(mTransitionListener);
        updateColors();
    }

@@ -192,7 +203,6 @@ public class KeyguardClockSwitch extends RelativeLayout {
        mClockManager.removeOnClockChangedListener(mClockChangedListener);
        mStatusBarStateController.removeCallback(mStateListener);
        mSysuiColorExtractor.removeOnColorsChangedListener(mColorsListener);
        mTransition.removeListener(mTransitionListener);
        setClockPlugin(null);
    }

@@ -287,7 +297,6 @@ public class KeyguardClockSwitch extends RelativeLayout {

    public void setTextSize(int unit, float size) {
        mClockView.setTextSize(unit, size);
        mClockViewBold.setTextSize(unit, size);
    }

    public void setFormat12Hour(CharSequence format) {
@@ -390,23 +399,46 @@ public class KeyguardClockSwitch extends RelativeLayout {
     * Sets if the keyguard slice is showing a center-aligned header. We need a smaller clock in
     * these cases.
     */
    public void setKeyguardShowingHeader(boolean hasHeader) {
    void setKeyguardShowingHeader(boolean hasHeader) {
        if (mShowingHeader == hasHeader || hasCustomClock()) {
            return;
        }
        mShowingHeader = hasHeader;

        float smallFontSize = mContext.getResources().getDimensionPixelSize(
                R.dimen.widget_small_font_size);
        float bigFontSize = mContext.getResources().getDimensionPixelSize(
                R.dimen.widget_big_font_size);
        mClockTransition.setScale(smallFontSize / bigFontSize);
        mBoldClockTransition.setScale(bigFontSize / smallFontSize);

        TransitionManager.beginDelayedTransition((ViewGroup) mClockView.getParent(), mTransition);
        int fontSize = mContext.getResources().getDimensionPixelSize(mShowingHeader
                ? R.dimen.widget_small_font_size : R.dimen.widget_big_font_size);
        int paddingBottom = mContext.getResources().getDimensionPixelSize(mShowingHeader
        mClockView.setVisibility(hasHeader ? View.INVISIBLE : View.VISIBLE);
        mClockViewBold.setVisibility(hasHeader ? View.VISIBLE : View.INVISIBLE);
        int paddingBottom = mContext.getResources().getDimensionPixelSize(hasHeader
                ? R.dimen.widget_vertical_padding_clock : R.dimen.header_subtitle_padding);
        mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
        mClockViewBold.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
        mClockView.setPadding(mClockView.getPaddingLeft(), mClockView.getPaddingTop(),
                mClockView.getPaddingRight(), paddingBottom);
        mClockViewBold.setPadding(mClockViewBold.getPaddingLeft(), mClockViewBold.getPaddingTop(),
                mClockViewBold.getPaddingRight(), paddingBottom);

        if (hasHeader) {
            // After the transition, make the default clock GONE so that it doesn't make the
            // KeyguardStatusView appear taller in KeyguardClockPositionAlgorithm and elsewhere.
            mTransition.addListener(new TransitionListenerAdapter() {
                @Override
                public void onTransitionEnd(Transition transition) {
                    super.onTransitionEnd(transition);
                    // Check that header is actually showing. I saw issues where this event was
                    // fired after the big clock transitioned back to visible, which causes the time
                    // to completely disappear.
                    if (mShowingHeader) {
                        mClockView.setVisibility(View.GONE);
                    }
                    transition.removeListener(this);
                }
            });
        }
    }

    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
@@ -434,91 +466,114 @@ public class KeyguardClockSwitch extends RelativeLayout {
    }

    /**
     * Special layout transition that scales the clock view as its bounds change, to make it look
     * like the text is shrinking.
     * {@link Visibility} transformation that scales the view while it is disappearing/appearing and
     * transitions suddenly at a cutoff fraction during the animation.
     */
    private class ClockBoundsTransition extends ChangeBounds {
    private class ClockVisibilityTransition extends android.transition.Visibility {

        private static final String PROPNAME_VISIBILITY = "systemui:keyguard:visibility";

        private float mCutoff;
        private float mScale;

        /**
         * Animation fraction when text is transitioned to/from bold.
         * Constructs a transition that switches between visible/invisible at a cutoff and scales in
         * size while appearing/disappearing.
         */
        private static final float TO_BOLD_TRANSITION_FRACTION = 0.7f;

        ClockBoundsTransition() {
            setDuration(KeyguardSliceView.DEFAULT_ANIM_DURATION / 2);
            setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
        ClockVisibilityTransition() {
            setCutoff(1f);
            setScale(1f);
        }

        @Override
        public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
                TransitionValues endValues) {
            Animator animator = super.createAnimator(sceneRoot, startValues, endValues);
            if (animator == null || startValues.view != mClockView) {
                return animator;
        /**
         * Sets the transition point between visible/invisible.
         *
         * @param cutoff The fraction in [0, 1] when the view switches between visible/invisible.
         * @return This transition object
         */
        public ClockVisibilityTransition setCutoff(float cutoff) {
            mCutoff = cutoff;
            return this;
        }

            ValueAnimator boundsAnimator = null;
            if (animator instanceof AnimatorSet) {
                Animator first = ((AnimatorSet) animator).getChildAnimations().get(0);
                if (first instanceof ValueAnimator) {
                    boundsAnimator = (ValueAnimator) first;
                }
            } else if (animator instanceof ValueAnimator) {
                boundsAnimator = (ValueAnimator) animator;
            }

            if (boundsAnimator != null) {
                float bigFontSize = mContext.getResources()
                        .getDimensionPixelSize(R.dimen.widget_big_font_size);
                float smallFontSize = mContext.getResources()
                        .getDimensionPixelSize(R.dimen.widget_small_font_size);
                float startScale = mShowingHeader
                        ? bigFontSize / smallFontSize : smallFontSize / bigFontSize;
                final int normalViewVisibility = mShowingHeader ? View.INVISIBLE : View.VISIBLE;
                final int boldViewVisibility = mShowingHeader ? View.VISIBLE : View.INVISIBLE;
                final float boldTransitionFraction = mShowingHeader ? TO_BOLD_TRANSITION_FRACTION :
                        1f - TO_BOLD_TRANSITION_FRACTION;
                boundsAnimator.addUpdateListener(animation -> {
                    final float fraction = animation.getAnimatedFraction();
                    if (fraction > boldTransitionFraction) {
                        mClockView.setVisibility(normalViewVisibility);
                        mClockViewBold.setVisibility(boldViewVisibility);
                    }
                    float scale = MathUtils.lerp(startScale, 1f /* stop */,
                            animation.getAnimatedFraction());
                    mClockView.setPivotX(mClockView.getWidth() / 2f);
                    mClockViewBold.setPivotX(mClockViewBold.getWidth() / 2f);
                    mClockView.setPivotY(0);
                    mClockViewBold.setPivotY(0);
                    mClockView.setScaleX(scale);
                    mClockViewBold.setScaleX(scale);
                    mClockView.setScaleY(scale);
                    mClockViewBold.setScaleY(scale);
                });
        /**
         * Sets the scale factor applied while appearing/disappearing.
         *
         * @param scale Scale factor applied while appearing/disappearing. When factor is less than
         *              one, the view will shrink while disappearing. When it is greater than one,
         *              the view will expand while disappearing.
         * @return This transition object
         */
        public ClockVisibilityTransition setScale(float scale) {
            mScale = scale;
            return this;
        }

            return animator;
        @Override
        public void captureStartValues(TransitionValues transitionValues) {
            super.captureStartValues(transitionValues);
            captureVisibility(transitionValues);
        }

        @Override
        public void captureEndValues(TransitionValues transitionValues) {
            super.captureStartValues(transitionValues);
            captureVisibility(transitionValues);
        }

    /**
     * Transition listener for layout transition that scales the clock view.
     */
    private class ClockBoundsTransitionListener extends TransitionListenerAdapter {
        private void captureVisibility(TransitionValues transitionValues) {
            transitionValues.values.put(PROPNAME_VISIBILITY,
                    transitionValues.view.getVisibility());
        }

        @Override
        public void onTransitionEnd(Transition transition) {
            mClockView.setVisibility(mShowingHeader ? View.INVISIBLE : View.VISIBLE);
            mClockViewBold.setVisibility(mShowingHeader ? View.VISIBLE : View.INVISIBLE);
            mClockView.setScaleX(1f);
            mClockViewBold.setScaleX(1f);
            mClockView.setScaleY(1f);
            mClockViewBold.setScaleY(1f);
        public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
                TransitionValues endValues) {
            final float cutoff = mCutoff;
            final int startVisibility = View.INVISIBLE;
            final int endVisibility = (int) endValues.values.get(PROPNAME_VISIBILITY);
            final float startScale = mScale;
            final float endScale = 1f;
            return createAnimator(view, cutoff, startVisibility, endVisibility, startScale,
                    endScale);
        }

        @Override
        public void onTransitionCancel(Transition transition) {
            onTransitionEnd(transition);
        public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
                TransitionValues endValues) {
            final float cutoff = 1f - mCutoff;
            final int startVisibility = View.VISIBLE;
            final int endVisibility = (int) endValues.values.get(PROPNAME_VISIBILITY);
            final float startScale = 1f;
            final float endScale = mScale;
            return createAnimator(view, cutoff, startVisibility, endVisibility, startScale,
                    endScale);
        }

        private Animator createAnimator(View view, float cutoff, int startVisibility,
                int endVisibility, float startScale, float endScale) {
            view.setPivotY(view.getHeight() - view.getPaddingBottom());
            view.setVisibility(startVisibility);
            ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
            animator.addUpdateListener(animation -> {
                final float fraction = animation.getAnimatedFraction();
                if (fraction > cutoff) {
                    view.setVisibility(endVisibility);
                }
                final float scale = MathUtils.lerp(startScale, endScale, fraction);
                view.setScaleX(scale);
                view.setScaleY(scale);
            });
            addListener(new TransitionListenerAdapter() {
                @Override
                public void onTransitionEnd(Transition transition) {
                    view.setVisibility(endVisibility);
                    view.setScaleX(1f);
                    view.setScaleY(1f);
                    transition.removeListener(this);
                }
            });
            return animator;
        }
    }
}