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 android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.content.res.Resources;
24 import android.graphics.Color;
25 import android.icu.text.NumberFormat;
26 
27 import androidx.annotation.VisibleForTesting;
28 
29 import com.android.settingslib.Utils;
30 import com.android.systemui.R;
31 import com.android.systemui.broadcast.BroadcastDispatcher;
32 import com.android.systemui.dagger.qualifiers.Main;
33 import com.android.systemui.plugins.statusbar.StatusBarStateController;
34 import com.android.systemui.statusbar.policy.BatteryController;
35 import com.android.systemui.util.ViewController;
36 
37 import java.util.Locale;
38 import java.util.Objects;
39 import java.util.TimeZone;
40 
41 /**
42  * Controller for an AnimatableClockView on the keyguard. Instantiated by
43  * {@link KeyguardClockSwitchController}.
44  */
45 public class AnimatableClockController extends ViewController<AnimatableClockView> {
46     private static final int FORMAT_NUMBER = 1234567890;
47 
48     private final StatusBarStateController mStatusBarStateController;
49     private final BroadcastDispatcher mBroadcastDispatcher;
50     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
51     private final BatteryController mBatteryController;
52     private final int mDozingColor = Color.WHITE;
53     private int mLockScreenColor;
54 
55     private boolean mIsDozing;
56     private boolean mIsCharging;
57     private float mDozeAmount;
58     boolean mKeyguardShowing;
59     private Locale mLocale;
60 
61     private final NumberFormat mBurmeseNf = NumberFormat.getInstance(Locale.forLanguageTag("my"));
62     private final String mBurmeseNumerals;
63     private final float mBurmeseLineSpacing;
64     private final float mDefaultLineSpacing;
65 
AnimatableClockController( AnimatableClockView view, StatusBarStateController statusBarStateController, BroadcastDispatcher broadcastDispatcher, BatteryController batteryController, KeyguardUpdateMonitor keyguardUpdateMonitor, @Main Resources resources )66     public AnimatableClockController(
67             AnimatableClockView view,
68             StatusBarStateController statusBarStateController,
69             BroadcastDispatcher broadcastDispatcher,
70             BatteryController batteryController,
71             KeyguardUpdateMonitor keyguardUpdateMonitor,
72             @Main Resources resources
73     ) {
74         super(view);
75         mStatusBarStateController = statusBarStateController;
76         mBroadcastDispatcher = broadcastDispatcher;
77         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
78         mBatteryController = batteryController;
79 
80         mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER);
81         mBurmeseLineSpacing = resources.getFloat(
82                 R.dimen.keyguard_clock_line_spacing_scale_burmese);
83         mDefaultLineSpacing = resources.getFloat(
84                 R.dimen.keyguard_clock_line_spacing_scale);
85     }
86 
reset()87     private void reset() {
88         mView.animateDoze(mIsDozing, false);
89     }
90 
91     private final BatteryController.BatteryStateChangeCallback mBatteryCallback =
92             new BatteryController.BatteryStateChangeCallback() {
93         @Override
94         public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
95             if (mKeyguardShowing && !mIsCharging && charging) {
96                 mView.animateCharge(mStatusBarStateController::isDozing);
97             }
98             mIsCharging = charging;
99         }
100     };
101 
102     private final BroadcastReceiver mLocaleBroadcastReceiver = new BroadcastReceiver() {
103         @Override
104         public void onReceive(Context context, Intent intent) {
105             updateLocale();
106         }
107     };
108 
109     private final StatusBarStateController.StateListener mStatusBarStateListener =
110             new StatusBarStateController.StateListener() {
111                 @Override
112                 public void onDozeAmountChanged(float linear, float eased) {
113                     boolean noAnimation = (mDozeAmount == 0f && linear == 1f)
114                             || (mDozeAmount == 1f && linear == 0f);
115                     boolean isDozing = linear > mDozeAmount;
116                     mDozeAmount = linear;
117                     if (mIsDozing != isDozing) {
118                         mIsDozing = isDozing;
119                         mView.animateDoze(mIsDozing, !noAnimation);
120                     }
121                 }
122             };
123 
124     private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
125             new KeyguardUpdateMonitorCallback() {
126         @Override
127         public void onKeyguardVisibilityChanged(boolean showing) {
128             mKeyguardShowing = showing;
129             if (!mKeyguardShowing) {
130                 // reset state (ie: after weight animations)
131                 reset();
132             }
133         }
134     };
135 
136     @Override
onInit()137     protected void onInit() {
138         mIsDozing = mStatusBarStateController.isDozing();
139     }
140 
141     @Override
onViewAttached()142     protected void onViewAttached() {
143         updateLocale();
144         mBroadcastDispatcher.registerReceiver(mLocaleBroadcastReceiver,
145                 new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
146         mDozeAmount = mStatusBarStateController.getDozeAmount();
147         mIsDozing = mStatusBarStateController.isDozing() || mDozeAmount != 0;
148         mBatteryController.addCallback(mBatteryCallback);
149         mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
150 
151         mStatusBarStateController.addCallback(mStatusBarStateListener);
152 
153         refreshTime();
154         initColors();
155         mView.animateDoze(mIsDozing, false);
156     }
157 
158     @Override
onViewDetached()159     protected void onViewDetached() {
160         mBroadcastDispatcher.unregisterReceiver(mLocaleBroadcastReceiver);
161         mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
162         mBatteryController.removeCallback(mBatteryCallback);
163         mStatusBarStateController.removeCallback(mStatusBarStateListener);
164     }
165 
166     /** Animate the clock appearance */
animateAppear()167     public void animateAppear() {
168         if (!mIsDozing) mView.animateAppearOnLockscreen();
169     }
170 
171     /**
172      * Updates the time for the view.
173      */
refreshTime()174     public void refreshTime() {
175         mView.refreshTime();
176     }
177 
178     /**
179      * Updates the timezone for the view.
180      */
onTimeZoneChanged(TimeZone timeZone)181     public void onTimeZoneChanged(TimeZone timeZone) {
182         mView.onTimeZoneChanged(timeZone);
183     }
184 
185     /**
186      * Trigger a time format update
187      */
refreshFormat()188     public void refreshFormat() {
189         mView.refreshFormat();
190     }
191 
192     /**
193      * Return locallly stored dozing state.
194      */
195     @VisibleForTesting
isDozing()196     public boolean isDozing() {
197         return mIsDozing;
198     }
199 
updateLocale()200     private void updateLocale() {
201         Locale currLocale = Locale.getDefault();
202         if (!Objects.equals(currLocale, mLocale)) {
203             mLocale = currLocale;
204             NumberFormat nf = NumberFormat.getInstance(mLocale);
205             if (nf.format(FORMAT_NUMBER).equals(mBurmeseNumerals)) {
206                 mView.setLineSpacingScale(mBurmeseLineSpacing);
207             } else {
208                 mView.setLineSpacingScale(mDefaultLineSpacing);
209             }
210             mView.refreshFormat();
211         }
212     }
213 
initColors()214     private void initColors() {
215         mLockScreenColor = Utils.getColorAttrDefaultColor(getContext(),
216                 com.android.systemui.R.attr.wallpaperTextColorAccent);
217         mView.setColors(mDozingColor, mLockScreenColor);
218         mView.animateDoze(mIsDozing, false);
219     }
220 }
221