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 static com.android.keyguard.KeyguardClockSwitch.LARGE;
23 import static com.android.keyguard.KeyguardClockSwitch.SMALL;
24 import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
25 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
26 
27 import android.annotation.Nullable;
28 import android.database.ContentObserver;
29 import android.os.UserHandle;
30 import android.provider.Settings;
31 import android.text.TextUtils;
32 import android.view.View;
33 import android.view.ViewGroup;
34 import android.widget.FrameLayout;
35 import android.widget.LinearLayout;
36 
37 import androidx.annotation.NonNull;
38 import androidx.annotation.VisibleForTesting;
39 
40 import com.android.systemui.Dumpable;
41 import com.android.systemui.R;
42 import com.android.systemui.dagger.qualifiers.Main;
43 import com.android.systemui.dump.DumpManager;
44 import com.android.systemui.flags.FeatureFlags;
45 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
46 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
47 import com.android.systemui.log.LogBuffer;
48 import com.android.systemui.log.core.LogLevel;
49 import com.android.systemui.log.dagger.KeyguardClockLog;
50 import com.android.systemui.plugins.ClockController;
51 import com.android.systemui.plugins.statusbar.StatusBarStateController;
52 import com.android.systemui.shared.clocks.ClockRegistry;
53 import com.android.systemui.shared.regionsampling.RegionSampler;
54 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
55 import com.android.systemui.statusbar.notification.AnimatableProperty;
56 import com.android.systemui.statusbar.notification.PropertyAnimator;
57 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
58 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
59 import com.android.systemui.statusbar.phone.NotificationIconContainer;
60 import com.android.systemui.util.ViewController;
61 import com.android.systemui.util.concurrency.DelayableExecutor;
62 import com.android.systemui.util.settings.SecureSettings;
63 
64 import java.io.PrintWriter;
65 import java.util.Locale;
66 import java.util.function.Consumer;
67 
68 import javax.inject.Inject;
69 
70 /**
71  * Injectable controller for {@link KeyguardClockSwitch}.
72  */
73 public class KeyguardClockSwitchController extends ViewController<KeyguardClockSwitch>
74         implements Dumpable {
75     private static final String TAG = "KeyguardClockSwitchController";
76 
77     private final StatusBarStateController mStatusBarStateController;
78     private final ClockRegistry mClockRegistry;
79     private final KeyguardSliceViewController mKeyguardSliceViewController;
80     private final NotificationIconAreaController mNotificationIconAreaController;
81     private final LockscreenSmartspaceController mSmartspaceController;
82     private final SecureSettings mSecureSettings;
83     private final DumpManager mDumpManager;
84     private final ClockEventController mClockEventController;
85     private final LogBuffer mLogBuffer;
86 
87     private FrameLayout mSmallClockFrame; // top aligned clock
88     private FrameLayout mLargeClockFrame; // centered clock
89 
90     @KeyguardClockSwitch.ClockSize
91     private int mCurrentClockSize = SMALL;
92 
93     private int mKeyguardSmallClockTopMargin = 0;
94     private int mKeyguardLargeClockTopMargin = 0;
95     private int mKeyguardDateWeatherViewInvisibility = View.INVISIBLE;
96     private final ClockRegistry.ClockChangeListener mClockChangedListener;
97 
98     private ViewGroup mStatusArea;
99 
100     // If the SMARTSPACE flag is set, keyguard_slice_view is replaced by the following views.
101     private ViewGroup mDateWeatherView;
102     private View mWeatherView;
103     private View mSmartspaceView;
104 
105     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
106 
107     private boolean mShownOnSecondaryDisplay = false;
108     private boolean mOnlyClock = false;
109     private boolean mIsActiveDreamLockscreenHosted = false;
110     private FeatureFlags mFeatureFlags;
111     private KeyguardInteractor mKeyguardInteractor;
112     private final DelayableExecutor mUiExecutor;
113     private boolean mCanShowDoubleLineClock = true;
114     @VisibleForTesting
115     final Consumer<Boolean> mIsActiveDreamLockscreenHostedCallback =
116             (Boolean isLockscreenHosted) -> {
117                 if (mIsActiveDreamLockscreenHosted == isLockscreenHosted) {
118                     return;
119                 }
120                 mIsActiveDreamLockscreenHosted = isLockscreenHosted;
121                 updateKeyguardStatusAreaVisibility();
122             };
123     private final ContentObserver mDoubleLineClockObserver = new ContentObserver(null) {
124         @Override
125         public void onChange(boolean change) {
126             updateDoubleLineClock();
127         }
128     };
129     private final ContentObserver mShowWeatherObserver = new ContentObserver(null) {
130         @Override
131         public void onChange(boolean change) {
132             setWeatherVisibility();
133         }
134     };
135 
136     private final KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener
137             mKeyguardUnlockAnimationListener =
138             new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() {
139                 @Override
140                 public void onUnlockAnimationFinished() {
141                     // For performance reasons, reset this once the unlock animation ends.
142                     setClipChildrenForUnlock(true);
143                 }
144             };
145 
146     @Inject
KeyguardClockSwitchController( KeyguardClockSwitch keyguardClockSwitch, StatusBarStateController statusBarStateController, ClockRegistry clockRegistry, KeyguardSliceViewController keyguardSliceViewController, NotificationIconAreaController notificationIconAreaController, LockscreenSmartspaceController smartspaceController, KeyguardUnlockAnimationController keyguardUnlockAnimationController, SecureSettings secureSettings, @Main DelayableExecutor uiExecutor, DumpManager dumpManager, ClockEventController clockEventController, @KeyguardClockLog LogBuffer logBuffer, KeyguardInteractor keyguardInteractor, FeatureFlags featureFlags)147     public KeyguardClockSwitchController(
148             KeyguardClockSwitch keyguardClockSwitch,
149             StatusBarStateController statusBarStateController,
150             ClockRegistry clockRegistry,
151             KeyguardSliceViewController keyguardSliceViewController,
152             NotificationIconAreaController notificationIconAreaController,
153             LockscreenSmartspaceController smartspaceController,
154             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
155             SecureSettings secureSettings,
156             @Main DelayableExecutor uiExecutor,
157             DumpManager dumpManager,
158             ClockEventController clockEventController,
159             @KeyguardClockLog LogBuffer logBuffer,
160             KeyguardInteractor keyguardInteractor,
161             FeatureFlags featureFlags) {
162         super(keyguardClockSwitch);
163         mStatusBarStateController = statusBarStateController;
164         mClockRegistry = clockRegistry;
165         mKeyguardSliceViewController = keyguardSliceViewController;
166         mNotificationIconAreaController = notificationIconAreaController;
167         mSmartspaceController = smartspaceController;
168         mSecureSettings = secureSettings;
169         mUiExecutor = uiExecutor;
170         mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
171         mDumpManager = dumpManager;
172         mClockEventController = clockEventController;
173         mLogBuffer = logBuffer;
174         mView.setLogBuffer(mLogBuffer);
175         mFeatureFlags = featureFlags;
176         mKeyguardInteractor = keyguardInteractor;
177 
178         mClockChangedListener = new ClockRegistry.ClockChangeListener() {
179             @Override
180             public void onCurrentClockChanged() {
181                 setClock(mClockRegistry.createCurrentClock());
182             }
183             @Override
184             public void onAvailableClocksChanged() { }
185         };
186     }
187 
188     /**
189      * When set, limits the information shown in an external display.
190      */
setShownOnSecondaryDisplay(boolean shownOnSecondaryDisplay)191     public void setShownOnSecondaryDisplay(boolean shownOnSecondaryDisplay) {
192         mShownOnSecondaryDisplay = shownOnSecondaryDisplay;
193     }
194 
195     /**
196      * Mostly used for alternate displays, limit the information shown
197      *
198      * @deprecated use {@link KeyguardClockSwitchController#setShownOnSecondaryDisplay}
199      */
200     @Deprecated
setOnlyClock(boolean onlyClock)201     public void setOnlyClock(boolean onlyClock) {
202         mOnlyClock = onlyClock;
203     }
204 
205     /**
206      * Used for status view to pass the screen offset from parent view
207      */
setLockscreenClockY(int clockY)208     public void setLockscreenClockY(int clockY) {
209         if (mView.screenOffsetYPadding != clockY) {
210             mView.screenOffsetYPadding = clockY;
211             mView.updateClockTargetRegions();
212         }
213     }
214 
215     /**
216      * Attach the controller to the view it relates to.
217      */
218     @Override
onInit()219     protected void onInit() {
220         mKeyguardSliceViewController.init();
221 
222         mSmallClockFrame = mView.findViewById(R.id.lockscreen_clock_view);
223         mLargeClockFrame = mView.findViewById(R.id.lockscreen_clock_view_large);
224 
225         if (!mOnlyClock) {
226             mDumpManager.unregisterDumpable(getClass().toString()); // unregister previous clocks
227             mDumpManager.registerDumpable(getClass().toString(), this);
228         }
229 
230         if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) {
231             mStatusArea = mView.findViewById(R.id.keyguard_status_area);
232             collectFlow(mStatusArea, mKeyguardInteractor.isActiveDreamLockscreenHosted(),
233                     mIsActiveDreamLockscreenHostedCallback);
234         }
235     }
236 
hideSliceViewAndNotificationIconContainer()237     private void hideSliceViewAndNotificationIconContainer() {
238         View ksv = mView.findViewById(R.id.keyguard_slice_view);
239         ksv.setVisibility(View.GONE);
240 
241         View nic = mView.findViewById(
242                 R.id.left_aligned_notification_icon_container);
243         nic.setVisibility(View.GONE);
244     }
245 
246     @Override
onViewAttached()247     protected void onViewAttached() {
248         mClockRegistry.registerClockChangeListener(mClockChangedListener);
249         setClock(mClockRegistry.createCurrentClock());
250         mClockEventController.registerListeners(mView);
251         mKeyguardSmallClockTopMargin =
252                 mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
253         mKeyguardLargeClockTopMargin =
254                 mView.getResources().getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin);
255         mKeyguardDateWeatherViewInvisibility =
256                 mView.getResources().getInteger(R.integer.keyguard_date_weather_view_invisibility);
257 
258         if (mShownOnSecondaryDisplay) {
259             mView.setLargeClockOnSecondaryDisplay(true);
260             displayClock(LARGE, /* animate= */ false);
261             hideSliceViewAndNotificationIconContainer();
262             return;
263         }
264 
265         if (mOnlyClock) {
266             hideSliceViewAndNotificationIconContainer();
267             return;
268         }
269         updateAodIcons();
270         mStatusArea = mView.findViewById(R.id.keyguard_status_area);
271 
272         mSecureSettings.registerContentObserverForUser(
273                 Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK,
274                 false, /* notifyForDescendants */
275                 mDoubleLineClockObserver,
276                 UserHandle.USER_ALL
277         );
278 
279         mSecureSettings.registerContentObserverForUser(
280                 Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED,
281                 false, /* notifyForDescendants */
282                 mShowWeatherObserver,
283                 UserHandle.USER_ALL
284         );
285         updateDoubleLineClock();
286 
287         mKeyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
288                 mKeyguardUnlockAnimationListener);
289 
290         if (mSmartspaceController.isEnabled()) {
291             View ksv = mView.findViewById(R.id.keyguard_slice_view);
292             int viewIndex = mStatusArea.indexOfChild(ksv);
293             ksv.setVisibility(View.GONE);
294 
295             removeViewsFromStatusArea();
296             addSmartspaceView();
297             // TODO(b/261757708): add content observer for the Settings toggle and add/remove
298             //  weather according to the Settings.
299             if (mSmartspaceController.isDateWeatherDecoupled()) {
300                 addDateWeatherView();
301             }
302         }
303 
304         setDateWeatherVisibility();
305         setWeatherVisibility();
306     }
307 
getNotificationIconAreaHeight()308     int getNotificationIconAreaHeight() {
309         return mNotificationIconAreaController.getHeight();
310     }
311 
312     @Override
onViewDetached()313     protected void onViewDetached() {
314         mClockRegistry.unregisterClockChangeListener(mClockChangedListener);
315         mClockEventController.unregisterListeners();
316         setClock(null);
317 
318         mSecureSettings.unregisterContentObserver(mDoubleLineClockObserver);
319         mSecureSettings.unregisterContentObserver(mShowWeatherObserver);
320 
321         mKeyguardUnlockAnimationController.removeKeyguardUnlockAnimationListener(
322                 mKeyguardUnlockAnimationListener);
323     }
324 
onLocaleListChanged()325     void onLocaleListChanged() {
326         if (mSmartspaceController.isEnabled()) {
327             removeViewsFromStatusArea();
328             addSmartspaceView();
329             if (mSmartspaceController.isDateWeatherDecoupled()) {
330                 mDateWeatherView.removeView(mWeatherView);
331                 addDateWeatherView();
332                 setDateWeatherVisibility();
333                 setWeatherVisibility();
334             }
335         }
336     }
337 
addDateWeatherView()338     private void addDateWeatherView() {
339         mDateWeatherView = (ViewGroup) mSmartspaceController.buildAndConnectDateView(mView);
340         LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
341                 MATCH_PARENT, WRAP_CONTENT);
342         mStatusArea.addView(mDateWeatherView, 0, lp);
343         int startPadding = getContext().getResources().getDimensionPixelSize(
344                 R.dimen.below_clock_padding_start);
345         int endPadding = getContext().getResources().getDimensionPixelSize(
346                 R.dimen.below_clock_padding_end);
347         mDateWeatherView.setPaddingRelative(startPadding, 0, endPadding, 0);
348 
349         addWeatherView();
350     }
351 
addWeatherView()352     private void addWeatherView() {
353         LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
354                 WRAP_CONTENT, WRAP_CONTENT);
355         mWeatherView = mSmartspaceController.buildAndConnectWeatherView(mView);
356         // Place weather right after the date, before the extras
357         final int index = mDateWeatherView.getChildCount() == 0 ? 0 : 1;
358         mDateWeatherView.addView(mWeatherView, index, lp);
359         mWeatherView.setPaddingRelative(0, 0, 4, 0);
360     }
361 
addSmartspaceView()362     private void addSmartspaceView() {
363         mSmartspaceView = mSmartspaceController.buildAndConnectView(mView);
364         LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
365                 MATCH_PARENT, WRAP_CONTENT);
366         mStatusArea.addView(mSmartspaceView, 0, lp);
367         int startPadding = getContext().getResources().getDimensionPixelSize(
368                 R.dimen.below_clock_padding_start);
369         int endPadding = getContext().getResources().getDimensionPixelSize(
370                 R.dimen.below_clock_padding_end);
371         mSmartspaceView.setPaddingRelative(startPadding, 0, endPadding, 0);
372 
373         mKeyguardUnlockAnimationController.setLockscreenSmartspace(mSmartspaceView);
374         mView.setSmartspace(mSmartspaceView);
375     }
376 
377     /**
378      * Apply dp changes on configuration change
379      */
onConfigChanged()380     public void onConfigChanged() {
381         mView.onConfigChanged();
382         mKeyguardSmallClockTopMargin =
383                 mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
384         mKeyguardLargeClockTopMargin =
385                 mView.getResources().getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin);
386         mKeyguardDateWeatherViewInvisibility =
387                 mView.getResources().getInteger(R.integer.keyguard_date_weather_view_invisibility);
388         mView.updateClockTargetRegions();
389         setDateWeatherVisibility();
390     }
391 
392     /**
393      * Enable or disable split shade center specific positioning
394      */
setSplitShadeCentered(boolean splitShadeCentered)395     public void setSplitShadeCentered(boolean splitShadeCentered) {
396         mView.setSplitShadeCentered(splitShadeCentered);
397     }
398 
399     /**
400      * Set if the split shade is enabled
401      */
setSplitShadeEnabled(boolean splitShadeEnabled)402     public void setSplitShadeEnabled(boolean splitShadeEnabled) {
403         mSmartspaceController.setSplitShadeEnabled(splitShadeEnabled);
404     }
405 
406     /**
407      * Set which clock should be displayed on the keyguard. The other one will be automatically
408      * hidden.
409      */
displayClock(@eyguardClockSwitch.ClockSize int clockSize, boolean animate)410     public void displayClock(@KeyguardClockSwitch.ClockSize int clockSize, boolean animate) {
411         if (!mCanShowDoubleLineClock && clockSize == KeyguardClockSwitch.LARGE) {
412             return;
413         }
414 
415         mCurrentClockSize = clockSize;
416         setDateWeatherVisibility();
417 
418         ClockController clock = getClock();
419         boolean appeared = mView.switchToClock(clockSize, animate);
420         if (clock != null && animate && appeared && clockSize == LARGE) {
421             mUiExecutor.executeDelayed(() -> clock.getLargeClock().getAnimations().enter(),
422                     KeyguardClockSwitch.CLOCK_IN_START_DELAY_MILLIS);
423         }
424     }
425 
426     /**
427      * Animates the clock view between folded and unfolded states
428      */
animateFoldToAod(float foldFraction)429     public void animateFoldToAod(float foldFraction) {
430         ClockController clock = getClock();
431         if (clock != null) {
432             clock.getSmallClock().getAnimations().fold(foldFraction);
433             clock.getLargeClock().getAnimations().fold(foldFraction);
434         }
435     }
436 
437     /**
438      * Refresh clock. Called in response to TIME_TICK broadcasts.
439      */
refresh()440     void refresh() {
441         mLogBuffer.log(TAG, LogLevel.INFO, "refresh");
442         if (mSmartspaceController != null) {
443             mSmartspaceController.requestSmartspaceUpdate();
444         }
445         ClockController clock = getClock();
446         if (clock != null) {
447             clock.getSmallClock().getEvents().onTimeTick();
448             clock.getLargeClock().getEvents().onTimeTick();
449         }
450     }
451 
452     /**
453      * Update position of the view, with optional animation. Move the slice view and the clock
454      * slightly towards the center in order to prevent burn-in. Y positioning occurs at the
455      * view parent level. The large clock view will scale instead of using x position offsets, to
456      * keep the clock centered.
457      */
updatePosition(int x, float scale, AnimationProperties props, boolean animate)458     void updatePosition(int x, float scale, AnimationProperties props, boolean animate) {
459         x = getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? -x : x;
460 
461         PropertyAnimator.setProperty(mSmallClockFrame, AnimatableProperty.TRANSLATION_X,
462                 x, props, animate);
463         PropertyAnimator.setProperty(mLargeClockFrame, AnimatableProperty.SCALE_X,
464                 scale, props, animate);
465         PropertyAnimator.setProperty(mLargeClockFrame, AnimatableProperty.SCALE_Y,
466                 scale, props, animate);
467 
468         if (mStatusArea != null) {
469             PropertyAnimator.setProperty(mStatusArea, KeyguardStatusAreaView.TRANSLATE_X_AOD,
470                     x, props, animate);
471         }
472     }
473 
474     /**
475      * Get y-bottom position of the currently visible clock on the keyguard.
476      * We can't directly getBottom() because clock changes positions in AOD for burn-in
477      */
getClockBottom(int statusBarHeaderHeight)478     int getClockBottom(int statusBarHeaderHeight) {
479         ClockController clock = getClock();
480         if (clock == null) {
481             return 0;
482         }
483 
484         if (mLargeClockFrame.getVisibility() == View.VISIBLE) {
485             // This gets the expected clock bottom if mLargeClockFrame had a top margin, but it's
486             // top margin only contributed to height and didn't move the top of the view (as this
487             // was the computation previously). As we no longer have a margin, we add this back
488             // into the computation manually.
489             int frameHeight = mLargeClockFrame.getHeight();
490             int clockHeight = clock.getLargeClock().getView().getHeight();
491             return frameHeight / 2 + clockHeight / 2 + mKeyguardLargeClockTopMargin / -2;
492         } else {
493             int clockHeight = clock.getSmallClock().getView().getHeight();
494             return clockHeight + statusBarHeaderHeight + mKeyguardSmallClockTopMargin;
495         }
496     }
497 
498     /**
499      * Get the height of the currently visible clock on the keyguard.
500      */
getClockHeight()501     int getClockHeight() {
502         ClockController clock = getClock();
503         if (clock == null) {
504             return 0;
505         }
506 
507         if (mLargeClockFrame.getVisibility() == View.VISIBLE) {
508             return clock.getLargeClock().getView().getHeight();
509         } else {
510             return clock.getSmallClock().getView().getHeight();
511         }
512     }
513 
isClockTopAligned()514     boolean isClockTopAligned() {
515         return mLargeClockFrame.getVisibility() != View.VISIBLE;
516     }
517 
updateAodIcons()518     private void updateAodIcons() {
519         NotificationIconContainer nic = (NotificationIconContainer)
520                 mView.findViewById(
521                         com.android.systemui.R.id.left_aligned_notification_icon_container);
522         mNotificationIconAreaController.setupAodIcons(nic);
523     }
524 
setClock(ClockController clock)525     private void setClock(ClockController clock) {
526         if (clock != null && mLogBuffer != null) {
527             mLogBuffer.log(TAG, LogLevel.INFO, "New Clock");
528         }
529 
530         mClockEventController.setClock(clock);
531         mView.setClock(clock, mStatusBarStateController.getState());
532         setDateWeatherVisibility();
533     }
534 
535     @Nullable
getClock()536     public ClockController getClock() {
537         return mClockEventController.getClock();
538     }
539 
getCurrentLayoutDirection()540     private int getCurrentLayoutDirection() {
541         return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault());
542     }
543 
updateDoubleLineClock()544     private void updateDoubleLineClock() {
545         mCanShowDoubleLineClock = mSecureSettings.getIntForUser(
546             Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, 1,
547                 UserHandle.USER_CURRENT) != 0;
548 
549         if (!mCanShowDoubleLineClock) {
550             mUiExecutor.execute(() -> displayClock(KeyguardClockSwitch.SMALL, /* animate */ true));
551         }
552     }
553 
setDateWeatherVisibility()554     private void setDateWeatherVisibility() {
555         if (mDateWeatherView != null) {
556             mUiExecutor.execute(() -> {
557                 mDateWeatherView.setVisibility(clockHasCustomWeatherDataDisplay()
558                         ? mKeyguardDateWeatherViewInvisibility
559                         : View.VISIBLE);
560             });
561         }
562     }
563 
setWeatherVisibility()564     private void setWeatherVisibility() {
565         if (mWeatherView != null) {
566             mUiExecutor.execute(() -> {
567                 mWeatherView.setVisibility(
568                         mSmartspaceController.isWeatherEnabled() ? View.VISIBLE : View.GONE);
569             });
570         }
571     }
572 
updateKeyguardStatusAreaVisibility()573     private void updateKeyguardStatusAreaVisibility() {
574         if (mStatusArea != null) {
575             mUiExecutor.execute(() -> {
576                 mStatusArea.setVisibility(
577                         mIsActiveDreamLockscreenHosted ? View.INVISIBLE : View.VISIBLE);
578             });
579         }
580     }
581 
582     /**
583      * Sets the clipChildren property on relevant views, to allow the smartspace to draw out of
584      * bounds during the unlock transition.
585      */
setClipChildrenForUnlock(boolean clip)586     private void setClipChildrenForUnlock(boolean clip) {
587         if (mStatusArea != null) {
588             mStatusArea.setClipChildren(clip);
589         }
590     }
591 
592     @Override
dump(@onNull PrintWriter pw, @NonNull String[] args)593     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
594         pw.println("currentClockSizeLarge: " + (mCurrentClockSize == LARGE));
595         pw.println("mCanShowDoubleLineClock: " + mCanShowDoubleLineClock);
596         mView.dump(pw, args);
597         mClockRegistry.dump(pw, args);
598         ClockController clock = getClock();
599         if (clock != null) {
600             clock.dump(pw);
601         }
602         final RegionSampler smallRegionSampler = mClockEventController.getSmallRegionSampler();
603         if (smallRegionSampler != null) {
604             smallRegionSampler.dump(pw);
605         }
606         final RegionSampler largeRegionSampler = mClockEventController.getLargeRegionSampler();
607         if (largeRegionSampler != null) {
608             largeRegionSampler.dump(pw);
609         }
610     }
611 
612     /** Returns true if the clock handles the display of weather information */
clockHasCustomWeatherDataDisplay()613     private boolean clockHasCustomWeatherDataDisplay() {
614         ClockController clock = getClock();
615         if (clock == null) {
616             return false;
617         }
618 
619         return ((mCurrentClockSize == LARGE) ? clock.getLargeClock() : clock.getSmallClock())
620                 .getConfig().getHasCustomWeatherDataDisplay();
621     }
622 
removeViewsFromStatusArea()623     private void removeViewsFromStatusArea() {
624         for  (int i = mStatusArea.getChildCount() - 1; i >= 0; i--) {
625             final View childView = mStatusArea.getChildAt(i);
626             if (childView.getTag(R.id.tag_smartspace_view) != null) {
627                 mStatusArea.removeViewAt(i);
628             }
629         }
630     }
631 }
632