Implement dream out overlay transition
The overlay should blur and animate the complications when the dream is waking up. This adds a new API to DreamOverlayService to get notified of dreams waking, so that it can run the animation. When the animation is done, we communicate back to the DreamService so that it may finish itself. A delay was added to the touch handler which hides the overlay when a touch happens, in order to ensure the exit animation has a chance to complete first. Bug: 220311554 Test: atest DreamOverlayAnimationsControllerTest Test: atest HideComplicationTouchHandlerTest Test: atest DreamOverlayContainerViewControllerTest Test: atest DreamOverlayServiceTest Test: flashed device and verified animation looks correct when tapping dream to exit Change-Id: Ibccd45aefbf9b8aeb462d843794c3434c49a6bf2
This commit is contained in:
@@ -49,6 +49,17 @@ public abstract class DreamOverlayService extends Service {
|
||||
mShowComplications = shouldShowComplications;
|
||||
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;
|
||||
@@ -70,6 +81,17 @@ public abstract class DreamOverlayService extends Service {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -312,7 +312,14 @@ public class DreamService extends Service implements Window.Callback {
|
||||
@Override
|
||||
public void onExitRequested() {
|
||||
// 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>
|
||||
*/
|
||||
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} */
|
||||
|
||||
@@ -38,4 +38,7 @@ interface IDreamOverlay {
|
||||
*/
|
||||
void startDream(in LayoutParams params, in IDreamOverlayCallback callback,
|
||||
in String dreamComponent, in boolean shouldShowComplications);
|
||||
|
||||
/** Called when the dream is waking, to do any exit animations */
|
||||
void wakeUp();
|
||||
}
|
||||
|
||||
@@ -28,4 +28,7 @@ interface IDreamOverlayCallback {
|
||||
* Invoked to request the dream exit.
|
||||
*/
|
||||
void onExitRequested();
|
||||
|
||||
/** Invoked when the dream overlay wakeUp animation is complete. */
|
||||
void onWakeUpComplete();
|
||||
}
|
||||
@@ -2485,7 +2485,7 @@
|
||||
<!-- The duration in milliseconds of the dream opening animation. -->
|
||||
<integer name="config_dreamOpenAnimationDuration">250</integer>
|
||||
<!-- 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
|
||||
assistant activities (ACTIVITY_TYPE_ASSISTANT) -->
|
||||
|
||||
@@ -743,12 +743,35 @@
|
||||
<!-- How long in milliseconds before full burn-in protection is achieved. -->
|
||||
<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="complicationFadeInMs">500</integer>
|
||||
|
||||
<integer name="complicationRestoreMs">1000</integer>
|
||||
|
||||
<integer name="complicationFadeOutDelayMs">200</integer>
|
||||
|
||||
<!-- Duration in milliseconds of the dream in un-blur animation. -->
|
||||
<integer name="config_dreamOverlayInBlurDurationMs">249</integer>
|
||||
<!-- Delay in milliseconds of the dream in un-blur animation. -->
|
||||
@@ -780,27 +803,6 @@
|
||||
<item>com.android.systemui</item>
|
||||
</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
|
||||
is available. If false, UI will never show regardless of tethering availability" -->
|
||||
<bool name="config_show_wifi_tethering">true</bool>
|
||||
|
||||
@@ -1534,6 +1534,7 @@
|
||||
<dimen name="dream_overlay_complication_margin">0dp</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_padding_vertical">6dp</dimen>
|
||||
|
||||
@@ -17,17 +17,19 @@
|
||||
package com.android.systemui.dreams
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ValueAnimator
|
||||
import android.view.View
|
||||
import android.view.animation.Interpolator
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.core.animation.doOnEnd
|
||||
import com.android.systemui.animation.Interpolators
|
||||
import com.android.systemui.dreams.complication.ComplicationHostViewController
|
||||
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.statusbar.BlurUtils
|
||||
import java.util.function.Consumer
|
||||
import com.android.systemui.statusbar.CrossFadeHelper
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
@@ -40,108 +42,239 @@ constructor(
|
||||
private val mStatusBarViewController: DreamOverlayStatusBarViewController,
|
||||
private val mOverlayStateController: DreamOverlayStateController,
|
||||
@Named(DreamOverlayModule.DREAM_IN_BLUR_ANIMATION_DURATION)
|
||||
private val mDreamInBlurAnimDuration: Int,
|
||||
@Named(DreamOverlayModule.DREAM_IN_BLUR_ANIMATION_DELAY) private val mDreamInBlurAnimDelay: Int,
|
||||
private val mDreamInBlurAnimDurationMs: Long,
|
||||
@Named(DreamOverlayModule.DREAM_IN_BLUR_ANIMATION_DELAY)
|
||||
private val mDreamInBlurAnimDelayMs: Long,
|
||||
@Named(DreamOverlayModule.DREAM_IN_COMPLICATIONS_ANIMATION_DURATION)
|
||||
private val mDreamInComplicationsAnimDuration: Int,
|
||||
private val mDreamInComplicationsAnimDurationMs: Long,
|
||||
@Named(DreamOverlayModule.DREAM_IN_TOP_COMPLICATIONS_ANIMATION_DELAY)
|
||||
private val mDreamInTopComplicationsAnimDelay: Int,
|
||||
private val mDreamInTopComplicationsAnimDelayMs: Long,
|
||||
@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. */
|
||||
fun startEntryAnimations(view: View) {
|
||||
cancelRunningEntryAnimations()
|
||||
@JvmOverloads
|
||||
fun startEntryAnimations(view: View, animatorBuilder: () -> AnimatorSet = { AnimatorSet() }) {
|
||||
cancelAnimations()
|
||||
|
||||
mEntryAnimations = AnimatorSet()
|
||||
mEntryAnimations?.apply {
|
||||
playTogether(
|
||||
buildDreamInBlurAnimator(view),
|
||||
buildDreamInTopComplicationsAnimator(),
|
||||
buildDreamInBottomComplicationsAnimator()
|
||||
)
|
||||
doOnEnd { mOverlayStateController.setEntryAnimationsFinished(true) }
|
||||
start()
|
||||
}
|
||||
mAnimator =
|
||||
animatorBuilder().apply {
|
||||
playTogether(
|
||||
blurAnimator(
|
||||
view = view,
|
||||
from = 1f,
|
||||
to = 0f,
|
||||
durationMs = mDreamInBlurAnimDurationMs,
|
||||
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. */
|
||||
fun cancelRunningEntryAnimations() {
|
||||
if (mEntryAnimations?.isRunning == true) {
|
||||
mEntryAnimations?.cancel()
|
||||
}
|
||||
mEntryAnimations = null
|
||||
fun cancelAnimations() {
|
||||
mAnimator =
|
||||
mAnimator?.let {
|
||||
it.cancel()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildDreamInBlurAnimator(view: View): Animator {
|
||||
return ValueAnimator.ofFloat(1f, 0f).apply {
|
||||
duration = mDreamInBlurAnimDuration.toLong()
|
||||
startDelay = mDreamInBlurAnimDelay.toLong()
|
||||
private fun blurAnimator(
|
||||
view: View,
|
||||
from: Float,
|
||||
to: Float,
|
||||
durationMs: Long,
|
||||
delayMs: Long = 0
|
||||
): Animator {
|
||||
return ValueAnimator.ofFloat(from, to).apply {
|
||||
duration = durationMs
|
||||
startDelay = delayMs
|
||||
interpolator = Interpolators.LINEAR
|
||||
addUpdateListener { animator: ValueAnimator ->
|
||||
mBlurProgress = animator.animatedValue as Float
|
||||
mBlurUtils.applyBlur(
|
||||
view.viewRootImpl,
|
||||
mBlurUtils.blurRadiusOfRatio(animator.animatedValue as Float).toInt(),
|
||||
false /*opaque*/
|
||||
viewRootImpl = view.viewRootImpl,
|
||||
radius = mBlurUtils.blurRadiusOfRatio(mBlurProgress).toInt(),
|
||||
opaque = false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildDreamInTopComplicationsAnimator(): Animator {
|
||||
return ValueAnimator.ofFloat(0f, 1f).apply {
|
||||
duration = mDreamInComplicationsAnimDuration.toLong()
|
||||
startDelay = mDreamInTopComplicationsAnimDelay.toLong()
|
||||
private fun alphaAnimator(
|
||||
from: Float,
|
||||
to: Float,
|
||||
durationMs: Long,
|
||||
delayMs: Long,
|
||||
@Position position: Int
|
||||
): Animator {
|
||||
return ValueAnimator.ofFloat(from, to).apply {
|
||||
duration = durationMs
|
||||
startDelay = delayMs
|
||||
interpolator = Interpolators.LINEAR
|
||||
addUpdateListener { va: ValueAnimator ->
|
||||
setTopElementsAlpha(va.animatedValue as Float)
|
||||
setElementsAlphaAtPosition(
|
||||
alpha = va.animatedValue as Float,
|
||||
position = position,
|
||||
fadingOut = to < from
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildDreamInBottomComplicationsAnimator(): Animator {
|
||||
return ValueAnimator.ofFloat(0f, 1f).apply {
|
||||
duration = mDreamInComplicationsAnimDuration.toLong()
|
||||
startDelay = mDreamInBottomComplicationsAnimDelay.toLong()
|
||||
interpolator = Interpolators.LINEAR
|
||||
private fun translationYAnimator(
|
||||
from: Float,
|
||||
to: Float,
|
||||
durationMs: Long,
|
||||
delayMs: Long,
|
||||
@Position position: Int,
|
||||
animInterpolator: Interpolator
|
||||
): Animator {
|
||||
return ValueAnimator.ofFloat(from, to).apply {
|
||||
duration = durationMs
|
||||
startDelay = delayMs
|
||||
interpolator = animInterpolator
|
||||
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. */
|
||||
private fun setTopElementsAlpha(alpha: Float) {
|
||||
mComplicationHostViewController
|
||||
.getViewsAtPosition(ComplicationLayoutParams.POSITION_TOP)
|
||||
.forEach(Consumer { v: View -> setAlphaAndEnsureVisible(v, alpha) })
|
||||
mStatusBarViewController.setAlpha(alpha)
|
||||
}
|
||||
|
||||
/** 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
|
||||
/** Sets alpha of complications at the specified position. */
|
||||
private fun setElementsAlphaAtPosition(alpha: Float, position: Int, fadingOut: Boolean) {
|
||||
mCurrentAlphaAtPosition[position] = alpha
|
||||
mComplicationHostViewController.getViewsAtPosition(position).forEach { view ->
|
||||
if (fadingOut) {
|
||||
CrossFadeHelper.fadeOut(view, 1 - alpha, /* remap= */ false)
|
||||
} else {
|
||||
CrossFadeHelper.fadeIn(view, alpha, /* remap= */ false)
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ import android.util.MathUtils;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.animation.Interpolators;
|
||||
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 java.util.Arrays;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
@@ -194,7 +197,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
||||
}
|
||||
mPrimaryBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback);
|
||||
|
||||
mDreamOverlayAnimationsController.cancelRunningEntryAnimations();
|
||||
mDreamOverlayAnimationsController.cancelAnimations();
|
||||
}
|
||||
|
||||
View getContainerView() {
|
||||
@@ -251,4 +254,17 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
||||
: aboutToShowBouncerProgress(expansion + 0.03f));
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +213,15 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
|
||||
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
|
||||
* called from the main executing thread. The window attributes closely mirror those that are
|
||||
|
||||
@@ -52,6 +52,7 @@ public class DreamOverlayStateController implements
|
||||
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_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_SET_STATE = 2;
|
||||
@@ -211,6 +212,14 @@ public class DreamOverlayStateController implements
|
||||
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) {
|
||||
return (mState & state) != 0;
|
||||
}
|
||||
@@ -256,6 +265,15 @@ public class DreamOverlayStateController implements
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.android.systemui.R;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem;
|
||||
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.NextAlarmController;
|
||||
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.
|
||||
*/
|
||||
protected void setAlpha(float alpha) {
|
||||
protected void setFadeAmount(float fadeAmount, boolean fadingOut) {
|
||||
updateVisibility();
|
||||
|
||||
if (mView.getVisibility() != View.VISIBLE) {
|
||||
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() {
|
||||
|
||||
@@ -197,11 +197,11 @@ public interface Complication {
|
||||
*/
|
||||
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 animate whether the change should be animated.
|
||||
*/
|
||||
void setVisibility(@View.Visibility int visibility, boolean animate);
|
||||
void setVisibility(@View.Visibility int visibility);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.SCOPED_COMPLICATIONS_LAYOUT;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewPropertyAnimator;
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.constraintlayout.widget.Constraints;
|
||||
@@ -34,6 +31,7 @@ import androidx.constraintlayout.widget.Constraints;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.dreams.complication.ComplicationLayoutParams.Position;
|
||||
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
|
||||
import com.android.systemui.statusbar.CrossFadeHelper;
|
||||
import com.android.systemui.touch.TouchInsetManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -481,7 +479,6 @@ public class ComplicationLayoutEngine implements Complication.VisibilityControll
|
||||
private final TouchInsetManager.TouchInsetSession mSession;
|
||||
private final int mFadeInDuration;
|
||||
private final int mFadeOutDuration;
|
||||
private ViewPropertyAnimator mViewPropertyAnimator;
|
||||
|
||||
/** */
|
||||
@Inject
|
||||
@@ -498,26 +495,16 @@ public class ComplicationLayoutEngine implements Complication.VisibilityControll
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisibility(int visibility, boolean animate) {
|
||||
final boolean appearing = visibility == View.VISIBLE;
|
||||
|
||||
if (mViewPropertyAnimator != null) {
|
||||
mViewPropertyAnimator.cancel();
|
||||
public void setVisibility(int visibility) {
|
||||
if (visibility == View.VISIBLE) {
|
||||
CrossFadeHelper.fadeIn(mLayout, mFadeInDuration, /* delay= */ 0);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,7 +38,7 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
|
||||
POSITION_START,
|
||||
})
|
||||
|
||||
@interface Position {}
|
||||
public @interface Position {}
|
||||
/** Align view with the top of parent or bottom of preceding {@link Complication}. */
|
||||
public static final int POSITION_TOP = 1 << 0;
|
||||
/** Align view with the bottom of parent or top of preceding {@link Complication}. */
|
||||
|
||||
@@ -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_IN_DURATION = "complications_fade_in_duration";
|
||||
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
|
||||
@@ -74,6 +75,16 @@ public abstract class ComplicationHostViewModule {
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -55,6 +55,22 @@ public abstract class DreamOverlayModule {
|
||||
"dream_in_top_complications_anim_delay";
|
||||
public static final String DREAM_IN_BOTTOM_COMPLICATIONS_ANIMATION_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
|
||||
@@ -127,8 +143,8 @@ public abstract class DreamOverlayModule {
|
||||
*/
|
||||
@Provides
|
||||
@Named(DREAM_IN_BLUR_ANIMATION_DURATION)
|
||||
static int providesDreamInBlurAnimationDuration(@Main Resources resources) {
|
||||
return resources.getInteger(R.integer.config_dreamOverlayInBlurDurationMs);
|
||||
static long providesDreamInBlurAnimationDuration(@Main Resources resources) {
|
||||
return (long) resources.getInteger(R.integer.config_dreamOverlayInBlurDurationMs);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,8 +152,8 @@ public abstract class DreamOverlayModule {
|
||||
*/
|
||||
@Provides
|
||||
@Named(DREAM_IN_BLUR_ANIMATION_DELAY)
|
||||
static int providesDreamInBlurAnimationDelay(@Main Resources resources) {
|
||||
return resources.getInteger(R.integer.config_dreamOverlayInBlurDelayMs);
|
||||
static long providesDreamInBlurAnimationDelay(@Main Resources resources) {
|
||||
return (long) resources.getInteger(R.integer.config_dreamOverlayInBlurDelayMs);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,8 +161,8 @@ public abstract class DreamOverlayModule {
|
||||
*/
|
||||
@Provides
|
||||
@Named(DREAM_IN_COMPLICATIONS_ANIMATION_DURATION)
|
||||
static int providesDreamInComplicationsAnimationDuration(@Main Resources resources) {
|
||||
return resources.getInteger(R.integer.config_dreamOverlayInComplicationsDurationMs);
|
||||
static long providesDreamInComplicationsAnimationDuration(@Main Resources resources) {
|
||||
return (long) resources.getInteger(R.integer.config_dreamOverlayInComplicationsDurationMs);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,8 +170,8 @@ public abstract class DreamOverlayModule {
|
||||
*/
|
||||
@Provides
|
||||
@Named(DREAM_IN_TOP_COMPLICATIONS_ANIMATION_DELAY)
|
||||
static int providesDreamInTopComplicationsAnimationDelay(@Main Resources resources) {
|
||||
return resources.getInteger(R.integer.config_dreamOverlayInTopComplicationsDelayMs);
|
||||
static long providesDreamInTopComplicationsAnimationDelay(@Main Resources resources) {
|
||||
return (long) resources.getInteger(R.integer.config_dreamOverlayInTopComplicationsDelayMs);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,8 +179,69 @@ public abstract class DreamOverlayModule {
|
||||
*/
|
||||
@Provides
|
||||
@Named(DREAM_IN_BOTTOM_COMPLICATIONS_ANIMATION_DELAY)
|
||||
static int providesDreamInBottomComplicationsAnimationDelay(@Main Resources resources) {
|
||||
return resources.getInteger(R.integer.config_dreamOverlayInBottomComplicationsDelayMs);
|
||||
static long providesDreamInBottomComplicationsAnimationDelay(@Main Resources resources) {
|
||||
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
|
||||
|
||||
@@ -16,22 +16,26 @@
|
||||
|
||||
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 android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.dreams.DreamOverlayStateController;
|
||||
import com.android.systemui.dreams.complication.Complication;
|
||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
||||
import com.android.systemui.touch.TouchInsetManager;
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
@@ -49,33 +53,58 @@ public class HideComplicationTouchHandler implements DreamTouchHandler {
|
||||
private static final String TAG = "HideComplicationHandler";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
private final Complication.VisibilityController mVisibilityController;
|
||||
private final int mRestoreTimeout;
|
||||
private final int mFadeOutDelay;
|
||||
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
|
||||
private final Handler mHandler;
|
||||
private final Executor mExecutor;
|
||||
private final DelayableExecutor mExecutor;
|
||||
private final DreamOverlayStateController mOverlayStateController;
|
||||
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() {
|
||||
@Override
|
||||
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
|
||||
HideComplicationTouchHandler(Complication.VisibilityController visibilityController,
|
||||
@Named(COMPLICATIONS_RESTORE_TIMEOUT) int restoreTimeout,
|
||||
@Named(COMPLICATIONS_FADE_OUT_DELAY) int fadeOutDelay,
|
||||
TouchInsetManager touchInsetManager,
|
||||
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
|
||||
@Main Executor executor,
|
||||
@Main Handler handler) {
|
||||
@Main DelayableExecutor executor,
|
||||
DreamOverlayStateController overlayStateController) {
|
||||
mVisibilityController = visibilityController;
|
||||
mRestoreTimeout = restoreTimeout;
|
||||
mFadeOutDelay = fadeOutDelay;
|
||||
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
|
||||
mHandler = handler;
|
||||
mTouchInsetManager = touchInsetManager;
|
||||
mExecutor = executor;
|
||||
mOverlayStateController = overlayStateController;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -87,7 +116,8 @@ public class HideComplicationTouchHandler implements DreamTouchHandler {
|
||||
final boolean bouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing();
|
||||
|
||||
// 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) {
|
||||
Log.d(TAG, "not fading. Active session count: " + session.getActiveSessionCount()
|
||||
+ ". Bouncer showing: " + bouncerShowing);
|
||||
@@ -115,8 +145,11 @@ public class HideComplicationTouchHandler implements DreamTouchHandler {
|
||||
touchCheck.addListener(() -> {
|
||||
try {
|
||||
if (!touchCheck.get()) {
|
||||
mHandler.removeCallbacks(mRestoreComplications);
|
||||
mVisibilityController.setVisibility(View.INVISIBLE, true);
|
||||
// Cancel all pending callbacks.
|
||||
while (!mCancelCallbacks.isEmpty()) mCancelCallbacks.pop().run();
|
||||
mCancelCallbacks.add(
|
||||
mExecutor.executeDelayed(
|
||||
mHideComplications, mFadeOutDelay));
|
||||
} else {
|
||||
// If a touch occurred inside the dream overlay touch insets, do not
|
||||
// handle the touch.
|
||||
@@ -130,7 +163,23 @@ public class HideComplicationTouchHandler implements DreamTouchHandler {
|
||||
|| motionEvent.getAction() == MotionEvent.ACTION_UP) {
|
||||
// End session and initiate delayed reappearance of the complications.
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -408,6 +408,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
*/
|
||||
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
|
||||
* WindowManager is not providing us with them.
|
||||
@@ -1054,7 +1059,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
}
|
||||
|
||||
mUnoccludeAnimator = ValueAnimator.ofFloat(1f, 0f);
|
||||
mUnoccludeAnimator.setDuration(UNOCCLUDE_ANIMATION_DURATION);
|
||||
mUnoccludeAnimator.setDuration(isDream ? mDreamCloseAnimationDuration
|
||||
: UNOCCLUDE_ANIMATION_DURATION);
|
||||
mUnoccludeAnimator.setInterpolator(Interpolators.TOUCH_RESPONSE);
|
||||
mUnoccludeAnimator.addUpdateListener(
|
||||
animation -> {
|
||||
@@ -1204,6 +1210,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
|
||||
mDreamOpenAnimationDuration = context.getResources().getInteger(
|
||||
com.android.internal.R.integer.config_dreamOpenAnimationDuration);
|
||||
mDreamCloseAnimationDuration = context.getResources().getInteger(
|
||||
com.android.internal.R.integer.config_dreamCloseAnimationDuration);
|
||||
}
|
||||
|
||||
public void userActivity() {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -204,7 +204,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
|
||||
mController.onViewAttached();
|
||||
|
||||
verify(mAnimationsController).startEntryAnimations(mDreamOverlayContainerView);
|
||||
verify(mAnimationsController, never()).cancelRunningEntryAnimations();
|
||||
verify(mAnimationsController, never()).cancelAnimations();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -221,6 +221,6 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
|
||||
mController.onViewAttached();
|
||||
mController.onViewDetached();
|
||||
|
||||
verify(mAnimationsController).cancelRunningEntryAnimations();
|
||||
verify(mAnimationsController).cancelAnimations();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -337,4 +338,28 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
|
||||
verify(mDreamOverlayComponent).getDreamOverlayContainerViewController();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
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.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
@@ -33,6 +31,7 @@ import androidx.concurrent.futures.CallbackToFutureAdapter;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.dreams.DreamOverlayStateController;
|
||||
import com.android.systemui.dreams.complication.Complication;
|
||||
import com.android.systemui.shared.system.InputChannelCompat;
|
||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
||||
@@ -52,6 +51,7 @@ import org.mockito.MockitoAnnotations;
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
public class HideComplicationTouchHandlerTest extends SysuiTestCase {
|
||||
private static final int RESTORE_TIMEOUT = 1000;
|
||||
private static final int HIDE_DELAY = 500;
|
||||
|
||||
@Mock
|
||||
Complication.VisibilityController mVisibilityController;
|
||||
@@ -71,11 +71,18 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
|
||||
@Mock
|
||||
DreamTouchHandler.TouchSession mSession;
|
||||
|
||||
FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
|
||||
@Mock
|
||||
DreamOverlayStateController mStateController;
|
||||
|
||||
FakeSystemClock mClock;
|
||||
|
||||
FakeExecutor mFakeExecutor;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mClock = new FakeSystemClock();
|
||||
mFakeExecutor = new FakeExecutor(mClock);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,10 +93,11 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
|
||||
final HideComplicationTouchHandler touchHandler = new HideComplicationTouchHandler(
|
||||
mVisibilityController,
|
||||
RESTORE_TIMEOUT,
|
||||
HIDE_DELAY,
|
||||
mTouchInsetManager,
|
||||
mStatusBarKeyguardViewManager,
|
||||
mFakeExecutor,
|
||||
mHandler);
|
||||
mStateController);
|
||||
|
||||
// Report multiple active sessions.
|
||||
when(mSession.getActiveSessionCount()).thenReturn(2);
|
||||
@@ -103,8 +111,10 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
|
||||
// Verify session end.
|
||||
verify(mSession).pop();
|
||||
|
||||
mClock.advanceTime(HIDE_DELAY);
|
||||
|
||||
// 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(
|
||||
mVisibilityController,
|
||||
RESTORE_TIMEOUT,
|
||||
HIDE_DELAY,
|
||||
mTouchInsetManager,
|
||||
mStatusBarKeyguardViewManager,
|
||||
mFakeExecutor,
|
||||
mHandler);
|
||||
mStateController);
|
||||
|
||||
// Report one session.
|
||||
when(mSession.getActiveSessionCount()).thenReturn(1);
|
||||
@@ -132,8 +143,10 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
|
||||
// Verify session end.
|
||||
verify(mSession).pop();
|
||||
|
||||
mClock.advanceTime(HIDE_DELAY);
|
||||
|
||||
// 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(
|
||||
mVisibilityController,
|
||||
RESTORE_TIMEOUT,
|
||||
HIDE_DELAY,
|
||||
mTouchInsetManager,
|
||||
mStatusBarKeyguardViewManager,
|
||||
mFakeExecutor,
|
||||
mHandler);
|
||||
mStateController);
|
||||
|
||||
// Report one session
|
||||
when(mSession.getActiveSessionCount()).thenReturn(1);
|
||||
@@ -177,8 +191,10 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
|
||||
// Verify session ended.
|
||||
verify(mSession).pop();
|
||||
|
||||
mClock.advanceTime(HIDE_DELAY);
|
||||
|
||||
// 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(
|
||||
mVisibilityController,
|
||||
RESTORE_TIMEOUT,
|
||||
HIDE_DELAY,
|
||||
mTouchInsetManager,
|
||||
mStatusBarKeyguardViewManager,
|
||||
mFakeExecutor,
|
||||
mHandler);
|
||||
mStateController);
|
||||
|
||||
// Report one session
|
||||
when(mSession.getActiveSessionCount()).thenReturn(1);
|
||||
@@ -221,11 +238,11 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
|
||||
inputEventListenerCaptor.getValue().onInputEvent(mMotionEvent);
|
||||
mFakeExecutor.runAllReady();
|
||||
|
||||
// Verify callback to restore visibility cancelled.
|
||||
verify(mHandler).removeCallbacks(any());
|
||||
|
||||
// Verify visibility controller doesn't hide until after timeout
|
||||
verify(mVisibilityController, never()).setVisibility(eq(View.INVISIBLE));
|
||||
mClock.advanceTime(HIDE_DELAY);
|
||||
// Verify visibility controller told to hide complications.
|
||||
verify(mVisibilityController).setVisibility(eq(View.INVISIBLE), anyBoolean());
|
||||
verify(mVisibilityController).setVisibility(eq(View.INVISIBLE));
|
||||
|
||||
Mockito.clearInvocations(mVisibilityController, mHandler);
|
||||
|
||||
@@ -235,11 +252,8 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
|
||||
mFakeExecutor.runAllReady();
|
||||
|
||||
// Verify visibility controller told to show complications.
|
||||
ArgumentCaptor<Runnable> delayRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
|
||||
verify(mHandler).postDelayed(delayRunnableCaptor.capture(),
|
||||
eq(Long.valueOf(RESTORE_TIMEOUT)));
|
||||
delayRunnableCaptor.getValue().run();
|
||||
verify(mVisibilityController).setVisibility(eq(View.VISIBLE), anyBoolean());
|
||||
mClock.advanceTime(RESTORE_TIMEOUT);
|
||||
verify(mVisibilityController).setVisibility(eq(View.VISIBLE));
|
||||
|
||||
// Verify session ended.
|
||||
verify(mSession).pop();
|
||||
|
||||
Reference in New Issue
Block a user