blob: 0caa6718bab01bc2fc27c525f62a4ef9c268c706 [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;
Jim Miller44c66fe2010-10-20 18:32:52 -070021import android.app.ActivityManager;
Michael Jurka56a57832012-05-14 13:24:43 -070022import android.app.ActivityManagerNative;
Dianne Hackborn8078d8c2012-03-20 11:11:26 -070023import android.app.ActivityOptions;
Adam Powell0fc5b2b2012-07-18 18:20:29 -070024import android.app.TaskStackBuilder;
Jim Miller44c66fe2010-10-20 18:32:52 -070025import android.content.Context;
26import android.content.Intent;
Michael Jurkabfd24ac2011-11-13 13:50:38 -080027import android.content.res.Resources;
Daniel Sandler4a066c52012-04-20 14:49:13 -040028import android.content.res.TypedArray;
Jim Miller44c66fe2010-10-20 18:32:52 -070029import android.graphics.Bitmap;
Jim Millerfefef312010-11-09 14:08:39 -080030import android.graphics.Matrix;
Jim Millere5c16ab2010-12-03 19:18:38 -080031import android.graphics.Shader.TileMode;
32import android.graphics.drawable.BitmapDrawable;
Michael Jurka99a96552012-01-27 17:23:38 -080033import android.graphics.drawable.Drawable;
Jim Millerc0d27312011-07-14 18:54:01 -070034import android.net.Uri;
Michael Jurka482f1592012-09-13 11:05:59 -070035import android.os.Bundle;
Michael Jurka56a57832012-05-14 13:24:43 -070036import android.os.RemoteException;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070037import android.os.UserHandle;
Jim Millerc0d27312011-07-14 18:54:01 -070038import android.provider.Settings;
Jim Miller44c66fe2010-10-20 18:32:52 -070039import android.util.AttributeSet;
40import android.util.Log;
Jim Miller5fda69c2011-02-16 15:17:11 -080041import android.view.LayoutInflater;
Jim Millerc0d27312011-07-14 18:54:01 -070042import android.view.MenuItem;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070043import android.view.MotionEvent;
Jim Miller44c66fe2010-10-20 18:32:52 -070044import android.view.View;
Jim Miller5fda69c2011-02-16 15:17:11 -080045import android.view.ViewGroup;
Jim Miller17377762011-10-11 15:33:34 -070046import android.view.accessibility.AccessibilityEvent;
Dianne Hackbornfc8fa632011-08-17 16:20:47 -070047import android.view.animation.AnimationUtils;
Jim Miller5fda69c2011-02-16 15:17:11 -080048import android.widget.AdapterView;
Michael Jurkabfd24ac2011-11-13 13:50:38 -080049import android.widget.AdapterView.OnItemClickListener;
Jim Miller5fda69c2011-02-16 15:17:11 -080050import android.widget.BaseAdapter;
Michael Jurka99a96552012-01-27 17:23:38 -080051import android.widget.FrameLayout;
Jim Miller44c66fe2010-10-20 18:32:52 -070052import android.widget.ImageView;
Michael Jurkabfd24ac2011-11-13 13:50:38 -080053import android.widget.ImageView.ScaleType;
Jim Millerc0d27312011-07-14 18:54:01 -070054import android.widget.PopupMenu;
Jim Miller44c66fe2010-10-20 18:32:52 -070055import android.widget.TextView;
Jim Miller44c66fe2010-10-20 18:32:52 -070056
57import com.android.systemui.R;
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -050058import com.android.systemui.statusbar.BaseStatusBar;
Michael Jurka3b1fc472011-06-13 10:54:40 -070059import com.android.systemui.statusbar.phone.PhoneStatusBar;
Jim Miller9f0f0e02011-05-17 20:06:29 -070060import com.android.systemui.statusbar.tablet.StatusBarPanel;
61import com.android.systemui.statusbar.tablet.TabletStatusBar;
Jim Miller44c66fe2010-10-20 18:32:52 -070062
Michael Jurkabfd24ac2011-11-13 13:50:38 -080063import java.util.ArrayList;
64
Michael Jurka99a96552012-01-27 17:23:38 -080065public class RecentsPanelView extends FrameLayout implements OnItemClickListener, RecentsCallback,
Michael Jurkacb2522c2012-04-13 09:32:47 -070066 StatusBarPanel, Animator.AnimatorListener {
Michael Jurka3cd0a592011-08-16 12:40:30 -070067 static final String TAG = "RecentsPanelView";
Dianne Hackbornfc8fa632011-08-17 16:20:47 -070068 static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
Michael Jurkacb466432012-04-03 05:22:45 -070069 private PopupMenu mPopup;
Jim Miller85babff2011-01-11 14:26:03 -080070 private View mRecentsScrim;
Michael Jurka6d667082011-09-16 17:04:40 -070071 private View mRecentsNoApps;
Jim Millerc0d27312011-07-14 18:54:01 -070072 private ViewGroup mRecentsContainer;
Daniel Sandler328310c2011-09-23 15:56:52 -040073 private StatusBarTouchProxy mStatusBarTouchProxy;
Peter Ng622a9762011-08-29 10:56:53 -070074
Jim Miller85babff2011-01-11 14:26:03 -080075 private boolean mShowing;
Michael Jurka99a96552012-01-27 17:23:38 -080076 private boolean mWaitingToShow;
Michael Jurka99a96552012-01-27 17:23:38 -080077 private int mNumItemsWaitingForThumbnailsAndIcons;
Michael Jurka21385cd2012-05-03 10:57:31 -070078
Michael Jurkaab48b682011-09-12 15:39:45 -070079 private RecentTasksLoader mRecentTasksLoader;
80 private ArrayList<TaskDescription> mRecentTaskDescriptions;
81 private TaskDescriptionAdapter mListAdapter;
82 private int mThumbnailWidth;
Michael Jurkabfd24ac2011-11-13 13:50:38 -080083 private boolean mFitThumbnailToXY;
Daniel Sandler4a066c52012-04-20 14:49:13 -040084 private int mRecentItemLayoutId;
Michael Jurkac016aaa2012-06-05 17:22:24 -070085 private boolean mHighEndGfx;
Dianne Hackborneeb1dca2011-09-08 13:30:11 -070086
Michael Jurka4eaa9832012-02-29 15:51:49 -080087 public static interface RecentsScrollView {
88 public int numItemsInOneScreenful();
89 public void setAdapter(TaskDescriptionAdapter adapter);
90 public void setCallback(RecentsCallback callback);
91 public void setMinSwipeAlpha(float minAlpha);
Michael Jurkacb2522c2012-04-13 09:32:47 -070092 public View findViewForTask(TaskDescription task);
Michael Jurka4eaa9832012-02-29 15:51:49 -080093 }
94
Jim Millerc0d27312011-07-14 18:54:01 -070095 private final class OnLongClickDelegate implements View.OnLongClickListener {
96 View mOtherView;
97 OnLongClickDelegate(View other) {
98 mOtherView = other;
99 }
100 public boolean onLongClick(View v) {
101 return mOtherView.performLongClick();
102 }
103 }
104
Jim Millerb5a2b292011-03-01 16:14:59 -0800105 /* package */ final static class ViewHolder {
Jim Millerc0d27312011-07-14 18:54:01 -0700106 View thumbnailView;
Dianne Hackbornfc8fa632011-08-17 16:20:47 -0700107 ImageView thumbnailViewImage;
Michael Jurka412cba82011-10-17 09:05:00 -0700108 Bitmap thumbnailViewImageBitmap;
Jim Miller9f0f0e02011-05-17 20:06:29 -0700109 ImageView iconView;
110 TextView labelView;
111 TextView descriptionView;
Michael Jurkaab48b682011-09-12 15:39:45 -0700112 TaskDescription taskDescription;
Michael Jurka99a96552012-01-27 17:23:38 -0800113 boolean loadedThumbnailAndIcon;
Jim Miller5fda69c2011-02-16 15:17:11 -0800114 }
115
Michael Jurkaab48b682011-09-12 15:39:45 -0700116 /* package */ final class TaskDescriptionAdapter extends BaseAdapter {
Jim Miller5fda69c2011-02-16 15:17:11 -0800117 private LayoutInflater mInflater;
118
Michael Jurkaab48b682011-09-12 15:39:45 -0700119 public TaskDescriptionAdapter(Context context) {
Jim Miller5fda69c2011-02-16 15:17:11 -0800120 mInflater = LayoutInflater.from(context);
121 }
122
123 public int getCount() {
Michael Jurkaab48b682011-09-12 15:39:45 -0700124 return mRecentTaskDescriptions != null ? mRecentTaskDescriptions.size() : 0;
Jim Miller5fda69c2011-02-16 15:17:11 -0800125 }
126
127 public Object getItem(int position) {
128 return position; // we only need the index
129 }
130
131 public long getItemId(int position) {
132 return position; // we just need something unique for this position
133 }
134
Michael Jurka99a96552012-01-27 17:23:38 -0800135 public View createView(ViewGroup parent) {
Daniel Sandler4a066c52012-04-20 14:49:13 -0400136 View convertView = mInflater.inflate(mRecentItemLayoutId, parent, false);
Michael Jurka99a96552012-01-27 17:23:38 -0800137 ViewHolder holder = new ViewHolder();
138 holder.thumbnailView = convertView.findViewById(R.id.app_thumbnail);
139 holder.thumbnailViewImage =
140 (ImageView) convertView.findViewById(R.id.app_thumbnail_image);
141 // If we set the default thumbnail now, we avoid an onLayout when we update
142 // the thumbnail later (if they both have the same dimensions)
143 if (mRecentTasksLoader != null) {
Michael Jurka412cba82011-10-17 09:05:00 -0700144 updateThumbnail(holder, mRecentTasksLoader.getDefaultThumbnail(), false, false);
Jim Miller5fda69c2011-02-16 15:17:11 -0800145 }
Michael Jurka99a96552012-01-27 17:23:38 -0800146 holder.iconView = (ImageView) convertView.findViewById(R.id.app_icon);
147 if (mRecentTasksLoader != null) {
148 holder.iconView.setImageBitmap(mRecentTasksLoader.getDefaultIcon());
149 }
150 holder.labelView = (TextView) convertView.findViewById(R.id.app_label);
151 holder.descriptionView = (TextView) convertView.findViewById(R.id.app_description);
152
153 convertView.setTag(holder);
154 return convertView;
155 }
156
157 public View getView(int position, View convertView, ViewGroup parent) {
158 if (convertView == null) {
159 convertView = createView(parent);
160 }
161 ViewHolder holder = (ViewHolder) convertView.getTag();
Jim Miller5fda69c2011-02-16 15:17:11 -0800162
Michael Jurkaab48b682011-09-12 15:39:45 -0700163 // index is reverse since most recent appears at the bottom...
164 final int index = mRecentTaskDescriptions.size() - position - 1;
Jim Miller5fda69c2011-02-16 15:17:11 -0800165
Michael Jurka412cba82011-10-17 09:05:00 -0700166 final TaskDescription td = mRecentTaskDescriptions.get(index);
Michael Jurka99a96552012-01-27 17:23:38 -0800167
Michael Jurka412cba82011-10-17 09:05:00 -0700168 holder.labelView.setText(td.getLabel());
169 holder.thumbnailView.setContentDescription(td.getLabel());
Michael Jurka99a96552012-01-27 17:23:38 -0800170 holder.loadedThumbnailAndIcon = td.isLoaded();
171 if (td.isLoaded()) {
172 updateThumbnail(holder, td.getThumbnail(), true, false);
173 updateIcon(holder, td.getIcon(), true, false);
174 mNumItemsWaitingForThumbnailsAndIcons--;
175 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700176
Michael Jurka412cba82011-10-17 09:05:00 -0700177 holder.thumbnailView.setTag(td);
Jim Millerc0d27312011-07-14 18:54:01 -0700178 holder.thumbnailView.setOnLongClickListener(new OnLongClickDelegate(convertView));
Michael Jurka412cba82011-10-17 09:05:00 -0700179 holder.taskDescription = td;
Jim Miller5fda69c2011-02-16 15:17:11 -0800180 return convertView;
181 }
Michael Jurka99a96552012-01-27 17:23:38 -0800182
183 public void recycleView(View v) {
184 ViewHolder holder = (ViewHolder) v.getTag();
185 updateThumbnail(holder, mRecentTasksLoader.getDefaultThumbnail(), false, false);
186 holder.iconView.setImageBitmap(mRecentTasksLoader.getDefaultIcon());
187 holder.iconView.setVisibility(INVISIBLE);
188 holder.labelView.setText(null);
189 holder.thumbnailView.setContentDescription(null);
190 holder.thumbnailView.setTag(null);
191 holder.thumbnailView.setOnLongClickListener(null);
192 holder.thumbnailView.setVisibility(INVISIBLE);
193 holder.taskDescription = null;
194 holder.loadedThumbnailAndIcon = false;
195 }
196 }
197
Michael Jurka8b2e37e2012-05-08 20:06:13 -0700198 public RecentsPanelView(Context context, AttributeSet attrs) {
199 this(context, attrs, 0);
200 }
201
202 public RecentsPanelView(Context context, AttributeSet attrs, int defStyle) {
203 super(context, attrs, defStyle);
Michael Jurka8b2e37e2012-05-08 20:06:13 -0700204 updateValuesFromResources();
205
206 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecentsPanelView,
207 defStyle, 0);
208
209 mRecentItemLayoutId = a.getResourceId(R.styleable.RecentsPanelView_recentItemLayout, 0);
210 a.recycle();
211 }
212
Michael Jurka99a96552012-01-27 17:23:38 -0800213 public int numItemsInOneScreenful() {
Michael Jurka4eaa9832012-02-29 15:51:49 -0800214 if (mRecentsContainer instanceof RecentsScrollView){
215 RecentsScrollView scrollView
216 = (RecentsScrollView) mRecentsContainer;
Michael Jurka99a96552012-01-27 17:23:38 -0800217 return scrollView.numItemsInOneScreenful();
Michael Jurka4eaa9832012-02-29 15:51:49 -0800218 } else {
Michael Jurka99a96552012-01-27 17:23:38 -0800219 throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView");
220 }
Jim Miller5fda69c2011-02-16 15:17:11 -0800221 }
222
Daniel Sandler328310c2011-09-23 15:56:52 -0400223 private boolean pointInside(int x, int y, View v) {
224 final int l = v.getLeft();
225 final int r = v.getRight();
226 final int t = v.getTop();
227 final int b = v.getBottom();
Jim Miller44c66fe2010-10-20 18:32:52 -0700228 return x >= l && x < r && y >= t && y < b;
229 }
230
Daniel Sandler328310c2011-09-23 15:56:52 -0400231 public boolean isInContentArea(int x, int y) {
Michael Jurkab144bd72012-01-12 06:56:39 -0800232 if (pointInside(x, y, mRecentsContainer)) {
233 return true;
234 } else if (mStatusBarTouchProxy != null &&
235 pointInside(x, y, mStatusBarTouchProxy)) {
236 return true;
237 } else {
238 return false;
239 }
Daniel Sandler328310c2011-09-23 15:56:52 -0400240 }
241
Michael Jurkacb2522c2012-04-13 09:32:47 -0700242 public void show(boolean show) {
243 show(show, null, false);
244 }
245
246 public void show(boolean show, ArrayList<TaskDescription> recentTaskDescriptions,
247 boolean firstScreenful) {
Michael Jurka99a96552012-01-27 17:23:38 -0800248 if (show) {
249 mWaitingToShow = true;
Michael Jurkacb2522c2012-04-13 09:32:47 -0700250 refreshRecentTasksList(recentTaskDescriptions, firstScreenful);
Michael Jurka99a96552012-01-27 17:23:38 -0800251 showIfReady();
252 } else {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700253 showImpl(false);
Michael Jurka99a96552012-01-27 17:23:38 -0800254 }
255 }
256
257 private void showIfReady() {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700258 // mWaitingToShow => there was a touch up on the recents button
259 // mRecentTaskDescriptions != null => we've created views for the first screenful of items
260 if (mWaitingToShow && mRecentTaskDescriptions != null) {
261 showImpl(true);
Michael Jurka99a96552012-01-27 17:23:38 -0800262 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700263 }
264
Michael Jurka56a57832012-05-14 13:24:43 -0700265 static void sendCloseSystemWindows(Context context, String reason) {
266 if (ActivityManagerNative.isSystemReady()) {
267 try {
268 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
269 } catch (RemoteException e) {
270 }
271 }
272 }
273
Michael Jurkacb2522c2012-04-13 09:32:47 -0700274 private void showImpl(boolean show) {
Michael Jurka56a57832012-05-14 13:24:43 -0700275 sendCloseSystemWindows(mContext, BaseStatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS);
276
Michael Jurkacb2522c2012-04-13 09:32:47 -0700277 mShowing = show;
278
Michael Jurka7daf95d2011-09-30 11:07:30 -0700279 if (show) {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700280 // if there are no apps, bring up a "No recent apps" message
281 boolean noApps = mRecentTaskDescriptions != null
282 && (mRecentTaskDescriptions.size() == 0);
283 mRecentsNoApps.setAlpha(1f);
284 mRecentsNoApps.setVisibility(noApps ? View.VISIBLE : View.INVISIBLE);
Michael Jurka7daf95d2011-09-30 11:07:30 -0700285
Michael Jurkaab48b682011-09-12 15:39:45 -0700286 onAnimationEnd(null);
Michael Jurkae9166ed2011-07-27 18:23:18 -0700287 setFocusable(true);
288 setFocusableInTouchMode(true);
289 requestFocus();
Michael Jurkacb466432012-04-03 05:22:45 -0700290 } else {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700291 mWaitingToShow = false;
292 // call onAnimationEnd() and clearRecentTasksList() in onUiHidden()
Michael Jurkacb466432012-04-03 05:22:45 -0700293 if (mPopup != null) {
294 mPopup.dismiss();
295 }
Michael Jurkae9166ed2011-07-27 18:23:18 -0700296 }
Jim Miller85babff2011-01-11 14:26:03 -0800297 }
298
Michael Jurkacb2522c2012-04-13 09:32:47 -0700299 public void onUiHidden() {
300 if (!mShowing && mRecentTaskDescriptions != null) {
301 onAnimationEnd(null);
302 clearRecentTasksList();
303 }
Michael Jurkac6461ca2011-09-02 12:12:15 -0700304 }
305
Michael Jurkacb2522c2012-04-13 09:32:47 -0700306 public void dismiss() {
307 ((RecentsActivity) mContext).dismissAndGoHome();
308 }
309
310 public void dismissAndGoBack() {
311 ((RecentsActivity) mContext).dismissAndGoBack();
Michael Jurka8b098662011-09-01 17:00:53 -0700312 }
313
Michael Jurka3b1fc472011-06-13 10:54:40 -0700314 public void onAnimationCancel(Animator animation) {
315 }
316
317 public void onAnimationEnd(Animator animation) {
318 if (mShowing) {
319 final LayoutTransition transitioner = new LayoutTransition();
320 ((ViewGroup)mRecentsContainer).setLayoutTransition(transitioner);
321 createCustomAnimations(transitioner);
322 } else {
323 ((ViewGroup)mRecentsContainer).setLayoutTransition(null);
324 }
325 }
326
327 public void onAnimationRepeat(Animator animation) {
328 }
329
330 public void onAnimationStart(Animator animation) {
331 }
332
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700333 @Override
334 public boolean dispatchHoverEvent(MotionEvent event) {
335 // Ignore hover events outside of this panel bounds since such events
336 // generate spurious accessibility events with the panel content when
337 // tapping outside of it, thus confusing the user.
338 final int x = (int) event.getX();
339 final int y = (int) event.getY();
340 if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) {
341 return super.dispatchHoverEvent(event);
342 }
343 return true;
344 }
345
Jim Miller85babff2011-01-11 14:26:03 -0800346 /**
347 * Whether the panel is showing, or, if it's animating, whether it will be
348 * when the animation is done.
349 */
350 public boolean isShowing() {
351 return mShowing;
352 }
353
Daniel Sandler328310c2011-09-23 15:56:52 -0400354 public void setStatusBarView(View statusBarView) {
355 if (mStatusBarTouchProxy != null) {
356 mStatusBarTouchProxy.setStatusBar(statusBarView);
357 }
358 }
359
360 public void setRecentTasksLoader(RecentTasksLoader loader) {
361 mRecentTasksLoader = loader;
362 }
363
Michael Jurkaab48b682011-09-12 15:39:45 -0700364 public void updateValuesFromResources() {
Michael Jurkabfd24ac2011-11-13 13:50:38 -0800365 final Resources res = mContext.getResources();
366 mThumbnailWidth = Math.round(res.getDimension(R.dimen.status_bar_recents_thumbnail_width));
367 mFitThumbnailToXY = res.getBoolean(R.bool.config_recents_thumbnail_image_fits_to_xy);
Jim Miller44c66fe2010-10-20 18:32:52 -0700368 }
369
370 @Override
371 protected void onFinishInflate() {
372 super.onFinishInflate();
Daniel Sandler4a066c52012-04-20 14:49:13 -0400373
Jim Millerc0d27312011-07-14 18:54:01 -0700374 mRecentsContainer = (ViewGroup) findViewById(R.id.recents_container);
Daniel Sandler328310c2011-09-23 15:56:52 -0400375 mStatusBarTouchProxy = (StatusBarTouchProxy) findViewById(R.id.status_bar_touch_proxy);
Michael Jurkaab48b682011-09-12 15:39:45 -0700376 mListAdapter = new TaskDescriptionAdapter(mContext);
Michael Jurka4eaa9832012-02-29 15:51:49 -0800377 if (mRecentsContainer instanceof RecentsScrollView){
378 RecentsScrollView scrollView
379 = (RecentsScrollView) mRecentsContainer;
Jim Miller9f0f0e02011-05-17 20:06:29 -0700380 scrollView.setAdapter(mListAdapter);
381 scrollView.setCallback(this);
Michael Jurka4eaa9832012-02-29 15:51:49 -0800382 } else {
Michael Jurka3cd0a592011-08-16 12:40:30 -0700383 throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView");
Jim Miller9f0f0e02011-05-17 20:06:29 -0700384 }
Jim Miller5fda69c2011-02-16 15:17:11 -0800385
Michael Jurka6d667082011-09-16 17:04:40 -0700386 mRecentsScrim = findViewById(R.id.recents_bg_protect);
387 mRecentsNoApps = findViewById(R.id.recents_no_apps);
Jim Millere5c16ab2010-12-03 19:18:38 -0800388
Michael Jurkaf16fc512012-02-27 15:02:23 -0800389 if (mRecentsScrim != null) {
Jeff Brown98365d72012-08-19 20:30:52 -0700390 mHighEndGfx = ActivityManager.isHighEndGfx();
Michael Jurkac016aaa2012-06-05 17:22:24 -0700391 if (!mHighEndGfx) {
Michael Jurkad5895a72012-05-12 13:24:58 -0700392 mRecentsScrim.setBackground(null);
Michael Jurkaf16fc512012-02-27 15:02:23 -0800393 } else if (mRecentsScrim.getBackground() instanceof BitmapDrawable) {
394 // In order to save space, we make the background texture repeat in the Y direction
395 ((BitmapDrawable) mRecentsScrim.getBackground()).setTileModeY(TileMode.REPEAT);
396 }
Jim Millere5c16ab2010-12-03 19:18:38 -0800397 }
398 }
399
Michael Jurka4eaa9832012-02-29 15:51:49 -0800400 public void setMinSwipeAlpha(float minAlpha) {
401 if (mRecentsContainer instanceof RecentsScrollView){
402 RecentsScrollView scrollView
403 = (RecentsScrollView) mRecentsContainer;
404 scrollView.setMinSwipeAlpha(minAlpha);
405 }
406 }
407
Jim Miller9f0f0e02011-05-17 20:06:29 -0700408 private void createCustomAnimations(LayoutTransition transitioner) {
Chet Haase212f6252011-08-12 09:19:10 -0700409 transitioner.setDuration(200);
410 transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
411 transitioner.setAnimator(LayoutTransition.DISAPPEARING, null);
Jim Miller9f0f0e02011-05-17 20:06:29 -0700412 }
413
Michael Jurka99a96552012-01-27 17:23:38 -0800414 private void updateIcon(ViewHolder h, Drawable icon, boolean show, boolean anim) {
415 if (icon != null) {
416 h.iconView.setImageDrawable(icon);
417 if (show && h.iconView.getVisibility() != View.VISIBLE) {
418 if (anim) {
419 h.iconView.setAnimation(
420 AnimationUtils.loadAnimation(mContext, R.anim.recent_appear));
421 }
422 h.iconView.setVisibility(View.VISIBLE);
423 }
424 }
425 }
426
Michael Jurka412cba82011-10-17 09:05:00 -0700427 private void updateThumbnail(ViewHolder h, Bitmap thumbnail, boolean show, boolean anim) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700428 if (thumbnail != null) {
429 // Should remove the default image in the frame
430 // that this now covers, to improve scrolling speed.
431 // That can't be done until the anim is complete though.
432 h.thumbnailViewImage.setImageBitmap(thumbnail);
Michael Jurka412cba82011-10-17 09:05:00 -0700433
434 // scale the image to fill the full width of the ImageView. do this only if
435 // we haven't set a bitmap before, or if the bitmap size has changed
436 if (h.thumbnailViewImageBitmap == null ||
437 h.thumbnailViewImageBitmap.getWidth() != thumbnail.getWidth() ||
438 h.thumbnailViewImageBitmap.getHeight() != thumbnail.getHeight()) {
Michael Jurkabfd24ac2011-11-13 13:50:38 -0800439 if (mFitThumbnailToXY) {
440 h.thumbnailViewImage.setScaleType(ScaleType.FIT_XY);
441 } else {
442 Matrix scaleMatrix = new Matrix();
443 float scale = mThumbnailWidth / (float) thumbnail.getWidth();
444 scaleMatrix.setScale(scale, scale);
445 h.thumbnailViewImage.setScaleType(ScaleType.MATRIX);
446 h.thumbnailViewImage.setImageMatrix(scaleMatrix);
447 }
Jim Miller44c66fe2010-10-20 18:32:52 -0700448 }
Michael Jurka412cba82011-10-17 09:05:00 -0700449 if (show && h.thumbnailView.getVisibility() != View.VISIBLE) {
450 if (anim) {
451 h.thumbnailView.setAnimation(
452 AnimationUtils.loadAnimation(mContext, R.anim.recent_appear));
453 }
454 h.thumbnailView.setVisibility(View.VISIBLE);
455 }
456 h.thumbnailViewImageBitmap = thumbnail;
Jim Miller44c66fe2010-10-20 18:32:52 -0700457 }
Jim Millerfefef312010-11-09 14:08:39 -0800458 }
459
Michael Jurka99a96552012-01-27 17:23:38 -0800460 void onTaskThumbnailLoaded(TaskDescription td) {
461 synchronized (td) {
Dianne Hackbornfc8fa632011-08-17 16:20:47 -0700462 if (mRecentsContainer != null) {
463 ViewGroup container = mRecentsContainer;
Michael Jurka4eaa9832012-02-29 15:51:49 -0800464 if (container instanceof RecentsScrollView) {
Michael Jurka99a96552012-01-27 17:23:38 -0800465 container = (ViewGroup) container.findViewById(
Dianne Hackbornfc8fa632011-08-17 16:20:47 -0700466 R.id.recents_linear_layout);
467 }
468 // Look for a view showing this thumbnail, to update.
Michael Jurka99a96552012-01-27 17:23:38 -0800469 for (int i=0; i < container.getChildCount(); i++) {
Dianne Hackbornfc8fa632011-08-17 16:20:47 -0700470 View v = container.getChildAt(i);
471 if (v.getTag() instanceof ViewHolder) {
472 ViewHolder h = (ViewHolder)v.getTag();
Michael Jurka99a96552012-01-27 17:23:38 -0800473 if (!h.loadedThumbnailAndIcon && h.taskDescription == td) {
Michael Jurka412cba82011-10-17 09:05:00 -0700474 // only fade in the thumbnail if recents is already visible-- we
475 // show it immediately otherwise
Michael Jurka99a96552012-01-27 17:23:38 -0800476 //boolean animateShow = mShowing &&
477 // mRecentsContainer.getAlpha() > ViewConfiguration.ALPHA_THRESHOLD;
478 boolean animateShow = false;
479 updateIcon(h, td.getIcon(), true, animateShow);
480 updateThumbnail(h, td.getThumbnail(), true, animateShow);
481 h.loadedThumbnailAndIcon = true;
482 mNumItemsWaitingForThumbnailsAndIcons--;
Dianne Hackbornfc8fa632011-08-17 16:20:47 -0700483 }
484 }
485 }
486 }
Craig Mautnerca6da002012-09-20 14:54:37 -0700487 }
Michael Jurka99a96552012-01-27 17:23:38 -0800488 showIfReady();
Dianne Hackbornfc8fa632011-08-17 16:20:47 -0700489 }
490
Michael Jurka412cba82011-10-17 09:05:00 -0700491 public void clearRecentTasksList() {
492 // Clear memory used by screenshots
Michael Jurkacb2522c2012-04-13 09:32:47 -0700493 if (mRecentTaskDescriptions != null) {
494 mRecentTasksLoader.cancelLoadingThumbnailsAndIcons(this);
495 onTaskLoadingCancelled();
496 }
497 }
498
499 public void onTaskLoadingCancelled() {
500 // Gets called by RecentTasksLoader when it's cancelled
501 if (mRecentTaskDescriptions != null) {
502 mRecentTaskDescriptions = null;
Michael Jurka412cba82011-10-17 09:05:00 -0700503 mListAdapter.notifyDataSetInvalidated();
Michael Jurka412cba82011-10-17 09:05:00 -0700504 }
505 }
506
Craig Mautnerca6da002012-09-20 14:54:37 -0700507 public void refreshViews() {
508 mListAdapter.notifyDataSetInvalidated();
509 updateUiElements();
510 showIfReady();
511 }
512
Michael Jurka412cba82011-10-17 09:05:00 -0700513 public void refreshRecentTasksList() {
Michael Jurka99a96552012-01-27 17:23:38 -0800514 refreshRecentTasksList(null, false);
Michael Jurka412cba82011-10-17 09:05:00 -0700515 }
516
Michael Jurka99a96552012-01-27 17:23:38 -0800517 private void refreshRecentTasksList(
518 ArrayList<TaskDescription> recentTasksList, boolean firstScreenful) {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700519 if (mRecentTaskDescriptions == null && recentTasksList != null) {
520 onTasksLoaded(recentTasksList, firstScreenful);
521 } else {
522 mRecentTasksLoader.loadTasksInBackground();
Michael Jurka412cba82011-10-17 09:05:00 -0700523 }
Michael Jurkaab48b682011-09-12 15:39:45 -0700524 }
Michael Jurka7daf95d2011-09-30 11:07:30 -0700525
Michael Jurkacb2522c2012-04-13 09:32:47 -0700526 public void onTasksLoaded(ArrayList<TaskDescription> tasks, boolean firstScreenful) {
527 mNumItemsWaitingForThumbnailsAndIcons = firstScreenful
Craig Mautnerbb1449b2012-03-23 16:11:14 -0700528 ? tasks.size() : mRecentTaskDescriptions == null
529 ? 0 : mRecentTaskDescriptions.size();
Michael Jurka99a96552012-01-27 17:23:38 -0800530 if (mRecentTaskDescriptions == null) {
Craig Mautnerbb1449b2012-03-23 16:11:14 -0700531 mRecentTaskDescriptions = new ArrayList<TaskDescription>(tasks);
Michael Jurka99a96552012-01-27 17:23:38 -0800532 } else {
533 mRecentTaskDescriptions.addAll(tasks);
534 }
Craig Mautnerca6da002012-09-20 14:54:37 -0700535 if (((RecentsActivity)mContext).isForeground()) {
536 refreshViews();
537 }
Michael Jurka99a96552012-01-27 17:23:38 -0800538 }
539
Craig Mautnerca6da002012-09-20 14:54:37 -0700540 private void updateUiElements() {
Michael Jurkaab48b682011-09-12 15:39:45 -0700541 final int items = mRecentTaskDescriptions.size();
542
543 mRecentsContainer.setVisibility(items > 0 ? View.VISIBLE : View.GONE);
Michael Jurkaab48b682011-09-12 15:39:45 -0700544
545 // Set description for accessibility
546 int numRecentApps = mRecentTaskDescriptions.size();
Michael Jurka7daf95d2011-09-30 11:07:30 -0700547 String recentAppsAccessibilityDescription;
548 if (numRecentApps == 0) {
549 recentAppsAccessibilityDescription =
550 getResources().getString(R.string.status_bar_no_recent_apps);
551 } else {
552 recentAppsAccessibilityDescription = getResources().getQuantityString(
553 R.plurals.status_bar_accessibility_recent_apps, numRecentApps, numRecentApps);
554 }
555 setContentDescription(recentAppsAccessibilityDescription);
Jim Miller44c66fe2010-10-20 18:32:52 -0700556 }
557
Michael Jurkacb2522c2012-04-13 09:32:47 -0700558 public boolean simulateClick(TaskDescription task) {
559 if (mRecentsContainer instanceof RecentsScrollView){
560 RecentsScrollView scrollView
561 = (RecentsScrollView) mRecentsContainer;
562 View v = scrollView.findViewForTask(task);
563 if (v != null) {
564 handleOnClick(v);
565 return true;
566 }
567 }
568 return false;
569 }
Michael Jurkac016aaa2012-06-05 17:22:24 -0700570
Jim Miller9f0f0e02011-05-17 20:06:29 -0700571 public void handleOnClick(View view) {
Dianne Hackborn8078d8c2012-03-20 11:11:26 -0700572 ViewHolder holder = (ViewHolder)view.getTag();
573 TaskDescription ad = holder.taskDescription;
Jim Miller9f0f0e02011-05-17 20:06:29 -0700574 final Context context = view.getContext();
Jim Millere5c16ab2010-12-03 19:18:38 -0800575 final ActivityManager am = (ActivityManager)
Jim Miller9f0f0e02011-05-17 20:06:29 -0700576 context.getSystemService(Context.ACTIVITY_SERVICE);
Michael Jurkac016aaa2012-06-05 17:22:24 -0700577 Bitmap bm = holder.thumbnailViewImageBitmap;
578 boolean usingDrawingCache;
579 if (bm.getWidth() == holder.thumbnailViewImage.getWidth() &&
580 bm.getHeight() == holder.thumbnailViewImage.getHeight()) {
581 usingDrawingCache = false;
582 } else {
583 holder.thumbnailViewImage.setDrawingCacheEnabled(true);
584 bm = holder.thumbnailViewImage.getDrawingCache();
585 usingDrawingCache = true;
586 }
Michael Jurka482f1592012-09-13 11:05:59 -0700587 Bundle opts = (bm == null) ?
588 null :
589 ActivityOptions.makeThumbnailScaleUpAnimation(
590 holder.thumbnailViewImage, bm, 0, 0, null).toBundle();
Michael Jurkacb2522c2012-04-13 09:32:47 -0700591
592 show(false);
Jim Miller9f0f0e02011-05-17 20:06:29 -0700593 if (ad.taskId >= 0) {
Dianne Hackbornb6777462010-11-12 12:43:22 -0800594 // This is an active task; it should just go to the foreground.
Dianne Hackborn8078d8c2012-03-20 11:11:26 -0700595 am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HOME,
Michael Jurka482f1592012-09-13 11:05:59 -0700596 opts);
Dianne Hackbornb6777462010-11-12 12:43:22 -0800597 } else {
598 Intent intent = ad.intent;
Dianne Hackborn621e17d2010-11-22 15:59:56 -0800599 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
Dianne Hackborn38cc8962011-10-13 11:33:55 -0700600 | Intent.FLAG_ACTIVITY_TASK_ON_HOME
601 | Intent.FLAG_ACTIVITY_NEW_TASK);
Dianne Hackbornb6777462010-11-12 12:43:22 -0800602 if (DEBUG) Log.v(TAG, "Starting activity " + intent);
Michael Jurka482f1592012-09-13 11:05:59 -0700603 context.startActivityAsUser(intent, opts,
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700604 new UserHandle(UserHandle.USER_CURRENT));
Dianne Hackbornb6777462010-11-12 12:43:22 -0800605 }
Michael Jurkab9a38c52012-06-14 11:57:50 -0700606 if (usingDrawingCache) {
Michael Jurkac016aaa2012-06-05 17:22:24 -0700607 holder.thumbnailViewImage.setDrawingCacheEnabled(false);
608 }
Jim Millerf1e22e52011-01-26 15:39:51 -0800609 }
Jim Miller9f0f0e02011-05-17 20:06:29 -0700610
611 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
612 handleOnClick(view);
613 }
614
Michael Jurka07d40462011-07-19 10:54:38 -0700615 public void handleSwipe(View view) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700616 TaskDescription ad = ((ViewHolder) view.getTag()).taskDescription;
Michael Jurkaa8eac1d2012-04-30 15:54:19 -0700617 if (ad == null) {
Michael Jurka9d69b442012-05-04 15:46:32 -0700618 Log.v(TAG, "Not able to find activity description for swiped task; view=" + view +
619 " tag=" + view.getTag());
620 return;
Michael Jurkaa8eac1d2012-04-30 15:54:19 -0700621 }
Dianne Hackbornfc8fa632011-08-17 16:20:47 -0700622 if (DEBUG) Log.v(TAG, "Jettison " + ad.getLabel());
Michael Jurkaab48b682011-09-12 15:39:45 -0700623 mRecentTaskDescriptions.remove(ad);
Jim Miller9f0f0e02011-05-17 20:06:29 -0700624
625 // Handled by widget containers to enable LayoutTransitions properly
626 // mListAdapter.notifyDataSetChanged();
627
Michael Jurkaab48b682011-09-12 15:39:45 -0700628 if (mRecentTaskDescriptions.size() == 0) {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700629 dismissAndGoBack();
Jim Miller9f0f0e02011-05-17 20:06:29 -0700630 }
631
632 // Currently, either direction means the same thing, so ignore direction and remove
633 // the task.
634 final ActivityManager am = (ActivityManager)
635 mContext.getSystemService(Context.ACTIVITY_SERVICE);
Michael Jurkaa8eac1d2012-04-30 15:54:19 -0700636 if (am != null) {
637 am.removeTask(ad.persistentTaskId, ActivityManager.REMOVE_TASK_KILL_PROCESS);
Jim Miller17377762011-10-11 15:33:34 -0700638
Michael Jurkaa8eac1d2012-04-30 15:54:19 -0700639 // Accessibility feedback
640 setContentDescription(
641 mContext.getString(R.string.accessibility_recents_item_dismissed, ad.getLabel()));
642 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
643 setContentDescription(null);
644 }
Jim Miller9f0f0e02011-05-17 20:06:29 -0700645 }
646
Jim Millerc0d27312011-07-14 18:54:01 -0700647 private void startApplicationDetailsActivity(String packageName) {
648 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
649 Uri.fromParts("package", packageName, null));
Adam Powell0fc5b2b2012-07-18 18:20:29 -0700650 intent.setComponent(intent.resolveActivity(mContext.getPackageManager()));
651 TaskStackBuilder.create(getContext())
652 .addNextIntentWithParentStack(intent).startActivities();
Jim Millerc0d27312011-07-14 18:54:01 -0700653 }
654
Michael Jurkacb466432012-04-03 05:22:45 -0700655 public boolean onInterceptTouchEvent(MotionEvent ev) {
656 if (mPopup != null) {
657 return true;
658 } else {
659 return super.onInterceptTouchEvent(ev);
660 }
661 }
662
Michael Jurkafaa790c2011-09-02 17:35:02 -0700663 public void handleLongPress(
664 final View selectedView, final View anchorView, final View thumbnailView) {
665 thumbnailView.setSelected(true);
Michael Jurkacb466432012-04-03 05:22:45 -0700666 final PopupMenu popup =
667 new PopupMenu(mContext, anchorView == null ? selectedView : anchorView);
668 mPopup = popup;
Jim Millerc0d27312011-07-14 18:54:01 -0700669 popup.getMenuInflater().inflate(R.menu.recent_popup_menu, popup.getMenu());
670 popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
671 public boolean onMenuItemClick(MenuItem item) {
672 if (item.getItemId() == R.id.recent_remove_item) {
673 mRecentsContainer.removeViewInLayout(selectedView);
674 } else if (item.getItemId() == R.id.recent_inspect_item) {
675 ViewHolder viewHolder = (ViewHolder) selectedView.getTag();
676 if (viewHolder != null) {
Michael Jurkaab48b682011-09-12 15:39:45 -0700677 final TaskDescription ad = viewHolder.taskDescription;
Jim Millerc0d27312011-07-14 18:54:01 -0700678 startApplicationDetailsActivity(ad.packageName);
Michael Jurkacb2522c2012-04-13 09:32:47 -0700679 show(false);
Jim Millerc0d27312011-07-14 18:54:01 -0700680 } else {
681 throw new IllegalStateException("Oops, no tag on view " + selectedView);
682 }
683 } else {
684 return false;
685 }
686 return true;
687 }
688 });
Michael Jurkafaa790c2011-09-02 17:35:02 -0700689 popup.setOnDismissListener(new PopupMenu.OnDismissListener() {
690 public void onDismiss(PopupMenu menu) {
691 thumbnailView.setSelected(false);
Michael Jurkacb466432012-04-03 05:22:45 -0700692 mPopup = null;
Michael Jurkafaa790c2011-09-02 17:35:02 -0700693 }
694 });
Jim Millerc0d27312011-07-14 18:54:01 -0700695 popup.show();
Jim Miller9f0f0e02011-05-17 20:06:29 -0700696 }
Jim Miller44c66fe2010-10-20 18:32:52 -0700697}