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