• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.systemui.charging;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.graphics.PixelFormat;
23 import android.os.Handler;
24 import android.os.Looper;
25 import android.os.Message;
26 import android.util.Log;
27 import android.util.Slog;
28 import android.view.Gravity;
29 import android.view.WindowManager;
30 
31 import com.android.internal.logging.UiEvent;
32 import com.android.internal.logging.UiEventLogger;
33 
34 /**
35  * A WirelessChargingAnimation is a view containing view + animation for wireless charging.
36  * @hide
37  */
38 public class WirelessChargingAnimation {
39 
40     public static final long DURATION = 1500;
41     private static final String TAG = "WirelessChargingView";
42     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
43 
44     private final WirelessChargingView mCurrentWirelessChargingView;
45     private static WirelessChargingView mPreviousWirelessChargingView;
46 
47     public interface Callback {
onAnimationStarting()48         void onAnimationStarting();
onAnimationEnded()49         void onAnimationEnded();
50     }
51 
52     /**
53      * Constructs an empty WirelessChargingAnimation object.  If looper is null,
54      * Looper.myLooper() is used.  Must set
55      * {@link WirelessChargingAnimation#mCurrentWirelessChargingView}
56      * before calling {@link #show} - can be done through {@link #makeWirelessChargingAnimation}.
57      * @hide
58      */
WirelessChargingAnimation(@onNull Context context, @Nullable Looper looper, int transmittingBatteryLevel, int batteryLevel, Callback callback, boolean isDozing, UiEventLogger uiEventLogger)59     public WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper,
60             int transmittingBatteryLevel, int batteryLevel, Callback callback, boolean isDozing,
61             UiEventLogger uiEventLogger) {
62         mCurrentWirelessChargingView = new WirelessChargingView(context, looper,
63                 transmittingBatteryLevel, batteryLevel, callback, isDozing, uiEventLogger);
64     }
65 
66     /**
67      * Creates a wireless charging animation object populated with next view.
68      *
69      * @hide
70      */
makeWirelessChargingAnimation(@onNull Context context, @Nullable Looper looper, int transmittingBatteryLevel, int batteryLevel, Callback callback, boolean isDozing, UiEventLogger uiEventLogger)71     public static WirelessChargingAnimation makeWirelessChargingAnimation(@NonNull Context context,
72             @Nullable Looper looper, int transmittingBatteryLevel, int batteryLevel,
73             Callback callback, boolean isDozing, UiEventLogger uiEventLogger) {
74         return new WirelessChargingAnimation(context, looper, transmittingBatteryLevel,
75                 batteryLevel, callback, isDozing, uiEventLogger);
76     }
77 
78     /**
79      * Show the view for the specified duration.
80      */
show(long delay)81     public void show(long delay) {
82         if (mCurrentWirelessChargingView == null ||
83                 mCurrentWirelessChargingView.mNextView == null) {
84             throw new RuntimeException("setView must have been called");
85         }
86 
87         if (mPreviousWirelessChargingView != null) {
88             mPreviousWirelessChargingView.hide(0);
89         }
90 
91         mPreviousWirelessChargingView = mCurrentWirelessChargingView;
92         mCurrentWirelessChargingView.show(delay);
93         mCurrentWirelessChargingView.hide(delay + DURATION);
94     }
95 
96     private static class WirelessChargingView {
97         private static final int SHOW = 0;
98         private static final int HIDE = 1;
99 
100         private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
101         private final Handler mHandler;
102         private final UiEventLogger mUiEventLogger;
103 
104         private int mGravity;
105         private WirelessChargingLayout mView;
106         private WirelessChargingLayout mNextView;
107         private WindowManager mWM;
108         private Callback mCallback;
109 
WirelessChargingView(Context context, @Nullable Looper looper, int transmittingBatteryLevel, int batteryLevel, Callback callback, boolean isDozing, UiEventLogger uiEventLogger)110         public WirelessChargingView(Context context, @Nullable Looper looper,
111                 int transmittingBatteryLevel, int batteryLevel, Callback callback,
112                 boolean isDozing, UiEventLogger uiEventLogger) {
113             mCallback = callback;
114             mNextView = new WirelessChargingLayout(context, transmittingBatteryLevel, batteryLevel,
115                     isDozing);
116             mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
117             mUiEventLogger = uiEventLogger;
118 
119             final WindowManager.LayoutParams params = mParams;
120             params.height = WindowManager.LayoutParams.MATCH_PARENT;
121             params.width = WindowManager.LayoutParams.MATCH_PARENT;
122             params.format = PixelFormat.TRANSLUCENT;
123             params.type = WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
124             params.setTitle("Charging Animation");
125             params.layoutInDisplayCutoutMode =
126                     WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
127             params.setFitInsetsTypes(0 /* ignore all system bar insets */);
128             params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
129                     | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
130             params.setTrustedOverlay();
131 
132             if (looper == null) {
133                 // Use Looper.myLooper() if looper is not specified.
134                 looper = Looper.myLooper();
135                 if (looper == null) {
136                     throw new RuntimeException(
137                             "Can't display wireless animation on a thread that has not called "
138                                     + "Looper.prepare()");
139                 }
140             }
141 
142             mHandler = new Handler(looper, null) {
143                 @Override
144                 public void handleMessage(Message msg) {
145                     switch (msg.what) {
146                         case SHOW: {
147                             handleShow();
148                             break;
149                         }
150                         case HIDE: {
151                             handleHide();
152                             // Don't do this in handleHide() because it is also invoked by
153                             // handleShow()
154                             mNextView = null;
155                             break;
156                         }
157                     }
158                 }
159             };
160         }
161 
show(long delay)162         public void show(long delay) {
163             if (DEBUG) Slog.d(TAG, "SHOW: " + this);
164             mHandler.sendMessageDelayed(Message.obtain(mHandler, SHOW), delay);
165         }
166 
hide(long duration)167         public void hide(long duration) {
168             mHandler.removeMessages(HIDE);
169 
170             if (DEBUG) Slog.d(TAG, "HIDE: " + this);
171             mHandler.sendMessageDelayed(Message.obtain(mHandler, HIDE), duration);
172         }
173 
handleShow()174         private void handleShow() {
175             if (DEBUG) {
176                 Slog.d(TAG, "HANDLE SHOW: " + this + " mView=" + mView + " mNextView="
177                         + mNextView);
178             }
179 
180             if (mView != mNextView) {
181                 // remove the old view if necessary
182                 handleHide();
183                 mView = mNextView;
184                 Context context = mView.getContext().getApplicationContext();
185                 String packageName = mView.getContext().getOpPackageName();
186                 if (context == null) {
187                     context = mView.getContext();
188                 }
189                 mWM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
190                 mParams.packageName = packageName;
191                 mParams.hideTimeoutMilliseconds = DURATION;
192 
193                 if (mView.getParent() != null) {
194                     if (DEBUG) Slog.d(TAG, "REMOVE! " + mView + " in " + this);
195                     mWM.removeView(mView);
196                 }
197                 if (DEBUG) Slog.d(TAG, "ADD! " + mView + " in " + this);
198 
199                 try {
200                     if (mCallback != null) {
201                         mCallback.onAnimationStarting();
202                     }
203                     mWM.addView(mView, mParams);
204                     mUiEventLogger.log(WirelessChargingRippleEvent.WIRELESS_RIPPLE_PLAYED);
205                 } catch (WindowManager.BadTokenException e) {
206                     Slog.d(TAG, "Unable to add wireless charging view. " + e);
207                 }
208             }
209         }
210 
handleHide()211         private void handleHide() {
212             if (DEBUG) Slog.d(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
213             if (mView != null) {
214                 if (mView.getParent() != null) {
215                     if (DEBUG) Slog.d(TAG, "REMOVE! " + mView + " in " + this);
216                     if (mCallback != null) {
217                         mCallback.onAnimationEnded();
218                     }
219                     mWM.removeViewImmediate(mView);
220                 }
221 
222                 mView = null;
223             }
224         }
225 
226         enum WirelessChargingRippleEvent implements UiEventLogger.UiEventEnum {
227             @UiEvent(doc = "Wireless charging ripple effect played")
228             WIRELESS_RIPPLE_PLAYED(830);
229 
230             private final int mInt;
WirelessChargingRippleEvent(int id)231             WirelessChargingRippleEvent(int id) {
232                 mInt = id;
233             }
234 
getId()235             @Override public int getId() {
236                 return mInt;
237             }
238         }
239     }
240 }
241