summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWinson Chung <winsonc@google.com>2014-06-03 16:24:04 -0700
committerWinson Chung <winsonc@google.com>2014-06-12 12:57:56 -0700
commitd42a6cfe2bf632222617450a1ed340268e82f06c (patch)
treeae2aaf137b4c3420458905182df4db7884a529f7
parent88b00784684d7b706c7b0c4e833b1d67d73358b9 (diff)
downloadframeworks_base-d42a6cfe2bf632222617450a1ed340268e82f06c.zip
frameworks_base-d42a6cfe2bf632222617450a1ed340268e82f06c.tar.gz
frameworks_base-d42a6cfe2bf632222617450a1ed340268e82f06c.tar.bz2
Exploring transitions to/from Recents.
- refactored hwlayers and change view property animations to use a reference counted trigger - cleaned up RecentsConfiguration, and move it into classes using it - moved task bar animations back into TaskBarView - refactoring enter/exit animations to use an animation context Change-Id: Ia66b622b094f22145c2fab07c2a9bdfd62344be2
-rw-r--r--core/java/android/view/View.java9
-rw-r--r--packages/SystemUI/res/anim/recents_from_app_enter.xml28
-rw-r--r--packages/SystemUI/res/anim/recents_from_app_exit.xml30
-rw-r--r--packages/SystemUI/res/anim/recents_from_launcher_enter.xml6
-rw-r--r--packages/SystemUI/res/anim/recents_from_launcher_exit.xml4
-rw-r--r--packages/SystemUI/res/anim/recents_from_unknown_enter.xml28
-rw-r--r--packages/SystemUI/res/anim/recents_from_unknown_exit.xml28
-rw-r--r--packages/SystemUI/res/anim/recents_to_launcher_enter.xml28
-rw-r--r--packages/SystemUI/res/anim/recents_to_launcher_exit.xml28
-rw-r--r--packages/SystemUI/res/values/config.xml6
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java228
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Constants.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java169
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java129
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsService.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java233
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java216
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java185
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java27
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindowManager.java7
28 files changed, 1345 insertions, 424 deletions
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 65b1f8c..69680c9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2775,6 +2775,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* @hide
*
+ * Whether Recents is visible or not.
+ */
+ public static final int RECENT_APPS_VISIBLE = 0x00004000;
+
+ /**
+ * @hide
+ *
* Makes system ui transparent.
*/
public static final int SYSTEM_UI_TRANSPARENT = 0x00008000;
@@ -2782,7 +2789,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* @hide
*/
- public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x00007FFF;
+ public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x00003FFF;
/**
* These are the system UI flags that can be cleared by events outside
diff --git a/packages/SystemUI/res/anim/recents_from_app_enter.xml b/packages/SystemUI/res/anim/recents_from_app_enter.xml
new file mode 100644
index 0000000..6abe8b3
--- /dev/null
+++ b/packages/SystemUI/res/anim/recents_from_app_enter.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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">
+ <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="0"/>
+</set>
diff --git a/packages/SystemUI/res/anim/recents_from_app_exit.xml b/packages/SystemUI/res/anim/recents_from_app_exit.xml
new file mode 100644
index 0000000..1447a5a
--- /dev/null
+++ b/packages/SystemUI/res/anim/recents_from_app_exit.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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="normal">
+
+ <!-- Animate the view out only after recents is visible -->
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="1"/>
+</set>
diff --git a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
index 4bd7e82..bac8cb6 100644
--- a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
+++ b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
@@ -20,9 +20,9 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
android:zAdjustment="top">
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/accelerate_cubic"
- android:duration="250"/>
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="100"/>
</set>
diff --git a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
index becc9d0..b0f8807 100644
--- a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
+++ b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
@@ -23,6 +23,6 @@
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/decelerate_cubic"
- android:duration="250"/>
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="100"/>
</set>
diff --git a/packages/SystemUI/res/anim/recents_from_unknown_enter.xml b/packages/SystemUI/res/anim/recents_from_unknown_enter.xml
new file mode 100644
index 0000000..f68a143
--- /dev/null
+++ b/packages/SystemUI/res/anim/recents_from_unknown_enter.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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">
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="200"/>
+</set>
diff --git a/packages/SystemUI/res/anim/recents_from_unknown_exit.xml b/packages/SystemUI/res/anim/recents_from_unknown_exit.xml
new file mode 100644
index 0000000..31cf26a
--- /dev/null
+++ b/packages/SystemUI/res/anim/recents_from_unknown_exit.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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="normal">
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="200"/>
+</set>
diff --git a/packages/SystemUI/res/anim/recents_to_launcher_enter.xml b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml
new file mode 100644
index 0000000..2857c04
--- /dev/null
+++ b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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="normal">
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="150"/>
+</set>
diff --git a/packages/SystemUI/res/anim/recents_to_launcher_exit.xml b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml
new file mode 100644
index 0000000..1139e72
--- /dev/null
+++ b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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">
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="150"/>
+</set>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 1ef5bcd..b39fa84 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -113,12 +113,16 @@
<!-- The min animation duration for animating views that are newly visible. -->
<integer name="recents_filter_animate_new_views_min_duration">125</integer>
<!-- The min animation duration for animating the task bar in. -->
- <integer name="recents_animate_task_bar_enter_duration">250</integer>
+ <integer name="recents_animate_task_bar_enter_duration">275</integer>
<!-- The animation delay for animating the first task in. This should roughly be the animation
duration of the transition in to recents. -->
<integer name="recents_animate_task_bar_enter_delay">225</integer>
<!-- The min animation duration for animating the task bar out. -->
<integer name="recents_animate_task_bar_exit_duration">125</integer>
+ <!-- The min animation duration for animating the task in when transitioning from home. -->
+ <integer name="recents_animate_task_enter_from_home_duration">325</integer>
+ <!-- The animation stagger to apply to each task animation when transitioning from home. -->
+ <integer name="recents_animate_task_enter_from_home_delay">16</integer>
<!-- The min animation duration for animating the nav bar scrim in. -->
<integer name="recents_nav_bar_scrim_enter_duration">400</integer>
<!-- The animation duration for animating the removal of a task view. -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 35bc7e3..fdf7f8d 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -235,9 +235,6 @@
<!-- The amount of highlight to make on each task view. -->
<dimen name="recents_task_view_highlight">1dp</dimen>
- <!-- The amount of space a user has to scroll to dismiss any info panes. -->
- <dimen name="recents_task_stack_scroll_dismiss_info_pane_distance">50dp</dimen>
-
<!-- The height of the search bar space. -->
<dimen name="recents_search_bar_space_height">64dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 2f6d58f..6d44f06 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -27,7 +27,6 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
@@ -35,16 +34,12 @@ import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.UserHandle;
-import android.util.DisplayMetrics;
-import android.view.Display;
-import android.view.Surface;
-import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
import com.android.systemui.R;
+import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -129,8 +124,10 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
final public static int MSG_TOGGLE_RECENTS = 6;
final public static int MSG_START_ENTER_ANIMATION = 7;
- final public static String EXTRA_ANIMATING_WITH_THUMBNAIL = "recents.animatingWithThumbnail";
- final public static String EXTRA_FROM_ALT_TAB = "recents.triggeredFromAltTab";
+ final public static String EXTRA_FROM_HOME = "recents.triggeredOverHome";
+ final public static String EXTRA_FROM_APP_THUMBNAIL = "recents.animatingWithThumbnail";
+ final public static String EXTRA_FROM_APP_FULL_SCREENSHOT = "recents.thumbnail";
+ final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "recents.triggeredFromAltTab";
final public static String KEY_CONFIGURATION_DATA = "recents.data.updateForConfiguration";
final public static String KEY_WINDOW_RECT = "recents.windowRect";
final public static String KEY_SYSTEM_INSETS = "recents.systemInsets";
@@ -138,7 +135,6 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
final public static String KEY_TWO_TASK_STACK_RECT = "recents.twoCountTaskRect";
final public static String KEY_MULTIPLE_TASK_STACK_RECT = "recents.multipleCountTaskRect";
-
final static int sMinToggleDelay = 425;
final static String sToggleRecentsAction = "com.android.systemui.recents.SHOW_RECENTS";
@@ -146,6 +142,8 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";
final static String sRecentsService = "com.android.systemui.recents.RecentsService";
+ static Bitmap sLastScreenshot;
+
Context mContext;
SystemServicesProxy mSystemServicesProxy;
@@ -213,15 +211,19 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
if (Console.Enabled) {
Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|hideRecents]");
}
+
if (mServiceIsBound && mBootCompleted) {
- // Notify recents to close it
- try {
- Bundle data = new Bundle();
- Message msg = Message.obtain(null, MSG_HIDE_RECENTS, triggeredFromAltTab ? 1 : 0, 0);
- msg.setData(data);
- mService.send(msg);
- } catch (RemoteException re) {
- re.printStackTrace();
+ if (isRecentsTopMost(null)) {
+ // Notify recents to close it
+ try {
+ Bundle data = new Bundle();
+ Message msg = Message.obtain(null, MSG_HIDE_RECENTS,
+ triggeredFromAltTab ? 1 : 0, 0);
+ msg.setData(data);
+ mService.send(msg);
+ } catch (RemoteException re) {
+ re.printStackTrace();
+ }
}
}
}
@@ -343,80 +345,6 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
}
}
- /** Converts from the device rotation to the degree */
- float getDegreesForRotation(int value) {
- switch (value) {
- case Surface.ROTATION_90:
- return 360f - 90f;
- case Surface.ROTATION_180:
- return 360f - 180f;
- case Surface.ROTATION_270:
- return 360f - 270f;
- }
- return 0f;
- }
-
- /** Takes a screenshot of the surface */
- Bitmap takeScreenshot(Display display) {
- DisplayMetrics dm = new DisplayMetrics();
- display.getRealMetrics(dm);
- float[] dims = {dm.widthPixels, dm.heightPixels};
- float degrees = getDegreesForRotation(display.getRotation());
- boolean requiresRotation = (degrees > 0);
- if (requiresRotation) {
- // Get the dimensions of the device in its native orientation
- Matrix m = new Matrix();
- m.preRotate(-degrees);
- m.mapPoints(dims);
- dims[0] = Math.abs(dims[0]);
- dims[1] = Math.abs(dims[1]);
- }
- return SurfaceControl.screenshot((int) dims[0], (int) dims[1]);
- }
-
- /** Creates the activity options for a thumbnail transition. */
- ActivityOptions getThumbnailTransitionActivityOptions(Rect taskRect) {
- // Loading from thumbnail
- Bitmap thumbnail;
- Bitmap firstThumbnail = loadFirstTaskThumbnail();
- if (firstThumbnail != null) {
- // Create the thumbnail
- thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
- Bitmap.Config.ARGB_8888);
- int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight());
- Canvas c = new Canvas(thumbnail);
- c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size),
- new Rect(0, 0, taskRect.width(), taskRect.height()), null);
- c.setBitmap(null);
- // Recycle the old thumbnail
- firstThumbnail.recycle();
- } else {
- // Load the thumbnail from the screenshot if can't get one from the system
- WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- Display display = wm.getDefaultDisplay();
- Bitmap screenshot = takeScreenshot(display);
- if (screenshot != null) {
- Resources res = mContext.getResources();
- int size = Math.min(screenshot.getWidth(), screenshot.getHeight());
- int statusBarHeight = res.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
- thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
- Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(thumbnail);
- c.drawBitmap(screenshot, new Rect(0, statusBarHeight, size, statusBarHeight +
- size), new Rect(0, 0, taskRect.width(), taskRect.height()), null);
- c.setBitmap(null);
- // Recycle the temporary screenshot
- screenshot.recycle();
- } else {
- return null;
- }
- }
-
- return ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView, thumbnail,
- taskRect.left, taskRect.top, this);
- }
-
/** Returns whether the recents is currently running */
boolean isRecentsTopMost(AtomicBoolean isHomeTopMost) {
SystemServicesProxy ssp = mSystemServicesProxy;
@@ -462,10 +390,12 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
mService.send(msg);
// Time this path
- Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
- Constants.Log.App.TimeRecentsStartupKey, "sendToggleRecents");
- Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
- Constants.Log.App.TimeRecentsLaunchKey, "sendToggleRecents");
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+ Constants.Log.App.TimeRecentsStartupKey, "sendToggleRecents");
+ Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+ Constants.Log.App.TimeRecentsLaunchKey, "sendToggleRecents");
+ }
} catch (RemoteException re) {
re.printStackTrace();
}
@@ -486,6 +416,68 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
}
}
+ /**
+ * Creates the activity options for a unknown state->recents transition.
+ */
+ ActivityOptions getUnknownTransitionActivityOptions() {
+ // Reset the last screenshot
+ consumeLastScreenshot();
+ return ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_from_unknown_enter,
+ R.anim.recents_from_unknown_exit, mHandler, this);
+ }
+
+ /**
+ * Creates the activity options for a home->recents transition.
+ */
+ ActivityOptions getHomeTransitionActivityOptions() {
+ // Reset the last screenshot
+ consumeLastScreenshot();
+ return ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_from_launcher_enter,
+ R.anim.recents_from_launcher_exit, mHandler, this);
+ }
+
+ /**
+ * Creates the activity options for an app->recents transition. If this method sets the static
+ * screenshot, then we will use that for the transition.
+ */
+ ActivityOptions getThumbnailTransitionActivityOptions(Rect taskRect) {
+ // Recycle the last screenshot
+ consumeLastScreenshot();
+
+ // Take the full screenshot
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ sLastScreenshot = mSystemServicesProxy.takeScreenshot();
+ if (sLastScreenshot != null) {
+ return ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_from_app_enter,
+ R.anim.recents_from_app_exit, mHandler, this);
+ }
+ }
+
+ // If the screenshot fails, then load the first task thumbnail and use that
+ Bitmap firstThumbnail = loadFirstTaskThumbnail();
+ if (firstThumbnail != null) {
+ // Create the new thumbnail for the animation down
+ // XXX: We should find a way to optimize this so we don't need to create a new bitmap
+ Bitmap thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
+ Bitmap.Config.ARGB_8888);
+ int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight());
+ Canvas c = new Canvas(thumbnail);
+ c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size),
+ new Rect(0, 0, taskRect.width(), taskRect.height()), null);
+ c.setBitmap(null);
+ // Recycle the old thumbnail
+ firstThumbnail.recycle();
+ return ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView,
+ thumbnail, taskRect.left, taskRect.top, this);
+ }
+
+ // If both the screenshot and thumbnail fails, then just fall back to the default transition
+ return getUnknownTransitionActivityOptions();
+ }
+
/** Starts the recents activity */
void startRecentsActivity(boolean isTopTaskHome) {
// If Recents is not the front-most activity and we should animate into it. If
@@ -503,7 +495,11 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
// Try starting with a thumbnail transition
ActivityOptions opts = getThumbnailTransitionActivityOptions(taskRect);
if (opts != null) {
- startAlternateRecentsActivity(opts, true);
+ if (sLastScreenshot != null) {
+ startAlternateRecentsActivity(opts, EXTRA_FROM_APP_FULL_SCREENSHOT);
+ } else {
+ startAlternateRecentsActivity(opts, EXTRA_FROM_APP_THUMBNAIL);
+ }
} else {
// Fall through below to the non-thumbnail transition
useThumbnailTransition = false;
@@ -512,25 +508,33 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
// If there is no thumbnail transition, then just use a generic transition
if (!useThumbnailTransition) {
- ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_from_launcher_enter,
- R.anim.recents_from_launcher_exit, mHandler, this);
- startAlternateRecentsActivity(opts, false);
+ if (Constants.DebugFlags.App.EnableHomeTransition) {
+ ActivityOptions opts = getHomeTransitionActivityOptions();
+ startAlternateRecentsActivity(opts, EXTRA_FROM_HOME);
+ } else {
+ ActivityOptions opts = getUnknownTransitionActivityOptions();
+ startAlternateRecentsActivity(opts, null);
+ }
}
- Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
- Constants.Log.App.TimeRecentsStartupKey, "startRecentsActivity");
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+ Constants.Log.App.TimeRecentsStartupKey, "startRecentsActivity");
+ }
mLastToggleTime = System.currentTimeMillis();
}
/** Starts the recents activity */
- void startAlternateRecentsActivity(ActivityOptions opts, boolean animatingWithThumbnail) {
+ void startAlternateRecentsActivity(ActivityOptions opts, String extraFlag) {
Intent intent = new Intent(sToggleRecentsAction);
intent.setClassName(sRecentsPackage, sRecentsActivity);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- intent.putExtra(EXTRA_ANIMATING_WITH_THUMBNAIL, animatingWithThumbnail);
- intent.putExtra(EXTRA_FROM_ALT_TAB, mTriggeredFromAltTab);
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+ if (extraFlag != null) {
+ intent.putExtra(extraFlag, true);
+ }
+ intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, mTriggeredFromAltTab);
if (opts != null) {
mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle(
UserHandle.USER_CURRENT));
@@ -539,6 +543,18 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
}
}
+ /** Returns the last screenshot taken, this will be called by the RecentsActivity. */
+ public static Bitmap getLastScreenshot() {
+ return sLastScreenshot;
+ }
+
+ /** Recycles the last screenshot taken, this will be called by the RecentsActivity. */
+ public static void consumeLastScreenshot() {
+ if (sLastScreenshot != null) {
+ sLastScreenshot.recycle();
+ sLastScreenshot = null;
+ }
+ }
/**** OnAnimationStartedListener Implementation ****/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 147ff62..cd4d206 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -25,6 +25,10 @@ public class Constants {
public static final boolean Verbose = false;
public static class App {
+ // Enables the home->Recents transition
+ public static final boolean EnableHomeTransition = false;
+ // Enables the screenshot app->Recents transition
+ public static final boolean EnableScreenshotAppTransition = false;
// Enables the filtering of tasks according to their grouping
public static final boolean EnableTaskFiltering = false;
// Enables clipping of tasks against each other
@@ -52,8 +56,11 @@ public class Constants {
public static class App {
public static final String TimeRecentsStartupKey = "startup";
public static final String TimeRecentsLaunchKey = "launchTask";
- public static final boolean TimeRecentsStartup = false;
- public static final boolean TimeRecentsLaunchTask = false;
+ public static final String TimeRecentsScreenshotTransitionKey = "screenshot";
+ public static final boolean TimeRecentsStartup = true;
+ public static final boolean TimeRecentsLaunchTask = true;
+ public static final boolean TimeRecentsScreenshotTransition = true;
+
public static final boolean RecentsComponent = false;
public static final boolean TaskDataLoader = false;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 96344d5..9800271 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -22,11 +22,9 @@ import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Pair;
import android.view.Gravity;
@@ -34,17 +32,17 @@ import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.WindowManager;
import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.recents.model.SpaceNode;
import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.FullScreenTransitionView;
import com.android.systemui.recents.views.RecentsView;
+import com.android.systemui.recents.views.ViewAnimation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
-import java.util.Set;
/** Our special app widget host */
class RecentsAppWidgetHost extends AppWidgetHost {
@@ -68,11 +66,16 @@ class RecentsAppWidgetHost extends AppWidgetHost {
/* Activity */
public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
- RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks {
+ RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks,
+ FullScreenTransitionView.FullScreenTransitionViewCallbacks {
+
FrameLayout mContainerView;
RecentsView mRecentsView;
View mEmptyView;
View mNavBarScrimView;
+ FullScreenTransitionView mFullScreenshotView;
+
+ RecentsConfiguration mConfig;
AppWidgetHost mAppWidgetHost;
AppWidgetProviderInfo mSearchAppWidgetInfo;
@@ -108,8 +111,15 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
// Dismiss recents, launching the focused task
dismissRecentsIfVisible();
} else {
- // Otherwise, just finish the activity without launching any other activities
- finish();
+ // If we are mid-animation into Recents, then reverse it and finish
+ if (mFullScreenshotView == null ||
+ !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
+ // Otherwise, just finish the activity without launching any other activities
+ ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(context,
+ null, mFinishRunnable, null);
+ mRecentsView.startOnExitAnimation(
+ new ViewAnimation.TaskViewExitContext(exitTrigger));
+ }
}
} else if (action.equals(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
// Try and unfilter and filtered stacks
@@ -119,7 +129,9 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
}
} else if (action.equals(RecentsService.ACTION_START_ENTER_ANIMATION)) {
// Try and start the enter animation (or restart it on configuration changed)
- mRecentsView.startOnEnterAnimation();
+ mRecentsView.startOnEnterAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
+ // Call our callback
+ onEnterAnimationTriggered();
}
}
};
@@ -132,14 +144,27 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
}
};
+ // A runnable to finish the Recents activity
+ Runnable mFinishRunnable = new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ overridePendingTransition(R.anim.recents_to_launcher_enter,
+ R.anim.recents_to_launcher_exit);
+ }
+ };
+
/** Updates the set of recent tasks */
void updateRecentsTasks(Intent launchIntent) {
// Update the configuration based on the launch intent
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- config.launchedWithThumbnailAnimation = launchIntent.getBooleanExtra(
- AlternateRecentsComponent.EXTRA_ANIMATING_WITH_THUMBNAIL, false);
- config.launchedFromAltTab = launchIntent.getBooleanExtra(
- AlternateRecentsComponent.EXTRA_FROM_ALT_TAB, false);
+ mConfig.launchedFromHome = launchIntent.getBooleanExtra(
+ AlternateRecentsComponent.EXTRA_FROM_HOME, false);
+ mConfig.launchedFromAppWithThumbnail = launchIntent.getBooleanExtra(
+ AlternateRecentsComponent.EXTRA_FROM_APP_THUMBNAIL, false);
+ mConfig.launchedFromAppWithScreenshot = launchIntent.getBooleanExtra(
+ AlternateRecentsComponent.EXTRA_FROM_APP_FULL_SCREENSHOT, false);
+ mConfig.launchedWithAltTab = launchIntent.getBooleanExtra(
+ AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
@@ -165,7 +190,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
/** Attempts to allocate and bind the search bar app widget */
void bindSearchBarAppWidget() {
if (Constants.DebugFlags.App.EnableSearchLayout) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
// Reset the host view and widget info
@@ -173,7 +197,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
mSearchAppWidgetInfo = null;
// Try and load the app widget id from the settings
- int appWidgetId = config.searchBarAppWidgetId;
+ int appWidgetId = mConfig.searchBarAppWidgetId;
if (appWidgetId >= 0) {
mSearchAppWidgetInfo = ssp.getAppWidgetInfo(appWidgetId);
if (mSearchAppWidgetInfo == null) {
@@ -203,7 +227,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
}
// Save the app widget id into the settings
- config.updateSearchBarAppWidgetId(this, widgetInfo.first);
+ mConfig.updateSearchBarAppWidgetId(this, widgetInfo.first);
mSearchAppWidgetInfo = widgetInfo.second;
}
}
@@ -213,8 +237,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
/** Creates the search bar app widget view */
void addSearchBarAppWidgetView() {
if (Constants.DebugFlags.App.EnableSearchLayout) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- int appWidgetId = config.searchBarAppWidgetId;
+ int appWidgetId = mConfig.searchBarAppWidgetId;
if (appWidgetId >= 0) {
if (Console.Enabled) {
Console.log(Constants.Log.App.SystemUIHandshake,
@@ -240,9 +263,19 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
/** Dismisses recents if we are already visible and the intent is to toggle the recents view */
boolean dismissRecentsIfVisible() {
if (mVisible) {
- if (!mRecentsView.launchFocusedTask()) {
- if (!mRecentsView.launchFirstTask()) {
- finish();
+ // If we are mid-animation into Recents, then reverse it and finish
+ if (mFullScreenshotView == null ||
+ !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
+ // If we have a focused task, then launch that task
+ if (!mRecentsView.launchFocusedTask()) {
+ // If there are any tasks, then launch the first task
+ if (!mRecentsView.launchFirstTask()) {
+ // We really shouldn't hit this, but if we do, just animate out (aka. finish)
+ ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
+ null, mFinishRunnable, null);
+ mRecentsView.startOnExitAnimation(
+ new ViewAnimation.TaskViewExitContext(exitTrigger));
+ }
}
}
return true;
@@ -264,7 +297,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
// Initialize the loader and the configuration
RecentsTaskLoader.initialize(this);
- RecentsConfiguration.reinitialize(this);
+ mConfig = RecentsConfiguration.reinitialize(this);
// Initialize the widget host (the host id is static and does not change)
mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId, this);
@@ -286,16 +319,29 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
mNavBarScrimView.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM));
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ mFullScreenshotView = new FullScreenTransitionView(this, this);
+ mFullScreenshotView.setLayoutParams(new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ }
mContainerView = new FrameLayout(this);
mContainerView.addView(mRecentsView);
mContainerView.addView(mEmptyView);
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ mContainerView.addView(mFullScreenshotView);
+ }
mContainerView.addView(mNavBarScrimView);
setContentView(mContainerView);
// Update the recent tasks
updateRecentsTasks(getIntent());
+ // Prepare the screenshot transition if necessary
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ mFullScreenshotView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot());
+ }
+
// Bind the search app widget when we first start up
bindSearchBarAppWidget();
// Add the search bar layout
@@ -319,7 +365,9 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
void onConfigurationChange() {
// Try and start the enter animation (or restart it on configuration changed)
- mRecentsView.startOnEnterAnimation();
+ mRecentsView.startOnEnterAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
+ // Call our callback
+ onEnterAnimationTriggered();
}
@Override
@@ -338,11 +386,16 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
// Initialize the loader and the configuration
RecentsTaskLoader.initialize(this);
- RecentsConfiguration.reinitialize(this);
+ mConfig = RecentsConfiguration.reinitialize(this);
// Update the recent tasks
updateRecentsTasks(intent);
+ // Prepare the screenshot transition if necessary
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ mFullScreenshotView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot());
+ }
+
// Don't attempt to rebind the search bar widget, but just add the search bar layout
addSearchBarAppWidgetView();
}
@@ -356,8 +409,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
super.onStart();
// Start listening for widget package changes if there is one bound
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (config.searchBarAppWidgetId >= 0) {
+ if (mConfig.searchBarAppWidgetId >= 0) {
mAppWidgetHost.startListening();
}
@@ -431,8 +483,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
super.onStop();
// Stop listening for widget package changes if there was one bound
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (config.searchBarAppWidgetId >= 0) {
+ if (mConfig.searchBarAppWidgetId >= 0) {
mAppWidgetHost.stopListening();
}
@@ -471,41 +522,70 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
@Override
public void onBackPressed() {
- // Unfilter any stacks
- if (!mRecentsView.unfilterFilteredStacks()) {
- if (!mRecentsView.launchFirstTask()) {
- super.onBackPressed();
+ // If we are mid-animation into Recents, then reverse it and finish
+ if (mFullScreenshotView == null ||
+ !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
+ // If we are currently filtering in any stacks, unfilter them first
+ if (!mRecentsView.unfilterFilteredStacks()) {
+ if (mConfig.launchedFromHome) {
+ // Just start the animation out of recents
+ ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
+ null, mFinishRunnable, null);
+ mRecentsView.startOnExitAnimation(
+ new ViewAnimation.TaskViewExitContext(exitTrigger));
+ } else {
+ // Otherwise, try and launch the first task
+ if (!mRecentsView.launchFirstTask()) {
+ // If there are no tasks, then just finish recents
+ ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
+ null, mFinishRunnable, null);
+ mRecentsView.startOnExitAnimation(
+ new ViewAnimation.TaskViewExitContext(exitTrigger));
+ }
+ }
}
}
}
- @Override
public void onEnterAnimationTriggered() {
// Fade in the scrim
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (config.hasNavBarScrim()) {
+ if (mConfig.hasNavBarScrim()) {
mNavBarScrimView.setVisibility(View.VISIBLE);
mNavBarScrimView.setAlpha(0f);
mNavBarScrimView.animate().alpha(1f)
- .setStartDelay(config.taskBarEnterAnimDelay)
- .setDuration(config.navBarScrimEnterDuration)
- .setInterpolator(config.fastOutSlowInInterpolator)
+ .setStartDelay(mConfig.taskBarEnterAnimDelay)
+ .setDuration(mConfig.navBarScrimEnterDuration)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
.withLayer()
.start();
}
}
@Override
+ public void onEnterAnimationComplete(boolean canceled) {
+ if (!canceled) {
+ // Reset the full screenshot transition view
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ mFullScreenshotView.reset();
+ }
+
+ // XXX: We should clean up the screenshot in this case as well, but it needs to happen
+ // after to animate up
+ }
+ // Recycle the full screen screenshot
+ AlternateRecentsComponent.consumeLastScreenshot();
+ }
+
+ @Override
public void onTaskLaunching(boolean isTaskInStackBounds) {
mTaskLaunched = true;
// Fade out the scrim
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (!isTaskInStackBounds && config.hasNavBarScrim()) {
+ if (!isTaskInStackBounds && mConfig.hasNavBarScrim()) {
mNavBarScrimView.animate().alpha(0f)
.setStartDelay(0)
- .setDuration(config.taskBarExitAnimDuration)
- .setInterpolator(config.fastOutSlowInInterpolator)
+ .setDuration(mConfig.taskBarExitAnimDuration)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
.withLayer()
.start();
}
@@ -513,12 +593,11 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
@Override
public void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
- if (appWidgetId > -1 && appWidgetId == config.searchBarAppWidgetId) {
+ if (appWidgetId > -1 && appWidgetId == mConfig.searchBarAppWidgetId) {
// The search provider may have changed, so just delete the old widget and bind it again
ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId);
- config.updateSearchBarAppWidgetId(this, -1);
+ mConfig.updateSearchBarAppWidgetId(this, -1);
// Load the widget again
bindSearchBarAppWidget();
addSearchBarAppWidgetView();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 0cf6ee6..a0c5253 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -37,27 +37,39 @@ public class RecentsConfiguration {
DisplayMetrics mDisplayMetrics;
- public Rect systemInsets = new Rect();
- public Rect displayRect = new Rect();
-
- boolean isLandscape;
- boolean transposeRecentsLayoutWithOrientation;
- int searchBarAppWidgetId = -1;
-
+ /** Animations */
public float animationPxMovementPerSecond;
+ /** Interpolators */
public Interpolator fastOutSlowInInterpolator;
public Interpolator fastOutLinearInInterpolator;
public Interpolator linearOutSlowInInterpolator;
+ public Interpolator quintOutInterpolator;
+ /** Filtering */
public int filteringCurrentViewsMinAnimDuration;
public int filteringNewViewsMinAnimDuration;
- public int taskStackScrollDismissInfoPaneDistance;
+ /** Insets */
+ public Rect systemInsets = new Rect();
+ public Rect displayRect = new Rect();
+
+ /** Layout */
+ boolean isLandscape;
+ boolean transposeRecentsLayoutWithOrientation;
+
+ /** Search bar */
+ int searchBarAppWidgetId = -1;
+ public int searchBarSpaceHeightPx;
+
+ /** Task stack */
public int taskStackMaxDim;
- public float taskStackWidthPaddingPct;
public int taskStackTopPaddingPx;
+ public float taskStackWidthPaddingPct;
+ /** Task view animation and styles */
+ public int taskViewEnterFromHomeDuration;
+ public int taskViewEnterFromHomeDelay;
public int taskViewRemoveAnimDuration;
public int taskViewRemoveAnimTranslationXPx;
public int taskViewTranslationZMinPx;
@@ -66,23 +78,28 @@ public class RecentsConfiguration {
public int taskViewRoundedCornerRadiusPx;
public int taskViewHighlightPx;
- public int searchBarSpaceHeightPx;
-
+ /** Task bar colors */
public int taskBarViewDefaultBackgroundColor;
public int taskBarViewDefaultTextColor;
public int taskBarViewLightTextColor;
public int taskBarViewDarkTextColor;
public int taskBarViewHighlightColor;
+ /** Task bar animations */
public int taskBarEnterAnimDuration;
public int taskBarEnterAnimDelay;
public int taskBarExitAnimDuration;
+ /** Nav bar scrim */
public int navBarScrimEnterDuration;
- public boolean launchedFromAltTab;
- public boolean launchedWithThumbnailAnimation;
+ /** Launch states */
+ public boolean launchedWithAltTab;
+ public boolean launchedFromAppWithThumbnail;
+ public boolean launchedFromAppWithScreenshot;
+ public boolean launchedFromHome;
+ /** Dev options */
public boolean developerOptionsEnabled;
/** Private constructor */
@@ -108,33 +125,54 @@ public class RecentsConfiguration {
DisplayMetrics dm = res.getDisplayMetrics();
mDisplayMetrics = dm;
- isLandscape = res.getConfiguration().orientation ==
- Configuration.ORIENTATION_LANDSCAPE;
- transposeRecentsLayoutWithOrientation =
- res.getBoolean(R.bool.recents_transpose_layout_with_orientation);
- if (Console.Enabled) {
- Console.log(Constants.Log.UI.MeasureAndLayout,
- "[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait",
- Console.AnsiGreen);
- }
-
- displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
+ // Animations
animationPxMovementPerSecond =
res.getDimensionPixelSize(R.dimen.recents_animation_movement_in_dps_per_second);
+
+ // Interpolators
+ fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
+ fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_linear_in);
+ linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.linear_out_slow_in);
+ quintOutInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.decelerate_quint);
+
+ // Filtering
filteringCurrentViewsMinAnimDuration =
res.getInteger(R.integer.recents_filter_animate_current_views_min_duration);
filteringNewViewsMinAnimDuration =
res.getInteger(R.integer.recents_filter_animate_new_views_min_duration);
- taskStackScrollDismissInfoPaneDistance = res.getDimensionPixelSize(
- R.dimen.recents_task_stack_scroll_dismiss_info_pane_distance);
- taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim);
+ // Insets
+ displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
+
+ // Layout
+ isLandscape = res.getConfiguration().orientation ==
+ Configuration.ORIENTATION_LANDSCAPE;
+ transposeRecentsLayoutWithOrientation =
+ res.getBoolean(R.bool.recents_transpose_layout_with_orientation);
+ // Search bar
+ searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
+
+ // Update the search widget id
+ SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0);
+ searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1);
+
+ // Task stack
TypedValue widthPaddingPctValue = new TypedValue();
res.getValue(R.dimen.recents_stack_width_padding_percentage, widthPaddingPctValue, true);
taskStackWidthPaddingPct = widthPaddingPctValue.getFloat();
+ taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim);
taskStackTopPaddingPx = res.getDimensionPixelSize(R.dimen.recents_stack_top_padding);
+ // Task view animation and styles
+ taskViewEnterFromHomeDuration =
+ res.getInteger(R.integer.recents_animate_task_enter_from_home_duration);
+ taskViewEnterFromHomeDelay =
+ res.getInteger(R.integer.recents_animate_task_enter_from_home_delay);
taskViewRemoveAnimDuration =
res.getInteger(R.integer.recents_animate_task_view_remove_duration);
taskViewRemoveAnimTranslationXPx =
@@ -148,8 +186,7 @@ public class RecentsConfiguration {
taskViewShadowOutlineBottomInsetPx =
res.getDimensionPixelSize(R.dimen.recents_task_view_shadow_outline_bottom_inset);
- searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
-
+ // Task bar colors
taskBarViewDefaultBackgroundColor =
res.getColor(R.color.recents_task_bar_default_background_color);
taskBarViewDefaultTextColor =
@@ -161,6 +198,7 @@ public class RecentsConfiguration {
taskBarViewHighlightColor =
res.getColor(R.color.recents_task_bar_highlight_color);
+ // Task bar animations
taskBarEnterAnimDuration =
res.getInteger(R.integer.recents_animate_task_bar_enter_duration);
taskBarEnterAnimDelay =
@@ -168,24 +206,20 @@ public class RecentsConfiguration {
taskBarExitAnimDuration =
res.getInteger(R.integer.recents_animate_task_bar_exit_duration);
+ // Nav bar scrim
navBarScrimEnterDuration =
res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration);
- fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_slow_in);
- fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_linear_in);
- linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.linear_out_slow_in);
-
// Check if the developer options are enabled
ContentResolver cr = context.getContentResolver();
developerOptionsEnabled = Settings.Global.getInt(cr,
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
- // Update the search widget id
- SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0);
- searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1);
+ if (Console.Enabled) {
+ Console.log(Constants.Log.UI.MeasureAndLayout,
+ "[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait",
+ Console.AnsiGreen);
+ }
}
/** Updates the system insets */
@@ -204,8 +238,10 @@ public class RecentsConfiguration {
/** Called when the configuration has changed, and we want to reset any configuration specific
* members. */
public void updateOnConfigurationChange() {
- launchedFromAltTab = false;
- launchedWithThumbnailAnimation = false;
+ launchedWithAltTab = false;
+ launchedFromAppWithThumbnail = false;
+ launchedFromAppWithScreenshot = false;
+ launchedFromHome = false;
}
/** Returns whether the search bar app widget exists. */
@@ -257,15 +293,4 @@ public class RecentsConfiguration {
searchBarSpaceBounds.set(0, 0, width, searchBarSpaceHeightPx);
}
}
-
- /** Converts from DPs to PXs */
- public int pxFromDp(float size) {
- return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- size, mDisplayMetrics));
- }
- /** Converts from SPs to PXs */
- public int pxFromSp(float size) {
- return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
- size, mDisplayMetrics));
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
index 0c2c11d..1cc57ef 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
@@ -132,10 +132,12 @@ class SystemUIMessageHandler extends Handler {
context.sendBroadcast(intent);
// Time this path
- Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
- Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents");
- Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
- Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents");
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+ Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents");
+ Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+ Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents");
+ }
} else if (msg.what == AlternateRecentsComponent.MSG_START_ENTER_ANIMATION) {
// Send a broadcast to start the enter animation
Intent intent = new Intent(RecentsService.ACTION_START_ENTER_ANIMATION);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index 4685186..dbcdb94 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -478,7 +478,7 @@ public class RecentsTaskLoader {
// Load the thumbnail (if possible and not the foremost task, from the cache)
if (!isForemostTask) {
task.thumbnail = mThumbnailCache.get(task.key);
- if (task.thumbnail != null) {
+ if (task.thumbnail != null && !tasksToForceLoad.contains(task)) {
// Even though we get things from the cache, we should update them if
// they've changed in the bg
tasksToForceLoad.add(task);
@@ -489,6 +489,7 @@ public class RecentsTaskLoader {
Console.log(Constants.Log.App.TaskDataLoader,
"[RecentsTaskLoader|loadingTaskThumbnail]");
}
+
task.thumbnail = ssp.getTaskThumbnail(task.key.id);
if (task.thumbnail != null) {
task.thumbnail.setHasAlpha(false);
@@ -512,20 +513,6 @@ public class RecentsTaskLoader {
"" + (System.currentTimeMillis() - t1) + "ms");
}
- /*
- // Get all the stacks
- t1 = System.currentTimeMillis();
- List<ActivityManager.StackInfo> stackInfos = ams.getAllStackInfos();
- Console.log(Constants.Log.App.TimeSystemCalls, "[RecentsTaskLoader|getAllStackInfos]", "" + (System.currentTimeMillis() - t1) + "ms");
- Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|stacks]", "" + tasks.size());
- for (ActivityManager.StackInfo s : stackInfos) {
- Console.log(Constants.Log.App.TaskDataLoader, " [RecentsTaskLoader|stack]", s.toString());
- if (stacks.containsKey(s.stackId)) {
- stacks.get(s.stackId).setRect(s.bounds);
- }
- }
- */
-
// Start the task loader
mLoader.start(context);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java
new file mode 100644
index 0000000..2f89e6d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+import android.content.Context;
+
+/**
+ * A ref counted trigger that does some logic when the count is first incremented, or last
+ * decremented. Not thread safe as it's not currently needed.
+ */
+public class ReferenceCountedTrigger {
+
+ Context mContext;
+ int mCount;
+ Runnable mFirstIncRunnable;
+ Runnable mLastDecRunnable;
+ Runnable mErrorRunnable;
+
+ // Convenience runnables
+ Runnable mIncrementRunnable = new Runnable() {
+ @Override
+ public void run() {
+ increment();
+ }
+ };
+ Runnable mDecrementRunnable = new Runnable() {
+ @Override
+ public void run() {
+ decrement();
+ }
+ };
+
+ public ReferenceCountedTrigger(Context context, Runnable firstIncRunnable,
+ Runnable lastDecRunnable, Runnable errorRunanable) {
+ mContext = context;
+ mFirstIncRunnable = firstIncRunnable;
+ mLastDecRunnable = lastDecRunnable;
+ mErrorRunnable = errorRunanable;
+ }
+
+ /** Increments the ref count */
+ public void increment() {
+ if (mCount == 0 && mFirstIncRunnable != null) {
+ mFirstIncRunnable.run();
+ }
+ mCount++;
+ }
+
+ /** Convenience method to increment this trigger as a runnable */
+ public Runnable incrementAsRunnable() {
+ return mIncrementRunnable;
+ }
+
+ /** Decrements the ref count */
+ public void decrement() {
+ mCount--;
+ if (mCount == 0 && mLastDecRunnable != null) {
+ mLastDecRunnable.run();
+ } else if (mCount < 0) {
+ if (mErrorRunnable != null) {
+ mErrorRunnable.run();
+ } else {
+ new Throwable("Invalid ref count").printStackTrace();
+ Console.logError(mContext, "Invalid ref count");
+ }
+ }
+ }
+
+ /** Convenience method to decrement this trigger as a runnable */
+ public Runnable decrementAsRunnable() {
+ return mDecrementRunnable;
+ }
+
+ /** Returns the current ref count */
+ public int getCount() {
+ return mCount;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
index 7a3ffb8..f532aa0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
@@ -30,13 +30,20 @@ import android.content.pm.ActivityInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.graphics.Matrix;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.DisplayMetrics;
import android.util.Pair;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
import java.util.ArrayList;
import java.util.Iterator;
@@ -54,6 +61,8 @@ public class SystemServicesProxy {
IPackageManager mIpm;
UserManager mUm;
SearchManager mSm;
+ WindowManager mWm;
+ Display mDisplay;
String mRecentsPackage;
ComponentName mAssistComponent;
@@ -67,6 +76,8 @@ public class SystemServicesProxy {
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
mIpm = AppGlobals.getPackageManager();
mSm = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
+ mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mDisplay = mWm.getDefaultDisplay();
mRecentsPackage = context.getPackageName();
// Resolve the assist intent
@@ -325,4 +336,13 @@ public class SystemServicesProxy {
// Delete the app widget
host.deleteAppWidgetId(appWidgetId);
}
+
+ /**
+ * Takes a screenshot of the current surface.
+ */
+ public Bitmap takeScreenshot() {
+ DisplayInfo di = new DisplayInfo();
+ mDisplay.getDisplayInfo(di);
+ return SurfaceControl.screenshot(di.getNaturalWidth(), di.getNaturalHeight());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
new file mode 100644
index 0000000..ad2fa8d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import com.android.systemui.recents.Console;
+import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsConfiguration;
+
+
+/**
+ * The full screen transition view that gets animated down from the full screen into a task
+ * thumbnail view.
+ */
+public class FullScreenTransitionView extends FrameLayout {
+
+ /** The FullScreenTransitionView callbacks */
+ public interface FullScreenTransitionViewCallbacks {
+ void onEnterAnimationComplete(boolean canceled);
+ }
+
+ RecentsConfiguration mConfig;
+
+ FullScreenTransitionViewCallbacks mCb;
+
+ ImageView mScreenshotView;
+
+ Rect mClipRect = new Rect();
+
+ boolean mIsAnimating;
+ AnimatorSet mEnterAnimation;
+
+ public FullScreenTransitionView(Context context, FullScreenTransitionViewCallbacks cb) {
+ super(context);
+ mConfig = RecentsConfiguration.getInstance();
+ mCb = cb;
+ mScreenshotView = new ImageView(context);
+ mScreenshotView.setScaleType(ImageView.ScaleType.FIT_XY);
+ mScreenshotView.setLayoutParams(new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ addView(mScreenshotView);
+ setClipTop(getClipTop());
+ setClipBottom(getClipBottom());
+ setWillNotDraw(false);
+ }
+
+ /** Sets the top clip */
+ public void setClipTop(int clip) {
+ mClipRect.top = clip;
+ postInvalidateOnAnimation();
+ }
+
+ /** Gets the top clip */
+ public int getClipTop() {
+ return mClipRect.top;
+ }
+
+ /** Sets the bottom clip */
+ public void setClipBottom(int clip) {
+ mClipRect.bottom = clip;
+ postInvalidateOnAnimation();
+ }
+
+ /** Gets the top clip */
+ public int getClipBottom() {
+ return mClipRect.bottom;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mClipRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+ canvas.clipRect(mClipRect);
+ super.draw(canvas);
+ canvas.restoreToCount(restoreCount);
+ }
+
+ /** Prepares the screenshot view for the transition into Recents */
+ public void prepareAnimateOnEnterRecents(Bitmap screenshot) {
+ if (!mConfig.launchedFromAppWithScreenshot) return;
+
+ if (Console.Enabled) {
+ Console.logStartTracingTime(Constants.Log.App.TimeRecentsScreenshotTransition,
+ Constants.Log.App.TimeRecentsScreenshotTransitionKey);
+ }
+
+ setClipTop(0);
+ setClipBottom(getMeasuredHeight());
+ setTranslationY(0f);
+ setScaleX(1f);
+ setScaleY(1f);
+ setVisibility(mConfig.launchedFromAppWithScreenshot ? View.VISIBLE : View.INVISIBLE);
+ if (screenshot != null) {
+ mScreenshotView.setImageBitmap(screenshot);
+ } else {
+ mScreenshotView.setImageDrawable(null);
+ }
+ }
+
+ /** Resets the transition view */
+ public void reset() {
+ setVisibility(View.INVISIBLE);
+ mScreenshotView.setImageDrawable(null);
+ }
+
+ /** Animates this view as it enters recents */
+ public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx,
+ final Runnable postAnimRunnable) {
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition,
+ Constants.Log.App.TimeRecentsScreenshotTransitionKey, "Starting");
+ }
+
+ // Cancel the current animation
+ if (mEnterAnimation != null) {
+ mEnterAnimation.removeAllListeners();
+ mEnterAnimation.cancel();
+ }
+
+ // Calculate the bottom clip
+ float scale = (float) ctx.taskRect.width() / getMeasuredWidth();
+ int translationY = -mConfig.systemInsets.top + ctx.stackRectSansPeek.top +
+ ctx.transform.translationY;
+ int clipBottom = mConfig.systemInsets.top + (int) (ctx.taskRect.height() / scale);
+
+ // Enable the HW Layers on the screenshot view
+ mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+
+ // Compose the animation
+ mEnterAnimation = new AnimatorSet();
+ mEnterAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Notify any callbacks
+ mCb.onEnterAnimationComplete(false);
+ // Run the given post-anim runnable
+ postAnimRunnable.run();
+ // Mark that we are no longer animating
+ mIsAnimating = false;
+ // Disable the HW Layers on this view
+ setLayerType(View.LAYER_TYPE_NONE, null);
+
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition,
+ Constants.Log.App.TimeRecentsScreenshotTransitionKey, "Completed");
+ }
+ }
+ });
+ mEnterAnimation.setStartDelay(0);
+ mEnterAnimation.setDuration(475);
+ mEnterAnimation.setInterpolator(mConfig.fastOutSlowInInterpolator);
+ mEnterAnimation.playTogether(
+ ObjectAnimator.ofInt(this, "clipTop", mConfig.systemInsets.top),
+ ObjectAnimator.ofInt(this, "clipBottom", clipBottom),
+ ObjectAnimator.ofFloat(this, "translationY", translationY),
+ ObjectAnimator.ofFloat(this, "scaleX", scale),
+ ObjectAnimator.ofFloat(this, "scaleY", scale)
+ );
+ mEnterAnimation.start();
+
+ mIsAnimating = true;
+ }
+
+ /** Animates this view back out of Recents if we were in the process of animating in. */
+ public boolean cancelAnimateOnEnterRecents(final Runnable postAnimRunnable) {
+ if (mIsAnimating) {
+ // Cancel the current animation
+ if (mEnterAnimation != null) {
+ mEnterAnimation.removeAllListeners();
+ mEnterAnimation.cancel();
+ }
+
+ // Compose the animation
+ mEnterAnimation = new AnimatorSet();
+ mEnterAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Notify any callbacks
+ mCb.onEnterAnimationComplete(true);
+ // Run the given post-anim runnable
+ postAnimRunnable.run();
+ // Mark that we are no longer animating
+ mIsAnimating = false;
+ // Disable the HW Layers on the screenshot view
+ mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ });
+ mEnterAnimation.setDuration(475);
+ mEnterAnimation.setInterpolator(mConfig.fastOutSlowInInterpolator);
+ mEnterAnimation.playTogether(
+ ObjectAnimator.ofInt(this, "clipTop", 0),
+ ObjectAnimator.ofInt(this, "clipBottom", getMeasuredHeight()),
+ ObjectAnimator.ofFloat(this, "translationY", 0f),
+ ObjectAnimator.ofFloat(this, "scaleX", 1f),
+ ObjectAnimator.ofFloat(this, "scaleY", 1f)
+ );
+ mEnterAnimation.start();
+
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index db398b1..a2c250c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -55,9 +55,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
/** The RecentsView callbacks */
public interface RecentsViewCallbacks {
public void onTaskLaunching(boolean isTaskInStackBounds);
- public void onEnterAnimationTriggered();
}
+ RecentsConfiguration mConfig;
+ LayoutInflater mInflater;
+
// The space partitioning root of this container
SpaceNode mBSP;
// Whether there are any tasks
@@ -67,10 +69,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
// Recents view callbacks
RecentsViewCallbacks mCb;
- LayoutInflater mInflater;
-
public RecentsView(Context context) {
super(context);
+ mConfig = RecentsConfiguration.getInstance();
mInflater = LayoutInflater.from(context);
setWillNotDraw(false);
}
@@ -160,18 +161,37 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
/** Requests all task stacks to start their enter-recents animation */
- public void startOnEnterAnimation() {
- // Notify callbacks that we are starting the enter animation
- mCb.onEnterAnimationTriggered();
-
+ public void startOnEnterAnimation(ViewAnimation.TaskViewEnterContext ctx) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child instanceof TaskStackView) {
TaskStackView stackView = (TaskStackView) child;
- stackView.startOnEnterAnimation();
+ stackView.startOnEnterAnimation(ctx);
+ }
+ }
+ }
+
+ /** Requests all task stacks to start their exit-recents animation */
+ public void startOnExitAnimation(ViewAnimation.TaskViewExitContext ctx) {
+ // Handle the case when there are no views by incrementing and decrementing after all
+ // animations are started.
+ ctx.postAnimationTrigger.increment();
+
+ if (Constants.DebugFlags.App.EnableHomeTransition) {
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child instanceof TaskStackView) {
+ TaskStackView stackView = (TaskStackView) child;
+ stackView.startOnExitAnimation(ctx);
+ }
}
}
+
+ // Handle the case when there are no views by incrementing and decrementing after all
+ // animations are started.
+ ctx.postAnimationTrigger.decrement();
}
/** Adds the search bar */
@@ -215,10 +235,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
// Get the search bar bounds and measure the search bar layout
- RecentsConfiguration config = RecentsConfiguration.getInstance();
if (mSearchBar != null) {
Rect searchBarSpaceBounds = new Rect();
- config.getSearchBarBounds(width, height - config.systemInsets.top, searchBarSpaceBounds);
+ mConfig.getSearchBarBounds(width, height - mConfig.systemInsets.top, searchBarSpaceBounds);
mSearchBar.measure(
MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY));
@@ -229,9 +248,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
// In addition, we give it the full height, not including the top inset or search bar space,
// since we want the tasks to render under the navigation buttons in portrait.
Rect taskStackBounds = new Rect();
- config.getTaskStackBounds(width, height, taskStackBounds);
- int childWidth = width - config.systemInsets.right;
- int childHeight = taskStackBounds.height() - config.systemInsets.top;
+ mConfig.getTaskStackBounds(width, height, taskStackBounds);
+ int childWidth = width - mConfig.systemInsets.right;
+ int childHeight = taskStackBounds.height() - mConfig.systemInsets.top;
// Measure each TaskStackView
int childCount = getChildCount();
@@ -259,23 +278,22 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
// Get the search bar bounds so that we lay it out
- RecentsConfiguration config = RecentsConfiguration.getInstance();
if (mSearchBar != null) {
Rect searchBarSpaceBounds = new Rect();
- config.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(), searchBarSpaceBounds);
- mSearchBar.layout(config.systemInsets.left + searchBarSpaceBounds.left,
- config.systemInsets.top + searchBarSpaceBounds.top,
- config.systemInsets.left + mSearchBar.getMeasuredWidth(),
- config.systemInsets.top + mSearchBar.getMeasuredHeight());
+ mConfig.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(), searchBarSpaceBounds);
+ mSearchBar.layout(mConfig.systemInsets.left + searchBarSpaceBounds.left,
+ mConfig.systemInsets.top + searchBarSpaceBounds.top,
+ mConfig.systemInsets.left + mSearchBar.getMeasuredWidth(),
+ mConfig.systemInsets.top + mSearchBar.getMeasuredHeight());
}
// We offset the stack view by the left inset (if any), but lay it out under the search bar.
// In addition, we offset our stack views by the top inset and search bar height, but not
// the bottom insets because we want it to render under the navigation buttons.
Rect taskStackBounds = new Rect();
- config.getTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(), taskStackBounds);
- left += config.systemInsets.left;
- top += config.systemInsets.top + taskStackBounds.top;
+ mConfig.getTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(), taskStackBounds);
+ left += mConfig.systemInsets.left;
+ top += mConfig.systemInsets.top + taskStackBounds.top;
// Layout each child
// XXX: Based on the space node for that task view
@@ -324,8 +342,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
// Update the configuration with the latest system insets and trigger a relayout
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- config.updateSystemInsets(insets.getSystemWindowInsets());
+ mConfig.updateSystemInsets(insets.getSystemWindowInsets());
requestLayout();
return insets.consumeSystemWindowInsets(false, false, false, true);
@@ -365,6 +382,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
final Runnable launchRunnable = new Runnable() {
@Override
public void run() {
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+ Constants.Log.App.TimeRecentsLaunchKey, "preStartActivity");
+ }
+
TaskViewTransform transform;
View sourceView = tv;
int offsetX = 0;
@@ -374,11 +396,10 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
// If there is no actual task view, then use the stack view as the source view
// and then offset to the expected transform rect, but bound this to just
// outside the display rect (to ensure we don't animate from too far away)
- RecentsConfiguration config = RecentsConfiguration.getInstance();
sourceView = stackView;
transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
offsetX = transform.rect.left;
- offsetY = Math.min(transform.rect.top, config.displayRect.height());
+ offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
} else {
transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
}
@@ -426,19 +447,23 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
onTaskRemoved(task);
}
- Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
- Constants.Log.App.TimeRecentsLaunchKey, "startActivity");
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+ Constants.Log.App.TimeRecentsLaunchKey, "startActivity");
+ }
}
};
- Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
- Constants.Log.App.TimeRecentsLaunchKey, "onTaskLaunched");
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+ Constants.Log.App.TimeRecentsLaunchKey, "onTaskLaunched");
+ }
// Launch the app right away if there is no task view, otherwise, animate the icon out first
if (tv == null) {
post(launchRunnable);
} else {
- tv.animateOnLeavingRecents(launchRunnable);
+ tv.animateOnLaunchingTask(launchRunnable);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index c10ddd1..2c637a8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -22,8 +22,10 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -36,6 +38,9 @@ import com.android.systemui.recents.model.Task;
/* The task bar view */
class TaskBarView extends FrameLayout {
+
+ RecentsConfiguration mConfig;
+
Task mTask;
ImageView mDismissButton;
@@ -61,6 +66,7 @@ class TaskBarView extends FrameLayout {
public TaskBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mConfig = RecentsConfiguration.getInstance();
setWillNotDraw(false);
// Load the dismiss resources
@@ -70,11 +76,10 @@ class TaskBarView extends FrameLayout {
// Configure the highlight paint
if (sHighlightPaint == null) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
sHighlightPaint = new Paint();
sHighlightPaint.setStyle(Paint.Style.STROKE);
- sHighlightPaint.setStrokeWidth(config.taskViewHighlightPx);
- sHighlightPaint.setColor(config.taskBarViewHighlightColor);
+ sHighlightPaint.setStrokeWidth(mConfig.taskViewHighlightPx);
+ sHighlightPaint.setColor(mConfig.taskBarViewHighlightColor);
sHighlightPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
sHighlightPaint.setAntiAlias(true);
}
@@ -90,11 +95,9 @@ class TaskBarView extends FrameLayout {
@Override
protected void onDraw(Canvas canvas) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
-
// Draw the highlight at the top edge (but put the bottom edge just out of view)
- float offset = config.taskViewHighlightPx / 2f;
- float radius = config.taskViewRoundedCornerRadiusPx;
+ float offset = mConfig.taskViewHighlightPx / 2f;
+ float radius = mConfig.taskViewRoundedCornerRadiusPx;
canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset,
getMeasuredHeight() + radius, radius, radius, sHighlightPaint);
}
@@ -102,7 +105,6 @@ class TaskBarView extends FrameLayout {
/** Synchronizes this bar view's properties with the task's transform */
void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform,
TaskViewTransform toTransform, int duration) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
if (duration > 0) {
if (animateFromTransform != null) {
mDismissButton.setAlpha(animateFromTransform.dismissAlpha);
@@ -111,18 +113,16 @@ class TaskBarView extends FrameLayout {
.alpha(toTransform.dismissAlpha)
.setStartDelay(0)
.setDuration(duration)
- .setInterpolator(config.fastOutSlowInInterpolator)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
.withLayer()
.start();
} else {
mDismissButton.setAlpha(toTransform.dismissAlpha);
}
- mDismissButton.invalidate();
}
/** Binds the bar view to the task */
void rebindToTask(Task t, boolean animate) {
- RecentsConfiguration configuration = RecentsConfiguration.getInstance();
mTask = t;
// If an activity icon is defined, then we use that as the primary icon to show in the bar,
// otherwise, we fall back to the application icon
@@ -137,12 +137,12 @@ class TaskBarView extends FrameLayout {
if (Constants.DebugFlags.App.EnableTaskBarThemeColors && tint != 0) {
setBackgroundColor(tint);
mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint,
- configuration.taskBarViewLightTextColor, configuration.taskBarViewDarkTextColor));
+ mConfig.taskBarViewLightTextColor, mConfig.taskBarViewDarkTextColor));
mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint,
mLightDismissDrawable, mDarkDismissDrawable));
} else {
- setBackgroundColor(configuration.taskBarViewDefaultBackgroundColor);
- mActivityDescription.setTextColor(configuration.taskBarViewDefaultTextColor);
+ setBackgroundColor(mConfig.taskBarViewDefaultBackgroundColor);
+ mActivityDescription.setTextColor(mConfig.taskBarViewDefaultTextColor);
}
if (animate) {
// XXX: Investigate how expensive it will be to create a second bitmap and crossfade
@@ -155,4 +155,51 @@ class TaskBarView extends FrameLayout {
mApplicationIcon.setImageDrawable(null);
mActivityDescription.setText("");
}
+
+ /** Prepares this task view for the enter-recents animations. This is called earlier in the
+ * first layout because the actual animation into recents may take a long time. */
+ public void prepareAnimateEnterRecents() {
+ setVisibility(View.INVISIBLE);
+ }
+
+ /** Animates this task bar as it enters recents */
+ public void animateOnEnterRecents(int delay) {
+ // Animate the task bar of the first task view
+ setVisibility(View.VISIBLE);
+ setTranslationY(-getMeasuredHeight());
+ animate()
+ .translationY(0)
+ .setStartDelay(delay > -1 ? delay : mConfig.taskBarEnterAnimDelay)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(mConfig.taskBarEnterAnimDuration)
+ .withLayer()
+ .start();
+ }
+
+ /** Animates this task bar as it exits recents */
+ public void animateOnLaunchingTask(final Runnable r) {
+ animate()
+ .translationY(-getMeasuredHeight())
+ .setStartDelay(0)
+ .setInterpolator(mConfig.fastOutLinearInInterpolator)
+ .setDuration(mConfig.taskBarExitAnimDuration)
+ .withLayer()
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ post(r);
+ }
+ })
+ .start();
+ }
+
+ /** Enable the hw layers on this task view */
+ void enableHwLayers() {
+ mDismissButton.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ }
+
+ /** Disable the hw layers on this task view */
+ void disableHwLayers() {
+ mDismissButton.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 8d9f8be..186565b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -41,6 +41,7 @@ import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.RecentsPackageMonitor;
import com.android.systemui.recents.RecentsTaskLoader;
+import com.android.systemui.recents.ReferenceCountedTrigger;
import com.android.systemui.recents.Utilities;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
@@ -62,10 +63,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
public void onTaskRemoved(Task t);
}
+ RecentsConfiguration mConfig;
+
TaskStack mStack;
TaskStackViewTouchHandler mTouchHandler;
TaskStackViewCallbacks mCb;
ViewPool<TaskView, Task> mViewPool;
+ ArrayList<TaskViewTransform> mTaskTransforms = new ArrayList<TaskViewTransform>();
// The various rects that define the stack view
Rect mRect = new Rect();
@@ -83,11 +87,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
ObjectAnimator mScrollAnimator;
// Optimizations
- int mHwLayersRefCount;
+ ReferenceCountedTrigger mHwLayersTrigger;
int mStackViewsAnimationDuration;
boolean mStackViewsDirty = true;
boolean mAwaitingFirstLayout = true;
boolean mStartEnterAnimationRequestedAfterLayout;
+ ViewAnimation.TaskViewEnterContext mStartEnterAnimationContext;
int[] mTmpVisibleRange = new int[2];
Rect mTmpRect = new Rect();
Rect mTmpRect2 = new Rect();
@@ -95,12 +100,40 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
public TaskStackView(Context context, TaskStack stack) {
super(context);
+ mConfig = RecentsConfiguration.getInstance();
mStack = stack;
mStack.setCallbacks(this);
mScroller = new OverScroller(context);
mTouchHandler = new TaskStackViewTouchHandler(context, this);
mViewPool = new ViewPool<TaskView, Task>(context, this);
mInflater = LayoutInflater.from(context);
+ mHwLayersTrigger = new ReferenceCountedTrigger(getContext(), new Runnable() {
+ @Override
+ public void run() {
+ // Enable hw layers on each of the children
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ TaskView tv = (TaskView) getChildAt(i);
+ tv.enableHwLayers();
+ }
+ }
+ }, new Runnable() {
+ @Override
+ public void run() {
+ // Disable hw layers on each of the children
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ TaskView tv = (TaskView) getChildAt(i);
+ tv.disableHwLayers();
+ }
+ }
+ }, new Runnable() {
+ @Override
+ public void run() {
+ new Throwable("Invalid hw layers ref count").printStackTrace();
+ Console.logError(getContext(), "Invalid HW layers ref count");
+ }
+ });
}
/** Sets the callbacks */
@@ -118,7 +151,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
"[TaskStackView|requestSynchronize]", "" + duration + "ms", Console.AnsiYellow);
}
if (!mStackViewsDirty) {
- invalidate();
+ invalidate(mStackRect);
}
if (mAwaitingFirstLayout) {
// Skip the animation if we are awaiting first layout
@@ -165,7 +198,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2;
transform.scale = scale;
- // Set the translation
+ // Set the y translation
if (boundedT < 0f) {
transform.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
numPeekCards) * peekHeight - scaleYOffset);
@@ -174,9 +207,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
// Set the z translation
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- int minZ = config.taskViewTranslationZMinPx;
- int incZ = config.taskViewTranslationZIncrementPx;
+ int minZ = mConfig.taskViewTranslationZMinPx;
+ int incZ = mConfig.taskViewTranslationZIncrementPx;
transform.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
// Set the alphas
@@ -198,16 +230,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/**
* Gets the stack transforms of a list of tasks, and returns the visible range of tasks.
*/
- private ArrayList<TaskViewTransform> getStackTransforms(ArrayList<Task> tasks,
- int stackScroll,
- int[] visibleRangeOut,
- boolean boundTranslationsToRect) {
+ private void updateStackTransforms(ArrayList<TaskViewTransform> taskTransforms,
+ ArrayList<Task> tasks,
+ int stackScroll,
+ int[] visibleRangeOut,
+ boolean boundTranslationsToRect) {
// XXX: Optimization: Use binary search to find the visible range
- ArrayList<TaskViewTransform> taskTransforms = new ArrayList<TaskViewTransform>();
int taskCount = tasks.size();
int firstVisibleIndex = -1;
int lastVisibleIndex = -1;
+ taskTransforms.clear();
+ taskTransforms.ensureCapacity(taskCount);
for (int i = 0; i < taskCount; i++) {
TaskViewTransform transform = getStackTransform(i, stackScroll);
taskTransforms.add(transform);
@@ -226,6 +260,19 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
visibleRangeOut[0] = firstVisibleIndex;
visibleRangeOut[1] = lastVisibleIndex;
}
+ }
+
+ /**
+ * Gets the stack transforms of a list of tasks, and returns the visible range of tasks. This
+ * call is less optimal than calling updateStackTransforms directly.
+ */
+ private ArrayList<TaskViewTransform> getStackTransforms(ArrayList<Task> tasks,
+ int stackScroll,
+ int[] visibleRangeOut,
+ boolean boundTranslationsToRect) {
+ ArrayList<TaskViewTransform> taskTransforms = new ArrayList<TaskViewTransform>();
+ updateStackTransforms(taskTransforms, tasks, stackScroll, visibleRangeOut,
+ boundTranslationsToRect);
return taskTransforms;
}
@@ -245,14 +292,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
int[] visibleRange = mTmpVisibleRange;
int stackScroll = getStackScroll();
ArrayList<Task> tasks = mStack.getTasks();
- ArrayList<TaskViewTransform> taskTransforms = getStackTransforms(tasks, stackScroll,
- visibleRange, false);
+ updateStackTransforms(mTaskTransforms, tasks, stackScroll, visibleRange, false);
// Update the visible state of all the tasks
int taskCount = tasks.size();
for (int i = 0; i < taskCount; i++) {
Task task = tasks.get(i);
- TaskViewTransform transform = taskTransforms.get(i);
+ TaskViewTransform transform = mTaskTransforms.get(i);
TaskView tv = getChildViewForTask(task);
if (transform.visible) {
@@ -281,10 +327,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
TaskView tv = (TaskView) getChildAt(i);
Task task = tv.getTask();
int taskIndex = mStack.indexOfTask(task);
- if (taskIndex < 0 || !taskTransforms.get(taskIndex).visible) {
+ if (taskIndex < 0 || !mTaskTransforms.get(taskIndex).visible) {
mViewPool.returnViewToPool(tv);
} else {
- tv.updateViewPropertiesToTaskTransform(null, taskTransforms.get(taskIndex),
+ tv.updateViewPropertiesToTaskTransform(null, mTaskTransforms.get(taskIndex),
mStackViewsAnimationDuration);
}
}
@@ -361,7 +407,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll);
mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll -
curScroll, 250));
- mScrollAnimator.setInterpolator(RecentsConfiguration.getInstance().fastOutSlowInInterpolator);
+ mScrollAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
@@ -543,48 +589,31 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/** Enables the hw layers and increments the hw layer requirement ref count */
void addHwLayersRefCount(String reason) {
if (Console.Enabled) {
+ int refCount = mHwLayersTrigger.getCount();
Console.log(Constants.Log.UI.HwLayers,
"[TaskStackView|addHwLayersRefCount] refCount: " +
- mHwLayersRefCount + "->" + (mHwLayersRefCount + 1) + " " + reason);
+ refCount + "->" + (refCount + 1) + " " + reason);
}
- if (mHwLayersRefCount == 0) {
- // Enable hw layers on each of the children
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- TaskView tv = (TaskView) getChildAt(i);
- tv.enableHwLayers();
- }
- }
- mHwLayersRefCount++;
+ mHwLayersTrigger.increment();
}
/** Decrements the hw layer requirement ref count and disables the hw layers when we don't
need them anymore. */
void decHwLayersRefCount(String reason) {
if (Console.Enabled) {
+ int refCount = mHwLayersTrigger.getCount();
Console.log(Constants.Log.UI.HwLayers,
"[TaskStackView|decHwLayersRefCount] refCount: " +
- mHwLayersRefCount + "->" + (mHwLayersRefCount - 1) + " " + reason);
- }
- mHwLayersRefCount--;
- if (mHwLayersRefCount == 0) {
- // Disable hw layers on each of the children
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- TaskView tv = (TaskView) getChildAt(i);
- tv.disableHwLayers();
- }
- } else if (mHwLayersRefCount < 0) {
- new Throwable("Invalid hw layers ref count").printStackTrace();
- Console.logError(getContext(), "Invalid HW layers ref count");
+ refCount + "->" + (refCount - 1) + " " + reason);
}
+ mHwLayersTrigger.decrement();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
setStackScroll(mScroller.getCurrY());
- invalidate();
+ invalidate(mStackRect);
// If we just finished scrolling, then disable the hw layers
if (mScroller.isFinished()) {
@@ -616,7 +645,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if (Constants.DebugFlags.App.EnableTaskStackClipping) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
TaskView tv = (TaskView) child;
TaskView nextTv = null;
TaskView tmpTv = null;
@@ -632,13 +660,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
// Clip against the next view (if we aren't animating its alpha)
- if (nextTv != null && nextTv.getAlpha() == 1f) {
+ if (nextTv != null) {
Rect curRect = tv.getClippingRect(mTmpRect);
Rect nextRect = nextTv.getClippingRect(mTmpRect2);
// The hit rects are relative to the task view, which needs to be offset by
// the system bar height
- curRect.offset(0, config.systemInsets.top);
- nextRect.offset(0, config.systemInsets.top);
+ curRect.offset(0, mConfig.systemInsets.top);
+ nextRect.offset(0, mConfig.systemInsets.top);
// Compute the clip region
Region clipRegion = new Region();
clipRegion.op(curRect, Region.Op.UNION);
@@ -660,7 +688,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Note: We let the stack view be the full height because we want the cards to go under the
// navigation bar if possible. However, the stack rects which we use to calculate
// max scroll, etc. need to take the nav bar into account
- RecentsConfiguration config = RecentsConfiguration.getInstance();
// Compute the stack rects
mRect.set(0, 0, width, height);
@@ -668,8 +695,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mStackRect.left += insetLeft;
mStackRect.bottom -= insetBottom;
- int widthPadding = (int) (config.taskStackWidthPaddingPct * mStackRect.width());
- int heightPadding = config.taskStackTopPaddingPx;
+ int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width());
+ int heightPadding = mConfig.taskStackTopPaddingPx;
if (Constants.DebugFlags.App.EnableSearchLayout) {
mStackRect.top += heightPadding;
mStackRect.left += widthPadding;
@@ -707,10 +734,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
// Compute our stack/task rects
- RecentsConfiguration config = RecentsConfiguration.getInstance();
Rect taskStackBounds = new Rect();
- config.getTaskStackBounds(width, height, taskStackBounds);
- computeRects(width, height, taskStackBounds.left, config.systemInsets.bottom);
+ mConfig.getTaskStackBounds(width, height, taskStackBounds);
+ computeRects(width, height, taskStackBounds.left, mConfig.systemInsets.bottom);
// Debug logging
if (Constants.Log.UI.MeasureAndLayout) {
@@ -768,47 +794,66 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
if (mAwaitingFirstLayout) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
-
- // Update the focused task index to be the next item to the top task
- if (config.launchedFromAltTab) {
- focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
- }
+ // Mark that we have completely the first layout
+ mAwaitingFirstLayout = false;
// Prepare the first view for its enter animation
- if (config.launchedWithThumbnailAnimation) {
- TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
- if (tv != null) {
- tv.prepareAnimateOnEnterRecents();
- }
+ int offsetTopAlign = -mTaskRect.top;
+ int offscreenY = mRect.bottom - (mTaskRect.top - mRect.top);
+ for (int i = childCount - 1; i >= 0; i--) {
+ TaskView tv = (TaskView) getChildAt(i);
+ tv.prepareAnimateEnterRecents((i == (getChildCount() - 1)), offsetTopAlign,
+ offscreenY, mTaskRect);
}
- // Mark that we have completely the first layout
- mAwaitingFirstLayout = false;
-
// If the enter animation started already and we haven't completed a layout yet, do the
// enter animation now
if (mStartEnterAnimationRequestedAfterLayout) {
- startOnEnterAnimation();
+ startOnEnterAnimation(mStartEnterAnimationContext);
+ mStartEnterAnimationRequestedAfterLayout = false;
+ mStartEnterAnimationContext = null;
+ }
+
+ // Update the focused task index to be the next item to the top task
+ if (mConfig.launchedWithAltTab) {
+ focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
}
}
}
/** Requests this task stacks to start it's enter-recents animation */
- public void startOnEnterAnimation() {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (!config.launchedWithThumbnailAnimation) return;
-
+ public void startOnEnterAnimation(ViewAnimation.TaskViewEnterContext ctx) {
// If we are still waiting to layout, then just defer until then
if (mAwaitingFirstLayout) {
mStartEnterAnimationRequestedAfterLayout = true;
+ mStartEnterAnimationContext = ctx;
return;
}
- // Animate the task bar of the first task view
- TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
- if (tv != null) {
- tv.animateOnEnterRecents();
+ // Animate all the task views into view
+ ctx.taskRect = mTaskRect;
+ ctx.stackRectSansPeek = mStackRectSansPeek;
+ int childCount = getChildCount();
+ for (int i = childCount - 1; i >= 0; i--) {
+ TaskView tv = (TaskView) getChildAt(i);
+ TaskViewTransform transform = getStackTransform(mStack.indexOfTask(tv.getTask()),
+ getStackScroll());
+ ctx.stackViewIndex = i;
+ ctx.stackViewCount = childCount;
+ ctx.isFrontMost = (i == (getChildCount() - 1));
+ ctx.transform = transform;
+ tv.animateOnEnterRecents(ctx);
+ }
+ }
+
+ /** Requests this task stacks to start it's exit-recents animation. */
+ public void startOnExitAnimation(ViewAnimation.TaskViewExitContext ctx) {
+ // Animate all the task views into view
+ ctx.offscreenTranslationY = mRect.bottom - (mTaskRect.top - mRect.top);
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ TaskView tv = (TaskView) getChildAt(i);
+ tv.animateOnExitRecents(ctx);
}
}
@@ -871,8 +916,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
ArrayList<TaskViewTransform> curTaskTransforms,
ArrayList<Task> tasks, ArrayList<TaskViewTransform> taskTransforms,
HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut,
- ArrayList<TaskView> childrenToRemoveOut,
- RecentsConfiguration config) {
+ ArrayList<TaskView> childrenToRemoveOut) {
// Animate all of the existing views out of view (if they are not in the visible range in
// the new stack) or to their final positions in the new stack
int movement = 0;
@@ -902,7 +946,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
childViewTransformsOut.put(tv, new Pair(0, toTransform));
}
return Utilities.calculateTranslationAnimationDuration(movement,
- config.filteringCurrentViewsMinAnimDuration);
+ mConfig.filteringCurrentViewsMinAnimDuration);
}
/**
@@ -911,8 +955,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
*/
int getEnterTransformsForFilterAnimation(ArrayList<Task> tasks,
ArrayList<TaskViewTransform> taskTransforms,
- HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut,
- RecentsConfiguration config) {
+ HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut) {
int offset = 0;
int movement = 0;
int taskCount = tasks.size();
@@ -942,7 +985,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
}
return Utilities.calculateTranslationAnimationDuration(movement,
- config.filteringNewViewsMinAnimDuration);
+ mConfig.filteringNewViewsMinAnimDuration);
}
/** Orchestrates the animations of the current child views and any new views. */
@@ -950,22 +993,20 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
ArrayList<TaskViewTransform> curTaskTransforms,
final ArrayList<Task> tasks,
final ArrayList<TaskViewTransform> taskTransforms) {
- final RecentsConfiguration config = RecentsConfiguration.getInstance();
-
// Calculate the transforms to animate out all the existing views if they are not in the
// new visible range (or to their final positions in the stack if they are)
final ArrayList<TaskView> childrenToRemove = new ArrayList<TaskView>();
final HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransforms =
new HashMap<TaskView, Pair<Integer, TaskViewTransform>>();
int duration = getExitTransformsForFilterAnimation(curTasks, curTaskTransforms, tasks,
- taskTransforms, childViewTransforms, childrenToRemove, config);
+ taskTransforms, childViewTransforms, childrenToRemove);
// If all the current views are in the visible range of the new stack, then don't wait for
// views to animate out and animate all the new views into their place
final boolean unifyNewViewAnimation = childrenToRemove.isEmpty();
if (unifyNewViewAnimation) {
int inDuration = getEnterTransformsForFilterAnimation(tasks, taskTransforms,
- childViewTransforms, config);
+ childViewTransforms);
duration = Math.max(duration, inDuration);
}
@@ -989,7 +1030,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// For views that are not already visible, animate them in
childViewTransforms.clear();
int duration = getEnterTransformsForFilterAnimation(tasks,
- taskTransforms, childViewTransforms, config);
+ taskTransforms, childViewTransforms);
for (final TaskView tv : childViewTransforms.keySet()) {
Pair<Integer, TaskViewTransform> t = childViewTransforms.get(tv);
tv.animate().setStartDelay(t.first);
@@ -1127,7 +1168,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
// Enable hw layers on this view if hw layers are enabled on the stack
- if (mHwLayersRefCount > 0) {
+ if (mHwLayersTrigger.getCount() > 0) {
tv.enableHwLayers();
}
}
@@ -1196,7 +1237,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override
public void onComponentRemoved(Set<ComponentName> cns) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
// For other tasks, just remove them directly if they no longer exist
ArrayList<Task> tasks = mStack.getTasks();
for (int i = tasks.size() - 1; i >= 0; i--) {
@@ -1502,7 +1542,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
mSv.mMinScroll, mSv.mMaxScroll,
0, overscrollRange);
// Invalidate to kick off computeScroll
- mSv.invalidate();
+ mSv.invalidate(mSv.mStackRect);
} else if (mSv.isScrollOutOfBounds()) {
// Animate the scroll back into bounds
// XXX: Make this animation a function of the velocity OR distance
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 5df5e4d..9e4386f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -28,9 +28,11 @@ import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewParent;
import android.view.animation.AccelerateInterpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
+import com.android.systemui.recents.Console;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.model.Task;
@@ -45,10 +47,10 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
public void onTaskAppInfoClicked(TaskView tv);
public void onTaskFocused(TaskView tv);
public void onTaskDismissed(TaskView tv);
-
- // public void onTaskViewReboundToTask(TaskView tv, Task t);
}
+ RecentsConfiguration mConfig;
+
int mDim;
int mMaxDim;
TimeInterpolator mDimInterpolator = new AccelerateInterpolator();
@@ -59,11 +61,21 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
boolean mClipViewInStack;
Point mLastTouchDown = new Point();
Path mRoundedRectClipPath = new Path();
+ Rect mTmpRect = new Rect();
TaskThumbnailView mThumbnailView;
TaskBarView mBarView;
TaskViewCallbacks mCb;
+ // Optimizations
+ ValueAnimator.AnimatorUpdateListener mUpdateDimListener =
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ updateDimOverlayFromScale();
+ }
+ };
+
public TaskView(Context context) {
this(context, null);
@@ -79,13 +91,13 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mConfig = RecentsConfiguration.getInstance();
setWillNotDraw(false);
}
@Override
protected void onFinishInflate() {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- mMaxDim = config.taskStackMaxDim;
+ mMaxDim = mConfig.taskStackMaxDim;
// By default, all views are clipped to other views in their stack
mClipViewInStack = true;
@@ -104,8 +116,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Update the rounded rect clip path
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- float radius = config.taskViewRoundedCornerRadiusPx;
+ float radius = mConfig.taskViewRoundedCornerRadiusPx;
mRoundedRectClipPath.reset();
mRoundedRectClipPath.addRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()),
radius, radius, Path.Direction.CW);
@@ -113,7 +124,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
// Update the outline
Outline o = new Outline();
o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight() -
- config.taskViewShadowOutlineBottomInsetPx, radius);
+ mConfig.taskViewShadowOutlineBottomInsetPx, radius);
setOutline(o);
}
@@ -141,7 +152,10 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
/** Synchronizes this view's properties with the task's transform */
void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform,
TaskViewTransform toTransform, int duration) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
+ if (Console.Enabled) {
+ Console.log(Constants.Log.UI.Draw, "[TaskView|updateViewPropertiesToTaskTransform]",
+ "duration: " + duration, Console.AnsiPurple);
+ }
// Update the bar view
mBarView.updateViewPropertiesToTaskTransform(animateFromTransform, toTransform, duration);
@@ -164,15 +178,10 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
.scaleX(toTransform.scale)
.scaleY(toTransform.scale)
.alpha(toTransform.alpha)
+ .setStartDelay(0)
.setDuration(duration)
- .setInterpolator(config.fastOutSlowInInterpolator)
- .withLayer()
- .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- updateDimOverlayFromScale();
- }
- })
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setUpdateListener(mUpdateDimListener)
.start();
} else {
setTranslationY(toTransform.translationY);
@@ -197,6 +206,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
setScaleX(1f);
setScaleY(1f);
setAlpha(1f);
+ mDim = 0;
invalidate();
}
@@ -221,40 +231,103 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
/** Prepares this task view for the enter-recents animations. This is called earlier in the
* first layout because the actual animation into recents may take a long time. */
- public void prepareAnimateOnEnterRecents() {
- mBarView.setVisibility(View.INVISIBLE);
+ public void prepareAnimateEnterRecents(boolean isTaskViewFrontMost, int offsetY, int offscreenY,
+ Rect taskRect) {
+ if (mConfig.launchedFromAppWithScreenshot) {
+ if (isTaskViewFrontMost) {
+ // Hide the task view as we are going to animate the full screenshot into view
+ // and then replace it with this view once we are done
+ setVisibility(View.INVISIBLE);
+ // Also hide the front most task bar view so we can animate it in
+ mBarView.prepareAnimateEnterRecents();
+ } else {
+ // Top align the task views
+ setTranslationY(offsetY);
+ setScaleX(1f);
+ setScaleY(1f);
+ }
+
+ } else if (mConfig.launchedFromAppWithThumbnail) {
+ if (isTaskViewFrontMost) {
+ // Hide the front most task bar view so we can animate it in
+ mBarView.prepareAnimateEnterRecents();
+ }
+
+ } else if (mConfig.launchedFromHome) {
+ // Move the task view off screen (below) so we can animate it in
+ setTranslationY(offscreenY);
+ setScaleX(1f);
+ setScaleY(1f);
+ }
}
/** Animates this task view as it enters recents */
- public void animateOnEnterRecents() {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- mBarView.setVisibility(View.VISIBLE);
- mBarView.setTranslationY(-mBarView.getMeasuredHeight());
- mBarView.animate()
- .translationY(0)
- .setStartDelay(config.taskBarEnterAnimDelay)
- .setInterpolator(config.fastOutSlowInInterpolator)
- .setDuration(config.taskBarEnterAnimDuration)
+ public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx) {
+ TaskViewTransform transform = ctx.transform;
+
+ if (mConfig.launchedFromAppWithScreenshot) {
+ if (ctx.isFrontMost) {
+ // Animate the full screenshot down first, before swapping with this task view
+ ctx.fullScreenshot.animateOnEnterRecents(ctx, new Runnable() {
+ @Override
+ public void run() {
+ // Animate the task bar of the first task view
+ mBarView.animateOnEnterRecents(0);
+ setVisibility(View.VISIBLE);
+ }
+ });
+ } else {
+ // Animate the tasks down behind the full screenshot
+ animate()
+ .scaleX(transform.scale)
+ .scaleY(transform.scale)
+ .translationY(transform.translationY)
+ .setStartDelay(0)
+ .setInterpolator(mConfig.linearOutSlowInInterpolator)
+ .setDuration(475)
+ .withLayer()
+ .start();
+ }
+
+ } else if (mConfig.launchedFromAppWithThumbnail) {
+ if (ctx.isFrontMost) {
+ // Animate the task bar of the first task view
+ mBarView.animateOnEnterRecents(-1);
+ }
+
+ } else if (mConfig.launchedFromHome) {
+ // Animate the tasks up
+ int frontIndex = (ctx.stackViewCount - ctx.stackViewIndex - 1);
+ int delay = mConfig.taskBarEnterAnimDelay +
+ frontIndex * mConfig.taskViewEnterFromHomeDelay;
+ animate()
+ .scaleX(transform.scale)
+ .scaleY(transform.scale)
+ .translationY(transform.translationY)
+ .setStartDelay(delay)
+ .setInterpolator(mConfig.quintOutInterpolator)
+ .setDuration(mConfig.taskViewEnterFromHomeDuration)
+ .withLayer()
+ .start();
+ }
+ }
+
+ /** Animates this task view as it leaves recents */
+ public void animateOnExitRecents(ViewAnimation.TaskViewExitContext ctx) {
+ animate()
+ .translationY(ctx.offscreenTranslationY)
+ .setStartDelay(0)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(mConfig.taskViewEnterFromHomeDuration)
.withLayer()
+ .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
.start();
+ ctx.postAnimationTrigger.increment();
}
/** Animates this task view as it exits recents */
- public void animateOnLeavingRecents(final Runnable r) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- mBarView.animate()
- .translationY(-mBarView.getMeasuredHeight())
- .setStartDelay(0)
- .setInterpolator(config.fastOutLinearInInterpolator)
- .setDuration(config.taskBarExitAnimDuration)
- .withLayer()
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- post(r);
- }
- })
- .start();
+ public void animateOnLaunchingTask(final Runnable r) {
+ mBarView.animateOnLaunchingTask(r);
}
/** Animates the deletion of this task view */
@@ -262,20 +335,24 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
// Disabling clipping with the stack while the view is animating away
setClipViewInStack(false);
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- animate().translationX(config.taskViewRemoveAnimTranslationXPx)
+ animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx)
.alpha(0f)
.setStartDelay(0)
- .setInterpolator(config.fastOutSlowInInterpolator)
- .setDuration(config.taskViewRemoveAnimDuration)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(mConfig.taskViewRemoveAnimDuration)
.withLayer()
.withEndAction(new Runnable() {
@Override
public void run() {
- post(r);
+ // We just throw this into a runnable because starting a view property
+ // animation using layers can cause inconsisten results if we try and
+ // update the layers while the animation is running. In some cases,
+ // the runnabled passed in may start an animation which also uses layers
+ // so we defer all this by posting this.
+ r.run();
// Re-enable clipping with the stack (we will reuse this view)
- setClipViewInStack(false);
+ setClipViewInStack(true);
}
})
.start();
@@ -293,11 +370,13 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
/** Enable the hw layers on this task view */
void enableHwLayers() {
mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mBarView.enableHwLayers();
}
/** Disable the hw layers on this task view */
void disableHwLayers() {
mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null);
+ mBarView.disableHwLayers();
}
/**
@@ -305,7 +384,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
* view.
*/
boolean shouldClipViewInStack() {
- return mClipViewInStack;
+ return mClipViewInStack && (getVisibility() == View.VISIBLE);
}
/** Sets whether this view should be clipped, or clipped against. */
@@ -313,9 +392,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
if (clip != mClipViewInStack) {
mClipViewInStack = clip;
if (getParent() instanceof View) {
- Rect r = new Rect();
- getHitRect(r);
- ((View) getParent()).invalidate(r);
+ getHitRect(mTmpRect);
+ ((View) getParent()).invalidate(mTmpRect);
}
}
}
@@ -391,8 +469,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
mBarView.mApplicationIcon.setOnClickListener(this);
mBarView.mDismissButton.setOnClickListener(this);
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (config.developerOptionsEnabled) {
+ if (mConfig.developerOptionsEnabled) {
mBarView.mApplicationIcon.setOnLongClickListener(this);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index 3c3ebd7..4a76872 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -47,7 +47,8 @@ public class TaskViewTransform {
@Override
public String toString() {
- return "TaskViewTransform y: " + translationY + " scale: " + scale + " alpha: " + alpha +
- " visible: " + visible + " rect: " + rect + " dismissAlpha: " + dismissAlpha;
+ return "TaskViewTransform y: " + translationY + " z: " + translationZ + " scale: " + scale +
+ " alpha: " + alpha + " visible: " + visible + " rect: " + rect +
+ " dismissAlpha: " + dismissAlpha;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
new file mode 100644
index 0000000..b5e8ffd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.graphics.Rect;
+import com.android.systemui.recents.ReferenceCountedTrigger;
+
+/* Common code related to view animations */
+public class ViewAnimation {
+
+ /* The animation context for a task view animation into Recents */
+ public static class TaskViewEnterContext {
+ // The full screenshot view that we are animating down
+ FullScreenTransitionView fullScreenshot;
+ // The transform of the current task view
+ TaskViewTransform transform;
+ // The stack rect that the transform is relative to
+ Rect stackRectSansPeek;
+ // The task rect
+ Rect taskRect;
+ // The view index of the current task view
+ int stackViewIndex;
+ // The total number of task views
+ int stackViewCount;
+ // Whether this is the front most task view
+ boolean isFrontMost;
+
+ public TaskViewEnterContext(FullScreenTransitionView fss) {
+ fullScreenshot = fss;
+ }
+ }
+
+ /* The animation context for a task view animation out of Recents */
+ public static class TaskViewExitContext {
+ // A trigger to run some logic when all the animations complete. This works around the fact
+ // that it is difficult to coordinate ViewPropertyAnimators
+ ReferenceCountedTrigger postAnimationTrigger;
+ // The translationY to apply to a TaskView to move it off screen
+ int offscreenTranslationY;
+
+ public TaskViewExitContext(ReferenceCountedTrigger t) {
+ postAnimationTrigger = t;
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 4ed1888..a38d18c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1708,7 +1708,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
if (mStatusBarWindow != null) {
-
// release focus immediately to kick off focus change transition
mStatusBarWindowManager.setStatusBarFocusable(false);
@@ -3134,4 +3133,30 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
}
};
+
+ // Recents
+
+ @Override
+ protected void showRecents(boolean triggeredFromAltTab) {
+ // Set the recents visibility flag
+ mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
+ notifyUiVisibilityChanged(mSystemUiVisibility);
+ super.showRecents(triggeredFromAltTab);
+ }
+
+ @Override
+ protected void hideRecents(boolean triggeredFromAltTab) {
+ // Unset the recents visibility flag
+ mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
+ notifyUiVisibilityChanged(mSystemUiVisibility);
+ super.hideRecents(triggeredFromAltTab);
+ }
+
+ @Override
+ protected void toggleRecents() {
+ // Toggle the recents visibility flag
+ mSystemUiVisibility ^= View.RECENT_APPS_VISIBLE;
+ notifyUiVisibilityChanged(mSystemUiVisibility);
+ super.toggleRecents();
+ }
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 03d29c0..cacf66b 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -296,6 +296,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
WindowState mLastInputMethodWindow = null;
WindowState mLastInputMethodTargetWindow = null;
+ boolean mRecentsVisible;
int mRecentAppsHeldModifiers;
boolean mLanguageSwitchKeyPressed;
@@ -2632,6 +2633,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
});
+ } else if (mRecentsVisible) {
+ // Recents is started on top of Home, so when we launch home while recents is open, let
+ // it do its own animation and then finish itself
+ sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
+ hideRecentApps(false);
} else {
// no keyguard stuff to worry about, just launch home!
try {
@@ -2723,6 +2729,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
public int adjustSystemUiVisibilityLw(int visibility) {
mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
+ mRecentsVisible = (visibility & View.RECENT_APPS_VISIBLE) > 0;
// Reset any bits in mForceClearingStatusBarVisibility that
// are now clear.