Merge "Revert "Refactor WirelessChargingAnimation.""

This commit is contained in:
Charlie Boutier
2022-08-29 17:40:43 +00:00
committed by Android (Google) Code Review
10 changed files with 287 additions and 432 deletions

View File

@@ -0,0 +1,254 @@
/*
* 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.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.WindowManager;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.ripple.RippleShader.RippleShape;
/**
* A WirelessChargingAnimation is a view containing view + animation for wireless charging.
* @hide
*/
public class WirelessChargingAnimation {
public static final int UNKNOWN_BATTERY_LEVEL = -1;
public static final long DURATION = 1500;
private static final String TAG = "WirelessChargingView";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final WirelessChargingView mCurrentWirelessChargingView;
private static WirelessChargingView mPreviousWirelessChargingView;
public interface Callback {
void onAnimationStarting();
void onAnimationEnded();
}
/**
* 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
*/
private WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper,
int transmittingBatteryLevel, int batteryLevel, Callback callback, boolean isDozing,
RippleShape rippleShape, UiEventLogger uiEventLogger) {
mCurrentWirelessChargingView = new WirelessChargingView(context, looper,
transmittingBatteryLevel, batteryLevel, callback, isDozing,
rippleShape, uiEventLogger);
}
/**
* Creates a wireless charging animation object populated with next view.
*
* @hide
*/
public static WirelessChargingAnimation makeWirelessChargingAnimation(@NonNull Context context,
@Nullable Looper looper, int transmittingBatteryLevel, int batteryLevel,
Callback callback, boolean isDozing, RippleShape rippleShape,
UiEventLogger uiEventLogger) {
return new WirelessChargingAnimation(context, looper, transmittingBatteryLevel,
batteryLevel, callback, isDozing, rippleShape, uiEventLogger);
}
/**
* Creates a charging animation object using mostly default values for non-dozing and unknown
* battery level without charging number shown.
*/
public static WirelessChargingAnimation makeChargingAnimationWithNoBatteryLevel(
@NonNull Context context, RippleShape rippleShape, UiEventLogger uiEventLogger) {
return makeWirelessChargingAnimation(context, null,
UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, null, false,
rippleShape, uiEventLogger);
}
/**
* Show the view for the specified duration.
*/
public void show(long delay) {
if (mCurrentWirelessChargingView == null ||
mCurrentWirelessChargingView.mNextView == null) {
throw new RuntimeException("setView must have been called");
}
if (mPreviousWirelessChargingView != null) {
mPreviousWirelessChargingView.hide(0);
}
mPreviousWirelessChargingView = mCurrentWirelessChargingView;
mCurrentWirelessChargingView.show(delay);
mCurrentWirelessChargingView.hide(delay + 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 final UiEventLogger mUiEventLogger;
private int mGravity;
private WirelessChargingLayout mView;
private WirelessChargingLayout mNextView;
private WindowManager mWM;
private Callback mCallback;
public WirelessChargingView(Context context, @Nullable Looper looper,
int transmittingBatteryLevel, int batteryLevel, Callback callback,
boolean isDozing, RippleShape rippleShape, UiEventLogger uiEventLogger) {
mCallback = callback;
mNextView = new WirelessChargingLayout(context, transmittingBatteryLevel, batteryLevel,
isDozing, rippleShape);
mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
mUiEventLogger = uiEventLogger;
final WindowManager.LayoutParams params = mParams;
params.height = WindowManager.LayoutParams.MATCH_PARENT;
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.format = PixelFormat.TRANSLUCENT;
params.type = WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
params.setTitle("Charging Animation");
params.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
params.setFitInsetsTypes(0 /* ignore all system bar insets */);
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
params.setTrustedOverlay();
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(long delay) {
if (DEBUG) Slog.d(TAG, "SHOW: " + this);
mHandler.sendMessageDelayed(Message.obtain(mHandler, SHOW), delay);
}
public void hide(long duration) {
mHandler.removeMessages(HIDE);
if (DEBUG) Slog.d(TAG, "HIDE: " + this);
mHandler.sendMessageDelayed(Message.obtain(mHandler, HIDE), duration);
}
private void handleShow() {
if (DEBUG) {
Slog.d(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);
mParams.packageName = packageName;
mParams.hideTimeoutMilliseconds = DURATION;
if (mView.getParent() != null) {
if (DEBUG) Slog.d(TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView);
}
if (DEBUG) Slog.d(TAG, "ADD! " + mView + " in " + this);
try {
if (mCallback != null) {
mCallback.onAnimationStarting();
}
mWM.addView(mView, mParams);
mUiEventLogger.log(WirelessChargingRippleEvent.WIRELESS_RIPPLE_PLAYED);
} catch (WindowManager.BadTokenException e) {
Slog.d(TAG, "Unable to add wireless charging view. " + e);
}
}
}
private void handleHide() {
if (DEBUG) Slog.d(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
if (mView != null) {
if (mView.getParent() != null) {
if (DEBUG) Slog.d(TAG, "REMOVE! " + mView + " in " + this);
if (mCallback != null) {
mCallback.onAnimationEnded();
}
mWM.removeViewImmediate(mView);
}
mView = null;
}
}
enum WirelessChargingRippleEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "Wireless charging ripple effect played")
WIRELESS_RIPPLE_PLAYED(830);
private final int mInt;
WirelessChargingRippleEvent(int id) {
mInt = id;
}
@Override public int getId() {
return mInt;
}
}
}
}

View File

@@ -16,9 +16,6 @@
package com.android.systemui.charging;
import static com.android.systemui.charging.WirelessChargingRippleControllerKt.DEFAULT_DURATION;
import static com.android.systemui.charging.WirelessChargingRippleControllerKt.UNKNOWN_BATTERY_LEVEL;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
@@ -44,43 +41,39 @@ import com.android.systemui.ripple.RippleView;
import java.text.NumberFormat;
/**
* Layout that is used for wireless charging.
*
* <p>Wireless charging layout has {@link RippleView} and text view to show the battery level.
* @hide
*/
final class WirelessChargingLayout extends FrameLayout {
private static final long CIRCLE_RIPPLE_ANIMATION_DURATION = 1500;
private static final long ROUNDED_BOX_RIPPLE_ANIMATION_DURATION = 1750;
private static final int SCRIM_COLOR = 0x4C000000;
private static final int SCRIM_FADE_DURATION = 300;
private RippleView mRippleView;
WirelessChargingLayout(Context context, int transmittingBatteryLevel, int batteryLevel,
boolean isDozing, RippleShape rippleShape, long duration) {
boolean isDozing, RippleShape rippleShape) {
super(context);
init(context, null, transmittingBatteryLevel, batteryLevel, isDozing, rippleShape,
duration);
init(context, null, transmittingBatteryLevel, batteryLevel, isDozing, rippleShape);
}
private WirelessChargingLayout(Context context) {
super(context);
init(context, null, /* isDozing= */ false, RippleShape.CIRCLE,
DEFAULT_DURATION);
init(context, null, /* isDozing= */ false, RippleShape.CIRCLE);
}
private WirelessChargingLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, /* isDozing= */false, RippleShape.CIRCLE,
DEFAULT_DURATION);
init(context, attrs, /* isDozing= */false, RippleShape.CIRCLE);
}
private void init(Context c, AttributeSet attrs, boolean isDozing, RippleShape rippleShape,
long duration) {
init(c, attrs, -1, -1, isDozing, rippleShape, duration);
private void init(Context c, AttributeSet attrs, boolean isDozing, RippleShape rippleShape) {
init(c, attrs, -1, -1, isDozing, rippleShape);
}
private void init(Context context, AttributeSet attrs, int transmittingBatteryLevel,
int batteryLevel, boolean isDozing, RippleShape rippleShape, long duration) {
int batteryLevel, boolean isDozing, RippleShape rippleShape) {
final boolean showTransmittingBatteryLevel =
(transmittingBatteryLevel != UNKNOWN_BATTERY_LEVEL);
(transmittingBatteryLevel != WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL);
// set style based on background
int style = R.style.ChargingAnim_WallpaperBackground;
@@ -93,7 +86,7 @@ final class WirelessChargingLayout extends FrameLayout {
// amount of battery:
final TextView percentage = findViewById(R.id.wireless_charging_percentage);
if (batteryLevel != UNKNOWN_BATTERY_LEVEL) {
if (batteryLevel != WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL) {
percentage.setText(NumberFormat.getPercentInstance().format(batteryLevel / 100f));
percentage.setAlpha(0);
}
@@ -132,6 +125,7 @@ final class WirelessChargingLayout extends FrameLayout {
// play all animations together
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(textSizeAnimator, textOpacityAnimator, textFadeAnimator);
ValueAnimator scrimFadeInAnimator = ObjectAnimator.ofArgb(this,
"backgroundColor", Color.TRANSPARENT, SCRIM_COLOR);
scrimFadeInAnimator.setDuration(SCRIM_FADE_DURATION);
@@ -140,21 +134,25 @@ final class WirelessChargingLayout extends FrameLayout {
"backgroundColor", SCRIM_COLOR, Color.TRANSPARENT);
scrimFadeOutAnimator.setDuration(SCRIM_FADE_DURATION);
scrimFadeOutAnimator.setInterpolator(Interpolators.LINEAR);
scrimFadeOutAnimator.setStartDelay(duration - SCRIM_FADE_DURATION);
scrimFadeOutAnimator.setStartDelay((rippleShape == RippleShape.CIRCLE
? CIRCLE_RIPPLE_ANIMATION_DURATION : ROUNDED_BOX_RIPPLE_ANIMATION_DURATION)
- SCRIM_FADE_DURATION);
AnimatorSet animatorSetScrim = new AnimatorSet();
animatorSetScrim.playTogether(scrimFadeInAnimator, scrimFadeOutAnimator);
animatorSetScrim.start();
mRippleView = findViewById(R.id.wireless_charging_ripple);
mRippleView.setupShader(rippleShape);
mRippleView.setDuration(duration);
int color = Utils.getColorAttr(mRippleView.getContext(),
android.R.attr.colorAccent).getDefaultColor();
if (mRippleView.getRippleShape() == RippleShape.ROUNDED_BOX) {
mRippleView.setDuration(ROUNDED_BOX_RIPPLE_ANIMATION_DURATION);
mRippleView.setSparkleStrength(0.22f);
int color = Utils.getColorAttr(mRippleView.getContext(),
android.R.attr.colorAccent).getDefaultColor();
mRippleView.setColor(ColorUtils.setAlphaComponent(color, 28));
} else {
mRippleView.setColor(ColorUtils.setAlphaComponent(color, 45));
mRippleView.setDuration(CIRCLE_RIPPLE_ANIMATION_DURATION);
mRippleView.setColor(Utils.getColorAttr(mRippleView.getContext(),
android.R.attr.colorAccent).getDefaultColor());
}
OnAttachStateChangeListener listener = new OnAttachStateChangeListener() {

View File

@@ -1,116 +0,0 @@
/*
* 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.view.WindowManager
import androidx.annotation.VisibleForTesting
import com.android.internal.logging.UiEventLogger
import com.android.systemui.charging.WirelessChargingView.WirelessChargingRippleEvent
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.dagger.ChargingLog
import com.android.systemui.util.concurrency.DelayableExecutor
import javax.inject.Inject
const val UNKNOWN_BATTERY_LEVEL = -1
const val DEFAULT_DURATION: Long = 1500
/**
* Controls the wireless charging animation.
*/
@SysUISingleton
class WirelessChargingRippleController @Inject constructor(
context: Context,
private val uiEventLogger: UiEventLogger,
@Main private val delayableExecutor: DelayableExecutor,
@ChargingLog private val logBuffer: LogBuffer
) {
private val windowManager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE)
as WindowManager
@VisibleForTesting
var wirelessChargingView: WirelessChargingView? = null
private var callback: Callback? = null
companion object {
private const val TAG = "WirelessChargingRippleController"
}
/**
* Shows the wireless charging view with the given delay.
*
* If there's already the animation is playing, the new request will be disregarded.
* @param wirelessChargingView WirelessChargingView to display.
* @param delay the start delay of the WirelessChargingView.
* @param callback optional callback that is triggered on animations start and end.
*/
fun show(wirelessChargingView: WirelessChargingView, delay: Long, callback: Callback? = null) {
// Avoid multiple animation getting triggered.
if (this.wirelessChargingView != null) {
logBuffer.log(TAG, LogLevel.INFO, "Already playing animation, disregard " +
"$wirelessChargingView")
return
}
this.wirelessChargingView = wirelessChargingView
this.callback = callback
logBuffer.log(TAG, LogLevel.DEBUG, "SHOW: $wirelessChargingView")
delayableExecutor.executeDelayed({ showInternal() }, delay)
logBuffer.log(TAG, LogLevel.DEBUG, "HIDE: $wirelessChargingView")
delayableExecutor.executeDelayed({ hideInternal() }, delay + wirelessChargingView.duration)
}
private fun showInternal() {
if (wirelessChargingView == null) {
return
}
val chargingLayout = wirelessChargingView!!.getWirelessChargingLayout()
try {
callback?.onAnimationStarting()
windowManager.addView(chargingLayout, wirelessChargingView!!.wmLayoutParams)
uiEventLogger.log(WirelessChargingRippleEvent.WIRELESS_RIPPLE_PLAYED)
} catch (e: WindowManager.BadTokenException) {
logBuffer.log(TAG, LogLevel.ERROR, "Unable to add wireless charging view. $e")
}
}
private fun hideInternal() {
wirelessChargingView?.getWirelessChargingLayout().let {
callback?.onAnimationEnded()
if (it?.parent != null) {
windowManager.removeViewImmediate(it)
}
}
wirelessChargingView = null
callback = null
}
/**
* Callbacks that are triggered on animation events.
*/
interface Callback {
/** Triggered when the animation starts playing. */
fun onAnimationStarting()
/** Triggered when the animation ends playing. */
fun onAnimationEnded()
}
}

View File

@@ -1,91 +0,0 @@
/*
* 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.PixelFormat
import android.view.View
import android.view.WindowManager
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger.UiEventEnum
import com.android.systemui.ripple.RippleShader.RippleShape
/**
* WirelessChargingView that encapsulates the current and next [WirelessChargingLayout]s.
*/
class WirelessChargingView (
context: Context,
transmittingBatteryLevel: Int,
batteryLevel: Int,
isDozing: Boolean,
rippleShape: RippleShape,
val duration: Long,
) {
companion object {
@JvmStatic
fun create(
context: Context,
transmittingBatteryLevel: Int,
batteryLevel: Int,
isDozing: Boolean,
rippleShape: RippleShape,
duration: Long = DEFAULT_DURATION
): WirelessChargingView {
return WirelessChargingView(context, transmittingBatteryLevel, batteryLevel, isDozing,
rippleShape, duration)
}
@JvmStatic
fun createWithNoBatteryLevel(
context: Context,
rippleShape: RippleShape,
duration: Long = DEFAULT_DURATION
): WirelessChargingView {
return create(context,
UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, false, rippleShape,
duration)
}
}
val wmLayoutParams = WindowManager.LayoutParams().apply {
height = WindowManager.LayoutParams.MATCH_PARENT
width = WindowManager.LayoutParams.MATCH_PARENT
format = PixelFormat.TRANSLUCENT
type = WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
title = "Charging Animation"
layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
fitInsetsTypes = 0
flags = (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
packageName = context.applicationContext.opPackageName
setTrustedOverlay()
}
private val wirelessChargingLayout: WirelessChargingLayout =
WirelessChargingLayout(context, transmittingBatteryLevel, batteryLevel, isDozing,
rippleShape, duration)
fun getWirelessChargingLayout(): View = wirelessChargingLayout
internal enum class WirelessChargingRippleEvent(private val mInt: Int) : UiEventEnum {
@UiEvent(doc = "Wireless charging ripple effect played")
WIRELESS_RIPPLE_PLAYED(830);
override fun getId(): Int {
return mInt
}
}
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright (C) 2022 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.log.dagger;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.android.systemui.log.LogBuffer;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import javax.inject.Qualifier;
/**
* A {@link LogBuffer} for {@link com.android.systemui.charging}
*/
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface ChargingLog {
}

View File

@@ -316,14 +316,4 @@ public class LogModule {
public static LogBuffer provideKeyguardViewMediatorLogBuffer(LogBufferFactory factory) {
return factory.create("KeyguardViewMediatorLog", 100);
}
/**
* Provides a {@link LogBuffer} for use by {@link com.android.systemui.charging}.
*/
@Provides
@SysUISingleton
@ChargingLog
public static LogBuffer provideChargingLogBuffer(LogBufferFactory factory) {
return factory.create("ChargingLog", 20);
}
}

View File

@@ -68,7 +68,7 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C
float rippleInsideAlpha = (1.-inside) * in_fadeFill;
float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing;
float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * in_color.a;
vec4 ripple = vec4(in_color.rgb, 1.0) * rippleAlpha;
vec4 ripple = in_color * rippleAlpha;
return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
}
"""
@@ -84,7 +84,7 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C
float rippleInsideAlpha = (1.-inside) * in_fadeFill;
float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing;
float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * in_color.a;
vec4 ripple = vec4(in_color.rgb, 1.0) * rippleAlpha;
vec4 ripple = in_color * rippleAlpha;
return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
}
"""
@@ -100,7 +100,7 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C
float rippleInsideAlpha = (1.-inside) * in_fadeFill;
float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing;
float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * in_color.a;
vec4 ripple = vec4(in_color.rgb, 1.0) * rippleAlpha;
vec4 ripple = in_color * rippleAlpha;
return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
}
"""

View File

@@ -31,8 +31,7 @@ import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_
import static androidx.lifecycle.Lifecycle.State.RESUMED;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import static com.android.systemui.charging.WirelessChargingRippleControllerKt.DEFAULT_DURATION;
import static com.android.systemui.charging.WirelessChargingRippleControllerKt.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
@@ -139,8 +138,7 @@ import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.camera.CameraIntents;
import com.android.systemui.charging.WiredChargingRippleController;
import com.android.systemui.charging.WirelessChargingRippleController;
import com.android.systemui.charging.WirelessChargingView;
import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
@@ -509,7 +507,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
private final WallpaperManager mWallpaperManager;
private CentralSurfacesComponent mCentralSurfacesComponent;
private WirelessChargingRippleController mWirelessChargingRippleController;
// Flags for disabling the status bar
// Two variables becaseu the first one evidently ran out of room for new flags.
@@ -735,7 +732,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
InteractionJankMonitor jankMonitor,
DeviceStateManager deviceStateManager,
WiredChargingRippleController wiredChargingRippleController,
WirelessChargingRippleController wirelessChargingRippleController,
IDreamManager dreamManager) {
super(context);
mNotificationsController = notificationsController;
@@ -853,7 +849,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
deviceStateManager.registerCallback(mMainExecutor,
new FoldStateListener(mContext, this::onFoldedStateChanged));
wiredChargingRippleController.registerCallbacks();
mWirelessChargingRippleController = wirelessChargingRippleController;
}
@Override
@@ -2167,11 +2162,9 @@ public class CentralSurfacesImpl extends CoreStartable implements
protected void showChargingAnimation(int batteryLevel, int transmittingBatteryLevel,
long animationDelay) {
WirelessChargingView wirelessChargingView = WirelessChargingView.create(mContext,
transmittingBatteryLevel, batteryLevel, /* isDozing= */ false,
RippleShape.CIRCLE, DEFAULT_DURATION);
mWirelessChargingRippleController.show(wirelessChargingView, animationDelay,
new WirelessChargingRippleController.Callback() {
WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
transmittingBatteryLevel, batteryLevel,
new WirelessChargingAnimation.Callback() {
@Override
public void onAnimationStarting() {
mNotificationShadeWindowController.setRequestTopUi(true, TAG);
@@ -2181,7 +2174,8 @@ public class CentralSurfacesImpl extends CoreStartable implements
public void onAnimationEnded() {
mNotificationShadeWindowController.setRequestTopUi(false, TAG);
}
});
}, /* isDozing= */ false, RippleShape.CIRCLE,
sUiEventLogger).show(animationDelay);
}
@Override

View File

@@ -1,135 +0,0 @@
/*
* Copyright (C) 2022 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.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.ripple.RippleShader.RippleShape
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class WirelessChargingRippleControllerTest : SysuiTestCase() {
private val duration: Long = 1000L
private val fakeSystemClock: FakeSystemClock = FakeSystemClock()
private lateinit var wirelessChargingRippleController: WirelessChargingRippleController
private val fakeExecutor = FakeExecutor(fakeSystemClock)
@Before
fun setUp() {
wirelessChargingRippleController = WirelessChargingRippleController(
context, UiEventLoggerFake(), fakeExecutor, logcatLogBuffer())
fakeSystemClock.setElapsedRealtime(0L)
}
@Test
fun showWirelessChargingView_hasCorrectDuration() {
val wirelessChargingView = WirelessChargingView.create(
context, UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, false,
RippleShape.ROUNDED_BOX, duration
)
wirelessChargingRippleController.show(wirelessChargingView, 0)
fakeExecutor.runAllReady()
assertEquals(duration, wirelessChargingView.duration)
}
@Test
fun showWirelessChargingView_triggerWhilePlayingAnim_doesNotShowRipple() {
val wirelessChargingViewFirst = WirelessChargingView.create(
context, UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, false,
RippleShape.ROUNDED_BOX, duration
)
wirelessChargingRippleController.show(wirelessChargingViewFirst, 0)
assertThat(fakeExecutor.numPending()).isEqualTo(2)
fakeExecutor.runNextReady() // run showInternal
// ensure we haven't run hideInternal, to simulate the first one is still playing.
assertThat(fakeExecutor.numPending()).isEqualTo(1)
val wirelessChargingViewSecond = WirelessChargingView.create(
context, UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, false,
RippleShape.ROUNDED_BOX, duration
)
wirelessChargingRippleController.show(wirelessChargingViewSecond, 0)
assertEquals(wirelessChargingViewFirst,
wirelessChargingRippleController.wirelessChargingView)
}
@Test
fun hideWirelessChargingView_afterDuration() {
fakeSystemClock.setElapsedRealtime(0L)
val wirelessChargingView = WirelessChargingView.create(
context, UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, false,
RippleShape.ROUNDED_BOX, duration
)
wirelessChargingRippleController.show(wirelessChargingView, 0)
assertThat(fakeExecutor.numPending()).isEqualTo(2)
with(fakeExecutor) {
runNextReady() // run showInternal
advanceClockToNext()
runNextReady() // run hideInternal
}
assertEquals(null, wirelessChargingRippleController.wirelessChargingView)
}
@Test
fun showWirelessChargingView_withCallback_triggersCallback() {
val callback = object : WirelessChargingRippleController.Callback {
var onAnimationStartingCalled = false
var onAnimationEndedCalled = false
override fun onAnimationStarting() {
onAnimationStartingCalled = true
}
override fun onAnimationEnded() {
onAnimationEndedCalled = true
}
}
val wirelessChargingView = WirelessChargingView.create(
context, UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, false,
RippleShape.CIRCLE, duration
)
wirelessChargingRippleController.show(wirelessChargingView, 0, callback)
assertThat(fakeExecutor.numPending()).isEqualTo(2)
fakeExecutor.runNextReady() // run showInternal
assertEquals(true, callback.onAnimationStartingCalled)
fakeExecutor.advanceClockToNext()
fakeExecutor.runNextReady() // run hideInternal
assertEquals(true, callback.onAnimationEndedCalled)
}
}

View File

@@ -88,7 +88,6 @@ import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.charging.WiredChargingRippleController;
import com.android.systemui.charging.WirelessChargingRippleController;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -284,7 +283,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private InteractionJankMonitor mJankMonitor;
@Mock private DeviceStateManager mDeviceStateManager;
@Mock private WiredChargingRippleController mWiredChargingRippleController;
@Mock private WirelessChargingRippleController mWirelessChargingRippleController;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -467,9 +465,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mActivityLaunchAnimator,
mJankMonitor,
mDeviceStateManager,
mWiredChargingRippleController,
mWirelessChargingRippleController,
mDreamManager);
mWiredChargingRippleController, mDreamManager);
when(mKeyguardViewMediator.registerCentralSurfaces(
any(CentralSurfacesImpl.class),
any(NotificationPanelViewController.class),