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 DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     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         private View mView;
     99         private View mNextView;
    100         private WindowManager mWM;
    101         private Callback mCallback;
    102 
    103         public WirelessChargingView(Context context, @Nullable Looper looper, int batteryLevel,
    104                 Callback callback, boolean isDozing) {
    105             mCallback = callback;
    106             mNextView = new WirelessChargingLayout(context, batteryLevel, isDozing);
    107             mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
    108 
    109             final WindowManager.LayoutParams params = mParams;
    110             params.height = WindowManager.LayoutParams.WRAP_CONTENT;
    111             params.width = WindowManager.LayoutParams.MATCH_PARENT;
    112             params.format = PixelFormat.TRANSLUCENT;
    113 
    114             params.type = WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
    115             params.setTitle("Charging Animation");
    116             params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
    117                     | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
    118                     | WindowManager.LayoutParams.FLAG_DIM_BEHIND;
    119 
    120             params.dimAmount = .3f;
    121 
    122             if (looper == null) {
    123                 // Use Looper.myLooper() if looper is not specified.
    124                 looper = Looper.myLooper();
    125                 if (looper == null) {
    126                     throw new RuntimeException(
    127                             "Can't display wireless animation on a thread that has not called "
    128                                     + "Looper.prepare()");
    129                 }
    130             }
    131 
    132             mHandler = new Handler(looper, null) {
    133                 @Override
    134                 public void handleMessage(Message msg) {
    135                     switch (msg.what) {
    136                         case SHOW: {
    137                             handleShow();
    138                             break;
    139                         }
    140                         case HIDE: {
    141                             handleHide();
    142                             // Don't do this in handleHide() because it is also invoked by
    143                             // handleShow()
    144                             mNextView = null;
    145                             break;
    146                         }
    147                     }
    148                 }
    149             };
    150         }
    151 
    152         public void show() {
    153             if (DEBUG) Slog.d(TAG, "SHOW: " + this);
    154             mHandler.obtainMessage(SHOW).sendToTarget();
    155         }
    156 
    157         public void hide(long duration) {
    158             mHandler.removeMessages(HIDE);
    159 
    160             if (DEBUG) Slog.d(TAG, "HIDE: " + this);
    161             mHandler.sendMessageDelayed(Message.obtain(mHandler, HIDE), duration);
    162         }
    163 
    164         private void handleShow() {
    165             if (DEBUG) {
    166                 Slog.d(TAG, "HANDLE SHOW: " + this + " mView=" + mView + " mNextView="
    167                         + mNextView);
    168             }
    169 
    170             if (mView != mNextView) {
    171                 // remove the old view if necessary
    172                 handleHide();
    173                 mView = mNextView;
    174                 Context context = mView.getContext().getApplicationContext();
    175                 String packageName = mView.getContext().getOpPackageName();
    176                 if (context == null) {
    177                     context = mView.getContext();
    178                 }
    179                 mWM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    180                 mParams.packageName = packageName;
    181                 mParams.hideTimeoutMilliseconds = DURATION;
    182 
    183                 if (mView.getParent() != null) {
    184                     if (DEBUG) Slog.d(TAG, "REMOVE! " + mView + " in " + this);
    185                     mWM.removeView(mView);
    186                 }
    187                 if (DEBUG) Slog.d(TAG, "ADD! " + mView + " in " + this);
    188 
    189                 try {
    190                     if (mCallback != null) {
    191                         mCallback.onAnimationStarting();
    192                     }
    193                     mWM.addView(mView, mParams);
    194                 } catch (WindowManager.BadTokenException e) {
    195                     Slog.d(TAG, "Unable to add wireless charging view. " + e);
    196                 }
    197             }
    198         }
    199 
    200         private void handleHide() {
    201             if (DEBUG) Slog.d(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
    202             if (mView != null) {
    203                 if (mView.getParent() != null) {
    204                     if (DEBUG) Slog.d(TAG, "REMOVE! " + mView + " in " + this);
    205                     if (mCallback != null) {
    206                         mCallback.onAnimationEnded();
    207                     }
    208                     mWM.removeViewImmediate(mView);
    209                 }
    210 
    211                 mView = null;
    212             }
    213         }
    214     }
    215 }
    216