RecentsPanelView.java revision 80343f646f9686528212f82163a77ef48e30f4c3
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 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 17package com.android.systemui.recent; 18 19import android.animation.Animator; 20import android.animation.LayoutTransition; 21import android.animation.TimeInterpolator; 22import android.app.Activity; 23import android.app.ActivityManager; 24import android.app.ActivityManagerNative; 25import android.app.ActivityOptions; 26import android.app.TaskStackBuilder; 27import android.content.Context; 28import android.content.Intent; 29import android.content.res.Configuration; 30import android.content.res.Resources; 31import android.content.res.TypedArray; 32import android.graphics.Bitmap; 33import android.graphics.Matrix; 34import android.graphics.Shader.TileMode; 35import android.graphics.drawable.BitmapDrawable; 36import android.graphics.drawable.Drawable; 37import android.net.Uri; 38import android.os.Bundle; 39import android.os.RemoteException; 40import android.os.UserHandle; 41import android.provider.Settings; 42import android.util.AttributeSet; 43import android.util.Log; 44import android.view.LayoutInflater; 45import android.view.MenuItem; 46import android.view.MotionEvent; 47import android.view.View; 48import android.view.ViewGroup; 49import android.view.accessibility.AccessibilityEvent; 50import android.view.animation.AnimationUtils; 51import android.view.animation.DecelerateInterpolator; 52import android.widget.AdapterView; 53import android.widget.AdapterView.OnItemClickListener; 54import android.widget.BaseAdapter; 55import android.widget.FrameLayout; 56import android.widget.ImageView; 57import android.widget.ImageView.ScaleType; 58import android.widget.PopupMenu; 59import android.widget.TextView; 60 61import com.android.systemui.R; 62import com.android.systemui.statusbar.BaseStatusBar; 63import com.android.systemui.statusbar.phone.PhoneStatusBar; 64import com.android.systemui.statusbar.tablet.StatusBarPanel; 65import com.android.systemui.statusbar.tablet.TabletStatusBar; 66 67import java.util.ArrayList; 68 69public class RecentsPanelView extends FrameLayout implements OnItemClickListener, RecentsCallback, 70 StatusBarPanel, Animator.AnimatorListener { 71 static final String TAG = "RecentsPanelView"; 72 static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false; 73 private PopupMenu mPopup; 74 private View mRecentsScrim; 75 private View mRecentsNoApps; 76 private ViewGroup mRecentsContainer; 77 private StatusBarTouchProxy mStatusBarTouchProxy; 78 79 private boolean mShowing; 80 private boolean mWaitingToShow; 81 private int mNumItemsWaitingForThumbnailsAndIcons; 82 private ViewHolder mItemToAnimateInWhenWindowAnimationIsFinished; 83 private boolean mWaitingForWindowAnimation; 84 85 private RecentTasksLoader mRecentTasksLoader; 86 private ArrayList<TaskDescription> mRecentTaskDescriptions; 87 private TaskDescriptionAdapter mListAdapter; 88 private int mThumbnailWidth; 89 private boolean mFitThumbnailToXY; 90 private int mRecentItemLayoutId; 91 private boolean mHighEndGfx; 92 93 public static interface RecentsScrollView { 94 public int numItemsInOneScreenful(); 95 public void setAdapter(TaskDescriptionAdapter adapter); 96 public void setCallback(RecentsCallback callback); 97 public void setMinSwipeAlpha(float minAlpha); 98 public View findViewForTask(int persistentTaskId); 99 } 100 101 private final class OnLongClickDelegate implements View.OnLongClickListener { 102 View mOtherView; 103 OnLongClickDelegate(View other) { 104 mOtherView = other; 105 } 106 public boolean onLongClick(View v) { 107 return mOtherView.performLongClick(); 108 } 109 } 110 111 /* package */ final static class ViewHolder { 112 View thumbnailView; 113 ImageView thumbnailViewImage; 114 Bitmap thumbnailViewImageBitmap; 115 ImageView iconView; 116 TextView labelView; 117 TextView descriptionView; 118 View calloutLine; 119 TaskDescription taskDescription; 120 boolean loadedThumbnailAndIcon; 121 } 122 123 /* package */ final class TaskDescriptionAdapter extends BaseAdapter { 124 private LayoutInflater mInflater; 125 126 public TaskDescriptionAdapter(Context context) { 127 mInflater = LayoutInflater.from(context); 128 } 129 130 public int getCount() { 131 return mRecentTaskDescriptions != null ? mRecentTaskDescriptions.size() : 0; 132 } 133 134 public Object getItem(int position) { 135 return position; // we only need the index 136 } 137 138 public long getItemId(int position) { 139 return position; // we just need something unique for this position 140 } 141 142 public View createView(ViewGroup parent) { 143 View convertView = mInflater.inflate(mRecentItemLayoutId, parent, false); 144 ViewHolder holder = new ViewHolder(); 145 holder.thumbnailView = convertView.findViewById(R.id.app_thumbnail); 146 holder.thumbnailViewImage = 147 (ImageView) convertView.findViewById(R.id.app_thumbnail_image); 148 // If we set the default thumbnail now, we avoid an onLayout when we update 149 // the thumbnail later (if they both have the same dimensions) 150 updateThumbnail(holder, mRecentTasksLoader.getDefaultThumbnail(), false, false); 151 holder.iconView = (ImageView) convertView.findViewById(R.id.app_icon); 152 holder.iconView.setImageBitmap(mRecentTasksLoader.getDefaultIcon()); 153 holder.labelView = (TextView) convertView.findViewById(R.id.app_label); 154 holder.calloutLine = convertView.findViewById(R.id.recents_callout_line); 155 holder.descriptionView = (TextView) convertView.findViewById(R.id.app_description); 156 157 convertView.setTag(holder); 158 return convertView; 159 } 160 161 public View getView(int position, View convertView, ViewGroup parent) { 162 if (convertView == null) { 163 convertView = createView(parent); 164 } 165 ViewHolder holder = (ViewHolder) convertView.getTag(); 166 167 // index is reverse since most recent appears at the bottom... 168 final int index = mRecentTaskDescriptions.size() - position - 1; 169 170 final TaskDescription td = mRecentTaskDescriptions.get(index); 171 172 holder.labelView.setText(td.getLabel()); 173 holder.thumbnailView.setContentDescription(td.getLabel()); 174 holder.loadedThumbnailAndIcon = td.isLoaded(); 175 if (td.isLoaded()) { 176 updateThumbnail(holder, td.getThumbnail(), true, false); 177 updateIcon(holder, td.getIcon(), true, false); 178 mNumItemsWaitingForThumbnailsAndIcons--; 179 } 180 if (index == 0) { 181 final Activity activity = (Activity) RecentsPanelView.this.getContext(); 182 if (mWaitingForWindowAnimation) { 183 if (mItemToAnimateInWhenWindowAnimationIsFinished != null) { 184 for (View v : 185 new View[] { holder.iconView, holder.labelView, holder.calloutLine }) { 186 if (v != null) { 187 v.setAlpha(1f); 188 v.setTranslationX(0f); 189 v.setTranslationY(0f); 190 } 191 } 192 } 193 mItemToAnimateInWhenWindowAnimationIsFinished = holder; 194 final int translation = -getResources().getDimensionPixelSize( 195 R.dimen.status_bar_recents_app_icon_translate_distance); 196 final Configuration config = getResources().getConfiguration(); 197 if (config.orientation == Configuration.ORIENTATION_PORTRAIT) { 198 for (View v : 199 new View[] { holder.iconView, holder.labelView, holder.calloutLine }) { 200 if (v != null) { 201 v.setAlpha(0f); 202 v.setTranslationX(translation); 203 } 204 } 205 } else { 206 holder.iconView.setAlpha(0f); 207 holder.iconView.setTranslationY(translation); 208 } 209 } 210 } 211 212 holder.thumbnailView.setTag(td); 213 holder.thumbnailView.setOnLongClickListener(new OnLongClickDelegate(convertView)); 214 holder.taskDescription = td; 215 return convertView; 216 } 217 218 public void recycleView(View v) { 219 ViewHolder holder = (ViewHolder) v.getTag(); 220 updateThumbnail(holder, mRecentTasksLoader.getDefaultThumbnail(), false, false); 221 holder.iconView.setImageBitmap(mRecentTasksLoader.getDefaultIcon()); 222 holder.iconView.setVisibility(INVISIBLE); 223 holder.labelView.setText(null); 224 holder.thumbnailView.setContentDescription(null); 225 holder.thumbnailView.setTag(null); 226 holder.thumbnailView.setOnLongClickListener(null); 227 holder.thumbnailView.setVisibility(INVISIBLE); 228 holder.taskDescription = null; 229 holder.loadedThumbnailAndIcon = false; 230 } 231 } 232 233 public RecentsPanelView(Context context, AttributeSet attrs) { 234 this(context, attrs, 0); 235 } 236 237 public RecentsPanelView(Context context, AttributeSet attrs, int defStyle) { 238 super(context, attrs, defStyle); 239 updateValuesFromResources(); 240 241 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecentsPanelView, 242 defStyle, 0); 243 244 mRecentItemLayoutId = a.getResourceId(R.styleable.RecentsPanelView_recentItemLayout, 0); 245 mRecentTasksLoader = RecentTasksLoader.getInstance(context); 246 a.recycle(); 247 } 248 249 public int numItemsInOneScreenful() { 250 if (mRecentsContainer instanceof RecentsScrollView){ 251 RecentsScrollView scrollView 252 = (RecentsScrollView) mRecentsContainer; 253 return scrollView.numItemsInOneScreenful(); 254 } else { 255 throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView"); 256 } 257 } 258 259 private boolean pointInside(int x, int y, View v) { 260 final int l = v.getLeft(); 261 final int r = v.getRight(); 262 final int t = v.getTop(); 263 final int b = v.getBottom(); 264 return x >= l && x < r && y >= t && y < b; 265 } 266 267 public boolean isInContentArea(int x, int y) { 268 if (pointInside(x, y, mRecentsContainer)) { 269 return true; 270 } else if (mStatusBarTouchProxy != null && 271 pointInside(x, y, mStatusBarTouchProxy)) { 272 return true; 273 } else { 274 return false; 275 } 276 } 277 278 public void show(boolean show) { 279 show(show, null, false, false); 280 } 281 282 public void show(boolean show, ArrayList<TaskDescription> recentTaskDescriptions, 283 boolean firstScreenful, boolean waitingForWindowAnimation) { 284 mWaitingForWindowAnimation = waitingForWindowAnimation; 285 if (show) { 286 mWaitingToShow = true; 287 refreshRecentTasksList(recentTaskDescriptions, firstScreenful); 288 showIfReady(); 289 } else { 290 showImpl(false); 291 } 292 } 293 294 private void showIfReady() { 295 // mWaitingToShow => there was a touch up on the recents button 296 // mRecentTaskDescriptions != null => we've created views for the first screenful of items 297 if (mWaitingToShow && mRecentTaskDescriptions != null) { 298 showImpl(true); 299 } 300 } 301 302 static void sendCloseSystemWindows(Context context, String reason) { 303 if (ActivityManagerNative.isSystemReady()) { 304 try { 305 ActivityManagerNative.getDefault().closeSystemDialogs(reason); 306 } catch (RemoteException e) { 307 } 308 } 309 } 310 311 private void showImpl(boolean show) { 312 sendCloseSystemWindows(mContext, BaseStatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS); 313 314 mShowing = show; 315 316 if (show) { 317 // if there are no apps, bring up a "No recent apps" message 318 boolean noApps = mRecentTaskDescriptions != null 319 && (mRecentTaskDescriptions.size() == 0); 320 mRecentsNoApps.setAlpha(1f); 321 mRecentsNoApps.setVisibility(noApps ? View.VISIBLE : View.INVISIBLE); 322 323 onAnimationEnd(null); 324 setFocusable(true); 325 setFocusableInTouchMode(true); 326 requestFocus(); 327 } else { 328 mWaitingToShow = false; 329 // call onAnimationEnd() and clearRecentTasksList() in onUiHidden() 330 if (mPopup != null) { 331 mPopup.dismiss(); 332 } 333 } 334 } 335 336 public void onUiHidden() { 337 if (!mShowing && mRecentTaskDescriptions != null) { 338 onAnimationEnd(null); 339 clearRecentTasksList(); 340 } 341 } 342 343 public void dismiss() { 344 ((RecentsActivity) mContext).dismissAndGoHome(); 345 } 346 347 public void dismissAndGoBack() { 348 ((RecentsActivity) mContext).dismissAndGoBack(); 349 } 350 351 public void onAnimationCancel(Animator animation) { 352 } 353 354 public void onAnimationEnd(Animator animation) { 355 if (mShowing) { 356 final LayoutTransition transitioner = new LayoutTransition(); 357 ((ViewGroup)mRecentsContainer).setLayoutTransition(transitioner); 358 createCustomAnimations(transitioner); 359 } else { 360 ((ViewGroup)mRecentsContainer).setLayoutTransition(null); 361 } 362 } 363 364 public void onAnimationRepeat(Animator animation) { 365 } 366 367 public void onAnimationStart(Animator animation) { 368 } 369 370 @Override 371 public boolean dispatchHoverEvent(MotionEvent event) { 372 // Ignore hover events outside of this panel bounds since such events 373 // generate spurious accessibility events with the panel content when 374 // tapping outside of it, thus confusing the user. 375 final int x = (int) event.getX(); 376 final int y = (int) event.getY(); 377 if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) { 378 return super.dispatchHoverEvent(event); 379 } 380 return true; 381 } 382 383 /** 384 * Whether the panel is showing, or, if it's animating, whether it will be 385 * when the animation is done. 386 */ 387 public boolean isShowing() { 388 return mShowing; 389 } 390 391 public void setStatusBarView(View statusBarView) { 392 if (mStatusBarTouchProxy != null) { 393 mStatusBarTouchProxy.setStatusBar(statusBarView); 394 } 395 } 396 397 public void setRecentTasksLoader(RecentTasksLoader loader) { 398 mRecentTasksLoader = loader; 399 } 400 401 public void updateValuesFromResources() { 402 final Resources res = mContext.getResources(); 403 mThumbnailWidth = Math.round(res.getDimension(R.dimen.status_bar_recents_thumbnail_width)); 404 mFitThumbnailToXY = res.getBoolean(R.bool.config_recents_thumbnail_image_fits_to_xy); 405 } 406 407 @Override 408 protected void onFinishInflate() { 409 super.onFinishInflate(); 410 411 mRecentsContainer = (ViewGroup) findViewById(R.id.recents_container); 412 mStatusBarTouchProxy = (StatusBarTouchProxy) findViewById(R.id.status_bar_touch_proxy); 413 mListAdapter = new TaskDescriptionAdapter(mContext); 414 if (mRecentsContainer instanceof RecentsScrollView){ 415 RecentsScrollView scrollView 416 = (RecentsScrollView) mRecentsContainer; 417 scrollView.setAdapter(mListAdapter); 418 scrollView.setCallback(this); 419 } else { 420 throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView"); 421 } 422 423 mRecentsScrim = findViewById(R.id.recents_bg_protect); 424 mRecentsNoApps = findViewById(R.id.recents_no_apps); 425 426 if (mRecentsScrim != null) { 427 mHighEndGfx = ActivityManager.isHighEndGfx(); 428 if (!mHighEndGfx) { 429 mRecentsScrim.setBackground(null); 430 } else if (mRecentsScrim.getBackground() instanceof BitmapDrawable) { 431 // In order to save space, we make the background texture repeat in the Y direction 432 ((BitmapDrawable) mRecentsScrim.getBackground()).setTileModeY(TileMode.REPEAT); 433 } 434 } 435 } 436 437 public void setMinSwipeAlpha(float minAlpha) { 438 if (mRecentsContainer instanceof RecentsScrollView){ 439 RecentsScrollView scrollView 440 = (RecentsScrollView) mRecentsContainer; 441 scrollView.setMinSwipeAlpha(minAlpha); 442 } 443 } 444 445 private void createCustomAnimations(LayoutTransition transitioner) { 446 transitioner.setDuration(200); 447 transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0); 448 transitioner.setAnimator(LayoutTransition.DISAPPEARING, null); 449 } 450 451 private void updateIcon(ViewHolder h, Drawable icon, boolean show, boolean anim) { 452 if (icon != null) { 453 h.iconView.setImageDrawable(icon); 454 if (show && h.iconView.getVisibility() != View.VISIBLE) { 455 if (anim) { 456 h.iconView.setAnimation( 457 AnimationUtils.loadAnimation(mContext, R.anim.recent_appear)); 458 } 459 h.iconView.setVisibility(View.VISIBLE); 460 } 461 } 462 } 463 464 private void updateThumbnail(ViewHolder h, Bitmap thumbnail, boolean show, boolean anim) { 465 if (thumbnail != null) { 466 // Should remove the default image in the frame 467 // that this now covers, to improve scrolling speed. 468 // That can't be done until the anim is complete though. 469 h.thumbnailViewImage.setImageBitmap(thumbnail); 470 471 // scale the image to fill the full width of the ImageView. do this only if 472 // we haven't set a bitmap before, or if the bitmap size has changed 473 if (h.thumbnailViewImageBitmap == null || 474 h.thumbnailViewImageBitmap.getWidth() != thumbnail.getWidth() || 475 h.thumbnailViewImageBitmap.getHeight() != thumbnail.getHeight()) { 476 if (mFitThumbnailToXY) { 477 h.thumbnailViewImage.setScaleType(ScaleType.FIT_XY); 478 } else { 479 Matrix scaleMatrix = new Matrix(); 480 float scale = mThumbnailWidth / (float) thumbnail.getWidth(); 481 scaleMatrix.setScale(scale, scale); 482 h.thumbnailViewImage.setScaleType(ScaleType.MATRIX); 483 h.thumbnailViewImage.setImageMatrix(scaleMatrix); 484 } 485 } 486 if (show && h.thumbnailView.getVisibility() != View.VISIBLE) { 487 if (anim) { 488 h.thumbnailView.setAnimation( 489 AnimationUtils.loadAnimation(mContext, R.anim.recent_appear)); 490 } 491 h.thumbnailView.setVisibility(View.VISIBLE); 492 } 493 h.thumbnailViewImageBitmap = thumbnail; 494 } 495 } 496 497 void onTaskThumbnailLoaded(TaskDescription td) { 498 synchronized (td) { 499 if (mRecentsContainer != null) { 500 ViewGroup container = mRecentsContainer; 501 if (container instanceof RecentsScrollView) { 502 container = (ViewGroup) container.findViewById( 503 R.id.recents_linear_layout); 504 } 505 // Look for a view showing this thumbnail, to update. 506 for (int i=0; i < container.getChildCount(); i++) { 507 View v = container.getChildAt(i); 508 if (v.getTag() instanceof ViewHolder) { 509 ViewHolder h = (ViewHolder)v.getTag(); 510 if (!h.loadedThumbnailAndIcon && h.taskDescription == td) { 511 // only fade in the thumbnail if recents is already visible-- we 512 // show it immediately otherwise 513 //boolean animateShow = mShowing && 514 // mRecentsContainer.getAlpha() > ViewConfiguration.ALPHA_THRESHOLD; 515 boolean animateShow = false; 516 updateIcon(h, td.getIcon(), true, animateShow); 517 updateThumbnail(h, td.getThumbnail(), true, animateShow); 518 h.loadedThumbnailAndIcon = true; 519 mNumItemsWaitingForThumbnailsAndIcons--; 520 } 521 } 522 } 523 } 524 } 525 showIfReady(); 526 } 527 528 public void onWindowAnimationStart() { 529 if (mItemToAnimateInWhenWindowAnimationIsFinished != null) { 530 final int startDelay = 150; 531 final int duration = 250; 532 final ViewHolder holder = mItemToAnimateInWhenWindowAnimationIsFinished; 533 final TimeInterpolator cubic = new DecelerateInterpolator(1.5f); 534 for (View v : 535 new View[] { holder.iconView, holder.labelView, holder.calloutLine }) { 536 if (v != null) { 537 v.animate().translationX(0).translationY(0).alpha(1f).setStartDelay(startDelay) 538 .setDuration(duration).setInterpolator(cubic); 539 } 540 } 541 mItemToAnimateInWhenWindowAnimationIsFinished = null; 542 mWaitingForWindowAnimation = false; 543 } 544 } 545 546 public void clearRecentTasksList() { 547 // Clear memory used by screenshots 548 if (mRecentTaskDescriptions != null) { 549 mRecentTasksLoader.cancelLoadingThumbnailsAndIcons(this); 550 onTaskLoadingCancelled(); 551 } 552 } 553 554 public void onTaskLoadingCancelled() { 555 // Gets called by RecentTasksLoader when it's cancelled 556 if (mRecentTaskDescriptions != null) { 557 mRecentTaskDescriptions = null; 558 mListAdapter.notifyDataSetInvalidated(); 559 } 560 } 561 562 public void refreshViews() { 563 mListAdapter.notifyDataSetInvalidated(); 564 updateUiElements(); 565 showIfReady(); 566 } 567 568 public void refreshRecentTasksList() { 569 refreshRecentTasksList(null, false); 570 } 571 572 private void refreshRecentTasksList( 573 ArrayList<TaskDescription> recentTasksList, boolean firstScreenful) { 574 if (mRecentTaskDescriptions == null && recentTasksList != null) { 575 onTasksLoaded(recentTasksList, firstScreenful); 576 } else { 577 mRecentTasksLoader.loadTasksInBackground(); 578 } 579 } 580 581 public void onTasksLoaded(ArrayList<TaskDescription> tasks, boolean firstScreenful) { 582 mNumItemsWaitingForThumbnailsAndIcons = firstScreenful 583 ? tasks.size() : mRecentTaskDescriptions == null 584 ? 0 : mRecentTaskDescriptions.size(); 585 if (mRecentTaskDescriptions == null) { 586 mRecentTaskDescriptions = new ArrayList<TaskDescription>(tasks); 587 } else { 588 mRecentTaskDescriptions.addAll(tasks); 589 } 590 if (((RecentsActivity) mContext).isActivityShowing()) { 591 refreshViews(); 592 } 593 } 594 595 private void updateUiElements() { 596 final int items = mRecentTaskDescriptions != null 597 ? mRecentTaskDescriptions.size() : 0; 598 599 mRecentsContainer.setVisibility(items > 0 ? View.VISIBLE : View.GONE); 600 601 // Set description for accessibility 602 int numRecentApps = mRecentTaskDescriptions != null 603 ? mRecentTaskDescriptions.size() : 0; 604 String recentAppsAccessibilityDescription; 605 if (numRecentApps == 0) { 606 recentAppsAccessibilityDescription = 607 getResources().getString(R.string.status_bar_no_recent_apps); 608 } else { 609 recentAppsAccessibilityDescription = getResources().getQuantityString( 610 R.plurals.status_bar_accessibility_recent_apps, numRecentApps, numRecentApps); 611 } 612 setContentDescription(recentAppsAccessibilityDescription); 613 } 614 615 public boolean simulateClick(int persistentTaskId) { 616 if (mRecentsContainer instanceof RecentsScrollView){ 617 RecentsScrollView scrollView 618 = (RecentsScrollView) mRecentsContainer; 619 View v = scrollView.findViewForTask(persistentTaskId); 620 if (v != null) { 621 handleOnClick(v); 622 return true; 623 } 624 } 625 return false; 626 } 627 628 public void handleOnClick(View view) { 629 ViewHolder holder = (ViewHolder)view.getTag(); 630 TaskDescription ad = holder.taskDescription; 631 final Context context = view.getContext(); 632 final ActivityManager am = (ActivityManager) 633 context.getSystemService(Context.ACTIVITY_SERVICE); 634 Bitmap bm = holder.thumbnailViewImageBitmap; 635 boolean usingDrawingCache; 636 if (bm.getWidth() == holder.thumbnailViewImage.getWidth() && 637 bm.getHeight() == holder.thumbnailViewImage.getHeight()) { 638 usingDrawingCache = false; 639 } else { 640 holder.thumbnailViewImage.setDrawingCacheEnabled(true); 641 bm = holder.thumbnailViewImage.getDrawingCache(); 642 usingDrawingCache = true; 643 } 644 Bundle opts = (bm == null) ? 645 null : 646 ActivityOptions.makeThumbnailScaleUpAnimation( 647 holder.thumbnailViewImage, bm, 0, 0, null).toBundle(); 648 649 show(false); 650 if (ad.taskId >= 0) { 651 // This is an active task; it should just go to the foreground. 652 am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HOME, 653 opts); 654 } else { 655 Intent intent = ad.intent; 656 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY 657 | Intent.FLAG_ACTIVITY_TASK_ON_HOME 658 | Intent.FLAG_ACTIVITY_NEW_TASK); 659 if (DEBUG) Log.v(TAG, "Starting activity " + intent); 660 context.startActivityAsUser(intent, opts, 661 new UserHandle(UserHandle.USER_CURRENT)); 662 } 663 if (usingDrawingCache) { 664 holder.thumbnailViewImage.setDrawingCacheEnabled(false); 665 } 666 } 667 668 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 669 handleOnClick(view); 670 } 671 672 public void handleSwipe(View view) { 673 TaskDescription ad = ((ViewHolder) view.getTag()).taskDescription; 674 if (ad == null) { 675 Log.v(TAG, "Not able to find activity description for swiped task; view=" + view + 676 " tag=" + view.getTag()); 677 return; 678 } 679 if (DEBUG) Log.v(TAG, "Jettison " + ad.getLabel()); 680 mRecentTaskDescriptions.remove(ad); 681 682 // Handled by widget containers to enable LayoutTransitions properly 683 // mListAdapter.notifyDataSetChanged(); 684 685 if (mRecentTaskDescriptions.size() == 0) { 686 dismissAndGoBack(); 687 } 688 689 // Currently, either direction means the same thing, so ignore direction and remove 690 // the task. 691 final ActivityManager am = (ActivityManager) 692 mContext.getSystemService(Context.ACTIVITY_SERVICE); 693 if (am != null) { 694 am.removeTask(ad.persistentTaskId, ActivityManager.REMOVE_TASK_KILL_PROCESS); 695 696 // Accessibility feedback 697 setContentDescription( 698 mContext.getString(R.string.accessibility_recents_item_dismissed, ad.getLabel())); 699 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); 700 setContentDescription(null); 701 } 702 } 703 704 private void startApplicationDetailsActivity(String packageName) { 705 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, 706 Uri.fromParts("package", packageName, null)); 707 intent.setComponent(intent.resolveActivity(mContext.getPackageManager())); 708 TaskStackBuilder.create(getContext()) 709 .addNextIntentWithParentStack(intent).startActivities(); 710 } 711 712 public boolean onInterceptTouchEvent(MotionEvent ev) { 713 if (mPopup != null) { 714 return true; 715 } else { 716 return super.onInterceptTouchEvent(ev); 717 } 718 } 719 720 public void handleLongPress( 721 final View selectedView, final View anchorView, final View thumbnailView) { 722 thumbnailView.setSelected(true); 723 final PopupMenu popup = 724 new PopupMenu(mContext, anchorView == null ? selectedView : anchorView); 725 mPopup = popup; 726 popup.getMenuInflater().inflate(R.menu.recent_popup_menu, popup.getMenu()); 727 popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { 728 public boolean onMenuItemClick(MenuItem item) { 729 if (item.getItemId() == R.id.recent_remove_item) { 730 mRecentsContainer.removeViewInLayout(selectedView); 731 } else if (item.getItemId() == R.id.recent_inspect_item) { 732 ViewHolder viewHolder = (ViewHolder) selectedView.getTag(); 733 if (viewHolder != null) { 734 final TaskDescription ad = viewHolder.taskDescription; 735 startApplicationDetailsActivity(ad.packageName); 736 show(false); 737 } else { 738 throw new IllegalStateException("Oops, no tag on view " + selectedView); 739 } 740 } else { 741 return false; 742 } 743 return true; 744 } 745 }); 746 popup.setOnDismissListener(new PopupMenu.OnDismissListener() { 747 public void onDismiss(PopupMenu menu) { 748 thumbnailView.setSelected(false); 749 mPopup = null; 750 } 751 }); 752 popup.show(); 753 } 754} 755