Basic workspace wireless charging animation
(No animation on ambient or lockscreen yet)
Test: manual (testing w/o wireless charger, set DEBUG_WIRELESS=true)
Bug: 67598445
Change-Id: Ifeef72805930187b8f78e72fd44bd7da25724e0c
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index f5af80a..ebb5f9f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -35,6 +35,8 @@
void animateCollapsePanels();
void togglePanel();
+ void showChargingAnimation(int batteryLevel);
+
/**
* Notifies the status bar of a System UI visibility flag change.
*
diff --git a/packages/SystemUI/res/layout/wireless_charging_layout.xml b/packages/SystemUI/res/layout/wireless_charging_layout.xml
new file mode 100644
index 0000000..113282b
--- /dev/null
+++ b/packages/SystemUI/res/layout/wireless_charging_layout.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2018, 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.
+*/
+-->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <!-- Circle animation -->
+ <com.android.systemui.charging.WirelessChargingView
+ android:id="@+id/wireless_charging_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:elevation="4dp"/>
+
+ <!-- Text inside circle -->
+ <LinearLayout
+ android:id="@+id/wireless_charging_text_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/wireless_charging_percentage"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:textSize="32sp"
+ android:textColor="?attr/wallpaperTextColor"/>
+
+ <TextView
+ android:id="@+id/wireless_charging_secondary_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:textColor="?attr/wallpaperTextColor"/>
+ </LinearLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b749a18..e702e2a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -893,4 +893,12 @@
<dimen name="fingerprint_dialog_icon_size">44dp</dimen>
<dimen name="fingerprint_dialog_fp_icon_size">60dp</dimen>
<dimen name="fingerprint_dialog_animation_translation_offset">350dp</dimen>
+
+ <!-- WirelessCharging Animation values -->
+ <!-- Starting text size of batteryLevel for wireless charging animation -->
+ <dimen name="config_batteryLevelTextSizeStart" format="float">5.0</dimen>
+ <!-- Ending text size of batteryLevel for wireless charging animation -->
+ <dimen name="config_batteryLevelTextSizeEnd" format="float">32.0</dimen>
+ <!-- Wireless Charging battery level text animation duration -->
+ <integer name="config_batteryLevelTextAnimationDuration">400</integer>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
new file mode 100644
index 0000000..348855b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package com.android.systemui.charging;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.View;
+import android.view.WindowManager;
+
+/**
+ * A WirelessChargingAnimation is a view containing view + animation for wireless charging.
+ * @hide
+ */
+public class WirelessChargingAnimation {
+
+ public static final long DURATION = 1400;
+ private static final String TAG = "WirelessChargingView";
+ private static final boolean LOCAL_LOGV = false;
+
+ private final WirelessChargingView mCurrentWirelessChargingView;
+ private static WirelessChargingView mPreviousWirelessChargingView;
+
+ /**
+ * Constructs an empty WirelessChargingAnimation object. If looper is null,
+ * Looper.myLooper() is used. Must set
+ * {@link WirelessChargingAnimation#mCurrentWirelessChargingView}
+ * before calling {@link #show} - can be done through {@link #makeWirelessChargingAnimation}.
+ * @hide
+ */
+ public WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper, int
+ batteryLevel) {
+ mCurrentWirelessChargingView = new WirelessChargingView(context, looper,
+ batteryLevel);
+ }
+
+ /**
+ * Creates a wireless charging animation object populated with next view.
+ * @hide
+ */
+ public static WirelessChargingAnimation makeWirelessChargingAnimation(@NonNull Context context,
+ @Nullable Looper looper, int batteryLevel) {
+ return new WirelessChargingAnimation(context, looper, batteryLevel);
+ }
+
+ /**
+ * Show the view for the specified duration.
+ */
+ public void show() {
+ if (mCurrentWirelessChargingView == null ||
+ mCurrentWirelessChargingView.mNextView == null) {
+ throw new RuntimeException("setView must have been called");
+ }
+
+ if (mPreviousWirelessChargingView != null) {
+ mPreviousWirelessChargingView.hide(0);
+ }
+
+ mPreviousWirelessChargingView = mCurrentWirelessChargingView;
+ mCurrentWirelessChargingView.show();
+ mCurrentWirelessChargingView.hide(DURATION);
+ }
+
+ private static class WirelessChargingView {
+ private static final int SHOW = 0;
+ private static final int HIDE = 1;
+
+ private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
+ private final Handler mHandler;
+
+ private int mGravity;
+
+ private View mView;
+ private View mNextView;
+ private WindowManager mWM;
+
+ public WirelessChargingView(Context context, @Nullable Looper looper, int batteryLevel) {
+ mNextView = new WirelessChargingLayout(context, batteryLevel);
+ mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
+
+ final WindowManager.LayoutParams params = mParams;
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ params.width = WindowManager.LayoutParams.WRAP_CONTENT;
+ params.format = PixelFormat.TRANSLUCENT;
+
+ params.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
+ params.setTitle("Charging Animation");
+ params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+
+ params.dimAmount = .3f;
+
+ if (looper == null) {
+ // Use Looper.myLooper() if looper is not specified.
+ looper = Looper.myLooper();
+ if (looper == null) {
+ throw new RuntimeException(
+ "Can't display wireless animation on a thread that has not called "
+ + "Looper.prepare()");
+ }
+ }
+
+ mHandler = new Handler(looper, null) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case SHOW: {
+ handleShow();
+ break;
+ }
+ case HIDE: {
+ handleHide();
+ // Don't do this in handleHide() because it is also invoked by
+ // handleShow()
+ mNextView = null;
+ break;
+ }
+ }
+ }
+ };
+ }
+
+ public void show() {
+ if (LOCAL_LOGV) Log.v(TAG, "SHOW: " + this);
+ mHandler.obtainMessage(SHOW).sendToTarget();
+ }
+
+ public void hide(long duration) {
+ if (LOCAL_LOGV) Log.v(TAG, "HIDE: " + this);
+ mHandler.sendMessageDelayed(Message.obtain(mHandler, HIDE), duration);
+ }
+
+ private void handleShow() {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView + " mNextView="
+ + mNextView);
+ }
+
+ if (mView != mNextView) {
+ // remove the old view if necessary
+ handleHide();
+ mView = mNextView;
+ Context context = mView.getContext().getApplicationContext();
+ String packageName = mView.getContext().getOpPackageName();
+ if (context == null) {
+ context = mView.getContext();
+ }
+ mWM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ // We can resolve the Gravity here by using the Locale for getting
+ // the layout direction
+ final Configuration config = mView.getContext().getResources().getConfiguration();
+ final int gravity = Gravity.getAbsoluteGravity(mGravity,
+ config.getLayoutDirection());
+ mParams.gravity = gravity;
+ if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
+ mParams.horizontalWeight = 1.0f;
+ }
+ if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
+ mParams.verticalWeight = 1.0f;
+ }
+ mParams.packageName = packageName;
+ mParams.hideTimeoutMilliseconds = DURATION;
+
+ if (mView.getParent() != null) {
+ if (LOCAL_LOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
+ mWM.removeView(mView);
+ }
+ if (LOCAL_LOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
+
+ try {
+ mWM.addView(mView, mParams);
+ } catch (WindowManager.BadTokenException e) {
+ Slog.d(TAG, "Unable to add wireless charging view. " + e);
+ }
+ }
+ }
+
+ private void handleHide() {
+ if (LOCAL_LOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
+ if (mView != null) {
+ if (mView.getParent() != null) {
+ if (LOCAL_LOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
+ mWM.removeViewImmediate(mView);
+ }
+
+ mView = null;
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
new file mode 100644
index 0000000..c78ea56
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package com.android.systemui.charging;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+
+import java.text.NumberFormat;
+
+/**
+ * @hide
+ */
+public class WirelessChargingLayout extends FrameLayout {
+ private final static int UNKNOWN_BATTERY_LEVEL = -1;
+
+ public WirelessChargingLayout(Context context) {
+ super(context);
+ init(context, null);
+ }
+
+ public WirelessChargingLayout(Context context, int batterylLevel) {
+ super(context);
+ init(context, null, batterylLevel);
+ }
+
+ public WirelessChargingLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs);
+ }
+
+ private void init(Context c, AttributeSet attrs) {
+ init(c, attrs, -1);
+ }
+
+ private void init(Context c, AttributeSet attrs, int batteryLevel) {
+ final int mBatteryLevel = batteryLevel;
+
+ inflate(c, R.layout.wireless_charging_layout, this);
+
+ // where the circle animation occurs:
+ final WirelessChargingView mChargingView = findViewById(R.id.wireless_charging_view);
+
+ // amount of battery:
+ final TextView mPercentage = findViewById(R.id.wireless_charging_percentage);
+
+ // (optional) time until full charge if available
+ final TextView mSecondaryText = findViewById(R.id.wireless_charging_secondary_text);
+
+ if (batteryLevel != UNKNOWN_BATTERY_LEVEL) {
+ mPercentage.setText(NumberFormat.getPercentInstance().format(mBatteryLevel / 100f));
+
+ ValueAnimator animator = ObjectAnimator.ofFloat(mPercentage, "textSize",
+ getContext().getResources().getFloat(R.dimen.config_batteryLevelTextSizeStart),
+ getContext().getResources().getFloat(R.dimen.config_batteryLevelTextSizeEnd));
+
+ animator.setDuration((long) getContext().getResources().getInteger(
+ R.integer.config_batteryLevelTextAnimationDuration));
+ animator.start();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java
new file mode 100644
index 0000000..f5edf52
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package com.android.systemui.charging;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
+
+final class WirelessChargingView extends View {
+
+ private Interpolator mInterpolator;
+ private float mPathGone;
+ private float mInterpolatedPathGone;
+ private long mAnimationStartTime;
+ private long mStartSpinCircleAnimationTime;
+ private long mAnimationOffset = 500;
+ private long mTotalAnimationDuration = WirelessChargingAnimation.DURATION - mAnimationOffset;
+ private long mExpandingCircle = (long) (mTotalAnimationDuration * .9);
+ private long mSpinCircleAnimationTime = mTotalAnimationDuration - mExpandingCircle;
+
+ private boolean mFinishedAnimatingSpinningCircles = false;
+
+ private int mStartAngle = -90;
+ private int mNumSmallCircles = 20;
+ private int mSmallCircleRadius = 10;
+
+ private int mMainCircleStartRadius = 100;
+ private int mMainCircleEndRadius = 230;
+ private int mMainCircleCurrentRadius = mMainCircleStartRadius;
+
+ private int mCenterX;
+ private int mCenterY;
+
+ private Paint mPaint;
+ private Context mContext;
+
+ public WirelessChargingView(Context context) {
+ super(context);
+ init(context, null);
+ }
+
+ public WirelessChargingView(Context context, AttributeSet attr) {
+ super(context, attr);
+ init(context, attr);
+ }
+
+ public WirelessChargingView(Context context, AttributeSet attr, int styleAttr) {
+ super(context, attr, styleAttr);
+ init(context, attr);
+ }
+
+ public void init(Context context, AttributeSet attr) {
+ mContext = context;
+ setupPaint();
+ mInterpolator = new DecelerateInterpolator();
+ }
+
+ private void setupPaint() {
+ mPaint = new Paint();
+ mPaint.setColor(Utils.getColorAttr(mContext, R.attr.wallpaperTextColor));
+ }
+
+ @Override
+ protected void onDraw(final Canvas canvas) {
+ super.onDraw(canvas);
+
+ if (mAnimationStartTime == 0) {
+ mAnimationStartTime = System.currentTimeMillis();
+ }
+
+ updateDrawingParameters();
+ drawCircles(canvas);
+
+ if (!mFinishedAnimatingSpinningCircles) {
+ invalidate();
+ }
+ }
+
+ /**
+ * Draws a larger circle of radius {@link WirelessChargingView#mMainCircleEndRadius} composed of
+ * {@link WirelessChargingView#mNumSmallCircles} smaller circles
+ * @param canvas
+ */
+ private void drawCircles(Canvas canvas) {
+ mCenterX = canvas.getWidth() / 2;
+ mCenterY = canvas.getHeight() / 2;
+
+ // angleOffset makes small circles look like they're moving around the main circle
+ float angleOffset = mPathGone * 10;
+
+ // draws mNumSmallCircles to compose a larger, main circle
+ for (int circle = 0; circle < mNumSmallCircles; circle++) {
+ double angle = ((mStartAngle + angleOffset) * Math.PI / 180) + (circle * ((2 * Math.PI)
+ / mNumSmallCircles));
+
+ int x = (int) (mCenterX + Math.cos(angle) * (mMainCircleCurrentRadius +
+ mSmallCircleRadius));
+ int y = (int) (mCenterY + Math.sin(angle) * (mMainCircleCurrentRadius +
+ mSmallCircleRadius));
+
+ canvas.drawCircle(x, y, mSmallCircleRadius, mPaint);
+ }
+
+ if (mMainCircleCurrentRadius >= mMainCircleEndRadius && !isSpinCircleAnimationStarted()) {
+ mStartSpinCircleAnimationTime = System.currentTimeMillis();
+ }
+
+ if (isSpinAnimationFinished()) {
+ mFinishedAnimatingSpinningCircles = true;
+ }
+ }
+
+ private boolean isSpinCircleAnimationStarted() {
+ return mStartSpinCircleAnimationTime != 0;
+ }
+
+ private boolean isSpinAnimationFinished() {
+ return isSpinCircleAnimationStarted() && System.currentTimeMillis() -
+ mStartSpinCircleAnimationTime > mSpinCircleAnimationTime;
+ }
+
+ private void updateDrawingParameters() {
+ mPathGone = getPathGone(System.currentTimeMillis());
+ mInterpolatedPathGone = mInterpolator.getInterpolation(mPathGone);
+
+ if (mPathGone < 1.0f) {
+ mMainCircleCurrentRadius = mMainCircleStartRadius + (int) (mInterpolatedPathGone *
+ (mMainCircleEndRadius - mMainCircleStartRadius));
+ } else {
+ mMainCircleCurrentRadius = mMainCircleEndRadius;
+ }
+ }
+
+ /**
+ * @return decimal depicting how far along the creation of the larger circle (of circles) is
+ * For values < 1.0, the larger circle is being drawn
+ * For values > 1.0 the larger circle has been drawn and further animation can occur
+ */
+ private float getPathGone(long now) {
+ return (float) (now - mAnimationStartTime) / (mExpandingCircle);
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
index f06cda0..aa08562 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -14,6 +14,10 @@
package com.android.systemui.globalactions;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dependency;
import com.android.systemui.SysUiServiceProvider;
@@ -25,10 +29,6 @@
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.ExtensionController.Extension;
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-
public class GlobalActionsComponent extends SystemUI implements Callbacks, GlobalActionsManager {
private GlobalActions mPlugin;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 00bc62e..79e9f7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -89,6 +89,7 @@
private static final int MSG_FINGERPRINT_HELP = 41 << MSG_SHIFT;
private static final int MSG_FINGERPRINT_ERROR = 42 << MSG_SHIFT;
private static final int MSG_FINGERPRINT_HIDE = 43 << MSG_SHIFT;
+ private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -150,6 +151,8 @@
default void handleShowGlobalActionsMenu() { }
default void handleShowShutdownUi(boolean isReboot, String reason) { }
+ default void showChargingAnimation(int batteryLevel) { }
+
default void onRotationProposal(int rotation, boolean isValid) { }
default void showFingerprintDialog(Bundle bundle, IFingerprintDialogReceiver receiver) { }
@@ -474,6 +477,13 @@
}
@Override
+ public void showChargingAnimation(int batteryLevel) {
+ mHandler.removeMessages(MSG_SHOW_CHARGING_ANIMATION);
+ mHandler.obtainMessage(MSG_SHOW_CHARGING_ANIMATION, batteryLevel, 0)
+ .sendToTarget();
+ }
+
+ @Override
public void onProposedRotationChanged(int rotation, boolean isValid) {
synchronized (mLock) {
mHandler.removeMessages(MSG_ROTATION_PROPOSAL);
@@ -751,6 +761,12 @@
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).hideFingerprintDialog();
}
+ break;
+ case MSG_SHOW_CHARGING_ANIMATION:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).showChargingAnimation(msg.arg1);
+ }
+ break;
}
}
}
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 3b783c2..d13ecae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -85,6 +85,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -138,6 +139,7 @@
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.ActivityStarterDelegate;
import com.android.systemui.AutoReinflateContainer;
+import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.DemoMode;
import com.android.systemui.Dependency;
import com.android.systemui.EventLogTags;
@@ -1419,7 +1421,6 @@
mQSPanel.clickTile(tile);
}
-
private void updateClearAll() {
if (!mClearAllEnabled) {
return;
@@ -2422,6 +2423,18 @@
mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode);
}
+ @Override
+ public void showChargingAnimation(int batteryLevel) {
+ if (mDozing) {
+ // ambient
+ } else if (mKeyguardManager.isKeyguardLocked()) {
+ // lockscreen
+ } else {
+ WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
+ batteryLevel).show();
+ }
+ }
+
void touchAutoHide() {
// update transient bar autohide
if (mStatusBarMode == MODE_SEMI_TRANSPARENT || (mNavigationBar != null
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 2a153ec..a536270 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -19,15 +19,6 @@
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
-
-import com.android.internal.app.IAppOpsService;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.server.EventLogTags;
-import com.android.server.LocalServices;
-import com.android.server.policy.WindowManagerPolicy;
-
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -54,6 +45,15 @@
import android.util.Slog;
import android.view.inputmethod.InputMethodManagerInternal;
+import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
+import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.policy.WindowManagerPolicy;
+
/**
* Sends broadcasts about important power state changes.
* <p>
@@ -96,6 +96,7 @@
private final ActivityManagerInternal mActivityManagerInternal;
private final InputManagerInternal mInputManagerInternal;
private final InputMethodManagerInternal mInputMethodManagerInternal;
+ private final StatusBarManagerInternal mStatusBarManagerInternal;
private final TrustManager mTrustManager;
private final NotifierHandler mHandler;
@@ -142,6 +143,7 @@
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
+ mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
mTrustManager = mContext.getSystemService(TrustManager.class);
mHandler = new NotifierHandler(looper);
@@ -545,9 +547,19 @@
}
/**
- * Called when wireless charging has started so as to provide user feedback.
+ * Called when profile screen lock timeout has expired.
*/
- public void onWirelessChargingStarted() {
+ public void onProfileTimeout(@UserIdInt int userId) {
+ final Message msg = mHandler.obtainMessage(MSG_PROFILE_TIMED_OUT);
+ msg.setAsynchronous(true);
+ msg.arg1 = userId;
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Called when wireless charging has started so as to provide user feedback (sound and visual).
+ */
+ public void onWirelessChargingStarted(int batteryLevel) {
if (DEBUG) {
Slog.d(TAG, "onWirelessChargingStarted");
}
@@ -555,16 +567,7 @@
mSuspendBlocker.acquire();
Message msg = mHandler.obtainMessage(MSG_WIRELESS_CHARGING_STARTED);
msg.setAsynchronous(true);
- mHandler.sendMessage(msg);
- }
-
- /**
- * Called when profile screen lock timeout has expired.
- */
- public void onProfileTimeout(@UserIdInt int userId) {
- final Message msg = mHandler.obtainMessage(MSG_PROFILE_TIMED_OUT);
- msg.setAsynchronous(true);
- msg.arg1 = userId;
+ msg.arg1 = batteryLevel;
mHandler.sendMessage(msg);
}
@@ -715,7 +718,11 @@
}
}
}
+ }
+ private void showWirelessChargingStarted(int batteryLevel) {
+ playWirelessChargingStartedSound();
+ mStatusBarManagerInternal.showChargingAnimation(batteryLevel);
mSuspendBlocker.release();
}
@@ -738,7 +745,7 @@
sendNextBroadcast();
break;
case MSG_WIRELESS_CHARGING_STARTED:
- playWirelessChargingStartedSound();
+ showWirelessChargingStarted(msg.arg1);
break;
case MSG_SCREEN_BRIGHTNESS_BOOST_CHANGED:
sendBrightnessBoostChangedBroadcast();
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 7273f62..fbdedce 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -16,6 +16,11 @@
package com.android.server.power;
+import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
+import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
+import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
+import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
+
import android.annotation.IntDef;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
@@ -103,11 +108,6 @@
import java.util.ArrayList;
import java.util.Arrays;
-import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
-import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
-import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
-import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
-
/**
* The power manager service is responsible for coordinating power management
* functions on the device.
@@ -119,6 +119,9 @@
private static final boolean DEBUG = false;
private static final boolean DEBUG_SPEW = DEBUG && true;
+ // if DEBUG_WIRELESS=true, plays wireless charging animation w/ sound on every plug + unplug
+ private static final boolean DEBUG_WIRELESS = false;
+
// Message: Sent when a user activity timeout occurs to update the power state.
private static final int MSG_USER_ACTIVITY_TIMEOUT = 1;
// Message: Sent when the device enters or exits a dreaming or dozing state.
@@ -1794,8 +1797,8 @@
// Tell the notifier whether wireless charging has started so that
// it can provide feedback to the user.
- if (dockedOnWirelessCharger) {
- mNotifier.onWirelessChargingStarted();
+ if (dockedOnWirelessCharger || DEBUG_WIRELESS) {
+ mNotifier.onWirelessChargingStarted(mBatteryLevel);
}
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 95006ff..3ab771b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -37,6 +37,8 @@
void dismissKeyboardShortcutsMenu();
void toggleKeyboardShortcutsMenu(int deviceId);
+ void showChargingAnimation(int batteryLevel);
+
/**
* Show picture-in-picture menu.
*/
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index d67f2d7..adb368b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -319,6 +319,16 @@
}
@Override
+ public void showChargingAnimation(int batteryLevel) {
+ if (mBar != null) {
+ try {
+ mBar.showChargingAnimation(batteryLevel);
+ } catch (RemoteException ex){
+ }
+ }
+ }
+
+ @Override
public void showPictureInPictureMenu() {
if (mBar != null) {
try {