Merge "Animate power menu dialog from power button"

This commit is contained in:
TreeHugger Robot
2022-02-17 13:51:09 +00:00
committed by Android (Google) Code Review
7 changed files with 137 additions and 5 deletions

View File

@@ -22,7 +22,7 @@
android:interpolator="@interpolator/standard"
android:duration="@android:integer/config_activityDefaultDur" />
<translate
android:fromYDelta="20dp"
android:fromYDelta="@android:dimen/popup_enter_animation_from_y_delta"
android:toYDelta="0"
android:interpolator="@interpolator/standard"
android:duration="@android:integer/config_activityDefaultDur" />

View File

@@ -23,7 +23,7 @@
android:duration="@android:integer/config_activityShortDur" />
<translate
android:fromYDelta="0"
android:toYDelta="-10dp"
android:toYDelta="@android:dimen/popup_exit_animation_to_y_delta"
android:interpolator="@interpolator/standard_accelerate"
android:duration="@android:integer/config_activityShortDur" />
</set>

View File

@@ -994,4 +994,8 @@
<!-- Default size for user icons (a.k.a. avatar images) -->
<dimen name="user_icon_size">190dp</dimen>
<!-- Dimensions for the translations of the default dialog animation. -->
<dimen name="popup_enter_animation_from_y_delta">20dp</dimen>
<dimen name="popup_exit_animation_to_y_delta">-10dp</dimen>
</resources>

View File

@@ -2358,6 +2358,10 @@
<java-symbol type="string" name="nas_upgrade_notification_learn_more_action" />
<java-symbol type="string" name="nas_upgrade_notification_learn_more_content" />
<java-symbol type="bool" name="config_settingsHelpLinksEnabled" />
<java-symbol type="integer" name="config_activityDefaultDur" />
<java-symbol type="integer" name="config_activityShortDur" />
<java-symbol type="dimen" name="popup_enter_animation_from_y_delta" />
<java-symbol type="dimen" name="popup_exit_animation_to_y_delta" />
<!-- ImfTest -->
<java-symbol type="layout" name="auto_complete_list" />

View File

@@ -399,6 +399,11 @@
<!-- that would otherwise be intercepted by the Shade. -->
<item name="android:windowFullscreen">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<!-- Empty enter/exit animation, we will animate in-window. Note that the implementation -->
<!-- of ActionsDialogLite relies on this to be null (resource=0) to detect when to run -->
<!-- the in-window animation. -->
<item name="android:windowAnimationStyle">@null</item>
</style>
<style name="QSBorderlessButton">

View File

@@ -31,6 +31,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_G
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.Dialog;
@@ -76,8 +77,10 @@ import android.view.GestureDetector;
import android.view.IWindowManager;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -109,6 +112,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.MultiListLayout;
import com.android.systemui.MultiListLayout.MultiListAdapter;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Background;
@@ -2170,6 +2174,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private Optional<StatusBar> mStatusBarOptional;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private LockPatternUtils mLockPatternUtils;
private float mWindowDimAmount;
protected ViewGroup mContainer;
@@ -2251,6 +2256,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initializeLayout();
mWindowDimAmount = getWindow().getAttributes().dimAmount;
}
@Override
@@ -2459,6 +2465,96 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mNotificationShadeWindowController.setRequestTopUi(true, TAG);
mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true)
.commitUpdate(mContext.getDisplayId());
// By default this dialog windowAnimationStyle is null, and therefore windowAnimations
// should be equal to 0 which means we need to animate the dialog in-window. If it's not
// equal to 0, it means it has been overridden to animate (e.g. by the
// DialogLaunchAnimator) so we don't run the animation.
boolean shouldAnimateInWindow = getWindow().getAttributes().windowAnimations == 0;
if (shouldAnimateInWindow) {
startAnimation(true /* isEnter */, null /* then */);
// Override the dialog dismiss so that we can animate in-window before dismissing
// the dialog.
setDismissOverride(() -> {
startAnimation(false /* isEnter */, /* then */ () -> {
setDismissOverride(null);
// Hide then dismiss to instantly dismiss.
hide();
dismiss();
});
});
}
}
/** Run either the enter or exit animation, then run {@code then}. */
private void startAnimation(boolean isEnter, @Nullable Runnable then) {
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
// Note: these specs should be the same as in popup_enter_material and
// popup_exit_material.
float translationPx;
Resources resources = getContext().getResources();
if (isEnter) {
translationPx = resources.getDimension(R.dimen.popup_enter_animation_from_y_delta);
animator.setInterpolator(Interpolators.STANDARD);
animator.setDuration(resources.getInteger(R.integer.config_activityDefaultDur));
} else {
translationPx = resources.getDimension(R.dimen.popup_exit_animation_to_y_delta);
animator.setInterpolator(Interpolators.STANDARD_ACCELERATE);
animator.setDuration(resources.getInteger(R.integer.config_activityShortDur));
}
Window window = getWindow();
int rotation = window.getWindowManager().getDefaultDisplay().getRotation();
animator.addUpdateListener(valueAnimator -> {
float progress = (float) valueAnimator.getAnimatedValue();
float alpha = isEnter ? progress : 1 - progress;
mGlobalActionsLayout.setAlpha(alpha);
window.setDimAmount(mWindowDimAmount * alpha);
// TODO(b/213872558): Support devices that don't have their power button on the
// right.
float translation =
isEnter ? translationPx * (1 - progress) : translationPx * progress;
switch (rotation) {
case Surface.ROTATION_0:
mGlobalActionsLayout.setTranslationX(translation);
break;
case Surface.ROTATION_90:
mGlobalActionsLayout.setTranslationY(-translation);
break;
case Surface.ROTATION_180:
mGlobalActionsLayout.setTranslationX(-translation);
break;
case Surface.ROTATION_270:
mGlobalActionsLayout.setTranslationY(translation);
break;
}
});
animator.addListener(new AnimatorListenerAdapter() {
private int mPreviousLayerType;
@Override
public void onAnimationStart(Animator animation, boolean isReverse) {
mPreviousLayerType = mGlobalActionsLayout.getLayerType();
mGlobalActionsLayout.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
@Override
public void onAnimationEnd(Animator animation) {
mGlobalActionsLayout.setLayerType(mPreviousLayerType, null);
if (then != null) {
then.run();
}
}
});
animator.start();
}
@Override

View File

@@ -23,6 +23,7 @@ import junit.framework.Assert.assertNotNull
import junit.framework.Assert.assertNull
import junit.framework.Assert.assertTrue
import org.junit.After
import org.junit.Assert.assertNotEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -145,7 +146,31 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
assertNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView))
}
@Test
fun testDialogAnimationIsChangedByAnimator() {
// Important: the power menu animation relies on this behavior to know when to animate (see
// http://ag/16774605).
val dialog = runOnMainThreadAndWaitForIdleSync { TestDialog(context) }
dialog.window.setWindowAnimations(0)
assertEquals(0, dialog.window.attributes.windowAnimations)
val touchSurface = createTouchSurface()
runOnMainThreadAndWaitForIdleSync {
dialogLaunchAnimator.showFromView(dialog, touchSurface)
}
assertNotEquals(0, dialog.window.attributes.windowAnimations)
}
private fun createAndShowDialog(): TestDialog {
val touchSurface = createTouchSurface()
return runOnMainThreadAndWaitForIdleSync {
val dialog = TestDialog(context)
dialogLaunchAnimator.showFromView(dialog, touchSurface)
dialog
}
}
private fun createTouchSurface(): View {
return runOnMainThreadAndWaitForIdleSync {
val touchSurfaceRoot = LinearLayout(context)
val touchSurface = View(context)
@@ -156,9 +181,7 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
ViewUtils.attachView(touchSurfaceRoot)
attachedViews.add(touchSurfaceRoot)
val dialog = TestDialog(context)
dialogLaunchAnimator.showFromView(dialog, touchSurface)
dialog
touchSurface
}
}