• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.keyguard;
18 
19 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
20 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
21 
22 import android.app.WallpaperManager;
23 import android.text.TextUtils;
24 import android.view.View;
25 import android.widget.FrameLayout;
26 import android.widget.RelativeLayout;
27 
28 import com.android.internal.colorextraction.ColorExtractor;
29 import com.android.keyguard.clock.ClockManager;
30 import com.android.systemui.R;
31 import com.android.systemui.broadcast.BroadcastDispatcher;
32 import com.android.systemui.colorextraction.SysuiColorExtractor;
33 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
34 import com.android.systemui.plugins.ClockPlugin;
35 import com.android.systemui.plugins.statusbar.StatusBarStateController;
36 import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
37 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
38 import com.android.systemui.statusbar.notification.AnimatableProperty;
39 import com.android.systemui.statusbar.notification.PropertyAnimator;
40 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
41 import com.android.systemui.statusbar.phone.KeyguardBypassController;
42 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
43 import com.android.systemui.statusbar.phone.NotificationIconContainer;
44 import com.android.systemui.statusbar.policy.BatteryController;
45 import com.android.systemui.util.ViewController;
46 
47 import java.util.HashSet;
48 import java.util.Locale;
49 import java.util.Set;
50 import java.util.TimeZone;
51 
52 import javax.inject.Inject;
53 
54 /**
55  * Injectable controller for {@link KeyguardClockSwitch}.
56  */
57 public class KeyguardClockSwitchController extends ViewController<KeyguardClockSwitch> {
58     private static final boolean CUSTOM_CLOCKS_ENABLED = true;
59 
60     private final StatusBarStateController mStatusBarStateController;
61     private final SysuiColorExtractor mColorExtractor;
62     private final ClockManager mClockManager;
63     private final KeyguardSliceViewController mKeyguardSliceViewController;
64     private final NotificationIconAreaController mNotificationIconAreaController;
65     private final BroadcastDispatcher mBroadcastDispatcher;
66     private final BatteryController mBatteryController;
67     private final LockscreenSmartspaceController mSmartspaceController;
68 
69     /**
70      * Clock for both small and large sizes
71      */
72     private AnimatableClockController mClockViewController;
73     private FrameLayout mClockFrame;
74     private AnimatableClockController mLargeClockViewController;
75     private FrameLayout mLargeClockFrame;
76 
77     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
78     private final KeyguardBypassController mBypassController;
79 
80     /**
81      * Listener for changes to the color palette.
82      *
83      * The color palette changes when the wallpaper is changed.
84      */
85     private final ColorExtractor.OnColorsChangedListener mColorsListener =
86             (extractor, which) -> {
87                 if ((which & WallpaperManager.FLAG_LOCK) != 0) {
88                     mView.updateColors(getGradientColors());
89                 }
90             };
91 
92     private final ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
93 
94     // If set, will replace keyguard_status_area
95     private View mSmartspaceView;
96 
97     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
98     private SmartspaceTransitionController mSmartspaceTransitionController;
99 
100     private boolean mOnlyClock = false;
101 
102     @Inject
KeyguardClockSwitchController( KeyguardClockSwitch keyguardClockSwitch, StatusBarStateController statusBarStateController, SysuiColorExtractor colorExtractor, ClockManager clockManager, KeyguardSliceViewController keyguardSliceViewController, NotificationIconAreaController notificationIconAreaController, BroadcastDispatcher broadcastDispatcher, BatteryController batteryController, KeyguardUpdateMonitor keyguardUpdateMonitor, KeyguardBypassController bypassController, LockscreenSmartspaceController smartspaceController, KeyguardUnlockAnimationController keyguardUnlockAnimationController, SmartspaceTransitionController smartspaceTransitionController)103     public KeyguardClockSwitchController(
104             KeyguardClockSwitch keyguardClockSwitch,
105             StatusBarStateController statusBarStateController,
106             SysuiColorExtractor colorExtractor,
107             ClockManager clockManager,
108             KeyguardSliceViewController keyguardSliceViewController,
109             NotificationIconAreaController notificationIconAreaController,
110             BroadcastDispatcher broadcastDispatcher,
111             BatteryController batteryController,
112             KeyguardUpdateMonitor keyguardUpdateMonitor,
113             KeyguardBypassController bypassController,
114             LockscreenSmartspaceController smartspaceController,
115             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
116             SmartspaceTransitionController smartspaceTransitionController) {
117         super(keyguardClockSwitch);
118         mStatusBarStateController = statusBarStateController;
119         mColorExtractor = colorExtractor;
120         mClockManager = clockManager;
121         mKeyguardSliceViewController = keyguardSliceViewController;
122         mNotificationIconAreaController = notificationIconAreaController;
123         mBroadcastDispatcher = broadcastDispatcher;
124         mBatteryController = batteryController;
125         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
126         mBypassController = bypassController;
127         mSmartspaceController = smartspaceController;
128 
129         mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
130         mSmartspaceTransitionController = smartspaceTransitionController;
131     }
132 
133     /**
134      * Mostly used for alternate displays, limit the information shown
135      */
setOnlyClock(boolean onlyClock)136     public void setOnlyClock(boolean onlyClock) {
137         mOnlyClock = onlyClock;
138     }
139 
140     /**
141      * Attach the controller to the view it relates to.
142      */
143     @Override
onInit()144     public void onInit() {
145         mKeyguardSliceViewController.init();
146 
147         mClockFrame = mView.findViewById(R.id.lockscreen_clock_view);
148         mLargeClockFrame = mView.findViewById(R.id.lockscreen_clock_view_large);
149 
150         mClockViewController =
151                 new AnimatableClockController(
152                         mView.findViewById(R.id.animatable_clock_view),
153                         mStatusBarStateController,
154                         mBroadcastDispatcher,
155                         mBatteryController,
156                         mKeyguardUpdateMonitor,
157                         mBypassController);
158         mClockViewController.init();
159 
160         mLargeClockViewController =
161                 new AnimatableClockController(
162                         mView.findViewById(R.id.animatable_clock_view_large),
163                         mStatusBarStateController,
164                         mBroadcastDispatcher,
165                         mBatteryController,
166                         mKeyguardUpdateMonitor,
167                         mBypassController);
168         mLargeClockViewController.init();
169     }
170 
171     @Override
onViewAttached()172     protected void onViewAttached() {
173         if (CUSTOM_CLOCKS_ENABLED) {
174             mClockManager.addOnClockChangedListener(mClockChangedListener);
175         }
176         mColorExtractor.addOnColorsChangedListener(mColorsListener);
177         mView.updateColors(getGradientColors());
178 
179         if (mOnlyClock) {
180             View ksa = mView.findViewById(R.id.keyguard_status_area);
181             ksa.setVisibility(View.GONE);
182 
183             View nic = mView.findViewById(
184                     R.id.left_aligned_notification_icon_container);
185             nic.setVisibility(View.GONE);
186             return;
187         }
188         updateAodIcons();
189 
190         if (mSmartspaceController.isEnabled()) {
191             mSmartspaceView = mSmartspaceController.buildAndConnectView(mView);
192 
193             View ksa = mView.findViewById(R.id.keyguard_status_area);
194             int ksaIndex = mView.indexOfChild(ksa);
195             ksa.setVisibility(View.GONE);
196 
197             // Place smartspace view below normal clock...
198             RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
199                     MATCH_PARENT, WRAP_CONTENT);
200             lp.addRule(RelativeLayout.BELOW, R.id.lockscreen_clock_view);
201 
202             mView.addView(mSmartspaceView, ksaIndex, lp);
203             int startPadding = getContext().getResources()
204                     .getDimensionPixelSize(R.dimen.below_clock_padding_start);
205             int endPadding = getContext().getResources()
206                     .getDimensionPixelSize(R.dimen.below_clock_padding_end);
207             mSmartspaceView.setPaddingRelative(startPadding, 0, endPadding, 0);
208 
209             updateClockLayout();
210 
211             View nic = mView.findViewById(
212                     R.id.left_aligned_notification_icon_container);
213             lp = (RelativeLayout.LayoutParams) nic.getLayoutParams();
214             lp.addRule(RelativeLayout.BELOW, mSmartspaceView.getId());
215             nic.setLayoutParams(lp);
216 
217             mView.setSmartspaceView(mSmartspaceView);
218             mSmartspaceTransitionController.setLockscreenSmartspace(mSmartspaceView);
219         }
220     }
221 
getNotificationIconAreaHeight()222     int getNotificationIconAreaHeight() {
223         return mNotificationIconAreaController.getHeight();
224     }
225 
226     @Override
onViewDetached()227     protected void onViewDetached() {
228         if (CUSTOM_CLOCKS_ENABLED) {
229             mClockManager.removeOnClockChangedListener(mClockChangedListener);
230         }
231         mColorExtractor.removeOnColorsChangedListener(mColorsListener);
232         mView.setClockPlugin(null, mStatusBarStateController.getState());
233 
234         mSmartspaceController.disconnect();
235 
236         // TODO: This is an unfortunate necessity since smartspace plugin retains a single instance
237         // of the smartspace view -- if we don't remove the view, it can't be reused by a later
238         // instance of this class. In order to fix this, we need to modify the plugin so that
239         // (a) we get a new view each time and (b) we can properly clean up an old view by making
240         // it unregister itself as a plugin listener.
241         if (mSmartspaceView != null) {
242             mView.removeView(mSmartspaceView);
243             mSmartspaceView = null;
244         }
245     }
246 
247     /**
248      * Apply dp changes on font/scale change
249      */
onDensityOrFontScaleChanged()250     public void onDensityOrFontScaleChanged() {
251         mView.onDensityOrFontScaleChanged();
252 
253         updateClockLayout();
254     }
255 
updateClockLayout()256     private void updateClockLayout() {
257         if (mSmartspaceController.isEnabled()) {
258             RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
259                     MATCH_PARENT);
260             lp.topMargin = getContext().getResources().getDimensionPixelSize(
261                     R.dimen.keyguard_large_clock_top_margin);
262             mLargeClockFrame.setLayoutParams(lp);
263         }
264     }
265 
266     /**
267      * Set whether or not the lock screen is showing notifications.
268      */
setHasVisibleNotifications(boolean hasVisibleNotifications)269     public void setHasVisibleNotifications(boolean hasVisibleNotifications) {
270         if (mView.willSwitchToLargeClock(hasVisibleNotifications)) {
271             mLargeClockViewController.animateAppear();
272         }
273     }
274 
275     /**
276      * If we're presenting a custom clock of just the default one.
277      */
hasCustomClock()278     public boolean hasCustomClock() {
279         return mView.hasCustomClock();
280     }
281 
282     /**
283      * Get the clock text size.
284      */
getClockTextSize()285     public float getClockTextSize() {
286         return mView.getTextSize();
287     }
288 
289     /**
290      * Refresh clock. Called in response to TIME_TICK broadcasts.
291      */
refresh()292     void refresh() {
293         if (mClockViewController != null) {
294             mClockViewController.refreshTime();
295             mLargeClockViewController.refreshTime();
296         }
297         if (mSmartspaceController != null) {
298             mSmartspaceController.requestSmartspaceUpdate();
299         }
300 
301         mView.refresh();
302     }
303 
304     /**
305      * Update position of the view, with optional animation. Move the slice view and the clock
306      * slightly towards the center in order to prevent burn-in. Y positioning occurs at the
307      * view parent level. The large clock view will scale instead of using x position offsets, to
308      * keep the clock centered.
309      */
updatePosition(int x, float scale, AnimationProperties props, boolean animate)310     void updatePosition(int x, float scale, AnimationProperties props, boolean animate) {
311         x = getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? -x : x;
312 
313         PropertyAnimator.setProperty(mClockFrame, AnimatableProperty.TRANSLATION_X,
314                 x, props, animate);
315         PropertyAnimator.setProperty(mLargeClockFrame, AnimatableProperty.SCALE_X,
316                 scale, props, animate);
317         PropertyAnimator.setProperty(mLargeClockFrame, AnimatableProperty.SCALE_Y,
318                 scale, props, animate);
319 
320         if (mSmartspaceView != null) {
321             PropertyAnimator.setProperty(mSmartspaceView, AnimatableProperty.TRANSLATION_X,
322                     x, props, animate);
323 
324             // If we're unlocking with the SmartSpace shared element transition, let the controller
325             // know that it should re-position our SmartSpace.
326             if (mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) {
327                 mKeyguardUnlockAnimationController.updateLockscreenSmartSpacePosition();
328             }
329         }
330 
331         mKeyguardSliceViewController.updatePosition(x, props, animate);
332         mNotificationIconAreaController.updatePosition(x, props, animate);
333     }
334 
335     /** Sets an alpha value on every child view except for the smartspace. */
setChildrenAlphaExcludingSmartspace(float alpha)336     public void setChildrenAlphaExcludingSmartspace(float alpha) {
337         final Set<View> excludedViews = new HashSet<>();
338 
339         if (mSmartspaceView != null) {
340             excludedViews.add(mSmartspaceView);
341         }
342 
343         setChildrenAlphaExcluding(alpha, excludedViews);
344     }
345 
346     /** Sets an alpha value on every child view except for the views in the provided set. */
setChildrenAlphaExcluding(float alpha, Set<View> excludedViews)347     public void setChildrenAlphaExcluding(float alpha, Set<View> excludedViews) {
348         for (int i = 0; i < mView.getChildCount(); i++) {
349             final View child = mView.getChildAt(i);
350 
351             if (!excludedViews.contains(child)) {
352                 child.setAlpha(alpha);
353             }
354         }
355     }
356 
updateTimeZone(TimeZone timeZone)357     void updateTimeZone(TimeZone timeZone) {
358         mView.onTimeZoneChanged(timeZone);
359         if (mClockViewController != null) {
360             mClockViewController.onTimeZoneChanged(timeZone);
361             mLargeClockViewController.onTimeZoneChanged(timeZone);
362         }
363     }
364 
refreshFormat()365     void refreshFormat() {
366         if (mClockViewController != null) {
367             mClockViewController.refreshFormat();
368             mLargeClockViewController.refreshFormat();
369         }
370     }
371 
updateAodIcons()372     private void updateAodIcons() {
373         NotificationIconContainer nic = (NotificationIconContainer)
374                 mView.findViewById(
375                         com.android.systemui.R.id.left_aligned_notification_icon_container);
376         mNotificationIconAreaController.setupAodIcons(nic);
377     }
378 
setClockPlugin(ClockPlugin plugin)379     private void setClockPlugin(ClockPlugin plugin) {
380         mView.setClockPlugin(plugin, mStatusBarStateController.getState());
381     }
382 
getGradientColors()383     private ColorExtractor.GradientColors getGradientColors() {
384         return mColorExtractor.getColors(WallpaperManager.FLAG_LOCK);
385     }
386 
getCurrentLayoutDirection()387     private int getCurrentLayoutDirection() {
388         return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault());
389     }
390 }
391