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