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