diff options
author | Winson Chung <winsonc@google.com> | 2014-06-03 16:24:04 -0700 |
---|---|---|
committer | Winson Chung <winsonc@google.com> | 2014-06-12 12:57:56 -0700 |
commit | d42a6cfe2bf632222617450a1ed340268e82f06c (patch) | |
tree | ae2aaf137b4c3420458905182df4db7884a529f7 | |
parent | 88b00784684d7b706c7b0c4e833b1d67d73358b9 (diff) | |
download | frameworks_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
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. |