When the large clock appears, move smartspace up to take the place of the small clock. Reverse that when notifications appear and the small clock shows up. Animate the large clock weight on appear. Fixes: 185301988 Test: manual (swipe away notifications) Change-Id: Ic293cebad4e7a5311c024a4d9cdc7b206689fde7
358 lines
14 KiB
Java
358 lines
14 KiB
Java
/*
|
|
* 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 static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
|
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
|
|
|
import android.app.WallpaperManager;
|
|
import android.content.res.Resources;
|
|
import android.text.TextUtils;
|
|
import android.text.format.DateFormat;
|
|
import android.view.View;
|
|
import android.widget.FrameLayout;
|
|
import android.widget.RelativeLayout;
|
|
|
|
import com.android.internal.colorextraction.ColorExtractor;
|
|
import com.android.keyguard.clock.ClockManager;
|
|
import com.android.systemui.R;
|
|
import com.android.systemui.broadcast.BroadcastDispatcher;
|
|
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
|
import com.android.systemui.plugins.ClockPlugin;
|
|
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
|
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
|
|
import com.android.systemui.statusbar.notification.AnimatableProperty;
|
|
import com.android.systemui.statusbar.notification.PropertyAnimator;
|
|
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
|
|
import com.android.systemui.statusbar.phone.KeyguardBypassController;
|
|
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
|
|
import com.android.systemui.statusbar.phone.NotificationIconContainer;
|
|
import com.android.systemui.statusbar.policy.BatteryController;
|
|
import com.android.systemui.util.ViewController;
|
|
|
|
import java.util.Locale;
|
|
import java.util.TimeZone;
|
|
|
|
import javax.inject.Inject;
|
|
|
|
/**
|
|
* Injectable controller for {@link KeyguardClockSwitch}.
|
|
*/
|
|
public class KeyguardClockSwitchController extends ViewController<KeyguardClockSwitch> {
|
|
private static final boolean CUSTOM_CLOCKS_ENABLED = true;
|
|
|
|
private final StatusBarStateController mStatusBarStateController;
|
|
private final SysuiColorExtractor mColorExtractor;
|
|
private final ClockManager mClockManager;
|
|
private final KeyguardSliceViewController mKeyguardSliceViewController;
|
|
private final NotificationIconAreaController mNotificationIconAreaController;
|
|
private final BroadcastDispatcher mBroadcastDispatcher;
|
|
private final BatteryController mBatteryController;
|
|
private final LockscreenSmartspaceController mSmartspaceController;
|
|
|
|
/**
|
|
* Clock for both small and large sizes
|
|
*/
|
|
private AnimatableClockController mClockViewController;
|
|
private FrameLayout mClockFrame;
|
|
private AnimatableClockController mLargeClockViewController;
|
|
private FrameLayout mLargeClockFrame;
|
|
|
|
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
|
|
private final KeyguardBypassController mBypassController;
|
|
|
|
/**
|
|
* Listener for changes to the color palette.
|
|
*
|
|
* The color palette changes when the wallpaper is changed.
|
|
*/
|
|
private final ColorExtractor.OnColorsChangedListener mColorsListener =
|
|
(extractor, which) -> {
|
|
if ((which & WallpaperManager.FLAG_LOCK) != 0) {
|
|
mView.updateColors(getGradientColors());
|
|
}
|
|
};
|
|
|
|
private final ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
|
|
|
|
// If set, will replace keyguard_status_area
|
|
private View mSmartspaceView;
|
|
|
|
@Inject
|
|
public KeyguardClockSwitchController(
|
|
KeyguardClockSwitch keyguardClockSwitch,
|
|
StatusBarStateController statusBarStateController,
|
|
SysuiColorExtractor colorExtractor,
|
|
ClockManager clockManager,
|
|
KeyguardSliceViewController keyguardSliceViewController,
|
|
NotificationIconAreaController notificationIconAreaController,
|
|
BroadcastDispatcher broadcastDispatcher,
|
|
BatteryController batteryController,
|
|
KeyguardUpdateMonitor keyguardUpdateMonitor,
|
|
KeyguardBypassController bypassController,
|
|
LockscreenSmartspaceController smartspaceController) {
|
|
super(keyguardClockSwitch);
|
|
mStatusBarStateController = statusBarStateController;
|
|
mColorExtractor = colorExtractor;
|
|
mClockManager = clockManager;
|
|
mKeyguardSliceViewController = keyguardSliceViewController;
|
|
mNotificationIconAreaController = notificationIconAreaController;
|
|
mBroadcastDispatcher = broadcastDispatcher;
|
|
mBatteryController = batteryController;
|
|
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
|
|
mBypassController = bypassController;
|
|
mSmartspaceController = smartspaceController;
|
|
}
|
|
|
|
/**
|
|
* Attach the controller to the view it relates to.
|
|
*/
|
|
@Override
|
|
public void onInit() {
|
|
mKeyguardSliceViewController.init();
|
|
}
|
|
|
|
@Override
|
|
protected void onViewAttached() {
|
|
if (CUSTOM_CLOCKS_ENABLED) {
|
|
mClockManager.addOnClockChangedListener(mClockChangedListener);
|
|
}
|
|
mColorExtractor.addOnColorsChangedListener(mColorsListener);
|
|
mView.updateColors(getGradientColors());
|
|
updateAodIcons();
|
|
|
|
mClockFrame = mView.findViewById(R.id.lockscreen_clock_view);
|
|
mLargeClockFrame = mView.findViewById(R.id.lockscreen_clock_view_large);
|
|
|
|
mClockViewController =
|
|
new AnimatableClockController(
|
|
mView.findViewById(R.id.animatable_clock_view),
|
|
mStatusBarStateController,
|
|
mBroadcastDispatcher,
|
|
mBatteryController,
|
|
mKeyguardUpdateMonitor,
|
|
mBypassController);
|
|
mClockViewController.init();
|
|
|
|
mLargeClockViewController =
|
|
new AnimatableClockController(
|
|
mView.findViewById(R.id.animatable_clock_view_large),
|
|
mStatusBarStateController,
|
|
mBroadcastDispatcher,
|
|
mBatteryController,
|
|
mKeyguardUpdateMonitor,
|
|
mBypassController);
|
|
mLargeClockViewController.init();
|
|
|
|
if (mSmartspaceController.isEnabled()) {
|
|
mSmartspaceView = mSmartspaceController.buildAndConnectView(mView);
|
|
|
|
View ksa = mView.findViewById(R.id.keyguard_status_area);
|
|
int ksaIndex = mView.indexOfChild(ksa);
|
|
ksa.setVisibility(View.GONE);
|
|
|
|
// Place smartspace view below normal clock...
|
|
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
|
|
MATCH_PARENT, WRAP_CONTENT);
|
|
lp.addRule(RelativeLayout.BELOW, R.id.lockscreen_clock_view);
|
|
|
|
mView.addView(mSmartspaceView, ksaIndex, lp);
|
|
int padding = getContext().getResources()
|
|
.getDimensionPixelSize(R.dimen.below_clock_padding_start);
|
|
mSmartspaceView.setPadding(padding, 0, padding, 0);
|
|
|
|
// ... but above the large clock
|
|
lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
|
|
lp.addRule(RelativeLayout.BELOW, mSmartspaceView.getId());
|
|
mLargeClockFrame.setLayoutParams(lp);
|
|
|
|
View nic = mView.findViewById(
|
|
R.id.left_aligned_notification_icon_container);
|
|
lp = (RelativeLayout.LayoutParams) nic.getLayoutParams();
|
|
lp.addRule(RelativeLayout.BELOW, mSmartspaceView.getId());
|
|
nic.setLayoutParams(lp);
|
|
|
|
mView.setSmartspaceView(mSmartspaceView);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onViewDetached() {
|
|
if (CUSTOM_CLOCKS_ENABLED) {
|
|
mClockManager.removeOnClockChangedListener(mClockChangedListener);
|
|
}
|
|
mColorExtractor.removeOnColorsChangedListener(mColorsListener);
|
|
mView.setClockPlugin(null, mStatusBarStateController.getState());
|
|
|
|
mSmartspaceController.disconnect();
|
|
|
|
// TODO: This is an unfortunate necessity since smartspace plugin retains a single instance
|
|
// of the smartspace view -- if we don't remove the view, it can't be reused by a later
|
|
// instance of this class. In order to fix this, we need to modify the plugin so that
|
|
// (a) we get a new view each time and (b) we can properly clean up an old view by making
|
|
// it unregister itself as a plugin listener.
|
|
if (mSmartspaceView != null) {
|
|
mView.removeView(mSmartspaceView);
|
|
mSmartspaceView = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Apply dp changes on font/scale change
|
|
*/
|
|
public void onDensityOrFontScaleChanged() {
|
|
mView.onDensityOrFontScaleChanged();
|
|
}
|
|
|
|
/**
|
|
* Set whether or not the lock screen is showing notifications.
|
|
*/
|
|
public void setHasVisibleNotifications(boolean hasVisibleNotifications) {
|
|
if (mView.willSwitchToLargeClock(hasVisibleNotifications)) {
|
|
mLargeClockViewController.animateAppear();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If we're presenting a custom clock of just the default one.
|
|
*/
|
|
public boolean hasCustomClock() {
|
|
return mView.hasCustomClock();
|
|
}
|
|
|
|
/**
|
|
* Get the clock text size.
|
|
*/
|
|
public float getClockTextSize() {
|
|
return mView.getTextSize();
|
|
}
|
|
|
|
/**
|
|
* Returns the preferred Y position of the clock.
|
|
*
|
|
* @param totalHeight The height available to position the clock.
|
|
* @return Y position of clock.
|
|
*/
|
|
public int getClockPreferredY(int totalHeight) {
|
|
return mView.getPreferredY(totalHeight);
|
|
}
|
|
|
|
/**
|
|
* Refresh clock. Called in response to TIME_TICK broadcasts.
|
|
*/
|
|
void refresh() {
|
|
if (mClockViewController != null) {
|
|
mClockViewController.refreshTime();
|
|
mLargeClockViewController.refreshTime();
|
|
}
|
|
|
|
mView.refresh();
|
|
}
|
|
|
|
/**
|
|
* Update position of the view, with optional animation. Move the slice view and the clock
|
|
* slightly towards the center in order to prevent burn-in. Y positioning occurs at the
|
|
* view parent level. The large clock view will scale instead of using x position offsets, to
|
|
* keep the clock centered.
|
|
*/
|
|
void updatePosition(int x, float scale, AnimationProperties props, boolean animate) {
|
|
x = getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? -x : x;
|
|
|
|
PropertyAnimator.setProperty(mClockFrame, AnimatableProperty.TRANSLATION_X,
|
|
x, props, animate);
|
|
PropertyAnimator.setProperty(mLargeClockFrame, AnimatableProperty.SCALE_X,
|
|
scale, props, animate);
|
|
PropertyAnimator.setProperty(mLargeClockFrame, AnimatableProperty.SCALE_Y,
|
|
scale, props, animate);
|
|
|
|
if (mSmartspaceView != null) {
|
|
PropertyAnimator.setProperty(mSmartspaceView, AnimatableProperty.TRANSLATION_X,
|
|
x, props, animate);
|
|
}
|
|
|
|
mKeyguardSliceViewController.updatePosition(x, props, animate);
|
|
mNotificationIconAreaController.updatePosition(x, props, animate);
|
|
}
|
|
|
|
void updateTimeZone(TimeZone timeZone) {
|
|
mView.onTimeZoneChanged(timeZone);
|
|
if (mClockViewController != null) {
|
|
mClockViewController.onTimeZoneChanged(timeZone);
|
|
mLargeClockViewController.onTimeZoneChanged(timeZone);
|
|
}
|
|
}
|
|
|
|
void refreshFormat(String timeFormat) {
|
|
if (mClockViewController != null) {
|
|
mClockViewController.refreshFormat();
|
|
mLargeClockViewController.refreshFormat();
|
|
}
|
|
}
|
|
|
|
private void updateAodIcons() {
|
|
NotificationIconContainer nic = (NotificationIconContainer)
|
|
mView.findViewById(
|
|
com.android.systemui.R.id.left_aligned_notification_icon_container);
|
|
|
|
// alt icon area is set in KeyguardClockSwitchController
|
|
mNotificationIconAreaController.setupAodIcons(nic);
|
|
}
|
|
|
|
private void setClockPlugin(ClockPlugin plugin) {
|
|
mView.setClockPlugin(plugin, mStatusBarStateController.getState());
|
|
}
|
|
|
|
private ColorExtractor.GradientColors getGradientColors() {
|
|
return mColorExtractor.getColors(WallpaperManager.FLAG_LOCK);
|
|
}
|
|
|
|
// DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
|
|
// This is an optimization to ensure we only recompute the patterns when the inputs change.
|
|
private static final class Patterns {
|
|
static String sClockView12;
|
|
static String sClockView24;
|
|
static String sCacheKey;
|
|
|
|
static void update(Resources res) {
|
|
final Locale locale = Locale.getDefault();
|
|
final String clockView12Skel = res.getString(R.string.clock_12hr_format);
|
|
final String clockView24Skel = res.getString(R.string.clock_24hr_format);
|
|
final String key = locale.toString() + clockView12Skel + clockView24Skel;
|
|
if (key.equals(sCacheKey)) return;
|
|
|
|
sClockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel);
|
|
// CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
|
|
// format. The following code removes the AM/PM indicator if we didn't want it.
|
|
if (!clockView12Skel.contains("a")) {
|
|
sClockView12 = sClockView12.replaceAll("a", "").trim();
|
|
}
|
|
|
|
sClockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel);
|
|
|
|
// Use fancy colon.
|
|
sClockView24 = sClockView24.replace(':', '\uee01');
|
|
sClockView12 = sClockView12.replace(':', '\uee01');
|
|
|
|
sCacheKey = key;
|
|
}
|
|
}
|
|
|
|
private int getCurrentLayoutDirection() {
|
|
return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault());
|
|
}
|
|
}
|