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