Modify WirelessChargingLayout for reverse charging

- add Tx battery level and Tx icon
- new LifecycleRegistry in StatusBar for BatteryStateChangeCallback

Bug: 153272360
Test: atest SystemUITests:StatusBarTest
Change-Id: Ibb719e45799f20a93875dc85ae60af75ed79cf14
diff --git a/packages/SystemUI/res/drawable/ic_reverse_charging.xml b/packages/SystemUI/res/drawable/ic_reverse_charging.xml
new file mode 100644
index 0000000..2268d86
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_reverse_charging.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="?attr/chargingAnimColor"
+        android:pathData="M18,16.5v4.17C18,21.4,17.4,22,16.66,22H7.33C6.6,22,6,21.4,6,20.67V15V5.33C6,4.6,6.6,4,7.33,4H9.5V2h5v2h2.17 C17.4,4,18,4.6,18,5.33V7.5h-2V6H8v9v5h8v-3.5H18z M13,15.5h-2V14c0-1.65,1.35-3,3-3h4V9l3,3l-3,3v-2h-4c-0.55,0-1,0.45-1,1V15.5z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/wireless_charging_layout.xml b/packages/SystemUI/res/layout/wireless_charging_layout.xml
index 4610409..730f24f 100644
--- a/packages/SystemUI/res/layout/wireless_charging_layout.xml
+++ b/packages/SystemUI/res/layout/wireless_charging_layout.xml
@@ -36,14 +36,26 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
-        android:orientation="vertical">
+        android:orientation="horizontal">
 
         <TextView
+            android:id="@+id/reverse_wireless_charging_percentage"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:visibility="gone"/>
+        <ImageView
+            android:id="@+id/reverse_wireless_charging_icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:src="@drawable/ic_reverse_charging"
+            android:visibility="gone"/>
+        <TextView
             android:id="@+id/wireless_charging_percentage"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:textSize="24sp"/>
+            android:layout_gravity="center"/>
     </LinearLayout>
 
-</FrameLayout>
\ No newline at end of file
+</FrameLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index d5b54f9..2569f7c 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -54,25 +54,28 @@
      * before calling {@link #show} - can be done through {@link #makeWirelessChargingAnimation}.
      * @hide
      */
-    public WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper, int
-            batteryLevel, Callback callback, boolean isDozing) {
+    public WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper,
+            int transmittingBatteryLevel, int batteryLevel, Callback callback, boolean isDozing) {
         mCurrentWirelessChargingView = new WirelessChargingView(context, looper,
-                batteryLevel, callback, isDozing);
+                transmittingBatteryLevel, batteryLevel, callback, isDozing);
     }
 
     /**
      * Creates a wireless charging animation object populated with next view.
+     *
      * @hide
      */
     public static WirelessChargingAnimation makeWirelessChargingAnimation(@NonNull Context context,
-            @Nullable Looper looper, int batteryLevel, Callback callback, boolean isDozing) {
-        return new WirelessChargingAnimation(context, looper, batteryLevel, callback, isDozing);
+            @Nullable Looper looper, int transmittingBatteryLevel, int batteryLevel,
+            Callback callback, boolean isDozing) {
+        return new WirelessChargingAnimation(context, looper, transmittingBatteryLevel,
+                batteryLevel, callback, isDozing);
     }
 
     /**
      * Show the view for the specified duration.
      */
-    public void show() {
+    public void show(long delay) {
         if (mCurrentWirelessChargingView == null ||
                 mCurrentWirelessChargingView.mNextView == null) {
             throw new RuntimeException("setView must have been called");
@@ -83,8 +86,8 @@
         }
 
         mPreviousWirelessChargingView = mCurrentWirelessChargingView;
-        mCurrentWirelessChargingView.show();
-        mCurrentWirelessChargingView.hide(DURATION);
+        mCurrentWirelessChargingView.show(delay);
+        mCurrentWirelessChargingView.hide(delay + DURATION);
     }
 
     private static class WirelessChargingView {
@@ -100,10 +103,12 @@
         private WindowManager mWM;
         private Callback mCallback;
 
-        public WirelessChargingView(Context context, @Nullable Looper looper, int batteryLevel,
-                Callback callback, boolean isDozing) {
+        public WirelessChargingView(Context context, @Nullable Looper looper,
+                int transmittingBatteryLevel, int batteryLevel, Callback callback,
+                boolean isDozing) {
             mCallback = callback;
-            mNextView = new WirelessChargingLayout(context, batteryLevel, isDozing);
+            mNextView = new WirelessChargingLayout(context, transmittingBatteryLevel, batteryLevel,
+                    isDozing);
             mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
 
             final WindowManager.LayoutParams params = mParams;
@@ -149,9 +154,9 @@
             };
         }
 
-        public void show() {
+        public void show(long delay) {
             if (DEBUG) Slog.d(TAG, "SHOW: " + this);
-            mHandler.obtainMessage(SHOW).sendToTarget();
+            mHandler.sendMessageDelayed(Message.obtain(mHandler, SHOW), delay);
         }
 
         public void hide(long duration) {
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index ec150873..e8407f0 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.graphics.drawable.Animatable;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
 import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
@@ -37,16 +38,17 @@
  * @hide
  */
 public class WirelessChargingLayout extends FrameLayout {
-    private final static int UNKNOWN_BATTERY_LEVEL = -1;
+    public final static int UNKNOWN_BATTERY_LEVEL = -1;
 
     public WirelessChargingLayout(Context context) {
         super(context);
         init(context, null, false);
     }
 
-    public WirelessChargingLayout(Context context, int batteryLevel, boolean isDozing) {
+    public WirelessChargingLayout(Context context, int transmittingBatteryLevel, int batteryLevel,
+            boolean isDozing) {
         super(context);
-        init(context, null, batteryLevel, isDozing);
+        init(context, null, transmittingBatteryLevel, batteryLevel, isDozing);
     }
 
     public WirelessChargingLayout(Context context, AttributeSet attrs) {
@@ -55,11 +57,13 @@
     }
 
     private void init(Context c, AttributeSet attrs, boolean isDozing) {
-        init(c, attrs, -1, false);
+        init(c, attrs, -1, -1, false);
     }
 
-    private void init(Context context, AttributeSet attrs, int batteryLevel, boolean isDozing) {
-        final int mBatteryLevel = batteryLevel;
+    private void init(Context context, AttributeSet attrs, int transmittingBatteryLevel,
+            int batteryLevel, boolean isDozing) {
+        final boolean showTransmittingBatteryLevel =
+                (transmittingBatteryLevel != UNKNOWN_BATTERY_LEVEL);
 
         // set style based on background
         int style = R.style.ChargingAnim_WallpaperBackground;
@@ -74,39 +78,40 @@
         final Animatable chargingAnimation = (Animatable) chargingView.getDrawable();
 
         // amount of battery:
-        final TextView mPercentage = findViewById(R.id.wireless_charging_percentage);
+        final TextView percentage = findViewById(R.id.wireless_charging_percentage);
 
         if (batteryLevel != UNKNOWN_BATTERY_LEVEL) {
-            mPercentage.setText(NumberFormat.getPercentInstance().format(mBatteryLevel / 100f));
-            mPercentage.setAlpha(0);
+            percentage.setText(NumberFormat.getPercentInstance().format(batteryLevel / 100f));
+            percentage.setAlpha(0);
         }
 
-        final long chargingAnimationFadeStartOffset = (long) context.getResources().getInteger(
+        final long chargingAnimationFadeStartOffset = context.getResources().getInteger(
                 R.integer.wireless_charging_fade_offset);
-        final long chargingAnimationFadeDuration = (long) context.getResources().getInteger(
+        final long chargingAnimationFadeDuration = context.getResources().getInteger(
                 R.integer.wireless_charging_fade_duration);
         final float batteryLevelTextSizeStart = context.getResources().getFloat(
                 R.dimen.wireless_charging_anim_battery_level_text_size_start);
         final float batteryLevelTextSizeEnd = context.getResources().getFloat(
-                R.dimen.wireless_charging_anim_battery_level_text_size_end);
+                R.dimen.wireless_charging_anim_battery_level_text_size_end) * (
+                showTransmittingBatteryLevel ? 0.75f : 1.0f);
 
         // Animation Scale: battery percentage text scales from 0% to 100%
-        ValueAnimator textSizeAnimator = ObjectAnimator.ofFloat(mPercentage, "textSize",
+        ValueAnimator textSizeAnimator = ObjectAnimator.ofFloat(percentage, "textSize",
                 batteryLevelTextSizeStart, batteryLevelTextSizeEnd);
         textSizeAnimator.setInterpolator(new PathInterpolator(0, 0, 0, 1));
-        textSizeAnimator.setDuration((long) context.getResources().getInteger(
+        textSizeAnimator.setDuration(context.getResources().getInteger(
                 R.integer.wireless_charging_battery_level_text_scale_animation_duration));
 
         // Animation Opacity: battery percentage text transitions from 0 to 1 opacity
-        ValueAnimator textOpacityAnimator = ObjectAnimator.ofFloat(mPercentage, "alpha", 0, 1);
+        ValueAnimator textOpacityAnimator = ObjectAnimator.ofFloat(percentage, "alpha", 0, 1);
         textOpacityAnimator.setInterpolator(Interpolators.LINEAR);
-        textOpacityAnimator.setDuration((long) context.getResources().getInteger(
+        textOpacityAnimator.setDuration(context.getResources().getInteger(
                 R.integer.wireless_charging_battery_level_text_opacity_duration));
-        textOpacityAnimator.setStartDelay((long) context.getResources().getInteger(
+        textOpacityAnimator.setStartDelay(context.getResources().getInteger(
                 R.integer.wireless_charging_anim_opacity_offset));
 
         // Animation Opacity: battery percentage text fades from 1 to 0 opacity
-        ValueAnimator textFadeAnimator = ObjectAnimator.ofFloat(mPercentage, "alpha", 1, 0);
+        ValueAnimator textFadeAnimator = ObjectAnimator.ofFloat(percentage, "alpha", 1, 0);
         textFadeAnimator.setDuration(chargingAnimationFadeDuration);
         textFadeAnimator.setInterpolator(Interpolators.LINEAR);
         textFadeAnimator.setStartDelay(chargingAnimationFadeStartOffset);
@@ -114,7 +119,80 @@
         // play all animations together
         AnimatorSet animatorSet = new AnimatorSet();
         animatorSet.playTogether(textSizeAnimator, textOpacityAnimator, textFadeAnimator);
+
+        if (!showTransmittingBatteryLevel) {
+            chargingAnimation.start();
+            animatorSet.start();
+            return;
+        }
+
+        // amount of transmitting battery:
+        final TextView transmittingPercentage = findViewById(
+                R.id.reverse_wireless_charging_percentage);
+        transmittingPercentage.setVisibility(VISIBLE);
+        transmittingPercentage.setText(
+                NumberFormat.getPercentInstance().format(transmittingBatteryLevel / 100f));
+        transmittingPercentage.setAlpha(0);
+
+        // Animation Scale: transmitting battery percentage text scales from 0% to 100%
+        ValueAnimator textSizeAnimatorTransmitting = ObjectAnimator.ofFloat(transmittingPercentage,
+                "textSize", batteryLevelTextSizeStart, batteryLevelTextSizeEnd);
+        textSizeAnimatorTransmitting.setInterpolator(new PathInterpolator(0, 0, 0, 1));
+        textSizeAnimatorTransmitting.setDuration(context.getResources().getInteger(
+                R.integer.wireless_charging_battery_level_text_scale_animation_duration));
+
+        // Animation Opacity: transmitting battery percentage text transitions from 0 to 1 opacity
+        ValueAnimator textOpacityAnimatorTransmitting = ObjectAnimator.ofFloat(
+                transmittingPercentage, "alpha", 0, 1);
+        textOpacityAnimatorTransmitting.setInterpolator(Interpolators.LINEAR);
+        textOpacityAnimatorTransmitting.setDuration(context.getResources().getInteger(
+                R.integer.wireless_charging_battery_level_text_opacity_duration));
+        textOpacityAnimatorTransmitting.setStartDelay(
+                context.getResources().getInteger(R.integer.wireless_charging_anim_opacity_offset));
+
+        // Animation Opacity: transmitting battery percentage text fades from 1 to 0 opacity
+        ValueAnimator textFadeAnimatorTransmitting = ObjectAnimator.ofFloat(transmittingPercentage,
+                "alpha", 1, 0);
+        textFadeAnimatorTransmitting.setDuration(chargingAnimationFadeDuration);
+        textFadeAnimatorTransmitting.setInterpolator(Interpolators.LINEAR);
+        textFadeAnimatorTransmitting.setStartDelay(chargingAnimationFadeStartOffset);
+
+        // play all animations together
+        AnimatorSet animatorSetTransmitting = new AnimatorSet();
+        animatorSetTransmitting.playTogether(textSizeAnimatorTransmitting,
+                textOpacityAnimatorTransmitting, textFadeAnimatorTransmitting);
+
+        // transmitting battery icon
+        final ImageView chargingViewIcon = findViewById(R.id.reverse_wireless_charging_icon);
+        chargingViewIcon.setVisibility(VISIBLE);
+        final int padding = Math.round(
+                TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, batteryLevelTextSizeEnd,
+                        getResources().getDisplayMetrics()));
+        chargingViewIcon.setPadding(padding, 0, padding, 0);
+
+        // Animation Opacity: transmitting battery icon transitions from 0 to 1 opacity
+        ValueAnimator textOpacityAnimatorIcon = ObjectAnimator.ofFloat(chargingViewIcon, "alpha", 0,
+                1);
+        textOpacityAnimatorIcon.setInterpolator(Interpolators.LINEAR);
+        textOpacityAnimatorIcon.setDuration(context.getResources().getInteger(
+                R.integer.wireless_charging_battery_level_text_opacity_duration));
+        textOpacityAnimatorIcon.setStartDelay(
+                context.getResources().getInteger(R.integer.wireless_charging_anim_opacity_offset));
+
+        // Animation Opacity: transmitting battery icon fades from 1 to 0 opacity
+        ValueAnimator textFadeAnimatorIcon = ObjectAnimator.ofFloat(chargingViewIcon, "alpha", 1,
+                0);
+        textFadeAnimatorIcon.setDuration(chargingAnimationFadeDuration);
+        textFadeAnimatorIcon.setInterpolator(Interpolators.LINEAR);
+        textFadeAnimatorIcon.setStartDelay(chargingAnimationFadeStartOffset);
+
+        // play all animations together
+        AnimatorSet animatorSetIcon = new AnimatorSet();
+        animatorSetIcon.playTogether(textOpacityAnimatorIcon, textFadeAnimatorIcon);
+
         chargingAnimation.start();
         animatorSet.start();
+        animatorSetTransmitting.start();
+        animatorSetIcon.start();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 1bc42d1..e2f9018 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -29,7 +29,10 @@
 import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
 
+import static androidx.lifecycle.Lifecycle.State.RESUMED;
+
 import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
+import static com.android.systemui.charging.WirelessChargingLayout.UNKNOWN_BATTERY_LEVEL;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
@@ -45,6 +48,7 @@
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
 import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
@@ -113,6 +117,10 @@
 import android.view.accessibility.AccessibilityManager;
 import android.widget.DateTimeView;
 
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.logging.MetricsLogger;
@@ -236,7 +244,8 @@
         ActivityStarter, KeyguardStateController.Callback,
         OnHeadsUpChangedListener, CommandQueue.Callbacks,
         ColorExtractor.OnColorsChangedListener, ConfigurationListener,
-        StatusBarStateController.StateListener, ActivityLaunchAnimator.Callback {
+        StatusBarStateController.StateListener, ActivityLaunchAnimator.Callback,
+        LifecycleOwner, BatteryController.BatteryStateChangeCallback {
     public static final boolean MULTIUSER_DEBUG = false;
 
     protected static final int MSG_HIDE_RECENT_APPS = 1020;
@@ -585,7 +594,8 @@
     private KeyguardUserSwitcher mKeyguardUserSwitcher;
     private final UserSwitcherController mUserSwitcherController;
     private final NetworkController mNetworkController;
-    private final BatteryController mBatteryController;
+    private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
+    protected final BatteryController mBatteryController;
     protected boolean mPanelExpanded;
     private UiModeManager mUiModeManager;
     protected boolean mIsKeyguard;
@@ -934,6 +944,9 @@
 
         mConfigurationController.addCallback(this);
 
+        mBatteryController.observe(mLifecycle, this);
+        mLifecycle.setCurrentState(RESUMED);
+
         // set the initial view visibility
         int disabledFlags1 = result.mDisabledFlags1;
         int disabledFlags2 = result.mDisabledFlags2;
@@ -1100,22 +1113,6 @@
         mAmbientIndicationContainer = mNotificationShadeWindowView.findViewById(
                 R.id.ambient_indication_container);
 
-        // TODO: Find better place for this callback.
-        mBatteryController.addCallback(new BatteryStateChangeCallback() {
-            @Override
-            public void onPowerSaveChanged(boolean isPowerSave) {
-                mHandler.post(mCheckBarModes);
-                if (mDozeServiceHost != null) {
-                    mDozeServiceHost.firePowerSaveChanged(isPowerSave);
-                }
-            }
-
-            @Override
-            public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
-                // noop
-            }
-        });
-
         mAutoHideController.setStatusBar(new AutoHideUiElement() {
             @Override
             public void synchronizeState() {
@@ -1257,6 +1254,25 @@
         ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
     }
 
+    @NonNull
+    @Override
+    public Lifecycle getLifecycle() {
+        return mLifecycle;
+    }
+
+    @Override
+    public void onPowerSaveChanged(boolean isPowerSave) {
+        mHandler.post(mCheckBarModes);
+        if (mDozeServiceHost != null) {
+            mDozeServiceHost.firePowerSaveChanged(isPowerSave);
+        }
+    }
+
+    @Override
+    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+        // noop
+    }
+
     @VisibleForTesting
     protected void registerBroadcastReceiver() {
         IntentFilter filter = new IntentFilter();
@@ -2356,10 +2372,16 @@
 
     @Override
     public void showWirelessChargingAnimation(int batteryLevel) {
+        showChargingAnimation(batteryLevel, UNKNOWN_BATTERY_LEVEL, 0);
+    }
+
+    protected void showChargingAnimation(int batteryLevel, int transmittingBatteryLevel,
+            long animationDelay) {
         if (mDozing || mKeyguardManager.isKeyguardLocked()) {
             // on ambient or lockscreen, hide notification panel
             WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
-                    batteryLevel, new WirelessChargingAnimation.Callback() {
+                    transmittingBatteryLevel, batteryLevel,
+                    new WirelessChargingAnimation.Callback() {
                         @Override
                         public void onAnimationStarting() {
                             CrossFadeHelper.fadeOut(mNotificationPanelViewController.getView(), 1);
@@ -2369,11 +2391,11 @@
                         public void onAnimationEnded() {
                             CrossFadeHelper.fadeIn(mNotificationPanelViewController.getView());
                         }
-                    }, mDozing).show();
+                    }, mDozing).show(animationDelay);
         } else {
             // workspace
             WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
-                    batteryLevel, null, false).show();
+                    transmittingBatteryLevel, batteryLevel, null, false).show(animationDelay);
         }
     }