Merge "Implement dream out overlay transition" into tm-qpr-dev

This commit is contained in:
Lucas Silva
2022-11-15 19:49:26 +00:00
committed by Android (Google) Code Review
23 changed files with 704 additions and 171 deletions

View File

@@ -49,6 +49,17 @@ public abstract class DreamOverlayService extends Service {
mShowComplications = shouldShowComplications; mShowComplications = shouldShowComplications;
onStartDream(layoutParams); onStartDream(layoutParams);
} }
@Override
public void wakeUp() {
onWakeUp(() -> {
try {
mDreamOverlayCallback.onWakeUpComplete();
} catch (RemoteException e) {
Log.e(TAG, "Could not notify dream of wakeUp:" + e);
}
});
}
}; };
IDreamOverlayCallback mDreamOverlayCallback; IDreamOverlayCallback mDreamOverlayCallback;
@@ -70,6 +81,17 @@ public abstract class DreamOverlayService extends Service {
*/ */
public abstract void onStartDream(@NonNull WindowManager.LayoutParams layoutParams); public abstract void onStartDream(@NonNull WindowManager.LayoutParams layoutParams);
/**
* This method is overridden by implementations to handle when the dream has been requested
* to wakeup. This allows any overlay animations to run.
*
* @param onCompleteCallback The callback to trigger to notify the dream service that the
* overlay has completed waking up.
* @hide
*/
public void onWakeUp(@NonNull Runnable onCompleteCallback) {
}
/** /**
* This method is invoked to request the dream exit. * This method is invoked to request the dream exit.
*/ */

View File

@@ -312,7 +312,14 @@ public class DreamService extends Service implements Window.Callback {
@Override @Override
public void onExitRequested() { public void onExitRequested() {
// Simply finish dream when exit is requested. // Simply finish dream when exit is requested.
finish(); mHandler.post(() -> finish());
}
@Override
public void onWakeUpComplete() {
// Finish the dream once overlay animations are complete. Execute on handler since
// this is coming in on the overlay binder.
mHandler.post(() -> finish());
} }
}; };
@@ -975,7 +982,18 @@ public class DreamService extends Service implements Window.Callback {
* </p> * </p>
*/ */
public void onWakeUp() { public void onWakeUp() {
finish(); if (mOverlayConnection != null) {
mOverlayConnection.addConsumer(overlay -> {
try {
overlay.wakeUp();
} catch (RemoteException e) {
Slog.e(TAG, "Error waking the overlay service", e);
finish();
}
});
} else {
finish();
}
} }
/** {@inheritDoc} */ /** {@inheritDoc} */

View File

@@ -38,4 +38,7 @@ interface IDreamOverlay {
*/ */
void startDream(in LayoutParams params, in IDreamOverlayCallback callback, void startDream(in LayoutParams params, in IDreamOverlayCallback callback,
in String dreamComponent, in boolean shouldShowComplications); in String dreamComponent, in boolean shouldShowComplications);
/** Called when the dream is waking, to do any exit animations */
void wakeUp();
} }

View File

@@ -28,4 +28,7 @@ interface IDreamOverlayCallback {
* Invoked to request the dream exit. * Invoked to request the dream exit.
*/ */
void onExitRequested(); void onExitRequested();
/** Invoked when the dream overlay wakeUp animation is complete. */
void onWakeUpComplete();
} }

View File

@@ -2485,7 +2485,7 @@
<!-- The duration in milliseconds of the dream opening animation. --> <!-- The duration in milliseconds of the dream opening animation. -->
<integer name="config_dreamOpenAnimationDuration">250</integer> <integer name="config_dreamOpenAnimationDuration">250</integer>
<!-- The duration in milliseconds of the dream closing animation. --> <!-- The duration in milliseconds of the dream closing animation. -->
<integer name="config_dreamCloseAnimationDuration">100</integer> <integer name="config_dreamCloseAnimationDuration">300</integer>
<!-- Whether to dismiss the active dream when an activity is started. Doesn't apply to <!-- Whether to dismiss the active dream when an activity is started. Doesn't apply to
assistant activities (ACTIVITY_TYPE_ASSISTANT) --> assistant activities (ACTIVITY_TYPE_ASSISTANT) -->

View File

@@ -743,12 +743,35 @@
<!-- How long in milliseconds before full burn-in protection is achieved. --> <!-- How long in milliseconds before full burn-in protection is achieved. -->
<integer name="config_dreamOverlayMillisUntilFullJitter">240000</integer> <integer name="config_dreamOverlayMillisUntilFullJitter">240000</integer>
<!-- The duration in milliseconds of the y-translation animation when waking up from
the dream -->
<integer name="config_dreamOverlayOutTranslationYDurationMs">333</integer>
<!-- The delay in milliseconds of the y-translation animation when waking up from
the dream for the complications at the bottom of the screen -->
<integer name="config_dreamOverlayOutTranslationYDelayBottomMs">33</integer>
<!-- The delay in milliseconds of the y-translation animation when waking up from
the dream for the complications at the top of the screen -->
<integer name="config_dreamOverlayOutTranslationYDelayTopMs">117</integer>
<!-- The duration in milliseconds of the alpha animation when waking up from the dream -->
<integer name="config_dreamOverlayOutAlphaDurationMs">200</integer>
<!-- The delay in milliseconds of the alpha animation when waking up from the dream for the
complications at the top of the screen -->
<integer name="config_dreamOverlayOutAlphaDelayTopMs">217</integer>
<!-- The delay in milliseconds of the alpha animation when waking up from the dream for the
complications at the bottom of the screen -->
<integer name="config_dreamOverlayOutAlphaDelayBottomMs">133</integer>
<!-- The duration in milliseconds of the blur animation when waking up from
the dream -->
<integer name="config_dreamOverlayOutBlurDurationMs">250</integer>
<integer name="complicationFadeOutMs">500</integer> <integer name="complicationFadeOutMs">500</integer>
<integer name="complicationFadeInMs">500</integer> <integer name="complicationFadeInMs">500</integer>
<integer name="complicationRestoreMs">1000</integer> <integer name="complicationRestoreMs">1000</integer>
<integer name="complicationFadeOutDelayMs">200</integer>
<!-- Duration in milliseconds of the dream in un-blur animation. --> <!-- Duration in milliseconds of the dream in un-blur animation. -->
<integer name="config_dreamOverlayInBlurDurationMs">249</integer> <integer name="config_dreamOverlayInBlurDurationMs">249</integer>
<!-- Delay in milliseconds of the dream in un-blur animation. --> <!-- Delay in milliseconds of the dream in un-blur animation. -->
@@ -780,27 +803,6 @@
<item>com.android.systemui</item> <item>com.android.systemui</item>
</string-array> </string-array>
<!-- The thresholds which determine the color used by the AQI dream overlay.
NOTE: This must always be kept sorted from low to high -->
<integer-array name="config_dreamAqiThresholds">
<item>-1</item>
<item>50</item>
<item>100</item>
<item>150</item>
<item>200</item>
<item>300</item>
</integer-array>
<!-- The color values which correspond to the thresholds above -->
<integer-array name="config_dreamAqiColorValues">
<item>@color/dream_overlay_aqi_good</item>
<item>@color/dream_overlay_aqi_moderate</item>
<item>@color/dream_overlay_aqi_unhealthy_sensitive</item>
<item>@color/dream_overlay_aqi_unhealthy</item>
<item>@color/dream_overlay_aqi_very_unhealthy</item>
<item>@color/dream_overlay_aqi_hazardous</item>
</integer-array>
<!-- Whether the device should display hotspot UI. If true, UI will display only when tethering <!-- Whether the device should display hotspot UI. If true, UI will display only when tethering
is available. If false, UI will never show regardless of tethering availability" --> is available. If false, UI will never show regardless of tethering availability" -->
<bool name="config_show_wifi_tethering">true</bool> <bool name="config_show_wifi_tethering">true</bool>

View File

@@ -1534,6 +1534,7 @@
<dimen name="dream_overlay_complication_margin">0dp</dimen> <dimen name="dream_overlay_complication_margin">0dp</dimen>
<dimen name="dream_overlay_y_offset">80dp</dimen> <dimen name="dream_overlay_y_offset">80dp</dimen>
<dimen name="dream_overlay_exit_y_offset">40dp</dimen>
<dimen name="dream_aqi_badge_corner_radius">28dp</dimen> <dimen name="dream_aqi_badge_corner_radius">28dp</dimen>
<dimen name="dream_aqi_badge_padding_vertical">6dp</dimen> <dimen name="dream_aqi_badge_padding_vertical">6dp</dimen>

View File

@@ -17,17 +17,19 @@
package com.android.systemui.dreams package com.android.systemui.dreams
import android.animation.Animator import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet import android.animation.AnimatorSet
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.view.View import android.view.View
import android.view.animation.Interpolator
import androidx.annotation.FloatRange
import androidx.core.animation.doOnEnd import androidx.core.animation.doOnEnd
import com.android.systemui.animation.Interpolators import com.android.systemui.animation.Interpolators
import com.android.systemui.dreams.complication.ComplicationHostViewController import com.android.systemui.dreams.complication.ComplicationHostViewController
import com.android.systemui.dreams.complication.ComplicationLayoutParams import com.android.systemui.dreams.complication.ComplicationLayoutParams
import com.android.systemui.dreams.complication.ComplicationLayoutParams.Position
import com.android.systemui.dreams.dagger.DreamOverlayModule import com.android.systemui.dreams.dagger.DreamOverlayModule
import com.android.systemui.statusbar.BlurUtils import com.android.systemui.statusbar.BlurUtils
import java.util.function.Consumer import com.android.systemui.statusbar.CrossFadeHelper
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Named import javax.inject.Named
@@ -40,108 +42,239 @@ constructor(
private val mStatusBarViewController: DreamOverlayStatusBarViewController, private val mStatusBarViewController: DreamOverlayStatusBarViewController,
private val mOverlayStateController: DreamOverlayStateController, private val mOverlayStateController: DreamOverlayStateController,
@Named(DreamOverlayModule.DREAM_IN_BLUR_ANIMATION_DURATION) @Named(DreamOverlayModule.DREAM_IN_BLUR_ANIMATION_DURATION)
private val mDreamInBlurAnimDuration: Int, private val mDreamInBlurAnimDurationMs: Long,
@Named(DreamOverlayModule.DREAM_IN_BLUR_ANIMATION_DELAY) private val mDreamInBlurAnimDelay: Int, @Named(DreamOverlayModule.DREAM_IN_BLUR_ANIMATION_DELAY)
private val mDreamInBlurAnimDelayMs: Long,
@Named(DreamOverlayModule.DREAM_IN_COMPLICATIONS_ANIMATION_DURATION) @Named(DreamOverlayModule.DREAM_IN_COMPLICATIONS_ANIMATION_DURATION)
private val mDreamInComplicationsAnimDuration: Int, private val mDreamInComplicationsAnimDurationMs: Long,
@Named(DreamOverlayModule.DREAM_IN_TOP_COMPLICATIONS_ANIMATION_DELAY) @Named(DreamOverlayModule.DREAM_IN_TOP_COMPLICATIONS_ANIMATION_DELAY)
private val mDreamInTopComplicationsAnimDelay: Int, private val mDreamInTopComplicationsAnimDelayMs: Long,
@Named(DreamOverlayModule.DREAM_IN_BOTTOM_COMPLICATIONS_ANIMATION_DELAY) @Named(DreamOverlayModule.DREAM_IN_BOTTOM_COMPLICATIONS_ANIMATION_DELAY)
private val mDreamInBottomComplicationsAnimDelay: Int private val mDreamInBottomComplicationsAnimDelayMs: Long,
@Named(DreamOverlayModule.DREAM_OUT_TRANSLATION_Y_DISTANCE)
private val mDreamOutTranslationYDistance: Int,
@Named(DreamOverlayModule.DREAM_OUT_TRANSLATION_Y_DURATION)
private val mDreamOutTranslationYDurationMs: Long,
@Named(DreamOverlayModule.DREAM_OUT_TRANSLATION_Y_DELAY_BOTTOM)
private val mDreamOutTranslationYDelayBottomMs: Long,
@Named(DreamOverlayModule.DREAM_OUT_TRANSLATION_Y_DELAY_TOP)
private val mDreamOutTranslationYDelayTopMs: Long,
@Named(DreamOverlayModule.DREAM_OUT_ALPHA_DURATION) private val mDreamOutAlphaDurationMs: Long,
@Named(DreamOverlayModule.DREAM_OUT_ALPHA_DELAY_BOTTOM)
private val mDreamOutAlphaDelayBottomMs: Long,
@Named(DreamOverlayModule.DREAM_OUT_ALPHA_DELAY_TOP) private val mDreamOutAlphaDelayTopMs: Long,
@Named(DreamOverlayModule.DREAM_OUT_BLUR_DURATION) private val mDreamOutBlurDurationMs: Long
) { ) {
var mEntryAnimations: AnimatorSet? = null private var mAnimator: Animator? = null
/**
* Store the current alphas at the various positions. This is so that we may resume an animation
* at the current alpha.
*/
private var mCurrentAlphaAtPosition = mutableMapOf<Int, Float>()
@FloatRange(from = 0.0, to = 1.0) private var mBlurProgress: Float = 0f
/** Starts the dream content and dream overlay entry animations. */ /** Starts the dream content and dream overlay entry animations. */
fun startEntryAnimations(view: View) { @JvmOverloads
cancelRunningEntryAnimations() fun startEntryAnimations(view: View, animatorBuilder: () -> AnimatorSet = { AnimatorSet() }) {
cancelAnimations()
mEntryAnimations = AnimatorSet() mAnimator =
mEntryAnimations?.apply { animatorBuilder().apply {
playTogether( playTogether(
buildDreamInBlurAnimator(view), blurAnimator(
buildDreamInTopComplicationsAnimator(), view = view,
buildDreamInBottomComplicationsAnimator() from = 1f,
) to = 0f,
doOnEnd { mOverlayStateController.setEntryAnimationsFinished(true) } durationMs = mDreamInBlurAnimDurationMs,
start() delayMs = mDreamInBlurAnimDelayMs
} ),
alphaAnimator(
from = 0f,
to = 1f,
durationMs = mDreamInComplicationsAnimDurationMs,
delayMs = mDreamInTopComplicationsAnimDelayMs,
position = ComplicationLayoutParams.POSITION_TOP
),
alphaAnimator(
from = 0f,
to = 1f,
durationMs = mDreamInComplicationsAnimDurationMs,
delayMs = mDreamInBottomComplicationsAnimDelayMs,
position = ComplicationLayoutParams.POSITION_BOTTOM
)
)
doOnEnd {
mAnimator = null
mOverlayStateController.setEntryAnimationsFinished(true)
}
start()
}
}
/** Starts the dream content and dream overlay exit animations. */
@JvmOverloads
fun startExitAnimations(
view: View,
doneCallback: () -> Unit,
animatorBuilder: () -> AnimatorSet = { AnimatorSet() }
) {
cancelAnimations()
mAnimator =
animatorBuilder().apply {
playTogether(
blurAnimator(
view = view,
// Start the blurring wherever the entry animation ended, in
// case it was cancelled early.
from = mBlurProgress,
to = 1f,
durationMs = mDreamOutBlurDurationMs
),
translationYAnimator(
from = 0f,
to = mDreamOutTranslationYDistance.toFloat(),
durationMs = mDreamOutTranslationYDurationMs,
delayMs = mDreamOutTranslationYDelayBottomMs,
position = ComplicationLayoutParams.POSITION_BOTTOM,
animInterpolator = Interpolators.EMPHASIZED_ACCELERATE
),
translationYAnimator(
from = 0f,
to = mDreamOutTranslationYDistance.toFloat(),
durationMs = mDreamOutTranslationYDurationMs,
delayMs = mDreamOutTranslationYDelayTopMs,
position = ComplicationLayoutParams.POSITION_TOP,
animInterpolator = Interpolators.EMPHASIZED_ACCELERATE
),
alphaAnimator(
from =
mCurrentAlphaAtPosition.getOrDefault(
key = ComplicationLayoutParams.POSITION_BOTTOM,
defaultValue = 1f
),
to = 0f,
durationMs = mDreamOutAlphaDurationMs,
delayMs = mDreamOutAlphaDelayBottomMs,
position = ComplicationLayoutParams.POSITION_BOTTOM
),
alphaAnimator(
from =
mCurrentAlphaAtPosition.getOrDefault(
key = ComplicationLayoutParams.POSITION_TOP,
defaultValue = 1f
),
to = 0f,
durationMs = mDreamOutAlphaDurationMs,
delayMs = mDreamOutAlphaDelayTopMs,
position = ComplicationLayoutParams.POSITION_TOP
)
)
doOnEnd {
mAnimator = null
mOverlayStateController.setExitAnimationsRunning(false)
doneCallback()
}
start()
}
mOverlayStateController.setExitAnimationsRunning(true)
} }
/** Cancels the dream content and dream overlay animations, if they're currently running. */ /** Cancels the dream content and dream overlay animations, if they're currently running. */
fun cancelRunningEntryAnimations() { fun cancelAnimations() {
if (mEntryAnimations?.isRunning == true) { mAnimator =
mEntryAnimations?.cancel() mAnimator?.let {
} it.cancel()
mEntryAnimations = null null
}
} }
private fun buildDreamInBlurAnimator(view: View): Animator { private fun blurAnimator(
return ValueAnimator.ofFloat(1f, 0f).apply { view: View,
duration = mDreamInBlurAnimDuration.toLong() from: Float,
startDelay = mDreamInBlurAnimDelay.toLong() to: Float,
durationMs: Long,
delayMs: Long = 0
): Animator {
return ValueAnimator.ofFloat(from, to).apply {
duration = durationMs
startDelay = delayMs
interpolator = Interpolators.LINEAR interpolator = Interpolators.LINEAR
addUpdateListener { animator: ValueAnimator -> addUpdateListener { animator: ValueAnimator ->
mBlurProgress = animator.animatedValue as Float
mBlurUtils.applyBlur( mBlurUtils.applyBlur(
view.viewRootImpl, viewRootImpl = view.viewRootImpl,
mBlurUtils.blurRadiusOfRatio(animator.animatedValue as Float).toInt(), radius = mBlurUtils.blurRadiusOfRatio(mBlurProgress).toInt(),
false /*opaque*/ opaque = false
) )
} }
} }
} }
private fun buildDreamInTopComplicationsAnimator(): Animator { private fun alphaAnimator(
return ValueAnimator.ofFloat(0f, 1f).apply { from: Float,
duration = mDreamInComplicationsAnimDuration.toLong() to: Float,
startDelay = mDreamInTopComplicationsAnimDelay.toLong() durationMs: Long,
delayMs: Long,
@Position position: Int
): Animator {
return ValueAnimator.ofFloat(from, to).apply {
duration = durationMs
startDelay = delayMs
interpolator = Interpolators.LINEAR interpolator = Interpolators.LINEAR
addUpdateListener { va: ValueAnimator -> addUpdateListener { va: ValueAnimator ->
setTopElementsAlpha(va.animatedValue as Float) setElementsAlphaAtPosition(
alpha = va.animatedValue as Float,
position = position,
fadingOut = to < from
)
} }
} }
} }
private fun buildDreamInBottomComplicationsAnimator(): Animator { private fun translationYAnimator(
return ValueAnimator.ofFloat(0f, 1f).apply { from: Float,
duration = mDreamInComplicationsAnimDuration.toLong() to: Float,
startDelay = mDreamInBottomComplicationsAnimDelay.toLong() durationMs: Long,
interpolator = Interpolators.LINEAR delayMs: Long,
@Position position: Int,
animInterpolator: Interpolator
): Animator {
return ValueAnimator.ofFloat(from, to).apply {
duration = durationMs
startDelay = delayMs
interpolator = animInterpolator
addUpdateListener { va: ValueAnimator -> addUpdateListener { va: ValueAnimator ->
setBottomElementsAlpha(va.animatedValue as Float) setElementsTranslationYAtPosition(va.animatedValue as Float, position)
} }
addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator) {
mComplicationHostViewController
.getViewsAtPosition(ComplicationLayoutParams.POSITION_BOTTOM)
.forEach(Consumer { v: View -> v.visibility = View.VISIBLE })
}
}
)
} }
} }
/** Sets alpha of top complications and the status bar. */ /** Sets alpha of complications at the specified position. */
private fun setTopElementsAlpha(alpha: Float) { private fun setElementsAlphaAtPosition(alpha: Float, position: Int, fadingOut: Boolean) {
mComplicationHostViewController mCurrentAlphaAtPosition[position] = alpha
.getViewsAtPosition(ComplicationLayoutParams.POSITION_TOP) mComplicationHostViewController.getViewsAtPosition(position).forEach { view ->
.forEach(Consumer { v: View -> setAlphaAndEnsureVisible(v, alpha) }) if (fadingOut) {
mStatusBarViewController.setAlpha(alpha) CrossFadeHelper.fadeOut(view, 1 - alpha, /* remap= */ false)
} } else {
CrossFadeHelper.fadeIn(view, alpha, /* remap= */ false)
/** Sets alpha of bottom complications. */ }
private fun setBottomElementsAlpha(alpha: Float) {
mComplicationHostViewController
.getViewsAtPosition(ComplicationLayoutParams.POSITION_BOTTOM)
.forEach(Consumer { v: View -> setAlphaAndEnsureVisible(v, alpha) })
}
private fun setAlphaAndEnsureVisible(view: View, alpha: Float) {
if (alpha > 0 && view.visibility != View.VISIBLE) {
view.visibility = View.VISIBLE
} }
if (position == ComplicationLayoutParams.POSITION_TOP) {
mStatusBarViewController.setFadeAmount(alpha, fadingOut)
}
}
view.alpha = alpha /** Sets y translation of complications at the specified position. */
private fun setElementsTranslationYAtPosition(translationY: Float, position: Int) {
mComplicationHostViewController.getViewsAtPosition(position).forEach { v ->
v.translationY = translationY
}
if (position == ComplicationLayoutParams.POSITION_TOP) {
mStatusBarViewController.setTranslationY(translationY)
}
} }
} }

View File

@@ -29,6 +29,8 @@ import android.util.MathUtils;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.animation.Interpolators; import com.android.systemui.animation.Interpolators;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
@@ -42,6 +44,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.ViewController; import com.android.systemui.util.ViewController;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.Executor;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@@ -194,7 +197,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
} }
mPrimaryBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback); mPrimaryBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback);
mDreamOverlayAnimationsController.cancelRunningEntryAnimations(); mDreamOverlayAnimationsController.cancelAnimations();
} }
View getContainerView() { View getContainerView() {
@@ -251,4 +254,17 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
: aboutToShowBouncerProgress(expansion + 0.03f)); : aboutToShowBouncerProgress(expansion + 0.03f));
return MathUtils.lerp(-mDreamOverlayMaxTranslationY, 0, fraction); return MathUtils.lerp(-mDreamOverlayMaxTranslationY, 0, fraction);
} }
/**
* Handle the dream waking up and run any necessary animations.
*
* @param onAnimationEnd Callback to trigger once animations are finished.
* @param callbackExecutor Executor to execute the callback on.
*/
public void wakeUp(@NonNull Runnable onAnimationEnd, @NonNull Executor callbackExecutor) {
mDreamOverlayAnimationsController.startExitAnimations(mView, () -> {
callbackExecutor.execute(onAnimationEnd);
return null;
});
}
} }

View File

@@ -213,6 +213,15 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
mLifecycleRegistry.setCurrentState(state); mLifecycleRegistry.setCurrentState(state);
} }
@Override
public void onWakeUp(@NonNull Runnable onCompletedCallback) {
mExecutor.execute(() -> {
if (mDreamOverlayContainerViewController != null) {
mDreamOverlayContainerViewController.wakeUp(onCompletedCallback, mExecutor);
}
});
}
/** /**
* Inserts {@link Window} to host the dream overlay into the dream's parent window. Must be * Inserts {@link Window} to host the dream overlay into the dream's parent window. Must be
* called from the main executing thread. The window attributes closely mirror those that are * called from the main executing thread. The window attributes closely mirror those that are

View File

@@ -52,6 +52,7 @@ public class DreamOverlayStateController implements
public static final int STATE_DREAM_OVERLAY_ACTIVE = 1 << 0; public static final int STATE_DREAM_OVERLAY_ACTIVE = 1 << 0;
public static final int STATE_LOW_LIGHT_ACTIVE = 1 << 1; public static final int STATE_LOW_LIGHT_ACTIVE = 1 << 1;
public static final int STATE_DREAM_ENTRY_ANIMATIONS_FINISHED = 1 << 2; public static final int STATE_DREAM_ENTRY_ANIMATIONS_FINISHED = 1 << 2;
public static final int STATE_DREAM_EXIT_ANIMATIONS_RUNNING = 1 << 3;
private static final int OP_CLEAR_STATE = 1; private static final int OP_CLEAR_STATE = 1;
private static final int OP_SET_STATE = 2; private static final int OP_SET_STATE = 2;
@@ -211,6 +212,14 @@ public class DreamOverlayStateController implements
return containsState(STATE_DREAM_ENTRY_ANIMATIONS_FINISHED); return containsState(STATE_DREAM_ENTRY_ANIMATIONS_FINISHED);
} }
/**
* Returns whether the dream content and dream overlay exit animations are running.
* @return {@code true} if animations are running, {@code false} otherwise.
*/
public boolean areExitAnimationsRunning() {
return containsState(STATE_DREAM_EXIT_ANIMATIONS_RUNNING);
}
private boolean containsState(int state) { private boolean containsState(int state) {
return (mState & state) != 0; return (mState & state) != 0;
} }
@@ -256,6 +265,15 @@ public class DreamOverlayStateController implements
STATE_DREAM_ENTRY_ANIMATIONS_FINISHED); STATE_DREAM_ENTRY_ANIMATIONS_FINISHED);
} }
/**
* Sets whether dream content and dream overlay exit animations are running.
* @param running {@code true} if exit animations are running, {@code false} otherwise.
*/
public void setExitAnimationsRunning(boolean running) {
modifyState(running ? OP_SET_STATE : OP_CLEAR_STATE,
STATE_DREAM_EXIT_ANIMATIONS_RUNNING);
}
/** /**
* Returns the available complication types. * Returns the available complication types.
*/ */

View File

@@ -37,6 +37,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem; import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem;
import com.android.systemui.dreams.dagger.DreamOverlayComponent; import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.policy.ZenModeController;
@@ -217,18 +218,29 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
} }
/** /**
* Sets alpha of the dream overlay status bar. * Sets fade of the dream overlay status bar.
* *
* No-op if the dream overlay status bar should not be shown. * No-op if the dream overlay status bar should not be shown.
*/ */
protected void setAlpha(float alpha) { protected void setFadeAmount(float fadeAmount, boolean fadingOut) {
updateVisibility(); updateVisibility();
if (mView.getVisibility() != View.VISIBLE) { if (mView.getVisibility() != View.VISIBLE) {
return; return;
} }
mView.setAlpha(alpha); if (fadingOut) {
CrossFadeHelper.fadeOut(mView, 1 - fadeAmount, /* remap= */ false);
} else {
CrossFadeHelper.fadeIn(mView, fadeAmount, /* remap= */ false);
}
}
/**
* Sets the y translation of the dream overlay status bar.
*/
public void setTranslationY(float translationY) {
mView.setTranslationY(translationY);
} }
private boolean shouldShowStatusBar() { private boolean shouldShowStatusBar() {

View File

@@ -197,11 +197,11 @@ public interface Complication {
*/ */
interface VisibilityController { interface VisibilityController {
/** /**
* Called to set the visibility of all shown and future complications. * Called to set the visibility of all shown and future complications. Changes in visibility
* will always be animated.
* @param visibility The desired future visibility. * @param visibility The desired future visibility.
* @param animate whether the change should be animated.
*/ */
void setVisibility(@View.Visibility int visibility, boolean animate); void setVisibility(@View.Visibility int visibility);
} }
/** /**

View File

@@ -21,12 +21,9 @@ import static com.android.systemui.dreams.complication.dagger.ComplicationHostVi
import static com.android.systemui.dreams.complication.dagger.ComplicationHostViewModule.COMPLICATION_MARGIN_DEFAULT; import static com.android.systemui.dreams.complication.dagger.ComplicationHostViewModule.COMPLICATION_MARGIN_DEFAULT;
import static com.android.systemui.dreams.complication.dagger.ComplicationHostViewModule.SCOPED_COMPLICATIONS_LAYOUT; import static com.android.systemui.dreams.complication.dagger.ComplicationHostViewModule.SCOPED_COMPLICATIONS_LAYOUT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Constraints; import androidx.constraintlayout.widget.Constraints;
@@ -34,6 +31,7 @@ import androidx.constraintlayout.widget.Constraints;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.dreams.complication.ComplicationLayoutParams.Position; import com.android.systemui.dreams.complication.ComplicationLayoutParams.Position;
import com.android.systemui.dreams.dagger.DreamOverlayComponent; import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.touch.TouchInsetManager; import com.android.systemui.touch.TouchInsetManager;
import java.util.ArrayList; import java.util.ArrayList;
@@ -481,7 +479,6 @@ public class ComplicationLayoutEngine implements Complication.VisibilityControll
private final TouchInsetManager.TouchInsetSession mSession; private final TouchInsetManager.TouchInsetSession mSession;
private final int mFadeInDuration; private final int mFadeInDuration;
private final int mFadeOutDuration; private final int mFadeOutDuration;
private ViewPropertyAnimator mViewPropertyAnimator;
/** */ /** */
@Inject @Inject
@@ -498,26 +495,16 @@ public class ComplicationLayoutEngine implements Complication.VisibilityControll
} }
@Override @Override
public void setVisibility(int visibility, boolean animate) { public void setVisibility(int visibility) {
final boolean appearing = visibility == View.VISIBLE; if (visibility == View.VISIBLE) {
CrossFadeHelper.fadeIn(mLayout, mFadeInDuration, /* delay= */ 0);
if (mViewPropertyAnimator != null) { } else {
mViewPropertyAnimator.cancel(); CrossFadeHelper.fadeOut(
mLayout,
mFadeOutDuration,
/* delay= */ 0,
/* endRunnable= */ null);
} }
if (appearing) {
mLayout.setVisibility(View.VISIBLE);
}
mViewPropertyAnimator = mLayout.animate()
.alpha(appearing ? 1f : 0f)
.setDuration(appearing ? mFadeInDuration : mFadeOutDuration)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mLayout.setVisibility(visibility);
}
});
} }
/** /**

View File

@@ -38,7 +38,7 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
POSITION_START, POSITION_START,
}) })
@interface Position {} public @interface Position {}
/** Align view with the top of parent or bottom of preceding {@link Complication}. */ /** Align view with the top of parent or bottom of preceding {@link Complication}. */
public static final int POSITION_TOP = 1 << 0; public static final int POSITION_TOP = 1 << 0;
/** Align view with the bottom of parent or top of preceding {@link Complication}. */ /** Align view with the bottom of parent or top of preceding {@link Complication}. */

View File

@@ -41,6 +41,7 @@ public abstract class ComplicationHostViewModule {
public static final String COMPLICATIONS_FADE_OUT_DURATION = "complications_fade_out_duration"; public static final String COMPLICATIONS_FADE_OUT_DURATION = "complications_fade_out_duration";
public static final String COMPLICATIONS_FADE_IN_DURATION = "complications_fade_in_duration"; public static final String COMPLICATIONS_FADE_IN_DURATION = "complications_fade_in_duration";
public static final String COMPLICATIONS_RESTORE_TIMEOUT = "complication_restore_timeout"; public static final String COMPLICATIONS_RESTORE_TIMEOUT = "complication_restore_timeout";
public static final String COMPLICATIONS_FADE_OUT_DELAY = "complication_fade_out_delay";
/** /**
* Generates a {@link ConstraintLayout}, which can host * Generates a {@link ConstraintLayout}, which can host
@@ -74,6 +75,16 @@ public abstract class ComplicationHostViewModule {
return resources.getInteger(R.integer.complicationFadeOutMs); return resources.getInteger(R.integer.complicationFadeOutMs);
} }
/**
* Provides the delay to wait for before fading out complications.
*/
@Provides
@Named(COMPLICATIONS_FADE_OUT_DELAY)
@DreamOverlayComponent.DreamOverlayScope
static int providesComplicationsFadeOutDelay(@Main Resources resources) {
return resources.getInteger(R.integer.complicationFadeOutDelayMs);
}
/** /**
* Provides the fade in duration for complications. * Provides the fade in duration for complications.
*/ */

View File

@@ -55,6 +55,22 @@ public abstract class DreamOverlayModule {
"dream_in_top_complications_anim_delay"; "dream_in_top_complications_anim_delay";
public static final String DREAM_IN_BOTTOM_COMPLICATIONS_ANIMATION_DELAY = public static final String DREAM_IN_BOTTOM_COMPLICATIONS_ANIMATION_DELAY =
"dream_in_bottom_complications_anim_delay"; "dream_in_bottom_complications_anim_delay";
public static final String DREAM_OUT_TRANSLATION_Y_DISTANCE =
"dream_out_complications_translation_y";
public static final String DREAM_OUT_TRANSLATION_Y_DURATION =
"dream_out_complications_translation_y_duration";
public static final String DREAM_OUT_TRANSLATION_Y_DELAY_BOTTOM =
"dream_out_complications_translation_y_delay_bottom";
public static final String DREAM_OUT_TRANSLATION_Y_DELAY_TOP =
"dream_out_complications_translation_y_delay_top";
public static final String DREAM_OUT_ALPHA_DURATION =
"dream_out_complications_alpha_duration";
public static final String DREAM_OUT_ALPHA_DELAY_BOTTOM =
"dream_out_complications_alpha_delay_bottom";
public static final String DREAM_OUT_ALPHA_DELAY_TOP =
"dream_out_complications_alpha_delay_top";
public static final String DREAM_OUT_BLUR_DURATION =
"dream_out_blur_duration";
/** */ /** */
@Provides @Provides
@@ -127,8 +143,8 @@ public abstract class DreamOverlayModule {
*/ */
@Provides @Provides
@Named(DREAM_IN_BLUR_ANIMATION_DURATION) @Named(DREAM_IN_BLUR_ANIMATION_DURATION)
static int providesDreamInBlurAnimationDuration(@Main Resources resources) { static long providesDreamInBlurAnimationDuration(@Main Resources resources) {
return resources.getInteger(R.integer.config_dreamOverlayInBlurDurationMs); return (long) resources.getInteger(R.integer.config_dreamOverlayInBlurDurationMs);
} }
/** /**
@@ -136,8 +152,8 @@ public abstract class DreamOverlayModule {
*/ */
@Provides @Provides
@Named(DREAM_IN_BLUR_ANIMATION_DELAY) @Named(DREAM_IN_BLUR_ANIMATION_DELAY)
static int providesDreamInBlurAnimationDelay(@Main Resources resources) { static long providesDreamInBlurAnimationDelay(@Main Resources resources) {
return resources.getInteger(R.integer.config_dreamOverlayInBlurDelayMs); return (long) resources.getInteger(R.integer.config_dreamOverlayInBlurDelayMs);
} }
/** /**
@@ -145,8 +161,8 @@ public abstract class DreamOverlayModule {
*/ */
@Provides @Provides
@Named(DREAM_IN_COMPLICATIONS_ANIMATION_DURATION) @Named(DREAM_IN_COMPLICATIONS_ANIMATION_DURATION)
static int providesDreamInComplicationsAnimationDuration(@Main Resources resources) { static long providesDreamInComplicationsAnimationDuration(@Main Resources resources) {
return resources.getInteger(R.integer.config_dreamOverlayInComplicationsDurationMs); return (long) resources.getInteger(R.integer.config_dreamOverlayInComplicationsDurationMs);
} }
/** /**
@@ -154,8 +170,8 @@ public abstract class DreamOverlayModule {
*/ */
@Provides @Provides
@Named(DREAM_IN_TOP_COMPLICATIONS_ANIMATION_DELAY) @Named(DREAM_IN_TOP_COMPLICATIONS_ANIMATION_DELAY)
static int providesDreamInTopComplicationsAnimationDelay(@Main Resources resources) { static long providesDreamInTopComplicationsAnimationDelay(@Main Resources resources) {
return resources.getInteger(R.integer.config_dreamOverlayInTopComplicationsDelayMs); return (long) resources.getInteger(R.integer.config_dreamOverlayInTopComplicationsDelayMs);
} }
/** /**
@@ -163,8 +179,69 @@ public abstract class DreamOverlayModule {
*/ */
@Provides @Provides
@Named(DREAM_IN_BOTTOM_COMPLICATIONS_ANIMATION_DELAY) @Named(DREAM_IN_BOTTOM_COMPLICATIONS_ANIMATION_DELAY)
static int providesDreamInBottomComplicationsAnimationDelay(@Main Resources resources) { static long providesDreamInBottomComplicationsAnimationDelay(@Main Resources resources) {
return resources.getInteger(R.integer.config_dreamOverlayInBottomComplicationsDelayMs); return (long) resources.getInteger(
R.integer.config_dreamOverlayInBottomComplicationsDelayMs);
}
/**
* Provides the number of pixels to translate complications when waking up from dream.
*/
@Provides
@Named(DREAM_OUT_TRANSLATION_Y_DISTANCE)
@DreamOverlayComponent.DreamOverlayScope
static int providesDreamOutComplicationsTranslationY(@Main Resources resources) {
return resources.getDimensionPixelSize(R.dimen.dream_overlay_exit_y_offset);
}
@Provides
@Named(DREAM_OUT_TRANSLATION_Y_DURATION)
@DreamOverlayComponent.DreamOverlayScope
static long providesDreamOutComplicationsTranslationYDuration(@Main Resources resources) {
return (long) resources.getInteger(R.integer.config_dreamOverlayOutTranslationYDurationMs);
}
@Provides
@Named(DREAM_OUT_TRANSLATION_Y_DELAY_BOTTOM)
@DreamOverlayComponent.DreamOverlayScope
static long providesDreamOutComplicationsTranslationYDelayBottom(@Main Resources resources) {
return (long) resources.getInteger(
R.integer.config_dreamOverlayOutTranslationYDelayBottomMs);
}
@Provides
@Named(DREAM_OUT_TRANSLATION_Y_DELAY_TOP)
@DreamOverlayComponent.DreamOverlayScope
static long providesDreamOutComplicationsTranslationYDelayTop(@Main Resources resources) {
return (long) resources.getInteger(R.integer.config_dreamOverlayOutTranslationYDelayTopMs);
}
@Provides
@Named(DREAM_OUT_ALPHA_DURATION)
@DreamOverlayComponent.DreamOverlayScope
static long providesDreamOutComplicationsAlphaDuration(@Main Resources resources) {
return (long) resources.getInteger(R.integer.config_dreamOverlayOutAlphaDurationMs);
}
@Provides
@Named(DREAM_OUT_ALPHA_DELAY_BOTTOM)
@DreamOverlayComponent.DreamOverlayScope
static long providesDreamOutComplicationsAlphaDelayBottom(@Main Resources resources) {
return (long) resources.getInteger(R.integer.config_dreamOverlayOutAlphaDelayBottomMs);
}
@Provides
@Named(DREAM_OUT_ALPHA_DELAY_TOP)
@DreamOverlayComponent.DreamOverlayScope
static long providesDreamOutComplicationsAlphaDelayTop(@Main Resources resources) {
return (long) resources.getInteger(R.integer.config_dreamOverlayOutAlphaDelayTopMs);
}
@Provides
@Named(DREAM_OUT_BLUR_DURATION)
@DreamOverlayComponent.DreamOverlayScope
static long providesDreamOutBlurDuration(@Main Resources resources) {
return (long) resources.getInteger(R.integer.config_dreamOverlayOutBlurDurationMs);
} }
@Provides @Provides

View File

@@ -16,22 +16,26 @@
package com.android.systemui.dreams.touch; package com.android.systemui.dreams.touch;
import static com.android.systemui.dreams.complication.dagger.ComplicationHostViewModule.COMPLICATIONS_FADE_OUT_DELAY;
import static com.android.systemui.dreams.complication.dagger.ComplicationHostViewModule.COMPLICATIONS_RESTORE_TIMEOUT; import static com.android.systemui.dreams.complication.dagger.ComplicationHostViewModule.COMPLICATIONS_RESTORE_TIMEOUT;
import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import androidx.annotation.Nullable;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.complication.Complication; import com.android.systemui.dreams.complication.Complication;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.touch.TouchInsetManager; import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayDeque;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@@ -49,33 +53,58 @@ public class HideComplicationTouchHandler implements DreamTouchHandler {
private static final String TAG = "HideComplicationHandler"; private static final String TAG = "HideComplicationHandler";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Complication.VisibilityController mVisibilityController;
private final int mRestoreTimeout; private final int mRestoreTimeout;
private final int mFadeOutDelay;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final Handler mHandler; private final DelayableExecutor mExecutor;
private final Executor mExecutor; private final DreamOverlayStateController mOverlayStateController;
private final TouchInsetManager mTouchInsetManager; private final TouchInsetManager mTouchInsetManager;
private final Complication.VisibilityController mVisibilityController;
private boolean mHidden = false;
@Nullable
private Runnable mHiddenCallback;
private final ArrayDeque<Runnable> mCancelCallbacks = new ArrayDeque<>();
private final Runnable mRestoreComplications = new Runnable() { private final Runnable mRestoreComplications = new Runnable() {
@Override @Override
public void run() { public void run() {
mVisibilityController.setVisibility(View.VISIBLE, true); mVisibilityController.setVisibility(View.VISIBLE);
mHidden = false;
}
};
private final Runnable mHideComplications = new Runnable() {
@Override
public void run() {
if (mOverlayStateController.areExitAnimationsRunning()) {
// Avoid interfering with the exit animations.
return;
}
mVisibilityController.setVisibility(View.INVISIBLE);
mHidden = true;
if (mHiddenCallback != null) {
mHiddenCallback.run();
mHiddenCallback = null;
}
} }
}; };
@Inject @Inject
HideComplicationTouchHandler(Complication.VisibilityController visibilityController, HideComplicationTouchHandler(Complication.VisibilityController visibilityController,
@Named(COMPLICATIONS_RESTORE_TIMEOUT) int restoreTimeout, @Named(COMPLICATIONS_RESTORE_TIMEOUT) int restoreTimeout,
@Named(COMPLICATIONS_FADE_OUT_DELAY) int fadeOutDelay,
TouchInsetManager touchInsetManager, TouchInsetManager touchInsetManager,
StatusBarKeyguardViewManager statusBarKeyguardViewManager, StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@Main Executor executor, @Main DelayableExecutor executor,
@Main Handler handler) { DreamOverlayStateController overlayStateController) {
mVisibilityController = visibilityController; mVisibilityController = visibilityController;
mRestoreTimeout = restoreTimeout; mRestoreTimeout = restoreTimeout;
mFadeOutDelay = fadeOutDelay;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mHandler = handler;
mTouchInsetManager = touchInsetManager; mTouchInsetManager = touchInsetManager;
mExecutor = executor; mExecutor = executor;
mOverlayStateController = overlayStateController;
} }
@Override @Override
@@ -87,7 +116,8 @@ public class HideComplicationTouchHandler implements DreamTouchHandler {
final boolean bouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing(); final boolean bouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing();
// If other sessions are interested in this touch, do not fade out elements. // If other sessions are interested in this touch, do not fade out elements.
if (session.getActiveSessionCount() > 1 || bouncerShowing) { if (session.getActiveSessionCount() > 1 || bouncerShowing
|| mOverlayStateController.areExitAnimationsRunning()) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "not fading. Active session count: " + session.getActiveSessionCount() Log.d(TAG, "not fading. Active session count: " + session.getActiveSessionCount()
+ ". Bouncer showing: " + bouncerShowing); + ". Bouncer showing: " + bouncerShowing);
@@ -115,8 +145,11 @@ public class HideComplicationTouchHandler implements DreamTouchHandler {
touchCheck.addListener(() -> { touchCheck.addListener(() -> {
try { try {
if (!touchCheck.get()) { if (!touchCheck.get()) {
mHandler.removeCallbacks(mRestoreComplications); // Cancel all pending callbacks.
mVisibilityController.setVisibility(View.INVISIBLE, true); while (!mCancelCallbacks.isEmpty()) mCancelCallbacks.pop().run();
mCancelCallbacks.add(
mExecutor.executeDelayed(
mHideComplications, mFadeOutDelay));
} else { } else {
// If a touch occurred inside the dream overlay touch insets, do not // If a touch occurred inside the dream overlay touch insets, do not
// handle the touch. // handle the touch.
@@ -130,7 +163,23 @@ public class HideComplicationTouchHandler implements DreamTouchHandler {
|| motionEvent.getAction() == MotionEvent.ACTION_UP) { || motionEvent.getAction() == MotionEvent.ACTION_UP) {
// End session and initiate delayed reappearance of the complications. // End session and initiate delayed reappearance of the complications.
session.pop(); session.pop();
mHandler.postDelayed(mRestoreComplications, mRestoreTimeout); runAfterHidden(() -> mCancelCallbacks.add(
mExecutor.executeDelayed(mRestoreComplications,
mRestoreTimeout)));
}
});
}
/**
* Triggers a runnable after complications have been hidden. Will override any previously set
* runnable currently waiting for hide to happen.
*/
private void runAfterHidden(Runnable runnable) {
mExecutor.execute(() -> {
if (mHidden) {
runnable.run();
} else {
mHiddenCallback = runnable;
} }
}); });
} }

View File

@@ -408,6 +408,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
*/ */
private final int mDreamOpenAnimationDuration; private final int mDreamOpenAnimationDuration;
/**
* The duration in milliseconds of the dream close animation.
*/
private final int mDreamCloseAnimationDuration;
/** /**
* The animation used for hiding keyguard. This is used to fetch the animation timings if * The animation used for hiding keyguard. This is used to fetch the animation timings if
* WindowManager is not providing us with them. * WindowManager is not providing us with them.
@@ -1054,7 +1059,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
} }
mUnoccludeAnimator = ValueAnimator.ofFloat(1f, 0f); mUnoccludeAnimator = ValueAnimator.ofFloat(1f, 0f);
mUnoccludeAnimator.setDuration(UNOCCLUDE_ANIMATION_DURATION); mUnoccludeAnimator.setDuration(isDream ? mDreamCloseAnimationDuration
: UNOCCLUDE_ANIMATION_DURATION);
mUnoccludeAnimator.setInterpolator(Interpolators.TOUCH_RESPONSE); mUnoccludeAnimator.setInterpolator(Interpolators.TOUCH_RESPONSE);
mUnoccludeAnimator.addUpdateListener( mUnoccludeAnimator.addUpdateListener(
animation -> { animation -> {
@@ -1204,6 +1210,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
mDreamOpenAnimationDuration = context.getResources().getInteger( mDreamOpenAnimationDuration = context.getResources().getInteger(
com.android.internal.R.integer.config_dreamOpenAnimationDuration); com.android.internal.R.integer.config_dreamOpenAnimationDuration);
mDreamCloseAnimationDuration = context.getResources().getInteger(
com.android.internal.R.integer.config_dreamCloseAnimationDuration);
} }
public void userActivity() { public void userActivity() {

View File

@@ -0,0 +1,125 @@
package com.android.systemui.dreams
import android.animation.Animator
import android.animation.AnimatorSet
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dreams.complication.ComplicationHostViewController
import com.android.systemui.statusbar.BlurUtils
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
companion object {
private const val DREAM_IN_BLUR_ANIMATION_DURATION = 1L
private const val DREAM_IN_BLUR_ANIMATION_DELAY = 2L
private const val DREAM_IN_COMPLICATIONS_ANIMATION_DURATION = 3L
private const val DREAM_IN_TOP_COMPLICATIONS_ANIMATION_DELAY = 4L
private const val DREAM_IN_BOTTOM_COMPLICATIONS_ANIMATION_DELAY = 5L
private const val DREAM_OUT_TRANSLATION_Y_DISTANCE = 6
private const val DREAM_OUT_TRANSLATION_Y_DURATION = 7L
private const val DREAM_OUT_TRANSLATION_Y_DELAY_BOTTOM = 8L
private const val DREAM_OUT_TRANSLATION_Y_DELAY_TOP = 9L
private const val DREAM_OUT_ALPHA_DURATION = 10L
private const val DREAM_OUT_ALPHA_DELAY_BOTTOM = 11L
private const val DREAM_OUT_ALPHA_DELAY_TOP = 12L
private const val DREAM_OUT_BLUR_DURATION = 13L
}
@Mock private lateinit var mockAnimator: AnimatorSet
@Mock private lateinit var blurUtils: BlurUtils
@Mock private lateinit var hostViewController: ComplicationHostViewController
@Mock private lateinit var statusBarViewController: DreamOverlayStatusBarViewController
@Mock private lateinit var stateController: DreamOverlayStateController
private lateinit var controller: DreamOverlayAnimationsController
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
controller =
DreamOverlayAnimationsController(
blurUtils,
hostViewController,
statusBarViewController,
stateController,
DREAM_IN_BLUR_ANIMATION_DURATION,
DREAM_IN_BLUR_ANIMATION_DELAY,
DREAM_IN_COMPLICATIONS_ANIMATION_DURATION,
DREAM_IN_TOP_COMPLICATIONS_ANIMATION_DELAY,
DREAM_IN_BOTTOM_COMPLICATIONS_ANIMATION_DELAY,
DREAM_OUT_TRANSLATION_Y_DISTANCE,
DREAM_OUT_TRANSLATION_Y_DURATION,
DREAM_OUT_TRANSLATION_Y_DELAY_BOTTOM,
DREAM_OUT_TRANSLATION_Y_DELAY_TOP,
DREAM_OUT_ALPHA_DURATION,
DREAM_OUT_ALPHA_DELAY_BOTTOM,
DREAM_OUT_ALPHA_DELAY_TOP,
DREAM_OUT_BLUR_DURATION
)
}
@Test
fun testExitAnimationOnEnd() {
val mockCallback: () -> Unit = mock()
controller.startExitAnimations(
view = mock(),
doneCallback = mockCallback,
animatorBuilder = { mockAnimator }
)
val captor = argumentCaptor<Animator.AnimatorListener>()
verify(mockAnimator).addListener(captor.capture())
val listener = captor.value
verify(mockCallback, never()).invoke()
listener.onAnimationEnd(mockAnimator)
verify(mockCallback, times(1)).invoke()
}
@Test
fun testCancellation() {
controller.startExitAnimations(
view = mock(),
doneCallback = mock(),
animatorBuilder = { mockAnimator }
)
verify(mockAnimator, never()).cancel()
controller.cancelAnimations()
verify(mockAnimator, times(1)).cancel()
}
@Test
fun testExitAfterStartWillCancel() {
val mockStartAnimator: AnimatorSet = mock()
val mockExitAnimator: AnimatorSet = mock()
controller.startEntryAnimations(view = mock(), animatorBuilder = { mockStartAnimator })
verify(mockStartAnimator, never()).cancel()
controller.startExitAnimations(
view = mock(),
doneCallback = mock(),
animatorBuilder = { mockExitAnimator }
)
// Verify that we cancelled the start animator in favor of the exit
// animator.
verify(mockStartAnimator, times(1)).cancel()
verify(mockExitAnimator, never()).cancel()
}
}

View File

@@ -204,7 +204,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
mController.onViewAttached(); mController.onViewAttached();
verify(mAnimationsController).startEntryAnimations(mDreamOverlayContainerView); verify(mAnimationsController).startEntryAnimations(mDreamOverlayContainerView);
verify(mAnimationsController, never()).cancelRunningEntryAnimations(); verify(mAnimationsController, never()).cancelAnimations();
} }
@Test @Test
@@ -221,6 +221,6 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
mController.onViewAttached(); mController.onViewAttached();
mController.onViewDetached(); mController.onViewDetached();
verify(mAnimationsController).cancelRunningEntryAnimations(); verify(mAnimationsController).cancelAnimations();
} }
} }

View File

@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -337,4 +338,28 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
verify(mDreamOverlayComponent).getDreamOverlayContainerViewController(); verify(mDreamOverlayComponent).getDreamOverlayContainerViewController();
verify(mDreamOverlayComponent).getDreamOverlayTouchMonitor(); verify(mDreamOverlayComponent).getDreamOverlayTouchMonitor();
} }
@Test
public void testWakeUp() throws RemoteException {
final IBinder proxy = mService.onBind(new Intent());
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
// Inform the overlay service of dream starting.
overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
true /*shouldShowComplication*/);
mMainExecutor.runAllReady();
final Runnable callback = mock(Runnable.class);
mService.onWakeUp(callback);
mMainExecutor.runAllReady();
verify(mDreamOverlayContainerViewController).wakeUp(callback, mMainExecutor);
}
@Test
public void testWakeUpBeforeStartDoesNothing() {
final Runnable callback = mock(Runnable.class);
mService.onWakeUp(callback);
mMainExecutor.runAllReady();
verify(mDreamOverlayContainerViewController, never()).wakeUp(callback, mMainExecutor);
}
} }

View File

@@ -16,8 +16,6 @@
package com.android.systemui.dreams.touch; package com.android.systemui.dreams.touch;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
@@ -33,6 +31,7 @@ import androidx.concurrent.futures.CallbackToFutureAdapter;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.complication.Complication; import com.android.systemui.dreams.complication.Complication;
import com.android.systemui.shared.system.InputChannelCompat; import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -52,6 +51,7 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class)
public class HideComplicationTouchHandlerTest extends SysuiTestCase { public class HideComplicationTouchHandlerTest extends SysuiTestCase {
private static final int RESTORE_TIMEOUT = 1000; private static final int RESTORE_TIMEOUT = 1000;
private static final int HIDE_DELAY = 500;
@Mock @Mock
Complication.VisibilityController mVisibilityController; Complication.VisibilityController mVisibilityController;
@@ -71,11 +71,18 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
@Mock @Mock
DreamTouchHandler.TouchSession mSession; DreamTouchHandler.TouchSession mSession;
FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); @Mock
DreamOverlayStateController mStateController;
FakeSystemClock mClock;
FakeExecutor mFakeExecutor;
@Before @Before
public void setup() { public void setup() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mClock = new FakeSystemClock();
mFakeExecutor = new FakeExecutor(mClock);
} }
/** /**
@@ -86,10 +93,11 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
final HideComplicationTouchHandler touchHandler = new HideComplicationTouchHandler( final HideComplicationTouchHandler touchHandler = new HideComplicationTouchHandler(
mVisibilityController, mVisibilityController,
RESTORE_TIMEOUT, RESTORE_TIMEOUT,
HIDE_DELAY,
mTouchInsetManager, mTouchInsetManager,
mStatusBarKeyguardViewManager, mStatusBarKeyguardViewManager,
mFakeExecutor, mFakeExecutor,
mHandler); mStateController);
// Report multiple active sessions. // Report multiple active sessions.
when(mSession.getActiveSessionCount()).thenReturn(2); when(mSession.getActiveSessionCount()).thenReturn(2);
@@ -103,8 +111,10 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
// Verify session end. // Verify session end.
verify(mSession).pop(); verify(mSession).pop();
mClock.advanceTime(HIDE_DELAY);
// Verify no interaction with visibility controller. // Verify no interaction with visibility controller.
verify(mVisibilityController, never()).setVisibility(anyInt(), anyBoolean()); verify(mVisibilityController, never()).setVisibility(anyInt());
} }
/** /**
@@ -115,10 +125,11 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
final HideComplicationTouchHandler touchHandler = new HideComplicationTouchHandler( final HideComplicationTouchHandler touchHandler = new HideComplicationTouchHandler(
mVisibilityController, mVisibilityController,
RESTORE_TIMEOUT, RESTORE_TIMEOUT,
HIDE_DELAY,
mTouchInsetManager, mTouchInsetManager,
mStatusBarKeyguardViewManager, mStatusBarKeyguardViewManager,
mFakeExecutor, mFakeExecutor,
mHandler); mStateController);
// Report one session. // Report one session.
when(mSession.getActiveSessionCount()).thenReturn(1); when(mSession.getActiveSessionCount()).thenReturn(1);
@@ -132,8 +143,10 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
// Verify session end. // Verify session end.
verify(mSession).pop(); verify(mSession).pop();
mClock.advanceTime(HIDE_DELAY);
// Verify no interaction with visibility controller. // Verify no interaction with visibility controller.
verify(mVisibilityController, never()).setVisibility(anyInt(), anyBoolean()); verify(mVisibilityController, never()).setVisibility(anyInt());
} }
/** /**
@@ -144,10 +157,11 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
final HideComplicationTouchHandler touchHandler = new HideComplicationTouchHandler( final HideComplicationTouchHandler touchHandler = new HideComplicationTouchHandler(
mVisibilityController, mVisibilityController,
RESTORE_TIMEOUT, RESTORE_TIMEOUT,
HIDE_DELAY,
mTouchInsetManager, mTouchInsetManager,
mStatusBarKeyguardViewManager, mStatusBarKeyguardViewManager,
mFakeExecutor, mFakeExecutor,
mHandler); mStateController);
// Report one session // Report one session
when(mSession.getActiveSessionCount()).thenReturn(1); when(mSession.getActiveSessionCount()).thenReturn(1);
@@ -177,8 +191,10 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
// Verify session ended. // Verify session ended.
verify(mSession).pop(); verify(mSession).pop();
mClock.advanceTime(HIDE_DELAY);
// Verify no interaction with visibility controller. // Verify no interaction with visibility controller.
verify(mVisibilityController, never()).setVisibility(anyInt(), anyBoolean()); verify(mVisibilityController, never()).setVisibility(anyInt());
} }
/** /**
@@ -189,10 +205,11 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
final HideComplicationTouchHandler touchHandler = new HideComplicationTouchHandler( final HideComplicationTouchHandler touchHandler = new HideComplicationTouchHandler(
mVisibilityController, mVisibilityController,
RESTORE_TIMEOUT, RESTORE_TIMEOUT,
HIDE_DELAY,
mTouchInsetManager, mTouchInsetManager,
mStatusBarKeyguardViewManager, mStatusBarKeyguardViewManager,
mFakeExecutor, mFakeExecutor,
mHandler); mStateController);
// Report one session // Report one session
when(mSession.getActiveSessionCount()).thenReturn(1); when(mSession.getActiveSessionCount()).thenReturn(1);
@@ -221,11 +238,11 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
inputEventListenerCaptor.getValue().onInputEvent(mMotionEvent); inputEventListenerCaptor.getValue().onInputEvent(mMotionEvent);
mFakeExecutor.runAllReady(); mFakeExecutor.runAllReady();
// Verify callback to restore visibility cancelled. // Verify visibility controller doesn't hide until after timeout
verify(mHandler).removeCallbacks(any()); verify(mVisibilityController, never()).setVisibility(eq(View.INVISIBLE));
mClock.advanceTime(HIDE_DELAY);
// Verify visibility controller told to hide complications. // Verify visibility controller told to hide complications.
verify(mVisibilityController).setVisibility(eq(View.INVISIBLE), anyBoolean()); verify(mVisibilityController).setVisibility(eq(View.INVISIBLE));
Mockito.clearInvocations(mVisibilityController, mHandler); Mockito.clearInvocations(mVisibilityController, mHandler);
@@ -235,11 +252,8 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
mFakeExecutor.runAllReady(); mFakeExecutor.runAllReady();
// Verify visibility controller told to show complications. // Verify visibility controller told to show complications.
ArgumentCaptor<Runnable> delayRunnableCaptor = ArgumentCaptor.forClass(Runnable.class); mClock.advanceTime(RESTORE_TIMEOUT);
verify(mHandler).postDelayed(delayRunnableCaptor.capture(), verify(mVisibilityController).setVisibility(eq(View.VISIBLE));
eq(Long.valueOf(RESTORE_TIMEOUT)));
delayRunnableCaptor.getValue().run();
verify(mVisibilityController).setVisibility(eq(View.VISIBLE), anyBoolean());
// Verify session ended. // Verify session ended.
verify(mSession).pop(); verify(mSession).pop();