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