diff options
10 files changed, 240 insertions, 18 deletions
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 93a75bc..c0376f0 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -118,6 +118,8 @@ <integer name="recents_animate_task_bar_exit_duration">150</integer> <!-- The animation duration for animating in the info pane. --> <integer name="recents_animate_task_view_info_pane_duration">150</integer> + <!-- The animation duration for animating the removal of a task view. --> + <integer name="recents_animate_task_view_remove_duration">150</integer> <!-- The minimum alpha for the dim applied to cards that go deeper into the stack. --> <integer name="recents_max_task_stack_view_dim">96</integer> <!-- Transposes the search bar layout in landscape --> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 8d3a565..c6fdc16 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -223,6 +223,9 @@ <!-- The translation in the Z index for each task above the last task. --> <dimen name="recents_task_view_z_increment">5dp</dimen> + <!-- The amount to translate when animating the removal of a task. --> + <dimen name="recents_task_view_remove_anim_translation_x">75dp</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> diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 3be6932..b74f6ac 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -22,6 +22,7 @@ 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; @@ -37,6 +38,7 @@ import com.android.systemui.recents.model.TaskStack; import com.android.systemui.recents.views.RecentsView; import java.util.ArrayList; +import java.util.Set; /** Our special app widget host */ class RecentsAppWidgetHost extends AppWidgetHost { @@ -60,7 +62,7 @@ class RecentsAppWidgetHost extends AppWidgetHost { /* Activity */ public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks, - RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks{ + RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks { FrameLayout mContainerView; RecentsView mRecentsView; View mEmptyView; @@ -308,6 +310,9 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_OFF); registerReceiver(mScreenOffReceiver, filter); + + // Register any broadcast receivers for the task loader + RecentsTaskLoader.getInstance().registerReceivers(this, mRecentsView); } @Override @@ -320,6 +325,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // Unregister any broadcast receivers we have registered unregisterReceiver(mServiceBroadcastReceiver); unregisterReceiver(mScreenOffReceiver); + RecentsTaskLoader.getInstance().unregisterReceivers(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index cb7e42a..463cf74 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -49,6 +49,8 @@ public class RecentsConfiguration { public int taskStackScrollDismissInfoPaneDistance; public int taskStackMaxDim; public int taskViewInfoPaneAnimDuration; + public int taskViewRemoveAnimDuration; + public int taskViewRemoveAnimTranslationXPx; public int taskViewTranslationZMinPx; public int taskViewTranslationZIncrementPx; public int taskViewRoundedCornerRadiusPx; @@ -108,6 +110,10 @@ public class RecentsConfiguration { taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim); taskViewInfoPaneAnimDuration = res.getInteger(R.integer.recents_animate_task_view_info_pane_duration); + taskViewRemoveAnimDuration = + res.getInteger(R.integer.recents_animate_task_view_remove_duration); + taskViewRemoveAnimTranslationXPx = + res.getDimensionPixelSize(R.dimen.recents_task_view_remove_anim_translation_x); taskViewRoundedCornerRadiusPx = res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius); taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsPackageMonitor.java new file mode 100644 index 0000000..4e620b6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsPackageMonitor.java @@ -0,0 +1,112 @@ +/* + * 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.app.ActivityManager; +import android.content.ComponentName; +import android.content.Context; +import android.os.Looper; +import com.android.internal.content.PackageMonitor; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * The package monitor listens for changes from PackageManager to update the contents of the Recents + * list. + */ +public class RecentsPackageMonitor extends PackageMonitor { + public interface PackageCallbacks { + public void onComponentRemoved(Set<ComponentName> cns); + } + + PackageCallbacks mCb; + List<ActivityManager.RecentTaskInfo> mTasks; + SystemServicesProxy mSsp; + + public RecentsPackageMonitor(Context context) { + mSsp = new SystemServicesProxy(context); + } + + /** Registers the broadcast receivers with the specified callbacks. */ + public void register(Context context, PackageCallbacks cb) { + mCb = cb; + register(context, Looper.getMainLooper(), false); + } + + /** Unregisters the broadcast receivers. */ + @Override + public void unregister() { + super.unregister(); + mTasks.clear(); + } + + /** Sets the list of tasks to match against package broadcast changes. */ + void setTasks(List<ActivityManager.RecentTaskInfo> tasks) { + mTasks = tasks; + } + + @Override + public void onPackageRemoved(String packageName, int uid) { + if (mCb == null) return; + + // Identify all the tasks that should be removed as a result of the package being removed. + // Using a set to ensure that we callback once per unique component. + HashSet<ComponentName> componentsToRemove = new HashSet<ComponentName>(); + for (ActivityManager.RecentTaskInfo t : mTasks) { + ComponentName cn = t.baseIntent.getComponent(); + if (cn.getPackageName().equals(packageName)) { + componentsToRemove.add(cn); + } + } + // Notify our callbacks that the components no longer exist + mCb.onComponentRemoved(componentsToRemove); + } + + @Override + public boolean onPackageChanged(String packageName, int uid, String[] components) { + onPackageModified(packageName); + return true; + } + + @Override + public void onPackageModified(String packageName) { + if (mCb == null) return; + + // Identify all the tasks that should be removed as a result of the package being removed. + // Using a set to ensure that we callback once per unique component. + HashSet<ComponentName> componentsKnownToExist = new HashSet<ComponentName>(); + HashSet<ComponentName> componentsToRemove = new HashSet<ComponentName>(); + for (ActivityManager.RecentTaskInfo t : mTasks) { + ComponentName cn = t.baseIntent.getComponent(); + if (cn.getPackageName().equals(packageName)) { + if (componentsKnownToExist.contains(cn)) { + // If we know that the component still exists in the package, then skip + continue; + } + if (mSsp.getActivityInfo(cn) != null) { + componentsKnownToExist.add(cn); + } else { + componentsToRemove.add(cn); + } + } + } + // Notify our callbacks that the components no longer exist + mCb.onComponentRemoved(componentsToRemove); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java index c9038ee..f3e411f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java @@ -300,6 +300,8 @@ public class RecentsTaskLoader { TaskResourceLoadQueue mLoadQueue; TaskResourceLoader mLoader; + RecentsPackageMonitor mPackageMonitor; + int mMaxThumbnailCacheSize; int mMaxIconCacheSize; @@ -325,6 +327,7 @@ public class RecentsTaskLoader { // Initialize the proxy, cache and loaders mSystemServicesProxy = new SystemServicesProxy(context); + mPackageMonitor = new RecentsPackageMonitor(context); mLoadQueue = new TaskResourceLoadQueue(); mApplicationIconCache = new DrawableLruCache(iconCacheSize); mThumbnailCache = new BitmapLruCache(thumbnailCacheSize); @@ -510,6 +513,9 @@ public class RecentsTaskLoader { mLoadQueue.addTask(t, true); } + // Update the package monitor with the list of packages to listen for + mPackageMonitor.setTasks(tasks); + return root; } @@ -565,6 +571,21 @@ public class RecentsTaskLoader { mLoadQueue.clearTasks(); } + /** Registers any broadcast receivers. */ + public void registerReceivers(Context context, RecentsPackageMonitor.PackageCallbacks cb) { + // Register the broadcast receiver to handle messages related to packages being added/removed + mPackageMonitor.register(context, cb); + } + + /** Unregisters any broadcast receivers. */ + public void unregisterReceivers() { + mPackageMonitor.unregister(); + } + + /** + * Handles signals from the system, trimming memory when requested to prevent us from running + * out of memory. + */ void onTrimMemory(int level) { Console.log(Constants.Log.App.Memory, "[RecentsTaskLoader|onTrimMemory]", Console.trimMemoryLevelToString(level)); 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 eb7b5c6..8168619 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -19,6 +19,7 @@ package com.android.systemui.recents.views; import android.app.ActivityOptions; import android.app.TaskStackBuilder; import android.content.ActivityNotFoundException; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; @@ -33,19 +34,22 @@ import android.widget.FrameLayout; import com.android.systemui.recents.Console; 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.model.SpaceNode; import com.android.systemui.recents.model.Task; import com.android.systemui.recents.model.TaskStack; import java.util.ArrayList; +import java.util.Set; /** * This view is the the top level layout that contains TaskStacks (which are laid out according * to their SpaceNode bounds. */ -public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks { +public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks, + RecentsPackageMonitor.PackageCallbacks { /** The RecentsView callbacks */ public interface RecentsViewCallbacks { @@ -385,4 +389,19 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV TaskStackBuilder.create(getContext()) .addNextIntentWithParentStack(intent).startActivities(); } + + /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/ + + @Override + public void onComponentRemoved(Set<ComponentName> cns) { + // Propagate this event down to each task stack view + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child instanceof TaskStackView) { + TaskStackView stackView = (TaskStackView) child; + stackView.onComponentRemoved(cns); + } + } + } } 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 cfacd18..ad0f2f82 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.app.Activity; +import android.content.ComponentName; import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; @@ -39,6 +40,7 @@ import com.android.systemui.recents.BakedBezierInterpolator; import com.android.systemui.recents.Console; 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.Utilities; import com.android.systemui.recents.model.Task; @@ -46,12 +48,13 @@ import com.android.systemui.recents.model.TaskStack; import java.util.ArrayList; import java.util.HashMap; +import java.util.Set; /* The visual representation of a task stack view */ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks, TaskView.TaskViewCallbacks, ViewPool.ViewPoolConsumer<TaskView, Task>, - View.OnClickListener, View.OnLongClickListener { + View.OnClickListener, View.OnLongClickListener, RecentsPackageMonitor.PackageCallbacks { /** The TaskView callbacks */ interface TaskStackViewCallbacks { @@ -705,9 +708,24 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } + // Update the min/max scroll and animate other task views into their new positions updateMinMaxScroll(true); int movement = (int) (Constants.Values.TaskStackView.StackOverlapPct * mTaskRect.height()); requestSynchronizeStackViewsWithModel(Utilities.calculateTranslationAnimationDuration(movement)); + + // If there are no remaining tasks, then either unfilter the current stack, or just close + // the activity if there are no filtered stacks + if (mStack.getTaskCount() == 0) { + boolean shouldFinishActivity = true; + if (mStack.hasFilteredTasks()) { + mStack.unfilterTasks(); + shouldFinishActivity = (mStack.getTaskCount() == 0); + } + if (shouldFinishActivity) { + Activity activity = (Activity) getContext(); + activity.finish(); + } + } } /** @@ -1070,6 +1088,32 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal tv.showInfoPane(new Rect(0, 0, 0, (int) overlapHeight)); return true; } + + /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/ + + @Override + public void onComponentRemoved(Set<ComponentName> cns) { + // 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--) { + final Task t = tasks.get(i); + if (cns.contains(t.key.baseIntent.getComponent())) { + TaskView tv = getChildViewForTask(t); + if (tv != null) { + // For visible children, defer removing the task until after the animation + tv.animateRemoval(new Runnable() { + @Override + public void run() { + mStack.removeTask(t); + } + }); + } else { + // Otherwise, remove the task from the stack immediately + mStack.removeTask(t); + } + } + } + } } /* Handles touch events */ @@ -1432,7 +1476,6 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { public void onChildDismissed(View v) { TaskView tv = (TaskView) v; Task task = tv.getTask(); - Activity activity = (Activity) mSv.getContext(); // Remove the task from the view mSv.mStack.removeTask(task); @@ -1444,19 +1487,6 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { // Remove the task from activity manager RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(tv.getTask().key.id); - // If there are no remaining tasks, then either unfilter the current stack, or just close - // the activity if there are no filtered stacks - if (mSv.mStack.getTaskCount() == 0) { - boolean shouldFinishActivity = true; - if (mSv.mStack.hasFilteredTasks()) { - mSv.mStack.unfilterTasks(); - shouldFinishActivity = (mSv.mStack.getTaskCount() == 0); - } - if (shouldFinishActivity) { - activity.finish(); - } - } - // Disable HW layers mSv.decHwLayersRefCount("swipeComplete"); } 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 45c2fae..b03f389 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -246,6 +246,24 @@ public class TaskView extends FrameLayout implements View.OnClickListener, .start(); } + /** Animates the deletion of this task view */ + public void animateRemoval(final Runnable r) { + RecentsConfiguration config = RecentsConfiguration.getInstance(); + animate().translationX(config.taskViewRemoveAnimTranslationXPx) + .alpha(0f) + .setStartDelay(0) + .setInterpolator(BakedBezierInterpolator.INSTANCE) + .setDuration(config.taskViewRemoveAnimDuration) + .withLayer() + .withEndAction(new Runnable() { + @Override + public void run() { + post(r); + } + }) + .start(); + } + /** Returns the rect we want to clip (it may not be the full rect) */ Rect getClippingRect(Rect outRect) { getHitRect(outRect); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 29c781b..efc5606 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1850,6 +1850,12 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public boolean onPackageChanged(String packageName, int uid, String[] components) { + onPackageModified(packageName); + return true; + } + + @Override + public void onPackageModified(String packageName) { final PackageManager pm = mContext.getPackageManager(); final ArrayList<Pair<Intent, Integer>> recentTaskIntents = new ArrayList<Pair<Intent, Integer>>(); @@ -1885,7 +1891,6 @@ public final class ActivityManagerService extends ActivityManagerNative removeTaskByIdLocked(tasksToRemove.get(i), 0); } } - return true; } @Override |