AppsCustomizePagedView.java revision 8c3339bde96d11fd1d7f818cd39e7438e309ffd1
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.launcher2; 18 19import android.animation.AnimatorSet; 20import android.animation.ValueAnimator; 21import android.appwidget.AppWidgetHostView; 22import android.appwidget.AppWidgetManager; 23import android.appwidget.AppWidgetProviderInfo; 24import android.content.ComponentName; 25import android.content.Context; 26import android.content.Intent; 27import android.content.pm.PackageManager; 28import android.content.pm.ResolveInfo; 29import android.content.res.Configuration; 30import android.content.res.Resources; 31import android.content.res.TypedArray; 32import android.graphics.Bitmap; 33import android.graphics.Bitmap.Config; 34import android.graphics.Canvas; 35import android.graphics.ColorMatrix; 36import android.graphics.ColorMatrixColorFilter; 37import android.graphics.MaskFilter; 38import android.graphics.Matrix; 39import android.graphics.Paint; 40import android.graphics.PorterDuff; 41import android.graphics.Rect; 42import android.graphics.RectF; 43import android.graphics.Shader; 44import android.graphics.drawable.BitmapDrawable; 45import android.graphics.drawable.Drawable; 46import android.os.AsyncTask; 47import android.os.Process; 48import android.util.AttributeSet; 49import android.util.Log; 50import android.view.Gravity; 51import android.view.KeyEvent; 52import android.view.LayoutInflater; 53import android.view.MotionEvent; 54import android.view.View; 55import android.view.ViewGroup; 56import android.view.animation.AccelerateInterpolator; 57import android.view.animation.DecelerateInterpolator; 58import android.widget.GridLayout; 59import android.widget.ImageView; 60import android.widget.Toast; 61 62import com.android.launcher.R; 63import com.android.launcher2.DropTarget.DragObject; 64 65import java.lang.ref.WeakReference; 66import java.util.ArrayList; 67import java.util.Collections; 68import java.util.Iterator; 69import java.util.List; 70 71/** 72 * A simple callback interface which also provides the results of the task. 73 */ 74interface AsyncTaskCallback { 75 void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data); 76} 77 78/** 79 * The data needed to perform either of the custom AsyncTasks. 80 */ 81class AsyncTaskPageData { 82 enum Type { 83 LoadWidgetPreviewData 84 } 85 86 AsyncTaskPageData(int p, ArrayList<Object> l, ArrayList<Bitmap> si, AsyncTaskCallback bgR, 87 AsyncTaskCallback postR) { 88 page = p; 89 items = l; 90 sourceImages = si; 91 generatedImages = new ArrayList<Bitmap>(); 92 maxImageWidth = maxImageHeight = -1; 93 doInBackgroundCallback = bgR; 94 postExecuteCallback = postR; 95 } 96 AsyncTaskPageData(int p, ArrayList<Object> l, int cw, int ch, AsyncTaskCallback bgR, 97 AsyncTaskCallback postR) { 98 page = p; 99 items = l; 100 generatedImages = new ArrayList<Bitmap>(); 101 maxImageWidth = cw; 102 maxImageHeight = ch; 103 doInBackgroundCallback = bgR; 104 postExecuteCallback = postR; 105 } 106 void cleanup(boolean cancelled) { 107 // Clean up any references to source/generated bitmaps 108 if (sourceImages != null) { 109 if (cancelled) { 110 for (Bitmap b : sourceImages) { 111 b.recycle(); 112 } 113 } 114 sourceImages.clear(); 115 } 116 if (generatedImages != null) { 117 if (cancelled) { 118 for (Bitmap b : generatedImages) { 119 b.recycle(); 120 } 121 } 122 generatedImages.clear(); 123 } 124 } 125 int page; 126 ArrayList<Object> items; 127 ArrayList<Bitmap> sourceImages; 128 ArrayList<Bitmap> generatedImages; 129 int maxImageWidth; 130 int maxImageHeight; 131 AsyncTaskCallback doInBackgroundCallback; 132 AsyncTaskCallback postExecuteCallback; 133} 134 135/** 136 * A generic template for an async task used in AppsCustomize. 137 */ 138class AppsCustomizeAsyncTask extends AsyncTask<AsyncTaskPageData, Void, AsyncTaskPageData> { 139 AppsCustomizeAsyncTask(int p, AsyncTaskPageData.Type ty) { 140 page = p; 141 threadPriority = Process.THREAD_PRIORITY_DEFAULT; 142 dataType = ty; 143 } 144 @Override 145 protected AsyncTaskPageData doInBackground(AsyncTaskPageData... params) { 146 if (params.length != 1) return null; 147 // Load each of the widget previews in the background 148 params[0].doInBackgroundCallback.run(this, params[0]); 149 return params[0]; 150 } 151 @Override 152 protected void onPostExecute(AsyncTaskPageData result) { 153 // All the widget previews are loaded, so we can just callback to inflate the page 154 result.postExecuteCallback.run(this, result); 155 } 156 157 void setThreadPriority(int p) { 158 threadPriority = p; 159 } 160 void syncThreadPriority() { 161 Process.setThreadPriority(threadPriority); 162 } 163 164 // The page that this async task is associated with 165 AsyncTaskPageData.Type dataType; 166 int page; 167 int threadPriority; 168} 169 170abstract class WeakReferenceThreadLocal<T> { 171 private ThreadLocal<WeakReference<T>> mThreadLocal; 172 public WeakReferenceThreadLocal() { 173 mThreadLocal = new ThreadLocal<WeakReference<T>>(); 174 } 175 176 abstract T initialValue(); 177 178 public void set(T t) { 179 mThreadLocal.set(new WeakReference<T>(t)); 180 } 181 182 public T get() { 183 WeakReference<T> reference = mThreadLocal.get(); 184 T obj; 185 if (reference == null) { 186 obj = initialValue(); 187 mThreadLocal.set(new WeakReference<T>(obj)); 188 return obj; 189 } else { 190 obj = reference.get(); 191 if (obj == null) { 192 obj = initialValue(); 193 mThreadLocal.set(new WeakReference<T>(obj)); 194 } 195 return obj; 196 } 197 } 198} 199 200class CanvasCache extends WeakReferenceThreadLocal<Canvas> { 201 @Override 202 protected Canvas initialValue() { 203 return new Canvas(); 204 } 205} 206 207class PaintCache extends WeakReferenceThreadLocal<Paint> { 208 @Override 209 protected Paint initialValue() { 210 return null; 211 } 212} 213 214class BitmapCache extends WeakReferenceThreadLocal<Bitmap> { 215 @Override 216 protected Bitmap initialValue() { 217 return null; 218 } 219} 220 221class RectCache extends WeakReferenceThreadLocal<Rect> { 222 @Override 223 protected Rect initialValue() { 224 return new Rect(); 225 } 226} 227 228/** 229 * The Apps/Customize page that displays all the applications, widgets, and shortcuts. 230 */ 231public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements 232 View.OnClickListener, View.OnKeyListener, DragSource, 233 PagedViewIcon.PressedCallback, PagedViewWidget.ShortPressListener, 234 LauncherTransitionable { 235 static final String TAG = "AppsCustomizePagedView"; 236 237 /** 238 * The different content types that this paged view can show. 239 */ 240 public enum ContentType { 241 Applications, 242 Widgets 243 } 244 245 // Refs 246 private Launcher mLauncher; 247 private DragController mDragController; 248 private final LayoutInflater mLayoutInflater; 249 private final PackageManager mPackageManager; 250 251 // Save and Restore 252 private int mSaveInstanceStateItemIndex = -1; 253 private PagedViewIcon mPressedIcon; 254 255 // Content 256 private ArrayList<ApplicationInfo> mApps; 257 private ArrayList<Object> mWidgets; 258 259 // Cling 260 private boolean mHasShownAllAppsCling; 261 private int mClingFocusedX; 262 private int mClingFocusedY; 263 264 // Caching 265 private Canvas mCanvas; 266 private IconCache mIconCache; 267 268 // Dimens 269 private int mContentWidth; 270 private int mAppIconSize; 271 private int mMaxAppCellCountX, mMaxAppCellCountY; 272 private int mWidgetCountX, mWidgetCountY; 273 private int mWidgetWidthGap, mWidgetHeightGap; 274 private final float sWidgetPreviewIconPaddingPercentage = 0.25f; 275 private PagedViewCellLayout mWidgetSpacingLayout; 276 private int mNumAppsPages; 277 private int mNumWidgetPages; 278 279 // Relating to the scroll and overscroll effects 280 Workspace.ZInterpolator mZInterpolator = new Workspace.ZInterpolator(0.5f); 281 private static float CAMERA_DISTANCE = 6500; 282 private static float TRANSITION_SCALE_FACTOR = 0.74f; 283 private static float TRANSITION_PIVOT = 0.65f; 284 private static float TRANSITION_MAX_ROTATION = 22; 285 private static final boolean PERFORM_OVERSCROLL_ROTATION = true; 286 private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f); 287 private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4); 288 289 // Previews & outlines 290 ArrayList<AppsCustomizeAsyncTask> mRunningTasks; 291 private static final int sPageSleepDelay = 200; 292 293 private Runnable mInflateWidgetRunnable = null; 294 private Runnable mBindWidgetRunnable = null; 295 static final int WIDGET_NO_CLEANUP_REQUIRED = -1; 296 static final int WIDGET_PRELOAD_PENDING = 0; 297 static final int WIDGET_BOUND = 1; 298 static final int WIDGET_INFLATED = 2; 299 int mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED; 300 int mWidgetLoadingId = -1; 301 PendingAddWidgetInfo mCreateWidgetInfo = null; 302 private boolean mDraggingWidget = false; 303 304 // Deferral of loading widget previews during launcher transitions 305 private boolean mInTransition; 306 private ArrayList<AsyncTaskPageData> mDeferredSyncWidgetPageItems = 307 new ArrayList<AsyncTaskPageData>(); 308 private ArrayList<Runnable> mDeferredPrepareLoadWidgetPreviewsTasks = 309 new ArrayList<Runnable>(); 310 311 // Used for drawing shortcut previews 312 BitmapCache mCachedShortcutPreviewBitmap = new BitmapCache(); 313 PaintCache mCachedShortcutPreviewPaint = new PaintCache(); 314 CanvasCache mCachedShortcutPreviewCanvas = new CanvasCache(); 315 316 // Used for drawing widget previews 317 CanvasCache mCachedAppWidgetPreviewCanvas = new CanvasCache(); 318 RectCache mCachedAppWidgetPreviewSrcRect = new RectCache(); 319 RectCache mCachedAppWidgetPreviewDestRect = new RectCache(); 320 PaintCache mCachedAppWidgetPreviewPaint = new PaintCache(); 321 322 public AppsCustomizePagedView(Context context, AttributeSet attrs) { 323 super(context, attrs); 324 mLayoutInflater = LayoutInflater.from(context); 325 mPackageManager = context.getPackageManager(); 326 mApps = new ArrayList<ApplicationInfo>(); 327 mWidgets = new ArrayList<Object>(); 328 mIconCache = ((LauncherApplication) context.getApplicationContext()).getIconCache(); 329 mCanvas = new Canvas(); 330 mRunningTasks = new ArrayList<AppsCustomizeAsyncTask>(); 331 332 // Save the default widget preview background 333 Resources resources = context.getResources(); 334 mAppIconSize = resources.getDimensionPixelSize(R.dimen.app_icon_size); 335 336 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0); 337 mMaxAppCellCountX = a.getInt(R.styleable.AppsCustomizePagedView_maxAppCellCountX, -1); 338 mMaxAppCellCountY = a.getInt(R.styleable.AppsCustomizePagedView_maxAppCellCountY, -1); 339 mWidgetWidthGap = 340 a.getDimensionPixelSize(R.styleable.AppsCustomizePagedView_widgetCellWidthGap, 0); 341 mWidgetHeightGap = 342 a.getDimensionPixelSize(R.styleable.AppsCustomizePagedView_widgetCellHeightGap, 0); 343 mWidgetCountX = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountX, 2); 344 mWidgetCountY = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountY, 2); 345 mClingFocusedX = a.getInt(R.styleable.AppsCustomizePagedView_clingFocusedX, 0); 346 mClingFocusedY = a.getInt(R.styleable.AppsCustomizePagedView_clingFocusedY, 0); 347 a.recycle(); 348 mWidgetSpacingLayout = new PagedViewCellLayout(getContext()); 349 350 // The padding on the non-matched dimension for the default widget preview icons 351 // (top + bottom) 352 mFadeInAdjacentScreens = false; 353 354 // Unless otherwise specified this view is important for accessibility. 355 if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 356 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); 357 } 358 } 359 360 @Override 361 protected void init() { 362 super.init(); 363 mCenterPagesVertically = false; 364 365 Context context = getContext(); 366 Resources r = context.getResources(); 367 setDragSlopeThreshold(r.getInteger(R.integer.config_appsCustomizeDragSlopeThreshold)/100f); 368 } 369 370 @Override 371 protected void onUnhandledTap(MotionEvent ev) { 372 if (LauncherApplication.isScreenLarge()) { 373 // Dismiss AppsCustomize if we tap 374 mLauncher.showWorkspace(true); 375 } 376 } 377 378 /** Returns the item index of the center item on this page so that we can restore to this 379 * item index when we rotate. */ 380 private int getMiddleComponentIndexOnCurrentPage() { 381 int i = -1; 382 if (getPageCount() > 0) { 383 int currentPage = getCurrentPage(); 384 if (currentPage < mNumAppsPages) { 385 PagedViewCellLayout layout = (PagedViewCellLayout) getPageAt(currentPage); 386 PagedViewCellLayoutChildren childrenLayout = layout.getChildrenLayout(); 387 int numItemsPerPage = mCellCountX * mCellCountY; 388 int childCount = childrenLayout.getChildCount(); 389 if (childCount > 0) { 390 i = (currentPage * numItemsPerPage) + (childCount / 2); 391 } 392 } else { 393 int numApps = mApps.size(); 394 PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(currentPage); 395 int numItemsPerPage = mWidgetCountX * mWidgetCountY; 396 int childCount = layout.getChildCount(); 397 if (childCount > 0) { 398 i = numApps + 399 ((currentPage - mNumAppsPages) * numItemsPerPage) + (childCount / 2); 400 } 401 } 402 } 403 return i; 404 } 405 406 /** Get the index of the item to restore to if we need to restore the current page. */ 407 int getSaveInstanceStateIndex() { 408 if (mSaveInstanceStateItemIndex == -1) { 409 mSaveInstanceStateItemIndex = getMiddleComponentIndexOnCurrentPage(); 410 } 411 return mSaveInstanceStateItemIndex; 412 } 413 414 /** Returns the page in the current orientation which is expected to contain the specified 415 * item index. */ 416 int getPageForComponent(int index) { 417 if (index < 0) return 0; 418 419 if (index < mApps.size()) { 420 int numItemsPerPage = mCellCountX * mCellCountY; 421 return (index / numItemsPerPage); 422 } else { 423 int numItemsPerPage = mWidgetCountX * mWidgetCountY; 424 return mNumAppsPages + ((index - mApps.size()) / numItemsPerPage); 425 } 426 } 427 428 /** Restores the page for an item at the specified index */ 429 void restorePageForIndex(int index) { 430 if (index < 0) return; 431 mSaveInstanceStateItemIndex = index; 432 } 433 434 private void updatePageCounts() { 435 mNumWidgetPages = (int) Math.ceil(mWidgets.size() / 436 (float) (mWidgetCountX * mWidgetCountY)); 437 mNumAppsPages = (int) Math.ceil((float) mApps.size() / (mCellCountX * mCellCountY)); 438 } 439 440 protected void onDataReady(int width, int height) { 441 // Note that we transpose the counts in portrait so that we get a similar layout 442 boolean isLandscape = getResources().getConfiguration().orientation == 443 Configuration.ORIENTATION_LANDSCAPE; 444 int maxCellCountX = Integer.MAX_VALUE; 445 int maxCellCountY = Integer.MAX_VALUE; 446 if (LauncherApplication.isScreenLarge()) { 447 maxCellCountX = (isLandscape ? LauncherModel.getCellCountX() : 448 LauncherModel.getCellCountY()); 449 maxCellCountY = (isLandscape ? LauncherModel.getCellCountY() : 450 LauncherModel.getCellCountX()); 451 } 452 if (mMaxAppCellCountX > -1) { 453 maxCellCountX = Math.min(maxCellCountX, mMaxAppCellCountX); 454 } 455 if (mMaxAppCellCountY > -1) { 456 maxCellCountY = Math.min(maxCellCountY, mMaxAppCellCountY); 457 } 458 459 // Now that the data is ready, we can calculate the content width, the number of cells to 460 // use for each page 461 mWidgetSpacingLayout.setGap(mPageLayoutWidthGap, mPageLayoutHeightGap); 462 mWidgetSpacingLayout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop, 463 mPageLayoutPaddingRight, mPageLayoutPaddingBottom); 464 mWidgetSpacingLayout.calculateCellCount(width, height, maxCellCountX, maxCellCountY); 465 mCellCountX = mWidgetSpacingLayout.getCellCountX(); 466 mCellCountY = mWidgetSpacingLayout.getCellCountY(); 467 updatePageCounts(); 468 469 // Force a measure to update recalculate the gaps 470 int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST); 471 int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST); 472 mWidgetSpacingLayout.measure(widthSpec, heightSpec); 473 mContentWidth = mWidgetSpacingLayout.getContentWidth(); 474 475 AppsCustomizeTabHost host = (AppsCustomizeTabHost) getTabHost(); 476 final boolean hostIsTransitioning = host.isTransitioning(); 477 478 // Restore the page 479 int page = getPageForComponent(mSaveInstanceStateItemIndex); 480 invalidatePageData(Math.max(0, page), hostIsTransitioning); 481 482 // Show All Apps cling if we are finished transitioning, otherwise, we will try again when 483 // the transition completes in AppsCustomizeTabHost (otherwise the wrong offsets will be 484 // returned while animating) 485 if (!hostIsTransitioning) { 486 post(new Runnable() { 487 @Override 488 public void run() { 489 showAllAppsCling(); 490 } 491 }); 492 } 493 } 494 495 void showAllAppsCling() { 496 if (!mHasShownAllAppsCling && isDataReady()) { 497 mHasShownAllAppsCling = true; 498 // Calculate the position for the cling punch through 499 int[] offset = new int[2]; 500 int[] pos = mWidgetSpacingLayout.estimateCellPosition(mClingFocusedX, mClingFocusedY); 501 mLauncher.getDragLayer().getLocationInDragLayer(this, offset); 502 // PagedViews are centered horizontally but top aligned 503 pos[0] += (getMeasuredWidth() - mWidgetSpacingLayout.getMeasuredWidth()) / 2 + 504 offset[0]; 505 pos[1] += offset[1]; 506 mLauncher.showFirstRunAllAppsCling(pos); 507 } 508 } 509 510 @Override 511 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 512 int width = MeasureSpec.getSize(widthMeasureSpec); 513 int height = MeasureSpec.getSize(heightMeasureSpec); 514 if (!isDataReady()) { 515 if (!mApps.isEmpty() && !mWidgets.isEmpty()) { 516 setDataIsReady(); 517 setMeasuredDimension(width, height); 518 onDataReady(width, height); 519 } 520 } 521 522 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 523 } 524 525 public void onPackagesUpdated() { 526 // Get the list of widgets and shortcuts 527 mWidgets.clear(); 528 List<AppWidgetProviderInfo> widgets = 529 AppWidgetManager.getInstance(mLauncher).getInstalledProviders(); 530 Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); 531 List<ResolveInfo> shortcuts = mPackageManager.queryIntentActivities(shortcutsIntent, 0); 532 for (AppWidgetProviderInfo widget : widgets) { 533 if (widget.minWidth > 0 && widget.minHeight > 0) { 534 // Ensure that all widgets we show can be added on a workspace of this size 535 int[] spanXY = Launcher.getSpanForWidget(mLauncher, widget); 536 int[] minSpanXY = Launcher.getMinSpanForWidget(mLauncher, widget); 537 int minSpanX = Math.min(spanXY[0], minSpanXY[0]); 538 int minSpanY = Math.min(spanXY[1], minSpanXY[1]); 539 if (minSpanX <= LauncherModel.getCellCountX() && 540 minSpanY <= LauncherModel.getCellCountY()) { 541 mWidgets.add(widget); 542 } else { 543 Log.e(TAG, "Widget " + widget.provider + " can not fit on this device (" + 544 widget.minWidth + ", " + widget.minHeight + ")"); 545 } 546 } else { 547 Log.e(TAG, "Widget " + widget.provider + " has invalid dimensions (" + 548 widget.minWidth + ", " + widget.minHeight + ")"); 549 } 550 } 551 mWidgets.addAll(shortcuts); 552 Collections.sort(mWidgets, 553 new LauncherModel.WidgetAndShortcutNameComparator(mPackageManager)); 554 updatePageCounts(); 555 invalidateOnDataChange(); 556 } 557 558 @Override 559 public void onClick(View v) { 560 // When we have exited all apps or are in transition, disregard clicks 561 if (!mLauncher.isAllAppsCustomizeOpen() || 562 mLauncher.getWorkspace().isSwitchingState()) return; 563 564 if (v instanceof PagedViewIcon) { 565 // Animate some feedback to the click 566 final ApplicationInfo appInfo = (ApplicationInfo) v.getTag(); 567 568 // Lock the drawable state to pressed until we return to Launcher 569 if (mPressedIcon != null) { 570 mPressedIcon.lockDrawableState(); 571 } 572 573 // NOTE: We want all transitions from launcher to act as if the wallpaper were enabled 574 // to be consistent. So re-enable the flag here, and we will re-disable it as necessary 575 // when Launcher resumes and we are still in AllApps. 576 mLauncher.updateWallpaperVisibility(true); 577 mLauncher.startActivitySafely(v, appInfo.intent, appInfo); 578 579 } else if (v instanceof PagedViewWidget) { 580 // Let the user know that they have to long press to add a widget 581 Toast.makeText(getContext(), R.string.long_press_widget_to_add, 582 Toast.LENGTH_SHORT).show(); 583 584 // Create a little animation to show that the widget can move 585 float offsetY = getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY); 586 final ImageView p = (ImageView) v.findViewById(R.id.widget_preview); 587 AnimatorSet bounce = LauncherAnimUtils.createAnimatorSet(); 588 ValueAnimator tyuAnim = LauncherAnimUtils.ofFloat(p, "translationY", offsetY); 589 tyuAnim.setDuration(125); 590 ValueAnimator tydAnim = LauncherAnimUtils.ofFloat(p, "translationY", 0f); 591 tydAnim.setDuration(100); 592 bounce.play(tyuAnim).before(tydAnim); 593 bounce.setInterpolator(new AccelerateInterpolator()); 594 bounce.start(); 595 } 596 } 597 598 public boolean onKey(View v, int keyCode, KeyEvent event) { 599 return FocusHelper.handleAppsCustomizeKeyEvent(v, keyCode, event); 600 } 601 602 /* 603 * PagedViewWithDraggableItems implementation 604 */ 605 @Override 606 protected void determineDraggingStart(android.view.MotionEvent ev) { 607 // Disable dragging by pulling an app down for now. 608 } 609 610 private void beginDraggingApplication(View v) { 611 mLauncher.getWorkspace().onDragStartedWithItem(v); 612 mLauncher.getWorkspace().beginDragShared(v, this); 613 } 614 615 private void preloadWidget(final PendingAddWidgetInfo info) { 616 final AppWidgetProviderInfo pInfo = info.info; 617 if (pInfo.configure != null) { 618 return; 619 } 620 621 mWidgetCleanupState = WIDGET_PRELOAD_PENDING; 622 mBindWidgetRunnable = new Runnable() { 623 @Override 624 public void run() { 625 mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId(); 626 if (AppWidgetManager.getInstance(mLauncher) 627 .bindAppWidgetIdIfAllowed(mWidgetLoadingId, info.componentName)) { 628 mWidgetCleanupState = WIDGET_BOUND; 629 } 630 } 631 }; 632 post(mBindWidgetRunnable); 633 634 mInflateWidgetRunnable = new Runnable() { 635 @Override 636 public void run() { 637 AppWidgetHostView hostView = mLauncher. 638 getAppWidgetHost().createView(getContext(), mWidgetLoadingId, pInfo); 639 info.boundWidget = hostView; 640 mWidgetCleanupState = WIDGET_INFLATED; 641 hostView.setVisibility(INVISIBLE); 642 int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(info.spanX, 643 info.spanY, info, false); 644 645 // We want the first widget layout to be the correct size. This will be important 646 // for width size reporting to the AppWidgetManager. 647 DragLayer.LayoutParams lp = new DragLayer.LayoutParams(unScaledSize[0], 648 unScaledSize[1]); 649 lp.x = lp.y = 0; 650 lp.customPosition = true; 651 hostView.setLayoutParams(lp); 652 mLauncher.getDragLayer().addView(hostView); 653 } 654 }; 655 post(mInflateWidgetRunnable); 656 } 657 658 @Override 659 public void onShortPress(View v) { 660 // We are anticipating a long press, and we use this time to load bind and instantiate 661 // the widget. This will need to be cleaned up if it turns out no long press occurs. 662 if (mCreateWidgetInfo != null) { 663 // Just in case the cleanup process wasn't properly executed. This shouldn't happen. 664 cleanupWidgetPreloading(false); 665 } 666 mCreateWidgetInfo = new PendingAddWidgetInfo((PendingAddWidgetInfo) v.getTag()); 667 preloadWidget(mCreateWidgetInfo); 668 } 669 670 private void cleanupWidgetPreloading(boolean widgetWasAdded) { 671 if (!widgetWasAdded) { 672 // If the widget was not added, we may need to do further cleanup. 673 PendingAddWidgetInfo info = mCreateWidgetInfo; 674 mCreateWidgetInfo = null; 675 676 if (mWidgetCleanupState == WIDGET_PRELOAD_PENDING) { 677 // We never did any preloading, so just remove pending callbacks to do so 678 removeCallbacks(mBindWidgetRunnable); 679 removeCallbacks(mInflateWidgetRunnable); 680 } else if (mWidgetCleanupState == WIDGET_BOUND) { 681 // Delete the widget id which was allocated 682 if (mWidgetLoadingId != -1) { 683 mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); 684 } 685 686 // We never got around to inflating the widget, so remove the callback to do so. 687 removeCallbacks(mInflateWidgetRunnable); 688 } else if (mWidgetCleanupState == WIDGET_INFLATED) { 689 // Delete the widget id which was allocated 690 if (mWidgetLoadingId != -1) { 691 mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); 692 } 693 694 // The widget was inflated and added to the DragLayer -- remove it. 695 AppWidgetHostView widget = info.boundWidget; 696 mLauncher.getDragLayer().removeView(widget); 697 } 698 } 699 mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED; 700 mWidgetLoadingId = -1; 701 mCreateWidgetInfo = null; 702 PagedViewWidget.resetShortPressTarget(); 703 } 704 705 @Override 706 public void cleanUpShortPress(View v) { 707 if (!mDraggingWidget) { 708 cleanupWidgetPreloading(false); 709 } 710 } 711 712 private boolean beginDraggingWidget(View v) { 713 mDraggingWidget = true; 714 // Get the widget preview as the drag representation 715 ImageView image = (ImageView) v.findViewById(R.id.widget_preview); 716 PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag(); 717 718 // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and 719 // we abort the drag. 720 if (image.getDrawable() == null) { 721 mDraggingWidget = false; 722 return false; 723 } 724 725 // Compose the drag image 726 Bitmap preview; 727 Bitmap outline; 728 float scale = 1f; 729 if (createItemInfo instanceof PendingAddWidgetInfo) { 730 // This can happen in some weird cases involving multi-touch. We can't start dragging 731 // the widget if this is null, so we break out. 732 if (mCreateWidgetInfo == null) { 733 return false; 734 } 735 736 PendingAddWidgetInfo createWidgetInfo = mCreateWidgetInfo; 737 createItemInfo = createWidgetInfo; 738 int spanX = createItemInfo.spanX; 739 int spanY = createItemInfo.spanY; 740 int[] size = mLauncher.getWorkspace().estimateItemSize(spanX, spanY, 741 createWidgetInfo, true); 742 743 FastBitmapDrawable previewDrawable = (FastBitmapDrawable) image.getDrawable(); 744 float minScale = 1.25f; 745 int maxWidth, maxHeight; 746 maxWidth = Math.min((int) (previewDrawable.getIntrinsicWidth() * minScale), size[0]); 747 maxHeight = Math.min((int) (previewDrawable.getIntrinsicHeight() * minScale), size[1]); 748 preview = getWidgetPreview(createWidgetInfo.componentName, createWidgetInfo.previewImage, 749 createWidgetInfo.icon, spanX, spanY, maxWidth, maxHeight); 750 751 // Determine the image view drawable scale relative to the preview 752 float[] mv = new float[9]; 753 Matrix m = new Matrix(); 754 m.setRectToRect( 755 new RectF(0f, 0f, (float) preview.getWidth(), (float) preview.getHeight()), 756 new RectF(0f, 0f, (float) previewDrawable.getIntrinsicWidth(), 757 (float) previewDrawable.getIntrinsicHeight()), 758 Matrix.ScaleToFit.START); 759 m.getValues(mv); 760 scale = (float) mv[0]; 761 } else { 762 PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) v.getTag(); 763 Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.shortcutActivityInfo); 764 preview = Bitmap.createBitmap(icon.getIntrinsicWidth(), 765 icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); 766 767 mCanvas.setBitmap(preview); 768 mCanvas.save(); 769 renderDrawableToBitmap(icon, preview, 0, 0, 770 icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); 771 mCanvas.restore(); 772 mCanvas.setBitmap(null); 773 createItemInfo.spanX = createItemInfo.spanY = 1; 774 } 775 776 // Don't clip alpha values for the drag outline if we're using the default widget preview 777 boolean clipAlpha = !(createItemInfo instanceof PendingAddWidgetInfo && 778 (((PendingAddWidgetInfo) createItemInfo).previewImage == 0)); 779 780 // Save the preview for the outline generation, then dim the preview 781 outline = Bitmap.createScaledBitmap(preview, preview.getWidth(), preview.getHeight(), 782 false); 783 784 // Start the drag 785 mLauncher.lockScreenOrientation(); 786 mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, outline, clipAlpha); 787 mDragController.startDrag(image, preview, this, createItemInfo, 788 DragController.DRAG_ACTION_COPY, null, scale); 789 outline.recycle(); 790 preview.recycle(); 791 return true; 792 } 793 794 @Override 795 protected boolean beginDragging(final View v) { 796 if (!super.beginDragging(v)) return false; 797 798 if (v instanceof PagedViewIcon) { 799 beginDraggingApplication(v); 800 } else if (v instanceof PagedViewWidget) { 801 if (!beginDraggingWidget(v)) { 802 return false; 803 } 804 } 805 806 // We delay entering spring-loaded mode slightly to make sure the UI 807 // thready is free of any work. 808 postDelayed(new Runnable() { 809 @Override 810 public void run() { 811 // We don't enter spring-loaded mode if the drag has been cancelled 812 if (mLauncher.getDragController().isDragging()) { 813 // Dismiss the cling 814 mLauncher.dismissAllAppsCling(null); 815 816 // Reset the alpha on the dragged icon before we drag 817 resetDrawableState(); 818 819 // Go into spring loaded mode (must happen before we startDrag()) 820 mLauncher.enterSpringLoadedDragMode(); 821 } 822 } 823 }, 150); 824 825 return true; 826 } 827 828 /** 829 * Clean up after dragging. 830 * 831 * @param target where the item was dragged to (can be null if the item was flung) 832 */ 833 private void endDragging(View target, boolean isFlingToDelete, boolean success) { 834 if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() && 835 !(target instanceof DeleteDropTarget))) { 836 // Exit spring loaded mode if we have not successfully dropped or have not handled the 837 // drop in Workspace 838 mLauncher.exitSpringLoadedDragMode(); 839 } 840 mLauncher.unlockScreenOrientation(false); 841 } 842 843 @Override 844 public View getContent() { 845 return null; 846 } 847 848 @Override 849 public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) { 850 mInTransition = true; 851 if (toWorkspace) { 852 cancelAllTasks(); 853 } 854 } 855 856 @Override 857 public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) { 858 } 859 860 @Override 861 public void onLauncherTransitionStep(Launcher l, float t) { 862 } 863 864 @Override 865 public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { 866 mInTransition = false; 867 for (AsyncTaskPageData d : mDeferredSyncWidgetPageItems) { 868 onSyncWidgetPageItems(d); 869 } 870 mDeferredSyncWidgetPageItems.clear(); 871 for (Runnable r : mDeferredPrepareLoadWidgetPreviewsTasks) { 872 r.run(); 873 } 874 mDeferredPrepareLoadWidgetPreviewsTasks.clear(); 875 mForceDrawAllChildrenNextFrame = !toWorkspace; 876 } 877 878 @Override 879 public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete, 880 boolean success) { 881 // Return early and wait for onFlingToDeleteCompleted if this was the result of a fling 882 if (isFlingToDelete) return; 883 884 endDragging(target, false, success); 885 886 // Display an error message if the drag failed due to there not being enough space on the 887 // target layout we were dropping on. 888 if (!success) { 889 boolean showOutOfSpaceMessage = false; 890 if (target instanceof Workspace) { 891 int currentScreen = mLauncher.getCurrentWorkspaceScreen(); 892 Workspace workspace = (Workspace) target; 893 CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen); 894 ItemInfo itemInfo = (ItemInfo) d.dragInfo; 895 if (layout != null) { 896 layout.calculateSpans(itemInfo); 897 showOutOfSpaceMessage = 898 !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY); 899 } 900 } 901 if (showOutOfSpaceMessage) { 902 mLauncher.showOutOfSpaceMessage(false); 903 } 904 905 d.deferDragViewCleanupPostAnimation = false; 906 } 907 cleanupWidgetPreloading(success); 908 mDraggingWidget = false; 909 } 910 911 @Override 912 public void onFlingToDeleteCompleted() { 913 // We just dismiss the drag when we fling, so cleanup here 914 endDragging(null, true, true); 915 cleanupWidgetPreloading(false); 916 mDraggingWidget = false; 917 } 918 919 @Override 920 public boolean supportsFlingToDelete() { 921 return true; 922 } 923 924 @Override 925 protected void onDetachedFromWindow() { 926 super.onDetachedFromWindow(); 927 cancelAllTasks(); 928 } 929 930 public void clearAllWidgetPages() { 931 cancelAllTasks(); 932 int count = getChildCount(); 933 for (int i = 0; i < count; i++) { 934 View v = getPageAt(i); 935 if (v instanceof PagedViewGridLayout) { 936 ((PagedViewGridLayout) v).removeAllViewsOnPage(); 937 mDirtyPageContent.set(i, true); 938 } 939 } 940 } 941 942 private void cancelAllTasks() { 943 // Clean up all the async tasks 944 Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator(); 945 while (iter.hasNext()) { 946 AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); 947 task.cancel(false); 948 iter.remove(); 949 mDirtyPageContent.set(task.page, true); 950 951 // We've already preallocated the views for the data to load into, so clear them as well 952 View v = getPageAt(task.page); 953 if (v instanceof PagedViewGridLayout) { 954 ((PagedViewGridLayout) v).removeAllViewsOnPage(); 955 } 956 } 957 mDeferredSyncWidgetPageItems.clear(); 958 mDeferredPrepareLoadWidgetPreviewsTasks.clear(); 959 } 960 961 public void setContentType(ContentType type) { 962 if (type == ContentType.Widgets) { 963 invalidatePageData(mNumAppsPages, true); 964 } else if (type == ContentType.Applications) { 965 invalidatePageData(0, true); 966 } 967 } 968 969 protected void snapToPage(int whichPage, int delta, int duration) { 970 super.snapToPage(whichPage, delta, duration); 971 updateCurrentTab(whichPage); 972 973 // Update the thread priorities given the direction lookahead 974 Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator(); 975 while (iter.hasNext()) { 976 AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); 977 int pageIndex = task.page; 978 if ((mNextPage > mCurrentPage && pageIndex >= mCurrentPage) || 979 (mNextPage < mCurrentPage && pageIndex <= mCurrentPage)) { 980 task.setThreadPriority(getThreadPriorityForPage(pageIndex)); 981 } else { 982 task.setThreadPriority(Process.THREAD_PRIORITY_LOWEST); 983 } 984 } 985 } 986 987 private void updateCurrentTab(int currentPage) { 988 AppsCustomizeTabHost tabHost = getTabHost(); 989 if (tabHost != null) { 990 String tag = tabHost.getCurrentTabTag(); 991 if (tag != null) { 992 if (currentPage >= mNumAppsPages && 993 !tag.equals(tabHost.getTabTagForContentType(ContentType.Widgets))) { 994 tabHost.setCurrentTabFromContent(ContentType.Widgets); 995 } else if (currentPage < mNumAppsPages && 996 !tag.equals(tabHost.getTabTagForContentType(ContentType.Applications))) { 997 tabHost.setCurrentTabFromContent(ContentType.Applications); 998 } 999 } 1000 } 1001 } 1002 1003 /* 1004 * Apps PagedView implementation 1005 */ 1006 private void setVisibilityOnChildren(ViewGroup layout, int visibility) { 1007 int childCount = layout.getChildCount(); 1008 for (int i = 0; i < childCount; ++i) { 1009 layout.getChildAt(i).setVisibility(visibility); 1010 } 1011 } 1012 private void setupPage(PagedViewCellLayout layout) { 1013 layout.setCellCount(mCellCountX, mCellCountY); 1014 layout.setGap(mPageLayoutWidthGap, mPageLayoutHeightGap); 1015 layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop, 1016 mPageLayoutPaddingRight, mPageLayoutPaddingBottom); 1017 1018 // Note: We force a measure here to get around the fact that when we do layout calculations 1019 // immediately after syncing, we don't have a proper width. That said, we already know the 1020 // expected page width, so we can actually optimize by hiding all the TextView-based 1021 // children that are expensive to measure, and let that happen naturally later. 1022 setVisibilityOnChildren(layout, View.GONE); 1023 int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST); 1024 int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST); 1025 layout.setMinimumWidth(getPageContentWidth()); 1026 layout.measure(widthSpec, heightSpec); 1027 setVisibilityOnChildren(layout, View.VISIBLE); 1028 } 1029 1030 public void syncAppsPageItems(int page, boolean immediate) { 1031 // ensure that we have the right number of items on the pages 1032 int numCells = mCellCountX * mCellCountY; 1033 int startIndex = page * numCells; 1034 int endIndex = Math.min(startIndex + numCells, mApps.size()); 1035 PagedViewCellLayout layout = (PagedViewCellLayout) getPageAt(page); 1036 1037 layout.removeAllViewsOnPage(); 1038 ArrayList<Object> items = new ArrayList<Object>(); 1039 ArrayList<Bitmap> images = new ArrayList<Bitmap>(); 1040 for (int i = startIndex; i < endIndex; ++i) { 1041 ApplicationInfo info = mApps.get(i); 1042 PagedViewIcon icon = (PagedViewIcon) mLayoutInflater.inflate( 1043 R.layout.apps_customize_application, layout, false); 1044 icon.applyFromApplicationInfo(info, true, this); 1045 icon.setOnClickListener(this); 1046 icon.setOnLongClickListener(this); 1047 icon.setOnTouchListener(this); 1048 icon.setOnKeyListener(this); 1049 1050 int index = i - startIndex; 1051 int x = index % mCellCountX; 1052 int y = index / mCellCountX; 1053 layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1)); 1054 1055 items.add(info); 1056 images.add(info.iconBitmap); 1057 } 1058 1059 layout.createHardwareLayers(); 1060 } 1061 1062 /** 1063 * A helper to return the priority for loading of the specified widget page. 1064 */ 1065 private int getWidgetPageLoadPriority(int page) { 1066 // If we are snapping to another page, use that index as the target page index 1067 int toPage = mCurrentPage; 1068 if (mNextPage > -1) { 1069 toPage = mNextPage; 1070 } 1071 1072 // We use the distance from the target page as an initial guess of priority, but if there 1073 // are no pages of higher priority than the page specified, then bump up the priority of 1074 // the specified page. 1075 Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator(); 1076 int minPageDiff = Integer.MAX_VALUE; 1077 while (iter.hasNext()) { 1078 AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); 1079 minPageDiff = Math.abs(task.page - toPage); 1080 } 1081 1082 int rawPageDiff = Math.abs(page - toPage); 1083 return rawPageDiff - Math.min(rawPageDiff, minPageDiff); 1084 } 1085 /** 1086 * Return the appropriate thread priority for loading for a given page (we give the current 1087 * page much higher priority) 1088 */ 1089 private int getThreadPriorityForPage(int page) { 1090 // TODO-APPS_CUSTOMIZE: detect number of cores and set thread priorities accordingly below 1091 int pageDiff = getWidgetPageLoadPriority(page); 1092 if (pageDiff <= 0) { 1093 return Process.THREAD_PRIORITY_LESS_FAVORABLE; 1094 } else if (pageDiff <= 1) { 1095 return Process.THREAD_PRIORITY_LOWEST; 1096 } else { 1097 return Process.THREAD_PRIORITY_LOWEST; 1098 } 1099 } 1100 private int getSleepForPage(int page) { 1101 int pageDiff = getWidgetPageLoadPriority(page); 1102 return Math.max(0, pageDiff * sPageSleepDelay); 1103 } 1104 /** 1105 * Creates and executes a new AsyncTask to load a page of widget previews. 1106 */ 1107 private void prepareLoadWidgetPreviewsTask(int page, ArrayList<Object> widgets, 1108 int cellWidth, int cellHeight, int cellCountX) { 1109 1110 // Prune all tasks that are no longer needed 1111 Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator(); 1112 while (iter.hasNext()) { 1113 AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); 1114 int taskPage = task.page; 1115 if (taskPage < getAssociatedLowerPageBound(mCurrentPage) || 1116 taskPage > getAssociatedUpperPageBound(mCurrentPage)) { 1117 task.cancel(false); 1118 iter.remove(); 1119 } else { 1120 task.setThreadPriority(getThreadPriorityForPage(taskPage)); 1121 } 1122 } 1123 1124 // We introduce a slight delay to order the loading of side pages so that we don't thrash 1125 final int sleepMs = getSleepForPage(page); 1126 AsyncTaskPageData pageData = new AsyncTaskPageData(page, widgets, cellWidth, cellHeight, 1127 new AsyncTaskCallback() { 1128 @Override 1129 public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) { 1130 try { 1131 try { 1132 Thread.sleep(sleepMs); 1133 } catch (Exception e) {} 1134 loadWidgetPreviewsInBackground(task, data); 1135 } finally { 1136 if (task.isCancelled()) { 1137 data.cleanup(true); 1138 } 1139 } 1140 } 1141 }, 1142 new AsyncTaskCallback() { 1143 @Override 1144 public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) { 1145 mRunningTasks.remove(task); 1146 if (task.isCancelled()) return; 1147 // do cleanup inside onSyncWidgetPageItems 1148 onSyncWidgetPageItems(data); 1149 } 1150 }); 1151 1152 // Ensure that the task is appropriately prioritized and runs in parallel 1153 AppsCustomizeAsyncTask t = new AppsCustomizeAsyncTask(page, 1154 AsyncTaskPageData.Type.LoadWidgetPreviewData); 1155 t.setThreadPriority(getThreadPriorityForPage(page)); 1156 t.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, pageData); 1157 mRunningTasks.add(t); 1158 } 1159 1160 /* 1161 * Widgets PagedView implementation 1162 */ 1163 private void setupPage(PagedViewGridLayout layout) { 1164 layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop, 1165 mPageLayoutPaddingRight, mPageLayoutPaddingBottom); 1166 1167 // Note: We force a measure here to get around the fact that when we do layout calculations 1168 // immediately after syncing, we don't have a proper width. 1169 int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST); 1170 int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST); 1171 layout.setMinimumWidth(getPageContentWidth()); 1172 layout.measure(widthSpec, heightSpec); 1173 } 1174 1175 private void renderDrawableToBitmap(Drawable d, Bitmap bitmap, int x, int y, int w, int h) { 1176 renderDrawableToBitmap(d, bitmap, x, y, w, h, 1f); 1177 } 1178 1179 private void renderDrawableToBitmap(Drawable d, Bitmap bitmap, int x, int y, int w, int h, 1180 float scale) { 1181 if (bitmap != null) { 1182 Canvas c = new Canvas(bitmap); 1183 c.scale(scale, scale); 1184 Rect oldBounds = d.copyBounds(); 1185 d.setBounds(x, y, x + w, y + h); 1186 d.draw(c); 1187 d.setBounds(oldBounds); // Restore the bounds 1188 c.setBitmap(null); 1189 } 1190 } 1191 1192 private Bitmap getShortcutPreview(ResolveInfo info, int maxWidth, int maxHeight) { 1193 Bitmap tempBitmap = mCachedShortcutPreviewBitmap.get(); 1194 final Canvas c = mCachedShortcutPreviewCanvas.get(); 1195 if (tempBitmap == null || 1196 tempBitmap.getWidth() != maxWidth || 1197 tempBitmap.getHeight() != maxHeight) { 1198 tempBitmap = Bitmap.createBitmap(maxWidth, maxHeight, Config.ARGB_8888); 1199 mCachedShortcutPreviewBitmap.set(tempBitmap); 1200 } else { 1201 c.setBitmap(tempBitmap); 1202 c.drawColor(0, PorterDuff.Mode.CLEAR); 1203 c.setBitmap(null); 1204 } 1205 // Render the icon 1206 Drawable icon = mIconCache.getFullResIcon(info); 1207 1208 int paddingTop = 1209 getResources().getDimensionPixelOffset(R.dimen.shortcut_preview_padding_top); 1210 int paddingLeft = 1211 getResources().getDimensionPixelOffset(R.dimen.shortcut_preview_padding_left); 1212 int paddingRight = 1213 getResources().getDimensionPixelOffset(R.dimen.shortcut_preview_padding_right); 1214 1215 int scaledIconWidth = (maxWidth - paddingLeft - paddingRight); 1216 1217 renderDrawableToBitmap( 1218 icon, tempBitmap, paddingLeft, paddingTop, scaledIconWidth, scaledIconWidth); 1219 1220 Bitmap preview = Bitmap.createBitmap(maxWidth, maxHeight, Config.ARGB_8888); 1221 c.setBitmap(preview); 1222 Paint p = mCachedShortcutPreviewPaint.get(); 1223 if (p == null) { 1224 p = new Paint(); 1225 ColorMatrix colorMatrix = new ColorMatrix(); 1226 colorMatrix.setSaturation(0); 1227 p.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); 1228 p.setAlpha((int) (255 * 0.06f)); 1229 //float density = 1f; 1230 //p.setMaskFilter(new BlurMaskFilter(15*density, BlurMaskFilter.Blur.NORMAL)); 1231 mCachedShortcutPreviewPaint.set(p); 1232 } 1233 c.drawBitmap(tempBitmap, 0, 0, p); 1234 c.setBitmap(null); 1235 1236 renderDrawableToBitmap(icon, preview, 0, 0, mAppIconSize, mAppIconSize); 1237 1238 return preview; 1239 } 1240 1241 private Bitmap getWidgetPreview(ComponentName provider, int previewImage, 1242 int iconId, int cellHSpan, int cellVSpan, int maxWidth, 1243 int maxHeight) { 1244 // Load the preview image if possible 1245 String packageName = provider.getPackageName(); 1246 if (maxWidth < 0) maxWidth = Integer.MAX_VALUE; 1247 if (maxHeight < 0) maxHeight = Integer.MAX_VALUE; 1248 1249 Drawable drawable = null; 1250 if (previewImage != 0) { 1251 drawable = mPackageManager.getDrawable(packageName, previewImage, null); 1252 if (drawable == null) { 1253 Log.w(TAG, "Can't load widget preview drawable 0x" + 1254 Integer.toHexString(previewImage) + " for provider: " + provider); 1255 } 1256 } 1257 1258 int bitmapWidth; 1259 int bitmapHeight; 1260 Bitmap defaultPreview = null; 1261 boolean widgetPreviewExists = (drawable != null); 1262 if (widgetPreviewExists) { 1263 bitmapWidth = drawable.getIntrinsicWidth(); 1264 bitmapHeight = drawable.getIntrinsicHeight(); 1265 } else { 1266 // Generate a preview image if we couldn't load one 1267 if (cellHSpan < 1) cellHSpan = 1; 1268 if (cellVSpan < 1) cellVSpan = 1; 1269 1270 BitmapDrawable previewDrawable = (BitmapDrawable) getResources() 1271 .getDrawable(R.drawable.widget_preview_tile); 1272 final int previewDrawableWidth = previewDrawable 1273 .getIntrinsicWidth(); 1274 final int previewDrawableHeight = previewDrawable 1275 .getIntrinsicHeight(); 1276 bitmapWidth = previewDrawableWidth * cellHSpan; // subtract 2 dips 1277 bitmapHeight = previewDrawableHeight * cellVSpan; 1278 1279 defaultPreview = Bitmap.createBitmap(bitmapWidth, bitmapHeight, 1280 Config.ARGB_8888); 1281 final Canvas c = mCachedAppWidgetPreviewCanvas.get(); 1282 c.setBitmap(defaultPreview); 1283 previewDrawable.setBounds(0, 0, bitmapWidth, bitmapHeight); 1284 previewDrawable.setTileModeXY(Shader.TileMode.REPEAT, 1285 Shader.TileMode.REPEAT); 1286 previewDrawable.draw(c); 1287 c.setBitmap(null); 1288 1289 // Draw the icon in the top left corner 1290 int minOffset = (int) (mAppIconSize * sWidgetPreviewIconPaddingPercentage); 1291 int smallestSide = Math.min(bitmapWidth, bitmapHeight); 1292 float iconScale = Math.min((float) smallestSide 1293 / (mAppIconSize + 2 * minOffset), 1f); 1294 1295 try { 1296 Drawable icon = null; 1297 int hoffset = 1298 (int) ((previewDrawableWidth - mAppIconSize * iconScale) / 2); 1299 int yoffset = 1300 (int) ((previewDrawableHeight - mAppIconSize * iconScale) / 2); 1301 if (iconId > 0) 1302 icon = mIconCache.getFullResIcon(packageName, iconId); 1303 if (icon != null) { 1304 renderDrawableToBitmap(icon, defaultPreview, hoffset, 1305 yoffset, (int) (mAppIconSize * iconScale), 1306 (int) (mAppIconSize * iconScale)); 1307 } 1308 } catch (Resources.NotFoundException e) { 1309 } 1310 } 1311 1312 // Scale to fit width only - let the widget preview be clipped in the 1313 // vertical dimension 1314 float scale = 1f; 1315 if (bitmapWidth > maxWidth) { 1316 scale = maxWidth / (float) bitmapWidth; 1317 } 1318 if (scale != 1f) { 1319 bitmapWidth = (int) (scale * bitmapWidth); 1320 bitmapHeight = (int) (scale * bitmapHeight); 1321 } 1322 1323 Bitmap preview = Bitmap.createBitmap(bitmapWidth, bitmapHeight, 1324 Config.ARGB_8888); 1325 1326 // Draw the scaled preview into the final bitmap 1327 if (widgetPreviewExists) { 1328 renderDrawableToBitmap(drawable, preview, 0, 0, bitmapWidth, 1329 bitmapHeight); 1330 } else { 1331 final Canvas c = mCachedAppWidgetPreviewCanvas.get(); 1332 final Rect src = mCachedAppWidgetPreviewSrcRect.get(); 1333 final Rect dest = mCachedAppWidgetPreviewDestRect.get(); 1334 c.setBitmap(preview); 1335 src.set(0, 0, defaultPreview.getWidth(), defaultPreview.getHeight()); 1336 dest.set(0, 0, preview.getWidth(), preview.getHeight()); 1337 1338 Paint p = mCachedAppWidgetPreviewPaint.get(); 1339 if (p == null) { 1340 p = new Paint(); 1341 p.setFilterBitmap(true); 1342 mCachedAppWidgetPreviewPaint.set(p); 1343 } 1344 c.drawBitmap(defaultPreview, src, dest, p); 1345 c.setBitmap(null); 1346 } 1347 return preview; 1348 } 1349 1350 public void syncWidgetPageItems(final int page, final boolean immediate) { 1351 int numItemsPerPage = mWidgetCountX * mWidgetCountY; 1352 1353 // Calculate the dimensions of each cell we are giving to each widget 1354 final ArrayList<Object> items = new ArrayList<Object>(); 1355 int contentWidth = mWidgetSpacingLayout.getContentWidth(); 1356 final int cellWidth = ((contentWidth - mPageLayoutPaddingLeft - mPageLayoutPaddingRight 1357 - ((mWidgetCountX - 1) * mWidgetWidthGap)) / mWidgetCountX); 1358 int contentHeight = mWidgetSpacingLayout.getContentHeight(); 1359 final int cellHeight = ((contentHeight - mPageLayoutPaddingTop - mPageLayoutPaddingBottom 1360 - ((mWidgetCountY - 1) * mWidgetHeightGap)) / mWidgetCountY); 1361 1362 // Prepare the set of widgets to load previews for in the background 1363 int offset = (page - mNumAppsPages) * numItemsPerPage; 1364 for (int i = offset; i < Math.min(offset + numItemsPerPage, mWidgets.size()); ++i) { 1365 items.add(mWidgets.get(i)); 1366 } 1367 1368 // Prepopulate the pages with the other widget info, and fill in the previews later 1369 final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page); 1370 layout.setColumnCount(layout.getCellCountX()); 1371 for (int i = 0; i < items.size(); ++i) { 1372 Object rawInfo = items.get(i); 1373 PendingAddItemInfo createItemInfo = null; 1374 PagedViewWidget widget = (PagedViewWidget) mLayoutInflater.inflate( 1375 R.layout.apps_customize_widget, layout, false); 1376 if (rawInfo instanceof AppWidgetProviderInfo) { 1377 // Fill in the widget information 1378 AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo; 1379 createItemInfo = new PendingAddWidgetInfo(info, null, null); 1380 1381 // Determine the widget spans and min resize spans. 1382 int[] spanXY = Launcher.getSpanForWidget(mLauncher, info); 1383 createItemInfo.spanX = spanXY[0]; 1384 createItemInfo.spanY = spanXY[1]; 1385 int[] minSpanXY = Launcher.getMinSpanForWidget(mLauncher, info); 1386 createItemInfo.minSpanX = minSpanXY[0]; 1387 createItemInfo.minSpanY = minSpanXY[1]; 1388 1389 widget.applyFromAppWidgetProviderInfo(info, -1, spanXY); 1390 widget.setTag(createItemInfo); 1391 widget.setShortPressListener(this); 1392 } else if (rawInfo instanceof ResolveInfo) { 1393 // Fill in the shortcuts information 1394 ResolveInfo info = (ResolveInfo) rawInfo; 1395 createItemInfo = new PendingAddShortcutInfo(info.activityInfo); 1396 createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; 1397 createItemInfo.componentName = new ComponentName(info.activityInfo.packageName, 1398 info.activityInfo.name); 1399 widget.applyFromResolveInfo(mPackageManager, info); 1400 widget.setTag(createItemInfo); 1401 } 1402 widget.setOnClickListener(this); 1403 widget.setOnLongClickListener(this); 1404 widget.setOnTouchListener(this); 1405 widget.setOnKeyListener(this); 1406 1407 // Layout each widget 1408 int ix = i % mWidgetCountX; 1409 int iy = i / mWidgetCountX; 1410 GridLayout.LayoutParams lp = new GridLayout.LayoutParams( 1411 GridLayout.spec(iy, GridLayout.LEFT), 1412 GridLayout.spec(ix, GridLayout.TOP)); 1413 lp.width = cellWidth; 1414 lp.height = cellHeight; 1415 lp.setGravity(Gravity.TOP | Gravity.LEFT); 1416 if (ix > 0) lp.leftMargin = mWidgetWidthGap; 1417 if (iy > 0) lp.topMargin = mWidgetHeightGap; 1418 layout.addView(widget, lp); 1419 } 1420 1421 // wait until a call on onLayout to start loading, because 1422 // PagedViewWidget.getPreviewSize() will return 0 if it hasn't been laid out 1423 // TODO: can we do a measure/layout immediately? 1424 layout.setOnLayoutListener(new Runnable() { 1425 public void run() { 1426 // Load the widget previews 1427 int maxPreviewWidth = cellWidth; 1428 int maxPreviewHeight = cellHeight; 1429 if (layout.getChildCount() > 0) { 1430 PagedViewWidget w = (PagedViewWidget) layout.getChildAt(0); 1431 int[] maxSize = w.getPreviewSize(); 1432 maxPreviewWidth = maxSize[0]; 1433 maxPreviewHeight = maxSize[1]; 1434 } 1435 if (immediate) { 1436 AsyncTaskPageData data = new AsyncTaskPageData(page, items, 1437 maxPreviewWidth, maxPreviewHeight, null, null); 1438 loadWidgetPreviewsInBackground(null, data); 1439 onSyncWidgetPageItems(data); 1440 } else { 1441 if (mInTransition) { 1442 mDeferredPrepareLoadWidgetPreviewsTasks.add(this); 1443 } else { 1444 prepareLoadWidgetPreviewsTask(page, items, 1445 maxPreviewWidth, maxPreviewHeight, mWidgetCountX); 1446 } 1447 } 1448 } 1449 }); 1450 } 1451 private void loadWidgetPreviewsInBackground(AppsCustomizeAsyncTask task, 1452 AsyncTaskPageData data) { 1453 // loadWidgetPreviewsInBackground can be called without a task to load a set of widget 1454 // previews synchronously 1455 if (task != null) { 1456 // Ensure that this task starts running at the correct priority 1457 task.syncThreadPriority(); 1458 } 1459 1460 // Load each of the widget/shortcut previews 1461 ArrayList<Object> items = data.items; 1462 ArrayList<Bitmap> images = data.generatedImages; 1463 int count = items.size(); 1464 for (int i = 0; i < count; ++i) { 1465 if (task != null) { 1466 // Ensure we haven't been cancelled yet 1467 if (task.isCancelled()) break; 1468 // Before work on each item, ensure that this task is running at the correct 1469 // priority 1470 task.syncThreadPriority(); 1471 } 1472 1473 Object rawInfo = items.get(i); 1474 if (rawInfo instanceof AppWidgetProviderInfo) { 1475 AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo; 1476 int[] cellSpans = Launcher.getSpanForWidget(mLauncher, info); 1477 1478 int maxWidth = Math.min(data.maxImageWidth, 1479 mWidgetSpacingLayout.estimateCellWidth(cellSpans[0])); 1480 int maxHeight = Math.min(data.maxImageHeight, 1481 mWidgetSpacingLayout.estimateCellHeight(cellSpans[1])); 1482 Bitmap b = getWidgetPreview(info.provider, info.previewImage, info.icon, 1483 cellSpans[0], cellSpans[1], maxWidth, maxHeight); 1484 images.add(b); 1485 } else if (rawInfo instanceof ResolveInfo) { 1486 // Fill in the shortcuts information 1487 ResolveInfo info = (ResolveInfo) rawInfo; 1488 images.add(getShortcutPreview(info, data.maxImageWidth, data.maxImageHeight)); 1489 } 1490 } 1491 } 1492 1493 private void onSyncWidgetPageItems(AsyncTaskPageData data) { 1494 if (mInTransition) { 1495 mDeferredSyncWidgetPageItems.add(data); 1496 return; 1497 } 1498 try { 1499 int page = data.page; 1500 PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page); 1501 1502 ArrayList<Object> items = data.items; 1503 int count = items.size(); 1504 for (int i = 0; i < count; ++i) { 1505 PagedViewWidget widget = (PagedViewWidget) layout.getChildAt(i); 1506 if (widget != null) { 1507 Bitmap preview = data.generatedImages.get(i); 1508 widget.applyPreview(new FastBitmapDrawable(preview), i); 1509 } 1510 } 1511 1512 layout.createHardwareLayer(); 1513 invalidate(); 1514 1515 // Update all thread priorities 1516 Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator(); 1517 while (iter.hasNext()) { 1518 AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); 1519 int pageIndex = task.page; 1520 task.setThreadPriority(getThreadPriorityForPage(pageIndex)); 1521 } 1522 } finally { 1523 data.cleanup(false); 1524 } 1525 } 1526 1527 @Override 1528 public void syncPages() { 1529 removeAllViews(); 1530 cancelAllTasks(); 1531 1532 Context context = getContext(); 1533 for (int j = 0; j < mNumWidgetPages; ++j) { 1534 PagedViewGridLayout layout = new PagedViewGridLayout(context, mWidgetCountX, 1535 mWidgetCountY); 1536 setupPage(layout); 1537 addView(layout, new PagedView.LayoutParams(LayoutParams.MATCH_PARENT, 1538 LayoutParams.MATCH_PARENT)); 1539 } 1540 1541 for (int i = 0; i < mNumAppsPages; ++i) { 1542 PagedViewCellLayout layout = new PagedViewCellLayout(context); 1543 setupPage(layout); 1544 addView(layout); 1545 } 1546 } 1547 1548 @Override 1549 public void syncPageItems(int page, boolean immediate) { 1550 if (page < mNumAppsPages) { 1551 syncAppsPageItems(page, immediate); 1552 } else { 1553 syncWidgetPageItems(page, immediate); 1554 } 1555 } 1556 1557 // We want our pages to be z-ordered such that the further a page is to the left, the higher 1558 // it is in the z-order. This is important to insure touch events are handled correctly. 1559 View getPageAt(int index) { 1560 return getChildAt(indexToPage(index)); 1561 } 1562 1563 @Override 1564 protected int indexToPage(int index) { 1565 return getChildCount() - index - 1; 1566 } 1567 1568 // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack. 1569 @Override 1570 protected void screenScrolled(int screenCenter) { 1571 super.screenScrolled(screenCenter); 1572 1573 for (int i = 0; i < getChildCount(); i++) { 1574 View v = getPageAt(i); 1575 if (v != null) { 1576 float scrollProgress = getScrollProgress(screenCenter, v, i); 1577 1578 float interpolatedProgress = 1579 mZInterpolator.getInterpolation(Math.abs(Math.min(scrollProgress, 0))); 1580 float scale = (1 - interpolatedProgress) + 1581 interpolatedProgress * TRANSITION_SCALE_FACTOR; 1582 float translationX = Math.min(0, scrollProgress) * v.getMeasuredWidth(); 1583 1584 float alpha; 1585 1586 if (scrollProgress < 0) { 1587 alpha = scrollProgress < 0 ? mAlphaInterpolator.getInterpolation( 1588 1 - Math.abs(scrollProgress)) : 1.0f; 1589 } else { 1590 // On large screens we need to fade the page as it nears its leftmost position 1591 alpha = mLeftScreenAlphaInterpolator.getInterpolation(1 - scrollProgress); 1592 } 1593 1594 v.setCameraDistance(mDensity * CAMERA_DISTANCE); 1595 int pageWidth = v.getMeasuredWidth(); 1596 int pageHeight = v.getMeasuredHeight(); 1597 1598 if (PERFORM_OVERSCROLL_ROTATION) { 1599 if (i == 0 && scrollProgress < 0) { 1600 // Overscroll to the left 1601 v.setPivotX(TRANSITION_PIVOT * pageWidth); 1602 v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress); 1603 scale = 1.0f; 1604 alpha = 1.0f; 1605 // On the first page, we don't want the page to have any lateral motion 1606 translationX = 0; 1607 } else if (i == getChildCount() - 1 && scrollProgress > 0) { 1608 // Overscroll to the right 1609 v.setPivotX((1 - TRANSITION_PIVOT) * pageWidth); 1610 v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress); 1611 scale = 1.0f; 1612 alpha = 1.0f; 1613 // On the last page, we don't want the page to have any lateral motion. 1614 translationX = 0; 1615 } else { 1616 v.setPivotY(pageHeight / 2.0f); 1617 v.setPivotX(pageWidth / 2.0f); 1618 v.setRotationY(0f); 1619 } 1620 } 1621 1622 v.setTranslationX(translationX); 1623 v.setScaleX(scale); 1624 v.setScaleY(scale); 1625 v.setAlpha(alpha); 1626 1627 // If the view has 0 alpha, we set it to be invisible so as to prevent 1628 // it from accepting touches 1629 if (alpha == 0) { 1630 v.setVisibility(INVISIBLE); 1631 } else if (v.getVisibility() != VISIBLE) { 1632 v.setVisibility(VISIBLE); 1633 } 1634 } 1635 } 1636 } 1637 1638 protected void overScroll(float amount) { 1639 acceleratedOverScroll(amount); 1640 } 1641 1642 /** 1643 * Used by the parent to get the content width to set the tab bar to 1644 * @return 1645 */ 1646 public int getPageContentWidth() { 1647 return mContentWidth; 1648 } 1649 1650 @Override 1651 protected void onPageEndMoving() { 1652 super.onPageEndMoving(); 1653 mForceDrawAllChildrenNextFrame = true; 1654 // We reset the save index when we change pages so that it will be recalculated on next 1655 // rotation 1656 mSaveInstanceStateItemIndex = -1; 1657 } 1658 1659 /* 1660 * AllAppsView implementation 1661 */ 1662 public void setup(Launcher launcher, DragController dragController) { 1663 mLauncher = launcher; 1664 mDragController = dragController; 1665 } 1666 1667 /** 1668 * We should call thise method whenever the core data changes (mApps, mWidgets) so that we can 1669 * appropriately determine when to invalidate the PagedView page data. In cases where the data 1670 * has yet to be set, we can requestLayout() and wait for onDataReady() to be called in the 1671 * next onMeasure() pass, which will trigger an invalidatePageData() itself. 1672 */ 1673 private void invalidateOnDataChange() { 1674 if (!isDataReady()) { 1675 // The next layout pass will trigger data-ready if both widgets and apps are set, so 1676 // request a layout to trigger the page data when ready. 1677 requestLayout(); 1678 } else { 1679 cancelAllTasks(); 1680 invalidatePageData(); 1681 } 1682 } 1683 1684 public void setApps(ArrayList<ApplicationInfo> list) { 1685 mApps = list; 1686 Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR); 1687 updatePageCounts(); 1688 invalidateOnDataChange(); 1689 } 1690 private void addAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) { 1691 // We add it in place, in alphabetical order 1692 int count = list.size(); 1693 for (int i = 0; i < count; ++i) { 1694 ApplicationInfo info = list.get(i); 1695 int index = Collections.binarySearch(mApps, info, LauncherModel.APP_NAME_COMPARATOR); 1696 if (index < 0) { 1697 mApps.add(-(index + 1), info); 1698 } 1699 } 1700 } 1701 public void addApps(ArrayList<ApplicationInfo> list) { 1702 addAppsWithoutInvalidate(list); 1703 updatePageCounts(); 1704 invalidateOnDataChange(); 1705 } 1706 private int findAppByComponent(List<ApplicationInfo> list, ApplicationInfo item) { 1707 ComponentName removeComponent = item.intent.getComponent(); 1708 int length = list.size(); 1709 for (int i = 0; i < length; ++i) { 1710 ApplicationInfo info = list.get(i); 1711 if (info.intent.getComponent().equals(removeComponent)) { 1712 return i; 1713 } 1714 } 1715 return -1; 1716 } 1717 private int findAppByPackage(List<ApplicationInfo> list, String packageName) { 1718 int length = list.size(); 1719 for (int i = 0; i < length; ++i) { 1720 ApplicationInfo info = list.get(i); 1721 if (ItemInfo.getPackageName(info.intent).equals(packageName)) { 1722 return i; 1723 } 1724 } 1725 return -1; 1726 } 1727 private void removeAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) { 1728 // loop through all the apps and remove apps that have the same component 1729 int length = list.size(); 1730 for (int i = 0; i < length; ++i) { 1731 ApplicationInfo info = list.get(i); 1732 int removeIndex = findAppByComponent(mApps, info); 1733 if (removeIndex > -1) { 1734 mApps.remove(removeIndex); 1735 } 1736 } 1737 } 1738 private void removeAppsWithPackageNameWithoutInvalidate(ArrayList<String> packageNames) { 1739 // loop through all the package names and remove apps that have the same package name 1740 for (String pn : packageNames) { 1741 int removeIndex = findAppByPackage(mApps, pn); 1742 while (removeIndex > -1) { 1743 mApps.remove(removeIndex); 1744 removeIndex = findAppByPackage(mApps, pn); 1745 } 1746 } 1747 } 1748 public void removeApps(ArrayList<String> packageNames) { 1749 removeAppsWithPackageNameWithoutInvalidate(packageNames); 1750 updatePageCounts(); 1751 invalidateOnDataChange(); 1752 } 1753 public void updateApps(ArrayList<ApplicationInfo> list) { 1754 // We remove and re-add the updated applications list because it's properties may have 1755 // changed (ie. the title), and this will ensure that the items will be in their proper 1756 // place in the list. 1757 removeAppsWithoutInvalidate(list); 1758 addAppsWithoutInvalidate(list); 1759 updatePageCounts(); 1760 invalidateOnDataChange(); 1761 } 1762 1763 public void reset() { 1764 // If we have reset, then we should not continue to restore the previous state 1765 mSaveInstanceStateItemIndex = -1; 1766 1767 AppsCustomizeTabHost tabHost = getTabHost(); 1768 String tag = tabHost.getCurrentTabTag(); 1769 if (tag != null) { 1770 if (!tag.equals(tabHost.getTabTagForContentType(ContentType.Applications))) { 1771 tabHost.setCurrentTabFromContent(ContentType.Applications); 1772 } 1773 } 1774 1775 if (mCurrentPage != 0) { 1776 invalidatePageData(0); 1777 } 1778 } 1779 1780 private AppsCustomizeTabHost getTabHost() { 1781 return (AppsCustomizeTabHost) mLauncher.findViewById(R.id.apps_customize_pane); 1782 } 1783 1784 public void dumpState() { 1785 // TODO: Dump information related to current list of Applications, Widgets, etc. 1786 ApplicationInfo.dumpApplicationInfoList(TAG, "mApps", mApps); 1787 dumpAppWidgetProviderInfoList(TAG, "mWidgets", mWidgets); 1788 } 1789 1790 private void dumpAppWidgetProviderInfoList(String tag, String label, 1791 ArrayList<Object> list) { 1792 Log.d(tag, label + " size=" + list.size()); 1793 for (Object i: list) { 1794 if (i instanceof AppWidgetProviderInfo) { 1795 AppWidgetProviderInfo info = (AppWidgetProviderInfo) i; 1796 Log.d(tag, " label=\"" + info.label + "\" previewImage=" + info.previewImage 1797 + " resizeMode=" + info.resizeMode + " configure=" + info.configure 1798 + " initialLayout=" + info.initialLayout 1799 + " minWidth=" + info.minWidth + " minHeight=" + info.minHeight); 1800 } else if (i instanceof ResolveInfo) { 1801 ResolveInfo info = (ResolveInfo) i; 1802 Log.d(tag, " label=\"" + info.loadLabel(mPackageManager) + "\" icon=" 1803 + info.icon); 1804 } 1805 } 1806 } 1807 1808 public void surrender() { 1809 // TODO: If we are in the middle of any process (ie. for holographic outlines, etc) we 1810 // should stop this now. 1811 1812 // Stop all background tasks 1813 cancelAllTasks(); 1814 } 1815 1816 @Override 1817 public void iconPressed(PagedViewIcon icon) { 1818 // Reset the previously pressed icon and store a reference to the pressed icon so that 1819 // we can reset it on return to Launcher (in Launcher.onResume()) 1820 if (mPressedIcon != null) { 1821 mPressedIcon.resetDrawableState(); 1822 } 1823 mPressedIcon = icon; 1824 } 1825 1826 public void resetDrawableState() { 1827 if (mPressedIcon != null) { 1828 mPressedIcon.resetDrawableState(); 1829 mPressedIcon = null; 1830 } 1831 } 1832 1833 /* 1834 * We load an extra page on each side to prevent flashes from scrolling and loading of the 1835 * widget previews in the background with the AsyncTasks. 1836 */ 1837 final static int sLookBehindPageCount = 2; 1838 final static int sLookAheadPageCount = 2; 1839 protected int getAssociatedLowerPageBound(int page) { 1840 final int count = getChildCount(); 1841 int windowSize = Math.min(count, sLookBehindPageCount + sLookAheadPageCount + 1); 1842 int windowMinIndex = Math.max(Math.min(page - sLookBehindPageCount, count - windowSize), 0); 1843 return windowMinIndex; 1844 } 1845 protected int getAssociatedUpperPageBound(int page) { 1846 final int count = getChildCount(); 1847 int windowSize = Math.min(count, sLookBehindPageCount + sLookAheadPageCount + 1); 1848 int windowMaxIndex = Math.min(Math.max(page + sLookAheadPageCount, windowSize - 1), 1849 count - 1); 1850 return windowMaxIndex; 1851 } 1852 1853 @Override 1854 protected String getCurrentPageDescription() { 1855 int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage; 1856 int stringId = R.string.default_scroll_format; 1857 int count = 0; 1858 1859 if (page < mNumAppsPages) { 1860 stringId = R.string.apps_customize_apps_scroll_format; 1861 count = mNumAppsPages; 1862 } else { 1863 page -= mNumAppsPages; 1864 stringId = R.string.apps_customize_widgets_scroll_format; 1865 count = mNumWidgetPages; 1866 } 1867 1868 return String.format(getContext().getString(stringId), page + 1, count); 1869 } 1870} 1871