From 4116599e26a4fb4f0c7e7671393734c11db892d6 Mon Sep 17 00:00:00 2001 From: Beverly Date: Mon, 19 Oct 2020 11:42:01 -0400 Subject: [PATCH] Add gradient clock to AOD/LS behind LS flag Also updates "new lockscreen" smart space layout. Currently TimeBasedColorsClockController will change the TextClock's colors based on hardcoded times. To toggle on the new lockscreen: adb shell settings put global show_new_lockscreen 1 Test: atest KeyguardSliceViewTest Test: manual Bug: 170228350 Change-Id: Ib72b45aad34fe4483538a9094154d6c473910612 --- .../layout/keyguard_clock_switch.xml | 22 +++ .../android/keyguard/GradientTextClock.java | 102 +++++++++++++ .../android/keyguard/KeyguardClockSwitch.java | 33 +++-- .../android/keyguard/KeyguardSliceView.java | 114 +++++++++++++- .../keyguard/KeyguardSliceViewController.java | 94 ++++++------ .../KeyguardStatusViewController.java | 1 + .../TimeBasedColorsClockController.java | 139 ++++++++++++++++++ 7 files changed, 445 insertions(+), 60 deletions(-) create mode 100644 packages/SystemUI/src/com/android/keyguard/GradientTextClock.java create mode 100644 packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index bf2963cd0b7f6..7e2e36a4907d4 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -61,6 +61,28 @@ android:visibility="invisible" /> + + + { + if (bottom != oldBottom || top != oldTop) { + updatePaint(); + } + }; + + public static final CharSequence FORMAT_12 = "hh\nmm"; + public static final CharSequence FORMAT_24 = "HH\nmm"; +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 272954df6dd64..cf363cc649f04 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -71,6 +71,16 @@ 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. + */ + private FrameLayout mNewLockscreenClockFrame; + /** * Frame for default and custom clock. */ @@ -137,23 +147,22 @@ public class KeyguardClockSwitch extends RelativeLayout { mLockScreenMode = mode; RelativeLayout.LayoutParams statusAreaLP = (RelativeLayout.LayoutParams) mKeyguardStatusArea.getLayoutParams(); - RelativeLayout.LayoutParams clockLP = (RelativeLayout.LayoutParams) - mSmallClockFrame.getLayoutParams(); if (mode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { - statusAreaLP.removeRule(RelativeLayout.BELOW); - statusAreaLP.addRule(RelativeLayout.LEFT_OF, R.id.clock_view); - statusAreaLP.addRule(RelativeLayout.ALIGN_PARENT_START); + mSmallClockFrame.setVisibility(GONE); + mNewLockscreenClockFrame.setVisibility(VISIBLE); + mNewLockscreenClockViewController.init(); - clockLP.addRule(RelativeLayout.ALIGN_PARENT_END); - clockLP.width = ViewGroup.LayoutParams.WRAP_CONTENT; + statusAreaLP.removeRule(RelativeLayout.BELOW); + statusAreaLP.addRule(RelativeLayout.LEFT_OF, R.id.new_lockscreen_clock_view); + statusAreaLP.addRule(RelativeLayout.ALIGN_PARENT_START); } else { + mSmallClockFrame.setVisibility(VISIBLE); + mNewLockscreenClockFrame.setVisibility(GONE); + statusAreaLP.removeRule(RelativeLayout.LEFT_OF); statusAreaLP.removeRule(RelativeLayout.ALIGN_PARENT_START); statusAreaLP.addRule(RelativeLayout.BELOW, R.id.clock_view); - - clockLP.removeRule(RelativeLayout.ALIGN_PARENT_END); - clockLP.width = ViewGroup.LayoutParams.MATCH_PARENT; } requestLayout(); @@ -164,6 +173,9 @@ public class KeyguardClockSwitch extends RelativeLayout { super.onFinishInflate(); 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); } @@ -336,6 +348,7 @@ 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) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index a479bca56c2ac..a9c06edf46ccb 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -33,9 +33,11 @@ import android.text.TextUtils; import android.text.TextUtils.TruncateAt; import android.util.AttributeSet; import android.util.TypedValue; +import android.view.Gravity; import android.view.View; import android.view.animation.Animation; import android.widget.LinearLayout; +import android.widget.RelativeLayout; import android.widget.TextView; import androidx.slice.SliceItem; @@ -55,8 +57,10 @@ import com.android.systemui.util.wakelock.KeepAwakeAnimationListener; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; /** * View visible under the clock on the lock screen and AoD. @@ -86,6 +90,8 @@ public class KeyguardSliceView extends LinearLayout { private float mRowWithHeaderTextSize; private View.OnClickListener mOnClickListener; + private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; + public KeyguardSliceView(Context context, AttributeSet attrs) { super(context, attrs); @@ -142,6 +148,40 @@ public class KeyguardSliceView extends LinearLayout { } } + /** + * Updates the lockscreen mode which may change the layout of the keyguard slice view. + */ + public void updateLockScreenMode(int mode) { + mLockScreenMode = mode; + if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { + // add top padding to better align with top of clock + final int topPadding = (int) TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 20, + getResources().getDisplayMetrics()); + mTitle.setPaddingRelative(0, topPadding, 0, 0); + mTitle.setGravity(Gravity.START); + setGravity(Gravity.START); + RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) getLayoutParams(); + lp.removeRule(RelativeLayout.CENTER_HORIZONTAL); + setLayoutParams(lp); + } else { + final int horizontalPaddingDpValue = (int) TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 44, + getResources().getDisplayMetrics() + ); + mTitle.setPaddingRelative(horizontalPaddingDpValue, 0, horizontalPaddingDpValue, 0); + mTitle.setGravity(Gravity.CENTER_HORIZONTAL); + setGravity(Gravity.CENTER_HORIZONTAL); + RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) getLayoutParams(); + lp.addRule(RelativeLayout.CENTER_HORIZONTAL); + setLayoutParams(lp); + } + mRow.setLockscreenMode(mode); + requestLayout(); + } + Map showSlice(RowContent header, List subItems) { Trace.beginSection("KeyguardSliceView#showSlice"); mHasHeader = header != null; @@ -166,6 +206,8 @@ public class KeyguardSliceView extends LinearLayout { final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it mRow.setVisibility(subItemsCount > 0 ? VISIBLE : GONE); LinearLayout.LayoutParams layoutParams = (LayoutParams) mRow.getLayoutParams(); + layoutParams.gravity = mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL + ? Gravity.START : Gravity.CENTER; layoutParams.topMargin = mHasHeader ? mRowWithHeaderPadding : mRowPadding; mRow.setLayoutParams(layoutParams); @@ -282,6 +324,7 @@ public class KeyguardSliceView extends LinearLayout { pw.println(" mTextColor: " + Integer.toHexString(mTextColor)); pw.println(" mDarkAmount: " + mDarkAmount); pw.println(" mHasHeader: " + mHasHeader); + pw.println(" mLockScreenMode: " + mLockScreenMode); } @Override @@ -291,6 +334,8 @@ public class KeyguardSliceView extends LinearLayout { } public static class Row extends LinearLayout { + private Set mKeyguardSliceTextViewSet = new HashSet(); + private int mLockScreenModeRow = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; /** * This view is visible in AOD, which means that the device will sleep if we @@ -361,12 +406,18 @@ public class KeyguardSliceView extends LinearLayout { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { View child = getChildAt(i); if (child instanceof KeyguardSliceTextView) { - ((KeyguardSliceTextView) child).setMaxWidth(width / 3); + if (mLockScreenModeRow == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { + ((KeyguardSliceTextView) child).setMaxWidth(Integer.MAX_VALUE); + } else { + ((KeyguardSliceTextView) child).setMaxWidth(width / 3); + } } } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @@ -384,6 +435,42 @@ public class KeyguardSliceView extends LinearLayout { public boolean hasOverlappingRendering() { return false; } + + @Override + public void addView(View view, int index) { + super.addView(view, index); + + if (view instanceof KeyguardSliceTextView) { + ((KeyguardSliceTextView) view).setLockScreenMode(mLockScreenModeRow); + mKeyguardSliceTextViewSet.add((KeyguardSliceTextView) view); + } + } + + @Override + public void removeView(View view) { + super.removeView(view); + if (view instanceof KeyguardSliceTextView) { + mKeyguardSliceTextViewSet.remove((KeyguardSliceTextView) view); + } + } + + /** + * Updates the lockscreen mode which may change the layout of this view. + */ + public void setLockscreenMode(int mode) { + mLockScreenModeRow = mode; + if (mLockScreenModeRow == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { + setOrientation(LinearLayout.VERTICAL); + setGravity(Gravity.START); + } else { + setOrientation(LinearLayout.HORIZONTAL); + setGravity(Gravity.CENTER); + } + + for (KeyguardSliceTextView textView : mKeyguardSliceTextViewSet) { + textView.setLockScreenMode(mLockScreenModeRow); + } + } } /** @@ -392,6 +479,7 @@ public class KeyguardSliceView extends LinearLayout { @VisibleForTesting static class KeyguardSliceTextView extends TextView implements ConfigurationController.ConfigurationListener { + private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; @StyleRes private static int sStyleId = R.style.TextAppearance_Keyguard_Secondary; @@ -432,9 +520,16 @@ public class KeyguardSliceView extends LinearLayout { private void updatePadding() { boolean hasText = !TextUtils.isEmpty(getText()); - int horizontalPadding = (int) getContext().getResources() + int padding = (int) getContext().getResources() .getDimension(R.dimen.widget_horizontal_padding) / 2; - setPadding(horizontalPadding, 0, horizontalPadding * (hasText ? 1 : -1), 0); + if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { + // orientation is vertical, so add padding to top & bottom + setPadding(0, padding, 0, padding * (hasText ? 1 : -1)); + } else { + // oreintation is horizontal, so add padding to left & right + setPadding(padding, 0, padding * (hasText ? 1 : -1), 0); + } + setCompoundDrawablePadding((int) mContext.getResources() .getDimension(R.dimen.widget_icon_padding)); } @@ -461,5 +556,18 @@ public class KeyguardSliceView extends LinearLayout { } } } + + /** + * Updates the lockscreen mode which may change the layout of this view. + */ + public void setLockScreenMode(int mode) { + mLockScreenMode = mode; + if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { + setGravity(Gravity.START); + } else { + setGravity(Gravity.CENTER); + } + updatePadding(); + } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java index 3be7e0a2b0b3a..8b55b06e5e5e4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java @@ -44,6 +44,7 @@ import com.android.systemui.keyguard.KeyguardSliceProvider; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; +import com.android.systemui.util.ViewController; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -55,10 +56,10 @@ import javax.inject.Inject; /** Controller for a {@link KeyguardSliceView}. */ @KeyguardStatusViewScope -public class KeyguardSliceViewController implements Dumpable { +public class KeyguardSliceViewController extends ViewController implements + Dumpable { private static final String TAG = "KeyguardSliceViewCtrl"; - private final KeyguardSliceView mView; private final ActivityStarter mActivityStarter; private final ConfigurationController mConfigurationController; private final TunerService mTunerService; @@ -68,41 +69,7 @@ public class KeyguardSliceViewController implements Dumpable { private Uri mKeyguardSliceUri; private Slice mSlice; private Map mClickActions; - - private final View.OnAttachStateChangeListener mOnAttachStateChangeListener = - new View.OnAttachStateChangeListener() { - - @Override - public void onViewAttachedToWindow(View v) { - - Display display = mView.getDisplay(); - if (display != null) { - mDisplayId = display.getDisplayId(); - } - mTunerService.addTunable(mTunable, Settings.Secure.KEYGUARD_SLICE_URI); - // Make sure we always have the most current slice - if (mDisplayId == DEFAULT_DISPLAY && mLiveData != null) { - mLiveData.observeForever(mObserver); - } - mConfigurationController.addCallback(mConfigurationListener); - mDumpManager.registerDumpable( - TAG + "@" + Integer.toHexString( - KeyguardSliceViewController.this.hashCode()), - KeyguardSliceViewController.this); - } - - @Override - public void onViewDetachedFromWindow(View v) { - - // TODO(b/117344873) Remove below work around after this issue be fixed. - if (mDisplayId == DEFAULT_DISPLAY) { - mLiveData.removeObserver(mObserver); - } - mTunerService.removeTunable(mTunable); - mConfigurationController.removeCallback(mConfigurationListener); - mDumpManager.unregisterDumpable(TAG); - } - }; + private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; TunerService.Tunable mTunable = (key, newValue) -> setupUri(newValue); @@ -133,24 +100,57 @@ public class KeyguardSliceViewController implements Dumpable { }; @Inject - public KeyguardSliceViewController(KeyguardSliceView keyguardSliceView, + public KeyguardSliceViewController( + KeyguardSliceView keyguardSliceView, ActivityStarter activityStarter, - ConfigurationController configurationController, TunerService tunerService, + ConfigurationController configurationController, + TunerService tunerService, DumpManager dumpManager) { - mView = keyguardSliceView; + super(keyguardSliceView); mActivityStarter = activityStarter; mConfigurationController = configurationController; mTunerService = tunerService; mDumpManager = dumpManager; } - /** Initialize the controller. */ - public void init() { - if (mView.isAttachedToWindow()) { - mOnAttachStateChangeListener.onViewAttachedToWindow(mView); + @Override + protected void onViewAttached() { + Display display = mView.getDisplay(); + if (display != null) { + mDisplayId = display.getDisplayId(); } - mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener); - mView.setOnClickListener(mOnClickListener); + mTunerService.addTunable(mTunable, Settings.Secure.KEYGUARD_SLICE_URI); + // Make sure we always have the most current slice + if (mDisplayId == DEFAULT_DISPLAY && mLiveData != null) { + mLiveData.observeForever(mObserver); + } + mConfigurationController.addCallback(mConfigurationListener); + mDumpManager.registerDumpable( + TAG + "@" + Integer.toHexString( + KeyguardSliceViewController.this.hashCode()), + KeyguardSliceViewController.this); + mView.updateLockScreenMode(mLockScreenMode); + } + + @Override + protected void onViewDetached() { + // TODO(b/117344873) Remove below work around after this issue be fixed. + if (mDisplayId == DEFAULT_DISPLAY) { + mLiveData.removeObserver(mObserver); + } + mTunerService.removeTunable(mTunable); + mConfigurationController.removeCallback(mConfigurationListener); + mDumpManager.unregisterDumpable( + TAG + "@" + Integer.toHexString( + KeyguardSliceViewController.this.hashCode())); + } + + /** + * Updates the lockscreen mode which may change the layout of the keyguard slice view. + */ + public void updateLockScreenMode(int mode) { + mLockScreenMode = mode; + mView.updateLockScreenMode(mLockScreenMode); } /** @@ -224,10 +224,10 @@ public class KeyguardSliceViewController implements Dumpable { Trace.endSection(); } - @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { pw.println(" mSlice: " + mSlice); pw.println(" mClickActions: " + mClickActions); + pw.println(" mLockScreenMode: " + mLockScreenMode); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 0efb5eea42174..7705db4e31472 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -267,6 +267,7 @@ public class KeyguardStatusViewController extends ViewController { + private final int[] mGradientColors = new int[3]; + private final float[] mPositions = new float[3]; + + public TimeBasedColorsClockController(GradientTextClock view) { + super(view); + } + + @Override + protected void onViewAttached() { + refreshTime(System.currentTimeMillis()); + } + + @Override + protected void onViewDetached() { + + } + + /** + * Updates the time for this view. Also updates any color changes. + */ + public void refreshTime(long timeInMillis) { + Calendar now = new GregorianCalendar(); + now.setTimeInMillis(timeInMillis); + updateColors(now); + updatePositions(now); + mView.refreshTime(); + } + + private int getTimeIndex(Calendar now) { + int hour = now.get(Calendar.HOUR_OF_DAY); // 0 - 23 + if (hour < mTimes[0]) { + return mTimes.length - 1; + } + + for (int i = 1; i < mTimes.length; i++) { + if (hour < mTimes[i]) { + return i - 1; + } + } + + return mTimes.length - 1; + } + + private void updateColors(Calendar now) { + final int index = getTimeIndex(now); + for (int i = 0; i < mGradientColors.length; i++) { + mGradientColors[i] = COLORS[index][i]; + } + mView.setGradientColors(mGradientColors); + } + + private void updatePositions(Calendar now) { + final int index = getTimeIndex(now); + + final Calendar startTime = new GregorianCalendar(); + startTime.setTimeInMillis(now.getTimeInMillis()); + startTime.set(Calendar.HOUR_OF_DAY, mTimes[index]); + if (startTime.getTimeInMillis() > now.getTimeInMillis()) { + // start should be earlier than 'now' + startTime.add(Calendar.DATE, -1); + } + + final Calendar endTime = new GregorianCalendar(); + endTime.setTimeInMillis(now.getTimeInMillis()); + if (index == mTimes.length - 1) { + endTime.set(Calendar.HOUR_OF_DAY, mTimes[0]); + endTime.add(Calendar.DATE, 1); // end time is tomorrow + } else { + endTime.set(Calendar.HOUR_OF_DAY, mTimes[index + 1]); + } + + long totalTimeInThisColorGradient = endTime.getTimeInMillis() - startTime.getTimeInMillis(); + long timeIntoThisColorGradient = now.getTimeInMillis() - startTime.getTimeInMillis(); + float percentageWithinGradient = + (float) timeIntoThisColorGradient / (float) totalTimeInThisColorGradient; + + for (int i = 0; i < mPositions.length; i++) { + // currently hard-coded .3 movement of gradient + mPositions[i] = POSITIONS[index][i] - (.3f * percentageWithinGradient); + } + mView.setColorPositions(mPositions); + } + + private static final int[] SUNRISE = new int[] {0xFF6F75AA, 0xFFAFF0FF, 0xFFFFDEBF}; + private static final int[] DAY = new int[] {0xFF9BD8FB, 0xFFD7F5FF, 0xFFFFF278}; + private static final int[] NIGHT = new int[] {0xFF333D5E, 0xFFC5A1D6, 0xFF907359}; + + private static final float[] SUNRISE_START_POSITIONS = new float[] {.3f, .5f, .8f}; + private static final float[] DAY_START_POSITIONS = new float[] {.4f, .8f, 1f}; + private static final float[] NIGHT_START_POSITIONS = new float[] {.25f, .5f, .8f}; + + // TODO (b/170228350): use TwilightManager to set sunrise/sunset times + private final int mSunriseTime = 6; // 6am + private final int mDaytime = 9; // 9 am + private final int mNightTime = 19; // 7pm + + private int[] mTimes = new int[] { + mSunriseTime, + mDaytime, + mNightTime + }; + private static final int[][] COLORS = new int[][] { + SUNRISE, + DAY, + NIGHT + }; + private static final float[][] POSITIONS = new float[][] { + SUNRISE_START_POSITIONS, + DAY_START_POSITIONS, + NIGHT_START_POSITIONS + }; +}