Add different animation for transitions with translucent apps

None of the existing animation really work with transitions in
which a translucent app is appearing/disappearing. Add a separate
animation for that and change all existing transitions to these
new transition types in case we detect that only translucent
activities are appearing/disappearing.

Test: Sharesheet animations
Test: go/wm-smoke
Change-Id: Iffe57b7664dddf647d723c91d115ade60c12ad33
Fixes: 37953606
Fixes: 70730519
Fixes: 72649981
Fixes: 72686618
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index abc19d0..469662f 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -236,6 +236,18 @@
     int TRANSIT_KEYGUARD_UNOCCLUDE = 23;
 
     /**
+     * A translucent activity is being opened.
+     * @hide
+     */
+    int TRANSIT_TRANSLUCENT_ACTIVITY_OPEN = 24;
+
+    /**
+     * A translucent activity is being closed.
+     * @hide
+     */
+    int TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE = 25;
+
+    /**
      * @hide
      */
     @IntDef(prefix = { "TRANSIT_" }, value = {
@@ -258,7 +270,9 @@
             TRANSIT_KEYGUARD_GOING_AWAY,
             TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
             TRANSIT_KEYGUARD_OCCLUDE,
-            TRANSIT_KEYGUARD_UNOCCLUDE
+            TRANSIT_KEYGUARD_UNOCCLUDE,
+            TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
+            TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface TransitionType {}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 063135d..a8d0825 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -156,6 +156,8 @@
     TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21;
     TRANSIT_KEYGUARD_OCCLUDE = 22;
     TRANSIT_KEYGUARD_UNOCCLUDE = 23;
+    TRANSIT_TRANSLUCENT_ACTIVITY_OPEN = 24;
+    TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE = 25;
   }
   optional TransitionType last_used_app_transition = 2;
 }
diff --git a/core/res/res/anim/activity_translucent_close_exit.xml b/core/res/res/anim/activity_translucent_close_exit.xml
new file mode 100644
index 0000000..04c5dea
--- /dev/null
+++ b/core/res/res/anim/activity_translucent_close_exit.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shareInterpolator="false"
+    android:zAdjustment="top">
+    <translate
+        android:fromYDelta="0%"
+        android:toYDelta="100%"
+        android:interpolator="@interpolator/fast_out_linear_in"
+        android:duration="300"/>
+</set>
diff --git a/core/res/res/anim/activity_translucent_open_enter.xml b/core/res/res/anim/activity_translucent_open_enter.xml
new file mode 100644
index 0000000..da3dded
--- /dev/null
+++ b/core/res/res/anim/activity_translucent_open_enter.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shareInterpolator="false">
+    <translate
+        android:fromYDelta="100%"
+        android:toYDelta="0"
+        android:interpolator="@interpolator/linear_out_slow_in"
+        android:duration="300"/>
+</set>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 75f8013..469314d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1611,6 +1611,8 @@
   <java-symbol type="anim" name="task_open_enter" />
   <java-symbol type="anim" name="cross_profile_apps_thumbnail_enter" />
   <java-symbol type="anim" name="task_open_enter_cross_profile_apps" />
+  <java-symbol type="anim" name="activity_translucent_open_enter" />
+  <java-symbol type="anim" name="activity_translucent_close_exit" />
 
   <java-symbol type="array" name="config_autoRotationTiltTolerance" />
   <java-symbol type="array" name="config_keyboardTapVibePattern" />
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4dc68ac..2e6e348 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3027,7 +3027,7 @@
 
         // Show IME over the keyguard if the target allows it
         boolean allowWhenLocked = (win.isInputMethodWindow() || imeTarget == this)
-                && showImeOverKeyguard;;
+                && showImeOverKeyguard;
 
         if (isKeyguardLocked() && isKeyguardOccluded()) {
             // Show SHOW_WHEN_LOCKED windows if Keyguard is occluded.
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 41ae48a..c6f156b 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -34,6 +34,8 @@
 import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_UNSET;
 import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE;
 import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
@@ -114,6 +116,7 @@
 import android.view.animation.ScaleAnimation;
 import android.view.animation.TranslateAnimation;
 
+import com.android.internal.R;
 import com.android.internal.util.DumpUtils.Dump;
 import com.android.server.AttributeCache;
 import com.android.server.wm.WindowManagerService.H;
@@ -1652,14 +1655,26 @@
                         + " Callers=" + Debug.getCallers(3));
             }
         } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS && enter) {
-
-            a = loadAnimationRes("android", enter
-                    ? com.android.internal.R.anim.task_open_enter_cross_profile_apps
-                    : com.android.internal.R.anim.task_open_exit);
+            a = loadAnimationRes("android",
+                    com.android.internal.R.anim.task_open_enter_cross_profile_apps);
             Slog.v(TAG,
                     "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS:"
                             + " anim=" + a + " transit=" + appTransitionToString(transit)
-                            + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
+                            + " isEntrance=true" + " Callers=" + Debug.getCallers(3));
+        } else if (transit == TRANSIT_TRANSLUCENT_ACTIVITY_OPEN && enter) {
+            a = loadAnimationRes("android",
+                    com.android.internal.R.anim.activity_translucent_open_enter);
+            Slog.v(TAG,
+                    "applyAnimation TRANSIT_TRANSLUCENT_ACTIVITY_OPEN:"
+                            + " anim=" + a + " transit=" + appTransitionToString(transit)
+                            + " isEntrance=true" + " Callers=" + Debug.getCallers(3));
+        } else if (transit == TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE && !enter) {
+            a = loadAnimationRes("android",
+                    com.android.internal.R.anim.activity_translucent_close_exit);
+            Slog.v(TAG,
+                    "applyAnimation TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE:"
+                            + " anim=" + a + " transit=" + appTransitionToString(transit)
+                            + " isEntrance=false" + " Callers=" + Debug.getCallers(3));
         } else {
             int animAttr = 0;
             switch (transit) {
@@ -2001,6 +2016,12 @@
             case TRANSIT_KEYGUARD_UNOCCLUDE: {
                 return "TRANSIT_KEYGUARD_UNOCCLUDE";
             }
+            case TRANSIT_TRANSLUCENT_ACTIVITY_OPEN: {
+                return "TRANSIT_TRANSLUCENT_ACTIVITY_OPEN";
+            }
+            case TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE: {
+                return "TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE";
+            }
             default: {
                 return "<UNKNOWN>";
             }
@@ -2173,16 +2194,20 @@
                 || transit == TRANSIT_KEYGUARD_UNOCCLUDE;
     }
 
-    private static boolean isTaskTransit(int transit) {
-        return transit == TRANSIT_TASK_OPEN
+    static boolean isTaskTransit(int transit) {
+        return isTaskOpenTransit(transit)
                 || transit == TRANSIT_TASK_CLOSE
-                || transit == TRANSIT_TASK_OPEN_BEHIND
                 || transit == TRANSIT_TASK_TO_BACK
-                || transit == TRANSIT_TASK_TO_FRONT
                 || transit == TRANSIT_TASK_IN_PLACE;
     }
 
-    private static boolean isActivityTransit(int transit) {
+    private static  boolean isTaskOpenTransit(int transit) {
+        return transit == TRANSIT_TASK_OPEN
+                || transit == TRANSIT_TASK_OPEN_BEHIND
+                || transit == TRANSIT_TASK_TO_FRONT;
+    }
+
+    static boolean isActivityTransit(int transit) {
         return transit == TRANSIT_ACTIVITY_OPEN
                 || transit == TRANSIT_ACTIVITY_CLOSE
                 || transit == TRANSIT_ACTIVITY_RELAUNCH;
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 3256762..b85f4a4 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -16,12 +16,13 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManagerInternal.APP_TRANSITION_SNAPSHOT;
 import static android.app.ActivityManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
 import static android.app.ActivityManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
 
 import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
+import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
@@ -42,6 +43,7 @@
 import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
 import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
 import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
+import static com.android.server.wm.AppTransition.isTaskTransit;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -61,16 +63,15 @@
 import android.view.Display;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
-import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManager.TransitionType;
 import android.view.animation.Animation;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.wm.WindowManagerService.H;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.function.Predicate;
 
 /**
@@ -288,6 +289,7 @@
         final boolean closingAppHasWallpaper = canBeWallpaperTarget(mService.mClosingApps)
                 && hasWallpaperTarget;
 
+        transit = maybeUpdateTransitToTranslucentAnim(transit);
         transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,
                 closingAppHasWallpaper);
 
@@ -676,8 +678,9 @@
                 transit = TRANSIT_WALLPAPER_CLOSE;
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: "
                         + AppTransition.appTransitionToString(transit));
-            } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw() &&
-                    openingApps.contains(wallpaperTarget.mAppToken)) {
+            } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw()
+                    && openingApps.contains(wallpaperTarget.mAppToken)
+                    && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE) {
                 // We are transitioning from an activity without
                 // a wallpaper to now showing the wallpaper
                 transit = TRANSIT_WALLPAPER_OPEN;
@@ -688,6 +691,50 @@
         return transit;
     }
 
+    /**
+     * There are cases where we open/close a new task/activity, but in reality only a translucent
+     * activity on top of existing activities is opening/closing. For that one, we have a different
+     * animation because non of the task/activity animations actually work well with translucent
+     * apps.
+     *
+     * @param transit The current transition type.
+     * @return The current transition type or
+     *         {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE}/
+     *         {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_OPEN} if appropriate for the
+     *         situation.
+     */
+    @VisibleForTesting
+    int maybeUpdateTransitToTranslucentAnim(int transit) {
+        final boolean taskOrActivity = AppTransition.isTaskTransit(transit)
+                || AppTransition.isActivityTransit(transit);
+        boolean allOpeningVisible = true;
+        boolean allTranslucentOpeningApps = !mService.mOpeningApps.isEmpty();
+        for (int i = mService.mOpeningApps.size() - 1; i >= 0; i--) {
+            final AppWindowToken token = mService.mOpeningApps.valueAt(i);
+            if (!token.isVisible()) {
+                allOpeningVisible = false;
+                if (token.fillsParent()) {
+                    allTranslucentOpeningApps = false;
+                }
+            }
+        }
+        boolean allTranslucentClosingApps = !mService.mClosingApps.isEmpty();
+        for (int i = mService.mClosingApps.size() - 1; i >= 0; i--) {
+            if (mService.mClosingApps.valueAt(i).fillsParent()) {
+                allTranslucentClosingApps = false;
+                break;
+            }
+        }
+
+        if (taskOrActivity && allTranslucentClosingApps && allOpeningVisible) {
+            return TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
+        }
+        if (taskOrActivity && allTranslucentOpeningApps && mService.mClosingApps.isEmpty()) {
+            return TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
+        }
+        return transit;
+    }
+
     private boolean canBeWallpaperTarget(ArraySet<AppWindowToken> apps) {
         for (int i = apps.size() - 1; i >= 0; i--) {
             if (apps.valueAt(i).windowsCanBeWallpaperTarget()) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowSurfacePlacerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowSurfacePlacerTest.java
new file mode 100644
index 0000000..e173b7d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowSurfacePlacerTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static junit.framework.Assert.assertEquals;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.WindowManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class WindowSurfacePlacerTest extends WindowTestsBase {
+
+    private WindowSurfacePlacer mWindowSurfacePlacer;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        mWindowSurfacePlacer = new WindowSurfacePlacer(sWm);
+    }
+
+    @Test
+    public void testTranslucentOpen() throws Exception {
+        synchronized (sWm.mWindowMap) {
+            final AppWindowToken behind = createAppWindowToken(mDisplayContent,
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+            final AppWindowToken translucentOpening = createAppWindowToken(mDisplayContent,
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+            translucentOpening.setFillsParent(false);
+            translucentOpening.setHidden(true);
+            sWm.mOpeningApps.add(behind);
+            sWm.mOpeningApps.add(translucentOpening);
+            assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
+                    mWindowSurfacePlacer.maybeUpdateTransitToTranslucentAnim(TRANSIT_TASK_OPEN));
+        }
+    }
+
+    @Test
+    public void testTranslucentClose() throws Exception {
+        synchronized (sWm.mWindowMap) {
+            final AppWindowToken behind = createAppWindowToken(mDisplayContent,
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+            final AppWindowToken translucentClosing = createAppWindowToken(mDisplayContent,
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+            translucentClosing.setFillsParent(false);
+            sWm.mClosingApps.add(translucentClosing);
+            assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
+                    mWindowSurfacePlacer.maybeUpdateTransitToTranslucentAnim(TRANSIT_TASK_CLOSE));
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 6fa2740..d2eee68 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -173,6 +173,8 @@
 
                 mDisplayContent.removeImmediately();
                 sWm.mInputMethodTarget = null;
+                sWm.mClosingApps.clear();
+                sWm.mOpeningApps.clear();
             }
 
             // Wait until everything is really cleaned up.