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

Commit 192b7c9e authored by Beverly's avatar Beverly
Browse files

Add support for LS Clock to use > 1 Paint object

Updated TextInterpolator and TextAnimator to support a
Paint object for each line of text. Now we can interpolate between
the colors when animating.

Clock colors are now based on the theme (for now, wallpaper text
color). ie: first line (of vertical clock) is one color, second line is
another color.

Also adjusted Keyguard clock logic to animate on doze changes instead of
animating incrementally on setDarkAmount.

Test: atest TextInterpolatorTest TextAnimatorTest
Bug: 170228350
Change-Id: Ia62f2de48534be2007afc5a95eac46b3c44ec880
parent 06f75983
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -69,8 +69,8 @@
        android:layout_alignParentTop="true"
        android:layout_marginBottom="24dp"
        android:visibility="gone">
        <com.android.keyguard.GradientTextClock
            android:id="@+id/gradient_clock_view"
        <com.android.keyguard.AnimatableClockView
            android:id="@+id/animatable_clock_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="80dp"
+81 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.keyguard;

import android.graphics.Color;

import com.android.settingslib.Utils;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.util.ViewController;

/**
 * Controls the color of a GradientTextClock.
 */
public class AnimatableClockController extends ViewController<AnimatableClockView> {

    private final StatusBarStateController mStatusBarStateController;
    private final int[] mDozingColors = new int[] {Color.WHITE, Color.WHITE};
    private int[] mLockScreenColors = new int[2];

    private boolean mIsDozing;

    public AnimatableClockController(
            AnimatableClockView view,
            StatusBarStateController statusBarStateController) {
        super(view);
        mStatusBarStateController = statusBarStateController;
        mIsDozing = mStatusBarStateController.isDozing();
    }

    @Override
    protected void onViewAttached() {
        mStatusBarStateController.addCallback(mStatusBarStateListener);
        mIsDozing = mStatusBarStateController.isDozing();
        refreshTime();
        initColors();
    }

    @Override
    protected void onViewDetached() {
        mStatusBarStateController.removeCallback(mStatusBarStateListener);
    }

    /**
     * Updates the time for this view.
     */
    public void refreshTime() {
        mView.refreshTime();
    }

    private void initColors() {
        mLockScreenColors[0] = Utils.getColorAttrDefaultColor(getContext(),
                com.android.systemui.R.attr.wallpaperTextColor);
        mLockScreenColors[1] = Utils.getColorAttrDefaultColor(getContext(),
                        com.android.systemui.R.attr.wallpaperTextColorSecondary);
        mView.setColors(mDozingColors, mLockScreenColors);
        mView.animateDoze(mIsDozing, false);
    }

    private final StatusBarStateController.StateListener mStatusBarStateListener =
            new StatusBarStateController.StateListener() {
                @Override
                public void onDozingChanged(boolean isDozing) {
                    mIsDozing = isDozing;
                    mView.animateDoze(mIsDozing, true);
                }
            };
}
+158 −0
Original line number Diff line number Diff line
@@ -20,10 +20,12 @@ import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Shader;
import android.icu.text.DateTimePatternGenerator;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.widget.TextClock;
import android.widget.TextView;

import java.util.Calendar;

import kotlin.Unit;

@@ -31,64 +33,70 @@ import kotlin.Unit;
 * Displays the time with the hour positioned above the minutes. (ie: 09 above 30 is 9:30)
 * The time's text color is a gradient that changes its colors based on its controller.
 */
public class GradientTextClock extends TextClock {
    private int[] mGradientColors;
    private float[] mPositions;
public class AnimatableClockView extends TextView {
    private static final CharSequence FORMAT_12_HOUR = "hh\nmm";
    private static final CharSequence FORMAT_24_HOUR = "HH\nmm";
    private static final long ANIM_DURATION = 300;

    private CharSequence mFormat;
    private CharSequence mDescFormat;
    private Calendar mTime = Calendar.getInstance();
    private int[] mDozingColors;
    private int[] mLockScreenColors;

    private TextAnimator mTextAnimator = null;
    private Runnable mOnTextAnimatorInitialized;

    public GradientTextClock(Context context) {
    public AnimatableClockView(Context context) {
        this(context, null, 0, 0);
    }

    public GradientTextClock(Context context, AttributeSet attrs) {
    public AnimatableClockView(Context context, AttributeSet attrs) {
        this(context, attrs, 0, 0);
    }

    public GradientTextClock(Context context, AttributeSet attrs, int defStyleAttr) {
    public AnimatableClockView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public GradientTextClock(Context context, AttributeSet attrs, int defStyleAttr,
    public AnimatableClockView(Context context, AttributeSet attrs, int defStyleAttr,
            int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        updateTimeFormat();
    }

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        addOnLayoutChangeListener(mOnLayoutChangeListener);
        updateTimeFormat();
    }

    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        removeOnLayoutChangeListener(mOnLayoutChangeListener);
    }

    @Override
    public void refreshTime() {
        super.refreshTime();
    }

    @Override
    public void setFormat12Hour(CharSequence format) {
        super.setFormat12Hour(FORMAT_12);
    }

    @Override
    public void setFormat24Hour(CharSequence format) {
        super.setFormat24Hour(FORMAT_24);
    void refreshTime() {
        mTime.setTimeInMillis(System.currentTimeMillis());
        setText(DateFormat.format(mFormat, mTime));
        setContentDescription(DateFormat.format(mDescFormat, mTime));
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (mTextAnimator == null) {
            mTextAnimator = new TextAnimator(getLayout(), () -> {
            mTextAnimator = new TextAnimator(
                    getLayout(),
                    () -> {
                        invalidate();
                        return Unit.INSTANCE;
            });
                    },
                    2 /* number of lines (each can have a unique Paint) */);
            if (mOnTextAnimatorInitialized != null) {
                mOnTextAnimatorInitialized.run();
                mOnTextAnimatorInitialized = null;
            }
        } else {
            mTextAnimator.updateLayout(getLayout());
        }
@@ -99,51 +107,52 @@ public class GradientTextClock extends TextClock {
        mTextAnimator.draw(canvas);
    }

    public void setGradientColors(int[] colors) {
        mGradientColors = colors;
        updatePaint();
    void setColors(int[] dozingColors, int[] lockScreenColors) {
        mDozingColors = dozingColors;
        mLockScreenColors = lockScreenColors;
    }

    public void setColorPositions(float[] positions) {
        mPositions = positions;
    void animateDoze(boolean isDozing, boolean animate) {
        setTextStyle(isDozing ? 100 : 300 /* weight */,
                -1,
                isDozing ? mDozingColors : mLockScreenColors,
                animate);
    }

    /**
     * Set text style with animation.
     * Set text style with an optional animation.
     *
     * By passing -1 to weight, the view preserve the current weight.
     * By passing -1 to textSize, the view preserve the current text size.
     * By passing -1 to weight, the view preserves its current weight.
     * By passing -1 to textSize, the view preserves its current text size.
     *
     * @param weight text weight.
     * @param textSize font size.
     * @param animate true for changing text style with animation, otherwise false.
     * @param animate true to animate the text style change, otherwise false.
     */
    public void setTextStyle(
    private void setTextStyle(
            @IntRange(from = 0, to = 1000) int weight,
            @FloatRange(from = 0) float textSize,
            int[] colors,
            boolean animate) {
        if (mTextAnimator != null) {
            mTextAnimator.setTextStyle(weight, textSize, animate, -1, null);
            mTextAnimator.setTextStyle(weight, textSize, colors, animate, ANIM_DURATION, null);
        } else {
            // when the text animator is set, update its start values
            mOnTextAnimatorInitialized =
                    () -> mTextAnimator.setTextStyle(
                            weight, textSize, colors, false, ANIM_DURATION, null);
        }
    }

    private void updatePaint() {
        Shader shader = new LinearGradient(
                getX(), getY(), getX(), getMeasuredHeight() + getY(), mGradientColors, mPositions,
                Shader.TileMode.REPEAT);
        getPaint().setShader(shader);
        if (mTextAnimator != null) {
            mTextAnimator.setShader(shader);
        }
    private void updateTimeFormat() {
        final boolean use24HourFormat = DateFormat.is24HourFormat(getContext());
        mFormat =  use24HourFormat ? FORMAT_24_HOUR : FORMAT_12_HOUR;
        mDescFormat = getBestDateTimePattern(getContext(), use24HourFormat ? "Hm" : "hm");
    }

    private final OnLayoutChangeListener mOnLayoutChangeListener =
            (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
                if (bottom != oldBottom || top != oldTop) {
                    updatePaint();
    private static String getBestDateTimePattern(Context context, String skeleton) {
        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(
                context.getResources().getConfiguration().locale);
        return dtpg.getBestPattern(skeleton);
    }
            };

    public static final CharSequence FORMAT_12 = "hh\nmm";
    public static final CharSequence FORMAT_24 = "HH\nmm";
}
+0 −10
Original line number Diff line number Diff line
@@ -72,11 +72,6 @@ public class KeyguardClockSwitch extends RelativeLayout {
     */
    private TextClock mClockViewBold;

    /**
     * Gradient clock for usage when mode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL.
     */
    private TimeBasedColorsClockController mNewLockscreenClockViewController;

    /**
     * Frame for clock when mode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL.
     */
@@ -157,7 +152,6 @@ public class KeyguardClockSwitch extends RelativeLayout {
            setPaddingRelative(startEndPadding, 0, startEndPadding, 0);
            mSmallClockFrame.setVisibility(GONE);
            mNewLockscreenClockFrame.setVisibility(VISIBLE);
            mNewLockscreenClockViewController.init();

            statusAreaLP.removeRule(RelativeLayout.BELOW);
            statusAreaLP.addRule(RelativeLayout.LEFT_OF, R.id.new_lockscreen_clock_view);
@@ -181,8 +175,6 @@ public class KeyguardClockSwitch extends RelativeLayout {
        mClockView = findViewById(R.id.default_clock_view);
        mClockViewBold = findViewById(R.id.default_clock_view_bold);
        mNewLockscreenClockFrame = findViewById(R.id.new_lockscreen_clock_view);
        mNewLockscreenClockViewController =
                new TimeBasedColorsClockController(findViewById(R.id.gradient_clock_view));
        mSmallClockFrame = findViewById(R.id.clock_view);
        mKeyguardStatusArea = findViewById(R.id.keyguard_status_area);
    }
@@ -305,7 +297,6 @@ public class KeyguardClockSwitch extends RelativeLayout {
        if (mClockPlugin != null) {
            mClockPlugin.setDarkAmount(darkAmount);
        }
        mNewLockscreenClockViewController.setDarkAmount(darkAmount);
        updateBigClockAlpha();
    }

@@ -356,7 +347,6 @@ public class KeyguardClockSwitch extends RelativeLayout {
     * Refresh the time of the clock, due to either time tick broadcast or doze time tick alarm.
     */
    public void refresh() {
        mNewLockscreenClockViewController.refreshTime(System.currentTimeMillis());
        mClockView.refreshTime();
        mClockViewBold.refreshTime();
        if (mClockPlugin != null) {
+24 −4
Original line number Diff line number Diff line
@@ -55,7 +55,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
    private final ClockManager mClockManager;
    private final KeyguardSliceViewController mKeyguardSliceViewController;
    private final NotificationIconAreaController mNotificationIconAreaController;
    private FrameLayout mNewLockscreenClockFrame;

    /**
     * Gradient clock for usage when mode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL.
     */
    private AnimatableClockController mNewLockScreenClockViewController;
    private FrameLayout mNewLockScreenClockFrame;

    private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;

@@ -119,7 +124,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
        mColorExtractor.addOnColorsChangedListener(mColorsListener);
        mView.updateColors(getGradientColors());
        updateAodIcons();
        mNewLockscreenClockFrame = mView.findViewById(R.id.new_lockscreen_clock_view);
        mNewLockScreenClockFrame = mView.findViewById(R.id.new_lockscreen_clock_view);
    }

    @Override
@@ -182,6 +187,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
     * Refresh clock. Called in response to TIME_TICK broadcasts.
     */
    void refresh() {
        if (mNewLockScreenClockViewController != null) {
            mNewLockScreenClockViewController.refreshTime();
        }

        mView.refresh();
    }

@@ -192,8 +201,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
     */
    void updatePosition(int x, AnimationProperties props, boolean animate) {
        x = Math.abs(x);
        if (mNewLockscreenClockFrame != null) {
            PropertyAnimator.setProperty(mNewLockscreenClockFrame, AnimatableProperty.TRANSLATION_X,
        if (mNewLockScreenClockFrame != null) {
            PropertyAnimator.setProperty(mNewLockScreenClockFrame, AnimatableProperty.TRANSLATION_X,
                    -x, props, animate);
        }
        mKeyguardSliceViewController.updatePosition(x, props, animate);
@@ -205,6 +214,17 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
     */
    void updateLockScreenMode(int mode) {
        mLockScreenMode = mode;
        if (mode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
            if (mNewLockScreenClockViewController == null) {
                mNewLockScreenClockViewController =
                        new AnimatableClockController(
                                mView.findViewById(R.id.animatable_clock_view),
                                mStatusBarStateController);
                mNewLockScreenClockViewController.init();
            }
        } else {
            mNewLockScreenClockViewController = null;
        }
        mView.updateLockScreenMode(mLockScreenMode);
        updateAodIcons();
    }
Loading