blob: ef5604474baf83d77f068cefcc3dc41d37369517 [file] [log] [blame]
Jim Miller44c66fe2010-10-20 18:32:52 -07001/*
Jim Miller9f0f0e02011-05-17 20:06:29 -07002 * Copyright (C) 2011 The Android Open Source Project
Jim Miller44c66fe2010-10-20 18:32:52 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Jim Miller9f0f0e02011-05-17 20:06:29 -070017package com.android.systemui.recent;
Jim Miller44c66fe2010-10-20 18:32:52 -070018
Michael Jurka3b1fc472011-06-13 10:54:40 -070019import android.animation.Animator;
Jim Miller9f0f0e02011-05-17 20:06:29 -070020import android.animation.LayoutTransition;
Michael Jurka9bdaada2012-10-01 13:58:29 +020021import android.animation.TimeInterpolator;
Jim Miller44c66fe2010-10-20 18:32:52 -070022import android.app.ActivityManager;
Michael Jurka56a57832012-05-14 13:24:43 -070023import android.app.ActivityManagerNative;
Dianne Hackborn8078d8c2012-03-20 11:11:26 -070024import android.app.ActivityOptions;
Adam Powell0fc5b2b2012-07-18 18:20:29 -070025import android.app.TaskStackBuilder;
Michael Jurka4e85f462013-09-16 19:52:21 +020026import android.content.ActivityNotFoundException;
Jim Miller44c66fe2010-10-20 18:32:52 -070027import android.content.Context;
28import android.content.Intent;
Michael Jurka9bdaada2012-10-01 13:58:29 +020029import android.content.res.Configuration;
Michael Jurkabfd24ac2011-11-13 13:50:38 -080030import android.content.res.Resources;
Daniel Sandler4a066c52012-04-20 14:49:13 -040031import android.content.res.TypedArray;
Jim Miller44c66fe2010-10-20 18:32:52 -070032import android.graphics.Bitmap;
Michael Jurka4a6bc552013-09-17 17:42:54 +020033import android.graphics.Canvas;
Jim Millerfefef312010-11-09 14:08:39 -080034import android.graphics.Matrix;
Jim Millere5c16ab2010-12-03 19:18:38 -080035import android.graphics.Shader.TileMode;
36import android.graphics.drawable.BitmapDrawable;
Michael Jurka99a96552012-01-27 17:23:38 -080037import android.graphics.drawable.Drawable;
Jim Millerc0d27312011-07-14 18:54:01 -070038import android.net.Uri;
Michael Jurka482f1592012-09-13 11:05:59 -070039import android.os.Bundle;
Michael Jurka56a57832012-05-14 13:24:43 -070040import android.os.RemoteException;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070041import android.os.UserHandle;
Jim Millerc0d27312011-07-14 18:54:01 -070042import android.provider.Settings;
Jim Miller44c66fe2010-10-20 18:32:52 -070043import android.util.AttributeSet;
44import android.util.Log;
Jim Miller5fda69c2011-02-16 15:17:11 -080045import android.view.LayoutInflater;
Jim Millerc0d27312011-07-14 18:54:01 -070046import android.view.MenuItem;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070047import android.view.MotionEvent;
Jim Miller44c66fe2010-10-20 18:32:52 -070048import android.view.View;
Jim Miller5fda69c2011-02-16 15:17:11 -080049import android.view.ViewGroup;
Michael Jurkae0523f72013-04-08 15:44:00 -070050import android.view.ViewPropertyAnimator;
Michael Jurkaf42d90102013-05-08 18:00:04 +020051import android.view.ViewRootImpl;
Jim Miller17377762011-10-11 15:33:34 -070052import android.view.accessibility.AccessibilityEvent;
Dianne Hackbornfc8fa632011-08-17 16:20:47 -070053import android.view.animation.AnimationUtils;
Michael Jurka9bdaada2012-10-01 13:58:29 +020054import android.view.animation.DecelerateInterpolator;
Jim Miller5fda69c2011-02-16 15:17:11 -080055import android.widget.AdapterView;
Michael Jurkabfd24ac2011-11-13 13:50:38 -080056import android.widget.AdapterView.OnItemClickListener;
Jim Miller5fda69c2011-02-16 15:17:11 -080057import android.widget.BaseAdapter;
Michael Jurka99a96552012-01-27 17:23:38 -080058import android.widget.FrameLayout;
Jim Miller44c66fe2010-10-20 18:32:52 -070059import android.widget.ImageView;
Michael Jurkabfd24ac2011-11-13 13:50:38 -080060import android.widget.ImageView.ScaleType;
Jim Millerc0d27312011-07-14 18:54:01 -070061import android.widget.PopupMenu;
Jim Miller44c66fe2010-10-20 18:32:52 -070062import android.widget.TextView;
Jim Miller44c66fe2010-10-20 18:32:52 -070063
64import com.android.systemui.R;
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -050065import com.android.systemui.statusbar.BaseStatusBar;
John Spurlockb0e49fc2013-06-12 15:50:50 -040066import com.android.systemui.statusbar.StatusBarPanel;
Michael Jurka3b1fc472011-06-13 10:54:40 -070067import com.android.systemui.statusbar.phone.PhoneStatusBar;
Jim Miller44c66fe2010-10-20 18:32:52 -070068
Michael Jurkabfd24ac2011-11-13 13:50:38 -080069import java.util.ArrayList;
70
Michael Jurka99a96552012-01-27 17:23:38 -080071public class RecentsPanelView extends FrameLayout implements OnItemClickListener, RecentsCallback,
Michael Jurka80343f62012-10-18 13:13:46 +020072 StatusBarPanel, Animator.AnimatorListener {
Michael Jurka3cd0a592011-08-16 12:40:30 -070073 static final String TAG = "RecentsPanelView";
John Spurlockb0e49fc2013-06-12 15:50:50 -040074 static final boolean DEBUG = PhoneStatusBar.DEBUG || false;
Michael Jurkacb466432012-04-03 05:22:45 -070075 private PopupMenu mPopup;
Jim Miller85babff2011-01-11 14:26:03 -080076 private View mRecentsScrim;
Michael Jurka6d667082011-09-16 17:04:40 -070077 private View mRecentsNoApps;
Michael Jurka4a6bc552013-09-17 17:42:54 +020078 private RecentsScrollView mRecentsContainer;
Peter Ng622a9762011-08-29 10:56:53 -070079
Jim Miller85babff2011-01-11 14:26:03 -080080 private boolean mShowing;
Michael Jurka99a96552012-01-27 17:23:38 -080081 private boolean mWaitingToShow;
Michael Jurka9bdaada2012-10-01 13:58:29 +020082 private ViewHolder mItemToAnimateInWhenWindowAnimationIsFinished;
Michael Jurkad894c8c2012-11-20 16:46:09 -080083 private boolean mAnimateIconOfFirstTask;
Michael Jurka80343f62012-10-18 13:13:46 +020084 private boolean mWaitingForWindowAnimation;
Michael Jurkad894c8c2012-11-20 16:46:09 -080085 private long mWindowAnimationStartTime;
Michael Jurka0e7757b2013-03-04 16:18:51 +010086 private boolean mCallUiHiddenBeforeNextReload;
Michael Jurka21385cd2012-05-03 10:57:31 -070087
Michael Jurkaab48b682011-09-12 15:39:45 -070088 private RecentTasksLoader mRecentTasksLoader;
89 private ArrayList<TaskDescription> mRecentTaskDescriptions;
90 private TaskDescriptionAdapter mListAdapter;
91 private int mThumbnailWidth;
Michael Jurkabfd24ac2011-11-13 13:50:38 -080092 private boolean mFitThumbnailToXY;
Daniel Sandler4a066c52012-04-20 14:49:13 -040093 private int mRecentItemLayoutId;
Michael Jurkac016aaa2012-06-05 17:22:24 -070094 private boolean mHighEndGfx;
Dianne Hackborneeb1dca2011-09-08 13:30:11 -070095
Michael Jurka4eaa9832012-02-29 15:51:49 -080096 public static interface RecentsScrollView {
97 public int numItemsInOneScreenful();
98 public void setAdapter(TaskDescriptionAdapter adapter);
99 public void setCallback(RecentsCallback callback);
100 public void setMinSwipeAlpha(float minAlpha);
Michael Jurkae5923632012-10-03 15:29:36 +0200101 public View findViewForTask(int persistentTaskId);
Michael Jurka4a6bc552013-09-17 17:42:54 +0200102 public void drawFadedEdges(Canvas c, int left, int right, int top, int bottom);
103 public void setOnScrollListener(Runnable listener);
Michael Jurka4eaa9832012-02-29 15:51:49 -0800104 }
105
Jim Millerc0d27312011-07-14 18:54:01 -0700106 private final class OnLongClickDelegate implements View.OnLongClickListener {
107 View mOtherView;
108 OnLongClickDelegate(View other) {
109 mOtherView = other;
110 }
111 public boolean onLongClick(View v) {
112 return mOtherView.performLongClick();
113 }
114 }
115
Jim Millerb5a2b292011-03-01 16:14:59 -0800116 /* package */ final static class ViewHolder {
Jim Millerc0d27312011-07-14 18:54:01 -0700117 View thumbnailView;
Dianne Hackbornfc8fa632011-08-17 16:20:47 -0700118 ImageView thumbnailViewImage;
Michael Jurka99262722013-09-17 14:18:04 +0200119 Drawable thumbnailViewDrawable;
Jim Miller9f0f0e02011-05-17 20:06:29 -0700120 ImageView iconView;
121 TextView labelView;
122 TextView descriptionView;
Michael Jurka9bdaada2012-10-01 13:58:29 +0200123 View calloutLine;
Michael Jurkaab48b682011-09-12 15:39:45 -0700124 TaskDescription taskDescription;
Michael Jurka99a96552012-01-27 17:23:38 -0800125 boolean loadedThumbnailAndIcon;
Jim Miller5fda69c2011-02-16 15:17:11 -0800126 }
127
Michael Jurkaab48b682011-09-12 15:39:45 -0700128 /* package */ final class TaskDescriptionAdapter extends BaseAdapter {
Jim Miller5fda69c2011-02-16 15:17:11 -0800129 private LayoutInflater mInflater;
130
Michael Jurkaab48b682011-09-12 15:39:45 -0700131 public TaskDescriptionAdapter(Context context) {
Jim Miller5fda69c2011-02-16 15:17:11 -0800132 mInflater = LayoutInflater.from(context);
133 }
134
135 public int getCount() {
Michael Jurkaab48b682011-09-12 15:39:45 -0700136 return mRecentTaskDescriptions != null ? mRecentTaskDescriptions.size() : 0;
Jim Miller5fda69c2011-02-16 15:17:11 -0800137 }
138
139 public Object getItem(int position) {
140 return position; // we only need the index
141 }
142
143 public long getItemId(int position) {
144 return position; // we just need something unique for this position
145 }
146
Michael Jurka99a96552012-01-27 17:23:38 -0800147 public View createView(ViewGroup parent) {
Daniel Sandler4a066c52012-04-20 14:49:13 -0400148 View convertView = mInflater.inflate(mRecentItemLayoutId, parent, false);
Michael Jurka99a96552012-01-27 17:23:38 -0800149 ViewHolder holder = new ViewHolder();
150 holder.thumbnailView = convertView.findViewById(R.id.app_thumbnail);
151 holder.thumbnailViewImage =
152 (ImageView) convertView.findViewById(R.id.app_thumbnail_image);
153 // If we set the default thumbnail now, we avoid an onLayout when we update
154 // the thumbnail later (if they both have the same dimensions)
Michael Jurka80343f62012-10-18 13:13:46 +0200155 updateThumbnail(holder, mRecentTasksLoader.getDefaultThumbnail(), false, false);
Michael Jurka99a96552012-01-27 17:23:38 -0800156 holder.iconView = (ImageView) convertView.findViewById(R.id.app_icon);
Michael Jurka99262722013-09-17 14:18:04 +0200157 holder.iconView.setImageDrawable(mRecentTasksLoader.getDefaultIcon());
Michael Jurka99a96552012-01-27 17:23:38 -0800158 holder.labelView = (TextView) convertView.findViewById(R.id.app_label);
Michael Jurka9bdaada2012-10-01 13:58:29 +0200159 holder.calloutLine = convertView.findViewById(R.id.recents_callout_line);
Michael Jurka99a96552012-01-27 17:23:38 -0800160 holder.descriptionView = (TextView) convertView.findViewById(R.id.app_description);
161
162 convertView.setTag(holder);
163 return convertView;
164 }
165
166 public View getView(int position, View convertView, ViewGroup parent) {
167 if (convertView == null) {
168 convertView = createView(parent);
169 }
Michael Jurka12cdec92013-02-07 17:51:09 +0100170 final ViewHolder holder = (ViewHolder) convertView.getTag();
Jim Miller5fda69c2011-02-16 15:17:11 -0800171
Michael Jurkaab48b682011-09-12 15:39:45 -0700172 // index is reverse since most recent appears at the bottom...
173 final int index = mRecentTaskDescriptions.size() - position - 1;
Jim Miller5fda69c2011-02-16 15:17:11 -0800174
Michael Jurka412cba82011-10-17 09:05:00 -0700175 final TaskDescription td = mRecentTaskDescriptions.get(index);
Michael Jurka99a96552012-01-27 17:23:38 -0800176
Michael Jurka412cba82011-10-17 09:05:00 -0700177 holder.labelView.setText(td.getLabel());
178 holder.thumbnailView.setContentDescription(td.getLabel());
Michael Jurka99a96552012-01-27 17:23:38 -0800179 holder.loadedThumbnailAndIcon = td.isLoaded();
180 if (td.isLoaded()) {
181 updateThumbnail(holder, td.getThumbnail(), true, false);
182 updateIcon(holder, td.getIcon(), true, false);
Michael Jurka99a96552012-01-27 17:23:38 -0800183 }
Michael Jurka9bdaada2012-10-01 13:58:29 +0200184 if (index == 0) {
Michael Jurkad894c8c2012-11-20 16:46:09 -0800185 if (mAnimateIconOfFirstTask) {
Michael Jurka0e7757b2013-03-04 16:18:51 +0100186 ViewHolder oldHolder = mItemToAnimateInWhenWindowAnimationIsFinished;
187 if (oldHolder != null) {
188 oldHolder.iconView.setAlpha(1f);
189 oldHolder.iconView.setTranslationX(0f);
190 oldHolder.iconView.setTranslationY(0f);
191 oldHolder.labelView.setAlpha(1f);
192 oldHolder.labelView.setTranslationX(0f);
193 oldHolder.labelView.setTranslationY(0f);
194 if (oldHolder.calloutLine != null) {
195 oldHolder.calloutLine.setAlpha(1f);
196 oldHolder.calloutLine.setTranslationX(0f);
197 oldHolder.calloutLine.setTranslationY(0f);
Michael Jurkaa9240762012-10-17 13:02:55 +0200198 }
199 }
Michael Jurkae0523f72013-04-08 15:44:00 -0700200 mItemToAnimateInWhenWindowAnimationIsFinished = holder;
201 int translation = -getResources().getDimensionPixelSize(
202 R.dimen.status_bar_recents_app_icon_translate_distance);
203 final Configuration config = getResources().getConfiguration();
204 if (config.orientation == Configuration.ORIENTATION_PORTRAIT) {
205 if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
206 translation = -translation;
Michael Jurka12cdec92013-02-07 17:51:09 +0100207 }
Michael Jurkae0523f72013-04-08 15:44:00 -0700208 holder.iconView.setAlpha(0f);
209 holder.iconView.setTranslationX(translation);
210 holder.labelView.setAlpha(0f);
211 holder.labelView.setTranslationX(translation);
212 holder.calloutLine.setAlpha(0f);
213 holder.calloutLine.setTranslationX(translation);
214 } else {
215 holder.iconView.setAlpha(0f);
216 holder.iconView.setTranslationY(translation);
217 }
218 if (!mWaitingForWindowAnimation) {
219 animateInIconOfFirstTask();
220 }
Michael Jurka9bdaada2012-10-01 13:58:29 +0200221 }
222 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700223
Michael Jurka412cba82011-10-17 09:05:00 -0700224 holder.thumbnailView.setTag(td);
Jim Millerc0d27312011-07-14 18:54:01 -0700225 holder.thumbnailView.setOnLongClickListener(new OnLongClickDelegate(convertView));
Michael Jurka412cba82011-10-17 09:05:00 -0700226 holder.taskDescription = td;
Jim Miller5fda69c2011-02-16 15:17:11 -0800227 return convertView;
228 }
Michael Jurka99a96552012-01-27 17:23:38 -0800229
230 public void recycleView(View v) {
231 ViewHolder holder = (ViewHolder) v.getTag();
232 updateThumbnail(holder, mRecentTasksLoader.getDefaultThumbnail(), false, false);
Michael Jurka99262722013-09-17 14:18:04 +0200233 holder.iconView.setImageDrawable(mRecentTasksLoader.getDefaultIcon());
Michael Jurka99a96552012-01-27 17:23:38 -0800234 holder.iconView.setVisibility(INVISIBLE);
Michael Jurkad894c8c2012-11-20 16:46:09 -0800235 holder.iconView.animate().cancel();
Michael Jurka99a96552012-01-27 17:23:38 -0800236 holder.labelView.setText(null);
Michael Jurkad894c8c2012-11-20 16:46:09 -0800237 holder.labelView.animate().cancel();
Michael Jurka99a96552012-01-27 17:23:38 -0800238 holder.thumbnailView.setContentDescription(null);
239 holder.thumbnailView.setTag(null);
240 holder.thumbnailView.setOnLongClickListener(null);
241 holder.thumbnailView.setVisibility(INVISIBLE);
Michael Jurkafe09ad82012-10-22 13:58:38 -0700242 holder.iconView.setAlpha(1f);
243 holder.iconView.setTranslationX(0f);
244 holder.iconView.setTranslationY(0f);
245 holder.labelView.setAlpha(1f);
246 holder.labelView.setTranslationX(0f);
247 holder.labelView.setTranslationY(0f);
248 if (holder.calloutLine != null) {
249 holder.calloutLine.setAlpha(1f);
250 holder.calloutLine.setTranslationX(0f);
251 holder.calloutLine.setTranslationY(0f);
Michael Jurkad894c8c2012-11-20 16:46:09 -0800252 holder.calloutLine.animate().cancel();
Michael Jurkafe09ad82012-10-22 13:58:38 -0700253 }
Michael Jurka99a96552012-01-27 17:23:38 -0800254 holder.taskDescription = null;
255 holder.loadedThumbnailAndIcon = false;
256 }
257 }
258
Michael Jurka8b2e37e2012-05-08 20:06:13 -0700259 public RecentsPanelView(Context context, AttributeSet attrs) {
260 this(context, attrs, 0);
261 }
262
263 public RecentsPanelView(Context context, AttributeSet attrs, int defStyle) {
264 super(context, attrs, defStyle);
Michael Jurka8b2e37e2012-05-08 20:06:13 -0700265 updateValuesFromResources();
266
267 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecentsPanelView,
268 defStyle, 0);
269
270 mRecentItemLayoutId = a.getResourceId(R.styleable.RecentsPanelView_recentItemLayout, 0);
Michael Jurka80343f62012-10-18 13:13:46 +0200271 mRecentTasksLoader = RecentTasksLoader.getInstance(context);
Michael Jurka8b2e37e2012-05-08 20:06:13 -0700272 a.recycle();
273 }
274
Michael Jurka99a96552012-01-27 17:23:38 -0800275 public int numItemsInOneScreenful() {
Michael Jurka4a6bc552013-09-17 17:42:54 +0200276 return mRecentsContainer.numItemsInOneScreenful();
Jim Miller5fda69c2011-02-16 15:17:11 -0800277 }
278
Daniel Sandler328310c2011-09-23 15:56:52 -0400279 private boolean pointInside(int x, int y, View v) {
280 final int l = v.getLeft();
281 final int r = v.getRight();
282 final int t = v.getTop();
283 final int b = v.getBottom();
Jim Miller44c66fe2010-10-20 18:32:52 -0700284 return x >= l && x < r && y >= t && y < b;
285 }
286
Daniel Sandler328310c2011-09-23 15:56:52 -0400287 public boolean isInContentArea(int x, int y) {
Michael Jurka4a6bc552013-09-17 17:42:54 +0200288 return pointInside(x, y, (View) mRecentsContainer);
Daniel Sandler328310c2011-09-23 15:56:52 -0400289 }
290
Michael Jurkacb2522c2012-04-13 09:32:47 -0700291 public void show(boolean show) {
Michael Jurka80343f62012-10-18 13:13:46 +0200292 show(show, null, false, false);
Michael Jurkacb2522c2012-04-13 09:32:47 -0700293 }
294
295 public void show(boolean show, ArrayList<TaskDescription> recentTaskDescriptions,
Michael Jurkad894c8c2012-11-20 16:46:09 -0800296 boolean firstScreenful, boolean animateIconOfFirstTask) {
Michael Jurka0e7757b2013-03-04 16:18:51 +0100297 if (show && mCallUiHiddenBeforeNextReload) {
298 onUiHidden();
299 recentTaskDescriptions = null;
300 mAnimateIconOfFirstTask = false;
301 mWaitingForWindowAnimation = false;
302 } else {
303 mAnimateIconOfFirstTask = animateIconOfFirstTask;
304 mWaitingForWindowAnimation = animateIconOfFirstTask;
305 }
Michael Jurka99a96552012-01-27 17:23:38 -0800306 if (show) {
307 mWaitingToShow = true;
Michael Jurkacb2522c2012-04-13 09:32:47 -0700308 refreshRecentTasksList(recentTaskDescriptions, firstScreenful);
Michael Jurka99a96552012-01-27 17:23:38 -0800309 showIfReady();
310 } else {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700311 showImpl(false);
Michael Jurka99a96552012-01-27 17:23:38 -0800312 }
313 }
314
315 private void showIfReady() {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700316 // mWaitingToShow => there was a touch up on the recents button
317 // mRecentTaskDescriptions != null => we've created views for the first screenful of items
318 if (mWaitingToShow && mRecentTaskDescriptions != null) {
319 showImpl(true);
Michael Jurka99a96552012-01-27 17:23:38 -0800320 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700321 }
322
Michael Jurka56a57832012-05-14 13:24:43 -0700323 static void sendCloseSystemWindows(Context context, String reason) {
324 if (ActivityManagerNative.isSystemReady()) {
325 try {
326 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
327 } catch (RemoteException e) {
328 }
329 }
330 }
331
Michael Jurkacb2522c2012-04-13 09:32:47 -0700332 private void showImpl(boolean show) {
John Spurlock01534782014-01-13 11:59:22 -0500333 sendCloseSystemWindows(getContext(), BaseStatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS);
Michael Jurka56a57832012-05-14 13:24:43 -0700334
Michael Jurkacb2522c2012-04-13 09:32:47 -0700335 mShowing = show;
336
Michael Jurka7daf95d2011-09-30 11:07:30 -0700337 if (show) {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700338 // if there are no apps, bring up a "No recent apps" message
339 boolean noApps = mRecentTaskDescriptions != null
340 && (mRecentTaskDescriptions.size() == 0);
341 mRecentsNoApps.setAlpha(1f);
342 mRecentsNoApps.setVisibility(noApps ? View.VISIBLE : View.INVISIBLE);
Michael Jurka7daf95d2011-09-30 11:07:30 -0700343
Michael Jurkaab48b682011-09-12 15:39:45 -0700344 onAnimationEnd(null);
Michael Jurkae9166ed2011-07-27 18:23:18 -0700345 setFocusable(true);
346 setFocusableInTouchMode(true);
347 requestFocus();
Michael Jurkacb466432012-04-03 05:22:45 -0700348 } else {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700349 mWaitingToShow = false;
350 // call onAnimationEnd() and clearRecentTasksList() in onUiHidden()
Michael Jurka0e7757b2013-03-04 16:18:51 +0100351 mCallUiHiddenBeforeNextReload = true;
Michael Jurkacb466432012-04-03 05:22:45 -0700352 if (mPopup != null) {
353 mPopup.dismiss();
354 }
Michael Jurkae9166ed2011-07-27 18:23:18 -0700355 }
Jim Miller85babff2011-01-11 14:26:03 -0800356 }
357
Michael Jurkaf42d90102013-05-08 18:00:04 +0200358 protected void onAttachedToWindow () {
359 super.onAttachedToWindow();
360 final ViewRootImpl root = getViewRootImpl();
361 if (root != null) {
362 root.setDrawDuringWindowsAnimating(true);
363 }
364 }
365
Michael Jurkacb2522c2012-04-13 09:32:47 -0700366 public void onUiHidden() {
Michael Jurka0e7757b2013-03-04 16:18:51 +0100367 mCallUiHiddenBeforeNextReload = false;
Michael Jurkacb2522c2012-04-13 09:32:47 -0700368 if (!mShowing && mRecentTaskDescriptions != null) {
369 onAnimationEnd(null);
370 clearRecentTasksList();
371 }
Michael Jurkac6461ca2011-09-02 12:12:15 -0700372 }
373
Michael Jurkacb2522c2012-04-13 09:32:47 -0700374 public void dismiss() {
John Spurlock01534782014-01-13 11:59:22 -0500375 ((RecentsActivity) getContext()).dismissAndGoHome();
Michael Jurkacb2522c2012-04-13 09:32:47 -0700376 }
377
378 public void dismissAndGoBack() {
John Spurlock01534782014-01-13 11:59:22 -0500379 ((RecentsActivity) getContext()).dismissAndGoBack();
Michael Jurka8b098662011-09-01 17:00:53 -0700380 }
381
Michael Jurka3b1fc472011-06-13 10:54:40 -0700382 public void onAnimationCancel(Animator animation) {
383 }
384
385 public void onAnimationEnd(Animator animation) {
386 if (mShowing) {
387 final LayoutTransition transitioner = new LayoutTransition();
388 ((ViewGroup)mRecentsContainer).setLayoutTransition(transitioner);
389 createCustomAnimations(transitioner);
390 } else {
391 ((ViewGroup)mRecentsContainer).setLayoutTransition(null);
392 }
393 }
394
395 public void onAnimationRepeat(Animator animation) {
396 }
397
398 public void onAnimationStart(Animator animation) {
399 }
400
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700401 @Override
402 public boolean dispatchHoverEvent(MotionEvent event) {
403 // Ignore hover events outside of this panel bounds since such events
404 // generate spurious accessibility events with the panel content when
405 // tapping outside of it, thus confusing the user.
406 final int x = (int) event.getX();
407 final int y = (int) event.getY();
408 if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) {
409 return super.dispatchHoverEvent(event);
410 }
411 return true;
412 }
413
Jim Miller85babff2011-01-11 14:26:03 -0800414 /**
415 * Whether the panel is showing, or, if it's animating, whether it will be
416 * when the animation is done.
417 */
418 public boolean isShowing() {
419 return mShowing;
420 }
421
Daniel Sandler328310c2011-09-23 15:56:52 -0400422 public void setRecentTasksLoader(RecentTasksLoader loader) {
423 mRecentTasksLoader = loader;
424 }
425
Michael Jurkaab48b682011-09-12 15:39:45 -0700426 public void updateValuesFromResources() {
John Spurlock01534782014-01-13 11:59:22 -0500427 final Resources res = getContext().getResources();
Michael Jurkabfd24ac2011-11-13 13:50:38 -0800428 mThumbnailWidth = Math.round(res.getDimension(R.dimen.status_bar_recents_thumbnail_width));
429 mFitThumbnailToXY = res.getBoolean(R.bool.config_recents_thumbnail_image_fits_to_xy);
Jim Miller44c66fe2010-10-20 18:32:52 -0700430 }
431
432 @Override
433 protected void onFinishInflate() {
434 super.onFinishInflate();
Daniel Sandler4a066c52012-04-20 14:49:13 -0400435
Michael Jurka4a6bc552013-09-17 17:42:54 +0200436 mRecentsContainer = (RecentsScrollView) findViewById(R.id.recents_container);
437 mRecentsContainer.setOnScrollListener(new Runnable() {
438 public void run() {
439 // need to redraw the faded edges
440 invalidate();
441 }
442 });
John Spurlock01534782014-01-13 11:59:22 -0500443 mListAdapter = new TaskDescriptionAdapter(getContext());
Michael Jurka4a6bc552013-09-17 17:42:54 +0200444 mRecentsContainer.setAdapter(mListAdapter);
445 mRecentsContainer.setCallback(this);
Jim Miller5fda69c2011-02-16 15:17:11 -0800446
Michael Jurka6d667082011-09-16 17:04:40 -0700447 mRecentsScrim = findViewById(R.id.recents_bg_protect);
448 mRecentsNoApps = findViewById(R.id.recents_no_apps);
Jim Millere5c16ab2010-12-03 19:18:38 -0800449
Michael Jurkaf16fc512012-02-27 15:02:23 -0800450 if (mRecentsScrim != null) {
Jeff Brown98365d72012-08-19 20:30:52 -0700451 mHighEndGfx = ActivityManager.isHighEndGfx();
Michael Jurkac016aaa2012-06-05 17:22:24 -0700452 if (!mHighEndGfx) {
Michael Jurkad5895a72012-05-12 13:24:58 -0700453 mRecentsScrim.setBackground(null);
Michael Jurkaf16fc512012-02-27 15:02:23 -0800454 } else if (mRecentsScrim.getBackground() instanceof BitmapDrawable) {
455 // In order to save space, we make the background texture repeat in the Y direction
456 ((BitmapDrawable) mRecentsScrim.getBackground()).setTileModeY(TileMode.REPEAT);
457 }
Jim Millere5c16ab2010-12-03 19:18:38 -0800458 }
459 }
460
Michael Jurka4eaa9832012-02-29 15:51:49 -0800461 public void setMinSwipeAlpha(float minAlpha) {
Michael Jurka4a6bc552013-09-17 17:42:54 +0200462 mRecentsContainer.setMinSwipeAlpha(minAlpha);
Michael Jurka4eaa9832012-02-29 15:51:49 -0800463 }
464
Jim Miller9f0f0e02011-05-17 20:06:29 -0700465 private void createCustomAnimations(LayoutTransition transitioner) {
Chet Haase212f6252011-08-12 09:19:10 -0700466 transitioner.setDuration(200);
467 transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
468 transitioner.setAnimator(LayoutTransition.DISAPPEARING, null);
Jim Miller9f0f0e02011-05-17 20:06:29 -0700469 }
470
Michael Jurka99a96552012-01-27 17:23:38 -0800471 private void updateIcon(ViewHolder h, Drawable icon, boolean show, boolean anim) {
472 if (icon != null) {
473 h.iconView.setImageDrawable(icon);
474 if (show && h.iconView.getVisibility() != View.VISIBLE) {
475 if (anim) {
476 h.iconView.setAnimation(
John Spurlock01534782014-01-13 11:59:22 -0500477 AnimationUtils.loadAnimation(getContext(), R.anim.recent_appear));
Michael Jurka99a96552012-01-27 17:23:38 -0800478 }
479 h.iconView.setVisibility(View.VISIBLE);
480 }
481 }
482 }
483
Michael Jurka99262722013-09-17 14:18:04 +0200484 private void updateThumbnail(ViewHolder h, Drawable thumbnail, boolean show, boolean anim) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700485 if (thumbnail != null) {
486 // Should remove the default image in the frame
487 // that this now covers, to improve scrolling speed.
488 // That can't be done until the anim is complete though.
Michael Jurka99262722013-09-17 14:18:04 +0200489 h.thumbnailViewImage.setImageDrawable(thumbnail);
Michael Jurka412cba82011-10-17 09:05:00 -0700490
491 // scale the image to fill the full width of the ImageView. do this only if
492 // we haven't set a bitmap before, or if the bitmap size has changed
Michael Jurka99262722013-09-17 14:18:04 +0200493 if (h.thumbnailViewDrawable == null ||
494 h.thumbnailViewDrawable.getIntrinsicWidth() != thumbnail.getIntrinsicWidth() ||
495 h.thumbnailViewDrawable.getIntrinsicHeight() != thumbnail.getIntrinsicHeight()) {
Michael Jurkabfd24ac2011-11-13 13:50:38 -0800496 if (mFitThumbnailToXY) {
497 h.thumbnailViewImage.setScaleType(ScaleType.FIT_XY);
498 } else {
499 Matrix scaleMatrix = new Matrix();
Michael Jurka99262722013-09-17 14:18:04 +0200500 float scale = mThumbnailWidth / (float) thumbnail.getIntrinsicWidth();
Michael Jurkabfd24ac2011-11-13 13:50:38 -0800501 scaleMatrix.setScale(scale, scale);
502 h.thumbnailViewImage.setScaleType(ScaleType.MATRIX);
503 h.thumbnailViewImage.setImageMatrix(scaleMatrix);
504 }
Jim Miller44c66fe2010-10-20 18:32:52 -0700505 }
Michael Jurka412cba82011-10-17 09:05:00 -0700506 if (show && h.thumbnailView.getVisibility() != View.VISIBLE) {
507 if (anim) {
508 h.thumbnailView.setAnimation(
John Spurlock01534782014-01-13 11:59:22 -0500509 AnimationUtils.loadAnimation(getContext(), R.anim.recent_appear));
Michael Jurka412cba82011-10-17 09:05:00 -0700510 }
511 h.thumbnailView.setVisibility(View.VISIBLE);
512 }
Michael Jurka99262722013-09-17 14:18:04 +0200513 h.thumbnailViewDrawable = thumbnail;
Jim Miller44c66fe2010-10-20 18:32:52 -0700514 }
Jim Millerfefef312010-11-09 14:08:39 -0800515 }
516
Michael Jurka99a96552012-01-27 17:23:38 -0800517 void onTaskThumbnailLoaded(TaskDescription td) {
518 synchronized (td) {
Dianne Hackbornfc8fa632011-08-17 16:20:47 -0700519 if (mRecentsContainer != null) {
Michael Jurka4a6bc552013-09-17 17:42:54 +0200520 ViewGroup container = (ViewGroup) mRecentsContainer;
Michael Jurka4eaa9832012-02-29 15:51:49 -0800521 if (container instanceof RecentsScrollView) {
Michael Jurka99a96552012-01-27 17:23:38 -0800522 container = (ViewGroup) container.findViewById(
Dianne Hackbornfc8fa632011-08-17 16:20:47 -0700523 R.id.recents_linear_layout);
524 }
525 // Look for a view showing this thumbnail, to update.
Michael Jurka99a96552012-01-27 17:23:38 -0800526 for (int i=0; i < container.getChildCount(); i++) {
Dianne Hackbornfc8fa632011-08-17 16:20:47 -0700527 View v = container.getChildAt(i);
528 if (v.getTag() instanceof ViewHolder) {
529 ViewHolder h = (ViewHolder)v.getTag();
Michael Jurka99a96552012-01-27 17:23:38 -0800530 if (!h.loadedThumbnailAndIcon && h.taskDescription == td) {
Michael Jurka412cba82011-10-17 09:05:00 -0700531 // only fade in the thumbnail if recents is already visible-- we
532 // show it immediately otherwise
Michael Jurka99a96552012-01-27 17:23:38 -0800533 //boolean animateShow = mShowing &&
534 // mRecentsContainer.getAlpha() > ViewConfiguration.ALPHA_THRESHOLD;
535 boolean animateShow = false;
536 updateIcon(h, td.getIcon(), true, animateShow);
537 updateThumbnail(h, td.getThumbnail(), true, animateShow);
538 h.loadedThumbnailAndIcon = true;
Dianne Hackbornfc8fa632011-08-17 16:20:47 -0700539 }
540 }
541 }
542 }
Craig Mautnerca6da002012-09-20 14:54:37 -0700543 }
Michael Jurka99a96552012-01-27 17:23:38 -0800544 showIfReady();
Dianne Hackbornfc8fa632011-08-17 16:20:47 -0700545 }
546
Michael Jurkad894c8c2012-11-20 16:46:09 -0800547 private void animateInIconOfFirstTask() {
548 if (mItemToAnimateInWhenWindowAnimationIsFinished != null &&
549 !mRecentTasksLoader.isFirstScreenful()) {
550 int timeSinceWindowAnimation =
551 (int) (System.currentTimeMillis() - mWindowAnimationStartTime);
Michael Jurkaf42d90102013-05-08 18:00:04 +0200552 final int minStartDelay = 150;
Michael Jurkad894c8c2012-11-20 16:46:09 -0800553 final int startDelay = Math.max(0, Math.min(
554 minStartDelay - timeSinceWindowAnimation, minStartDelay));
Michael Jurka9bdaada2012-10-01 13:58:29 +0200555 final int duration = 250;
556 final ViewHolder holder = mItemToAnimateInWhenWindowAnimationIsFinished;
557 final TimeInterpolator cubic = new DecelerateInterpolator(1.5f);
Michael Jurkae0523f72013-04-08 15:44:00 -0700558 FirstFrameAnimatorHelper.initializeDrawListener(holder.iconView);
Michael Jurka9bdaada2012-10-01 13:58:29 +0200559 for (View v :
560 new View[] { holder.iconView, holder.labelView, holder.calloutLine }) {
561 if (v != null) {
Michael Jurkae0523f72013-04-08 15:44:00 -0700562 ViewPropertyAnimator vpa = v.animate().translationX(0).translationY(0)
563 .alpha(1f).setStartDelay(startDelay)
Michael Jurka9bdaada2012-10-01 13:58:29 +0200564 .setDuration(duration).setInterpolator(cubic);
Michael Jurkae0523f72013-04-08 15:44:00 -0700565 FirstFrameAnimatorHelper h = new FirstFrameAnimatorHelper(vpa, v);
Michael Jurka9bdaada2012-10-01 13:58:29 +0200566 }
567 }
568 mItemToAnimateInWhenWindowAnimationIsFinished = null;
Michael Jurkad894c8c2012-11-20 16:46:09 -0800569 mAnimateIconOfFirstTask = false;
Michael Jurka9bdaada2012-10-01 13:58:29 +0200570 }
571 }
572
Michael Jurkad894c8c2012-11-20 16:46:09 -0800573 public void onWindowAnimationStart() {
574 mWaitingForWindowAnimation = false;
575 mWindowAnimationStartTime = System.currentTimeMillis();
576 animateInIconOfFirstTask();
577 }
578
Michael Jurka412cba82011-10-17 09:05:00 -0700579 public void clearRecentTasksList() {
580 // Clear memory used by screenshots
Michael Jurkacb2522c2012-04-13 09:32:47 -0700581 if (mRecentTaskDescriptions != null) {
582 mRecentTasksLoader.cancelLoadingThumbnailsAndIcons(this);
583 onTaskLoadingCancelled();
584 }
585 }
586
587 public void onTaskLoadingCancelled() {
588 // Gets called by RecentTasksLoader when it's cancelled
589 if (mRecentTaskDescriptions != null) {
590 mRecentTaskDescriptions = null;
Michael Jurka412cba82011-10-17 09:05:00 -0700591 mListAdapter.notifyDataSetInvalidated();
Michael Jurka412cba82011-10-17 09:05:00 -0700592 }
593 }
594
Craig Mautnerca6da002012-09-20 14:54:37 -0700595 public void refreshViews() {
596 mListAdapter.notifyDataSetInvalidated();
597 updateUiElements();
598 showIfReady();
599 }
600
Michael Jurka412cba82011-10-17 09:05:00 -0700601 public void refreshRecentTasksList() {
Michael Jurka99a96552012-01-27 17:23:38 -0800602 refreshRecentTasksList(null, false);
Michael Jurka412cba82011-10-17 09:05:00 -0700603 }
604
Michael Jurka99a96552012-01-27 17:23:38 -0800605 private void refreshRecentTasksList(
606 ArrayList<TaskDescription> recentTasksList, boolean firstScreenful) {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700607 if (mRecentTaskDescriptions == null && recentTasksList != null) {
608 onTasksLoaded(recentTasksList, firstScreenful);
609 } else {
610 mRecentTasksLoader.loadTasksInBackground();
Michael Jurka412cba82011-10-17 09:05:00 -0700611 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700612 }
Michael Jurka7daf95d2011-09-30 11:07:30 -0700613
Michael Jurkacb2522c2012-04-13 09:32:47 -0700614 public void onTasksLoaded(ArrayList<TaskDescription> tasks, boolean firstScreenful) {
Michael Jurka99a96552012-01-27 17:23:38 -0800615 if (mRecentTaskDescriptions == null) {
Craig Mautnerbb1449b2012-03-23 16:11:14 -0700616 mRecentTaskDescriptions = new ArrayList<TaskDescription>(tasks);
Michael Jurka99a96552012-01-27 17:23:38 -0800617 } else {
618 mRecentTaskDescriptions.addAll(tasks);
619 }
John Spurlock01534782014-01-13 11:59:22 -0500620 if (((RecentsActivity) getContext()).isActivityShowing()) {
Craig Mautnerca6da002012-09-20 14:54:37 -0700621 refreshViews();
622 }
Michael Jurka99a96552012-01-27 17:23:38 -0800623 }
624
Craig Mautnerca6da002012-09-20 14:54:37 -0700625 private void updateUiElements() {
Dianne Hackborn8da429e2012-09-23 12:52:19 -0700626 final int items = mRecentTaskDescriptions != null
627 ? mRecentTaskDescriptions.size() : 0;
Michael Jurkaab48b682011-09-12 15:39:45 -0700628
Michael Jurka4a6bc552013-09-17 17:42:54 +0200629 ((View) mRecentsContainer).setVisibility(items > 0 ? View.VISIBLE : View.GONE);
Michael Jurkaab48b682011-09-12 15:39:45 -0700630
631 // Set description for accessibility
Dianne Hackborn8da429e2012-09-23 12:52:19 -0700632 int numRecentApps = mRecentTaskDescriptions != null
633 ? mRecentTaskDescriptions.size() : 0;
Michael Jurka7daf95d2011-09-30 11:07:30 -0700634 String recentAppsAccessibilityDescription;
635 if (numRecentApps == 0) {
636 recentAppsAccessibilityDescription =
637 getResources().getString(R.string.status_bar_no_recent_apps);
638 } else {
639 recentAppsAccessibilityDescription = getResources().getQuantityString(
640 R.plurals.status_bar_accessibility_recent_apps, numRecentApps, numRecentApps);
641 }
642 setContentDescription(recentAppsAccessibilityDescription);
Jim Miller44c66fe2010-10-20 18:32:52 -0700643 }
644
Michael Jurkae5923632012-10-03 15:29:36 +0200645 public boolean simulateClick(int persistentTaskId) {
Michael Jurka4a6bc552013-09-17 17:42:54 +0200646 View v = mRecentsContainer.findViewForTask(persistentTaskId);
647 if (v != null) {
648 handleOnClick(v);
649 return true;
Michael Jurkacb2522c2012-04-13 09:32:47 -0700650 }
651 return false;
652 }
Michael Jurkac016aaa2012-06-05 17:22:24 -0700653
Jim Miller9f0f0e02011-05-17 20:06:29 -0700654 public void handleOnClick(View view) {
Michael Jurka99262722013-09-17 14:18:04 +0200655 ViewHolder holder = (ViewHolder) view.getTag();
Dianne Hackborn8078d8c2012-03-20 11:11:26 -0700656 TaskDescription ad = holder.taskDescription;
Jim Miller9f0f0e02011-05-17 20:06:29 -0700657 final Context context = view.getContext();
Jim Millere5c16ab2010-12-03 19:18:38 -0800658 final ActivityManager am = (ActivityManager)
Jim Miller9f0f0e02011-05-17 20:06:29 -0700659 context.getSystemService(Context.ACTIVITY_SERVICE);
Michael Jurka99262722013-09-17 14:18:04 +0200660
661 Bitmap bm = null;
662 boolean usingDrawingCache = true;
663 if (holder.thumbnailViewDrawable instanceof BitmapDrawable) {
664 bm = ((BitmapDrawable) holder.thumbnailViewDrawable).getBitmap();
665 if (bm.getWidth() == holder.thumbnailViewImage.getWidth() &&
666 bm.getHeight() == holder.thumbnailViewImage.getHeight()) {
667 usingDrawingCache = false;
668 }
669 }
670 if (usingDrawingCache) {
Michael Jurkac016aaa2012-06-05 17:22:24 -0700671 holder.thumbnailViewImage.setDrawingCacheEnabled(true);
672 bm = holder.thumbnailViewImage.getDrawingCache();
Michael Jurkac016aaa2012-06-05 17:22:24 -0700673 }
Michael Jurka482f1592012-09-13 11:05:59 -0700674 Bundle opts = (bm == null) ?
675 null :
676 ActivityOptions.makeThumbnailScaleUpAnimation(
677 holder.thumbnailViewImage, bm, 0, 0, null).toBundle();
Michael Jurkacb2522c2012-04-13 09:32:47 -0700678
679 show(false);
Jim Miller9f0f0e02011-05-17 20:06:29 -0700680 if (ad.taskId >= 0) {
Dianne Hackbornb6777462010-11-12 12:43:22 -0800681 // This is an active task; it should just go to the foreground.
Dianne Hackborn8078d8c2012-03-20 11:11:26 -0700682 am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HOME,
Michael Jurka482f1592012-09-13 11:05:59 -0700683 opts);
Dianne Hackbornb6777462010-11-12 12:43:22 -0800684 } else {
685 Intent intent = ad.intent;
Dianne Hackborn621e17d2010-11-22 15:59:56 -0800686 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
Dianne Hackborn38cc8962011-10-13 11:33:55 -0700687 | Intent.FLAG_ACTIVITY_TASK_ON_HOME
688 | Intent.FLAG_ACTIVITY_NEW_TASK);
Dianne Hackbornb6777462010-11-12 12:43:22 -0800689 if (DEBUG) Log.v(TAG, "Starting activity " + intent);
Michael Jurkaa2bdffe2013-02-08 15:41:56 +0100690 try {
691 context.startActivityAsUser(intent, opts,
692 new UserHandle(UserHandle.USER_CURRENT));
693 } catch (SecurityException e) {
694 Log.e(TAG, "Recents does not have the permission to launch " + intent, e);
Michael Jurka4e85f462013-09-16 19:52:21 +0200695 } catch (ActivityNotFoundException e) {
696 Log.e(TAG, "Error launching activity " + intent, e);
Michael Jurkaa2bdffe2013-02-08 15:41:56 +0100697 }
Dianne Hackbornb6777462010-11-12 12:43:22 -0800698 }
Michael Jurkab9a38c52012-06-14 11:57:50 -0700699 if (usingDrawingCache) {
Michael Jurkac016aaa2012-06-05 17:22:24 -0700700 holder.thumbnailViewImage.setDrawingCacheEnabled(false);
701 }
Jim Millerf1e22e52011-01-26 15:39:51 -0800702 }
Jim Miller9f0f0e02011-05-17 20:06:29 -0700703
704 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
705 handleOnClick(view);
706 }
707
Michael Jurka07d40462011-07-19 10:54:38 -0700708 public void handleSwipe(View view) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700709 TaskDescription ad = ((ViewHolder) view.getTag()).taskDescription;
Michael Jurkaa8eac1d2012-04-30 15:54:19 -0700710 if (ad == null) {
Michael Jurka9d69b442012-05-04 15:46:32 -0700711 Log.v(TAG, "Not able to find activity description for swiped task; view=" + view +
712 " tag=" + view.getTag());
713 return;
Michael Jurkaa8eac1d2012-04-30 15:54:19 -0700714 }
Dianne Hackbornfc8fa632011-08-17 16:20:47 -0700715 if (DEBUG) Log.v(TAG, "Jettison " + ad.getLabel());
Michael Jurkaab48b682011-09-12 15:39:45 -0700716 mRecentTaskDescriptions.remove(ad);
Michael Jurkae57922c2012-11-26 16:05:12 -0800717 mRecentTasksLoader.remove(ad);
Jim Miller9f0f0e02011-05-17 20:06:29 -0700718
719 // Handled by widget containers to enable LayoutTransitions properly
720 // mListAdapter.notifyDataSetChanged();
721
Michael Jurkaab48b682011-09-12 15:39:45 -0700722 if (mRecentTaskDescriptions.size() == 0) {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700723 dismissAndGoBack();
Jim Miller9f0f0e02011-05-17 20:06:29 -0700724 }
725
726 // Currently, either direction means the same thing, so ignore direction and remove
727 // the task.
728 final ActivityManager am = (ActivityManager)
John Spurlock01534782014-01-13 11:59:22 -0500729 getContext().getSystemService(Context.ACTIVITY_SERVICE);
Michael Jurkaa8eac1d2012-04-30 15:54:19 -0700730 if (am != null) {
731 am.removeTask(ad.persistentTaskId, ActivityManager.REMOVE_TASK_KILL_PROCESS);
Jim Miller17377762011-10-11 15:33:34 -0700732
Michael Jurkaa8eac1d2012-04-30 15:54:19 -0700733 // Accessibility feedback
734 setContentDescription(
John Spurlock01534782014-01-13 11:59:22 -0500735 getContext().getString(R.string.accessibility_recents_item_dismissed, ad.getLabel()));
Michael Jurkaa8eac1d2012-04-30 15:54:19 -0700736 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
737 setContentDescription(null);
738 }
Jim Miller9f0f0e02011-05-17 20:06:29 -0700739 }
740
Jim Millerc0d27312011-07-14 18:54:01 -0700741 private void startApplicationDetailsActivity(String packageName) {
742 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
743 Uri.fromParts("package", packageName, null));
John Spurlock01534782014-01-13 11:59:22 -0500744 intent.setComponent(intent.resolveActivity(getContext().getPackageManager()));
Adam Powell0fc5b2b2012-07-18 18:20:29 -0700745 TaskStackBuilder.create(getContext())
746 .addNextIntentWithParentStack(intent).startActivities();
Jim Millerc0d27312011-07-14 18:54:01 -0700747 }
748
Michael Jurkacb466432012-04-03 05:22:45 -0700749 public boolean onInterceptTouchEvent(MotionEvent ev) {
750 if (mPopup != null) {
751 return true;
752 } else {
753 return super.onInterceptTouchEvent(ev);
754 }
755 }
756
Michael Jurkafaa790c2011-09-02 17:35:02 -0700757 public void handleLongPress(
758 final View selectedView, final View anchorView, final View thumbnailView) {
759 thumbnailView.setSelected(true);
Michael Jurkacb466432012-04-03 05:22:45 -0700760 final PopupMenu popup =
John Spurlock01534782014-01-13 11:59:22 -0500761 new PopupMenu(getContext(), anchorView == null ? selectedView : anchorView);
Michael Jurkacb466432012-04-03 05:22:45 -0700762 mPopup = popup;
Jim Millerc0d27312011-07-14 18:54:01 -0700763 popup.getMenuInflater().inflate(R.menu.recent_popup_menu, popup.getMenu());
764 popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
765 public boolean onMenuItemClick(MenuItem item) {
766 if (item.getItemId() == R.id.recent_remove_item) {
Michael Jurka4a6bc552013-09-17 17:42:54 +0200767 ((ViewGroup) mRecentsContainer).removeViewInLayout(selectedView);
Jim Millerc0d27312011-07-14 18:54:01 -0700768 } else if (item.getItemId() == R.id.recent_inspect_item) {
769 ViewHolder viewHolder = (ViewHolder) selectedView.getTag();
770 if (viewHolder != null) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700771 final TaskDescription ad = viewHolder.taskDescription;
Jim Millerc0d27312011-07-14 18:54:01 -0700772 startApplicationDetailsActivity(ad.packageName);
Michael Jurkacb2522c2012-04-13 09:32:47 -0700773 show(false);
Jim Millerc0d27312011-07-14 18:54:01 -0700774 } else {
775 throw new IllegalStateException("Oops, no tag on view " + selectedView);
776 }
777 } else {
778 return false;
779 }
780 return true;
781 }
782 });
Michael Jurkafaa790c2011-09-02 17:35:02 -0700783 popup.setOnDismissListener(new PopupMenu.OnDismissListener() {
784 public void onDismiss(PopupMenu menu) {
785 thumbnailView.setSelected(false);
Michael Jurkacb466432012-04-03 05:22:45 -0700786 mPopup = null;
Michael Jurkafaa790c2011-09-02 17:35:02 -0700787 }
788 });
Jim Millerc0d27312011-07-14 18:54:01 -0700789 popup.show();
Jim Miller9f0f0e02011-05-17 20:06:29 -0700790 }
Michael Jurka4a6bc552013-09-17 17:42:54 +0200791
792 @Override
793 protected void dispatchDraw(Canvas canvas) {
794 super.dispatchDraw(canvas);
795
John Spurlock01534782014-01-13 11:59:22 -0500796 int paddingLeft = getPaddingLeft();
Michael Jurka4a6bc552013-09-17 17:42:54 +0200797 final boolean offsetRequired = isPaddingOffsetRequired();
798 if (offsetRequired) {
799 paddingLeft += getLeftPaddingOffset();
800 }
801
John Spurlock01534782014-01-13 11:59:22 -0500802 int left = getScrollX() + paddingLeft;
803 int right = left + getRight() - getLeft() - getPaddingRight() - paddingLeft;
804 int top = getScrollY() + getFadeTop(offsetRequired);
Michael Jurka4a6bc552013-09-17 17:42:54 +0200805 int bottom = top + getFadeHeight(offsetRequired);
806
807 if (offsetRequired) {
808 right += getRightPaddingOffset();
809 bottom += getBottomPaddingOffset();
810 }
811 mRecentsContainer.drawFadedEdges(canvas, left, right, top, bottom);
812 }
Jim Miller44c66fe2010-10-20 18:32:52 -0700813}