AppsCustomizePagedView.java revision b0fa3529edf7c4f6ce088d23d80461dcad43aa15
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.ObjectAnimator; 21import android.animation.ValueAnimator; 22import android.appwidget.AppWidgetManager; 23import android.appwidget.AppWidgetProviderInfo; 24import android.content.ComponentName; 25import android.content.Context; 26import android.content.Intent; 27import android.content.pm.ActivityInfo; 28import android.content.pm.PackageManager; 29import android.content.pm.ResolveInfo; 30import android.content.res.Configuration; 31import android.content.res.Resources; 32import android.content.res.TypedArray; 33import android.graphics.Bitmap; 34import android.graphics.Bitmap.Config; 35import android.graphics.Canvas; 36import android.graphics.PorterDuff; 37import android.graphics.Rect; 38import android.graphics.drawable.Drawable; 39import android.os.AsyncTask; 40import android.os.Process; 41import android.util.AttributeSet; 42import android.util.Log; 43import android.view.Gravity; 44import android.view.LayoutInflater; 45import android.view.MotionEvent; 46import android.view.View; 47import android.view.ViewGroup; 48import android.view.animation.AccelerateInterpolator; 49import android.widget.GridLayout; 50import android.widget.ImageView; 51import android.widget.Toast; 52 53import com.android.launcher.R; 54import com.android.launcher2.DropTarget.DragObject; 55 56import java.util.ArrayList; 57import java.util.Collections; 58import java.util.Iterator; 59import java.util.List; 60 61/** 62 * A simple callback interface which also provides the results of the task. 63 */ 64interface AsyncTaskCallback { 65 void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data); 66} 67 68/** 69 * The data needed to perform either of the custom AsyncTasks. 70 */ 71class AsyncTaskPageData { 72 enum Type { 73 LoadWidgetPreviewData, 74 LoadHolographicIconsData 75 } 76 77 AsyncTaskPageData(int p, ArrayList<Object> l, ArrayList<Bitmap> si, AsyncTaskCallback bgR, 78 AsyncTaskCallback postR) { 79 page = p; 80 items = l; 81 sourceImages = si; 82 generatedImages = new ArrayList<Bitmap>(); 83 cellWidth = cellHeight = -1; 84 doInBackgroundCallback = bgR; 85 postExecuteCallback = postR; 86 } 87 AsyncTaskPageData(int p, ArrayList<Object> l, int cw, int ch, int ccx, AsyncTaskCallback bgR, 88 AsyncTaskCallback postR) { 89 page = p; 90 items = l; 91 generatedImages = new ArrayList<Bitmap>(); 92 cellWidth = cw; 93 cellHeight = ch; 94 cellCountX = ccx; 95 doInBackgroundCallback = bgR; 96 postExecuteCallback = postR; 97 } 98 int page; 99 ArrayList<Object> items; 100 ArrayList<Bitmap> sourceImages; 101 ArrayList<Bitmap> generatedImages; 102 int cellWidth; 103 int cellHeight; 104 int cellCountX; 105 AsyncTaskCallback doInBackgroundCallback; 106 AsyncTaskCallback postExecuteCallback; 107} 108 109/** 110 * A generic template for an async task used in AppsCustomize. 111 */ 112class AppsCustomizeAsyncTask extends AsyncTask<AsyncTaskPageData, Void, AsyncTaskPageData> { 113 AppsCustomizeAsyncTask(int p, AppsCustomizePagedView.ContentType t, AsyncTaskPageData.Type ty) { 114 page = p; 115 pageContentType = t; 116 threadPriority = Process.THREAD_PRIORITY_DEFAULT; 117 dataType = ty; 118 } 119 @Override 120 protected AsyncTaskPageData doInBackground(AsyncTaskPageData... params) { 121 if (params.length != 1) return null; 122 // Load each of the widget previews in the background 123 params[0].doInBackgroundCallback.run(this, params[0]); 124 return params[0]; 125 } 126 @Override 127 protected void onPostExecute(AsyncTaskPageData result) { 128 // All the widget previews are loaded, so we can just callback to inflate the page 129 result.postExecuteCallback.run(this, result); 130 } 131 132 void setThreadPriority(int p) { 133 threadPriority = p; 134 } 135 void syncThreadPriority() { 136 Process.setThreadPriority(threadPriority); 137 } 138 139 // The page that this async task is associated with 140 AsyncTaskPageData.Type dataType; 141 int page; 142 AppsCustomizePagedView.ContentType pageContentType; 143 int threadPriority; 144} 145 146/** 147 * The Apps/Customize page that displays all the applications, widgets, and shortcuts. 148 */ 149public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements 150 AllAppsView, View.OnClickListener, DragSource { 151 static final String LOG_TAG = "AppsCustomizePagedView"; 152 153 /** 154 * The different content types that this paged view can show. 155 */ 156 public enum ContentType { 157 Applications, 158 Widgets 159 } 160 161 // Refs 162 private Launcher mLauncher; 163 private DragController mDragController; 164 private final LayoutInflater mLayoutInflater; 165 private final PackageManager mPackageManager; 166 167 // Content 168 private ContentType mContentType; 169 private ArrayList<ApplicationInfo> mApps; 170 private ArrayList<Object> mWidgets; 171 172 // Caching 173 private Canvas mCanvas; 174 private Drawable mDefaultWidgetBackground; 175 private IconCache mIconCache; 176 private int mDragViewMultiplyColor; 177 178 // Dimens 179 private int mContentWidth; 180 private int mAppIconSize; 181 private int mMaxWidgetSpan, mMinWidgetSpan; 182 private int mWidgetCountX, mWidgetCountY; 183 private int mWidgetWidthGap, mWidgetHeightGap; 184 private final int mWidgetPreviewIconPaddedDimension; 185 private final float sWidgetPreviewIconPaddingPercentage = 0.25f; 186 private PagedViewCellLayout mWidgetSpacingLayout; 187 188 // Previews & outlines 189 ArrayList<AppsCustomizeAsyncTask> mRunningTasks; 190 private HolographicOutlineHelper mHolographicOutlineHelper; 191 private static final int sPageSleepDelay = 200; 192 193 public AppsCustomizePagedView(Context context, AttributeSet attrs) { 194 super(context, attrs); 195 mLayoutInflater = LayoutInflater.from(context); 196 mPackageManager = context.getPackageManager(); 197 mContentType = ContentType.Applications; 198 mApps = new ArrayList<ApplicationInfo>(); 199 mWidgets = new ArrayList<Object>(); 200 mIconCache = ((LauncherApplication) context.getApplicationContext()).getIconCache(); 201 mHolographicOutlineHelper = new HolographicOutlineHelper(); 202 mCanvas = new Canvas(); 203 mRunningTasks = new ArrayList<AppsCustomizeAsyncTask>(); 204 205 // Save the default widget preview background 206 Resources resources = context.getResources(); 207 mDefaultWidgetBackground = resources.getDrawable(R.drawable.default_widget_preview_holo); 208 mAppIconSize = resources.getDimensionPixelSize(R.dimen.app_icon_size); 209 mDragViewMultiplyColor = resources.getColor(R.color.drag_view_multiply_color); 210 211 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagedView, 0, 0); 212 // TODO-APPS_CUSTOMIZE: remove these unnecessary attrs after 213 mCellCountX = a.getInt(R.styleable.PagedView_cellCountX, 6); 214 mCellCountY = a.getInt(R.styleable.PagedView_cellCountY, 4); 215 a.recycle(); 216 a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0); 217 mWidgetWidthGap = 218 a.getDimensionPixelSize(R.styleable.AppsCustomizePagedView_widgetCellWidthGap, 0); 219 mWidgetHeightGap = 220 a.getDimensionPixelSize(R.styleable.AppsCustomizePagedView_widgetCellHeightGap, 0); 221 mWidgetCountX = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountX, 2); 222 mWidgetCountY = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountY, 2); 223 a.recycle(); 224 mWidgetSpacingLayout = new PagedViewCellLayout(getContext()); 225 226 // The max widget span is the length N, such that NxN is the largest bounds that the widget 227 // preview can be before applying the widget scaling 228 mMinWidgetSpan = 1; 229 mMaxWidgetSpan = 3; 230 231 // The padding on the non-matched dimension for the default widget preview icons 232 // (top + bottom) 233 mWidgetPreviewIconPaddedDimension = 234 (int) (mAppIconSize * (1 + (2 * sWidgetPreviewIconPaddingPercentage))); 235 mFadeInAdjacentScreens = LauncherApplication.isScreenLarge(); 236 } 237 238 @Override 239 protected void init() { 240 super.init(); 241 mCenterPagesVertically = false; 242 243 Context context = getContext(); 244 Resources r = context.getResources(); 245 setDragSlopeThreshold(r.getInteger(R.integer.config_appsCustomizeDragSlopeThreshold)/100f); 246 } 247 248 @Override 249 protected void onUnhandledTap(MotionEvent ev) { 250 if (LauncherApplication.isScreenLarge()) { 251 // Dismiss AppsCustomize if we tap 252 mLauncher.showWorkspace(true); 253 } 254 } 255 256 /** 257 * This differs from isDataReady as this is the test done if isDataReady is not set. 258 */ 259 private boolean testDataReady() { 260 // We only do this test once, and we default to the Applications page, so we only really 261 // have to wait for there to be apps. 262 return !mApps.isEmpty(); 263 } 264 265 protected void onDataReady(int width, int height) { 266 // Note that we transpose the counts in portrait so that we get a similar layout 267 boolean isLandscape = getResources().getConfiguration().orientation == 268 Configuration.ORIENTATION_LANDSCAPE; 269 int maxCellCountX = Integer.MAX_VALUE; 270 int maxCellCountY = Integer.MAX_VALUE; 271 if (LauncherApplication.isScreenLarge()) { 272 maxCellCountX = (isLandscape ? LauncherModel.getCellCountX() : 273 LauncherModel.getCellCountY()); 274 maxCellCountY = (isLandscape ? LauncherModel.getCellCountY() : 275 LauncherModel.getCellCountX()); 276 } 277 278 // Now that the data is ready, we can calculate the content width, the number of cells to 279 // use for each page 280 mWidgetSpacingLayout.setGap(mPageLayoutWidthGap, mPageLayoutHeightGap); 281 mWidgetSpacingLayout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop, 282 mPageLayoutPaddingRight, mPageLayoutPaddingBottom); 283 mWidgetSpacingLayout.calculateCellCount(width, height, maxCellCountX, maxCellCountY); 284 mCellCountX = mWidgetSpacingLayout.getCellCountX(); 285 mCellCountY = mWidgetSpacingLayout.getCellCountY(); 286 287 // Force a measure to update recalculate the gaps 288 int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST); 289 int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST); 290 mWidgetSpacingLayout.measure(widthSpec, heightSpec); 291 mContentWidth = mWidgetSpacingLayout.getContentWidth(); 292 invalidatePageData(); 293 } 294 295 @Override 296 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 297 int width = MeasureSpec.getSize(widthMeasureSpec); 298 int height = MeasureSpec.getSize(heightMeasureSpec); 299 if (!isDataReady()) { 300 if (testDataReady()) { 301 setDataIsReady(); 302 setMeasuredDimension(width, height); 303 onDataReady(width, height); 304 } 305 } 306 307 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 308 } 309 310 /** Removes and returns the ResolveInfo with the specified ComponentName */ 311 private ResolveInfo removeResolveInfoWithComponentName(List<ResolveInfo> list, 312 ComponentName cn) { 313 Iterator<ResolveInfo> iter = list.iterator(); 314 while (iter.hasNext()) { 315 ResolveInfo rinfo = iter.next(); 316 ActivityInfo info = rinfo.activityInfo; 317 ComponentName c = new ComponentName(info.packageName, info.name); 318 if (c.equals(cn)) { 319 iter.remove(); 320 return rinfo; 321 } 322 } 323 return null; 324 } 325 326 public void onPackagesUpdated() { 327 // TODO: this isn't ideal, but we actually need to delay here. This call is triggered 328 // by a broadcast receiver, and in order for it to work correctly, we need to know that 329 // the AppWidgetService has already received and processed the same broadcast. Since there 330 // is no guarantee about ordering of broadcast receipt, we just delay here. Ideally, 331 // we should have a more precise way of ensuring the AppWidgetService is up to date. 332 postDelayed(new Runnable() { 333 public void run() { 334 updatePackages(); 335 } 336 }, 500); 337 } 338 339 public void updatePackages() { 340 // Get the list of widgets and shortcuts 341 boolean wasEmpty = mWidgets.isEmpty(); 342 mWidgets.clear(); 343 List<AppWidgetProviderInfo> widgets = 344 AppWidgetManager.getInstance(mLauncher).getInstalledProviders(); 345 Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); 346 List<ResolveInfo> shortcuts = mPackageManager.queryIntentActivities(shortcutsIntent, 0); 347 mWidgets.addAll(widgets); 348 mWidgets.addAll(shortcuts); 349 Collections.sort(mWidgets, 350 new LauncherModel.WidgetAndShortcutNameComparator(mPackageManager)); 351 352 if (wasEmpty) { 353 // The next layout pass will trigger data-ready if both widgets and apps are set, so request 354 // a layout to do this test and invalidate the page data when ready. 355 if (testDataReady()) requestLayout(); 356 } else { 357 invalidatePageData(); 358 } 359 } 360 361 @Override 362 public void onClick(View v) { 363 // When we have exited all apps or are in transition, disregard clicks 364 if (!mLauncher.isAllAppsCustomizeOpen() || 365 mLauncher.getWorkspace().isSwitchingState()) return; 366 367 if (v instanceof PagedViewIcon) { 368 // Animate some feedback to the click 369 final ApplicationInfo appInfo = (ApplicationInfo) v.getTag(); 370 animateClickFeedback(v, new Runnable() { 371 @Override 372 public void run() { 373 mLauncher.startActivitySafely(appInfo.intent, appInfo); 374 } 375 }); 376 } else if (v instanceof PagedViewWidget) { 377 // Let the user know that they have to long press to add a widget 378 Toast.makeText(getContext(), R.string.long_press_widget_to_add, 379 Toast.LENGTH_SHORT).show(); 380 381 // Create a little animation to show that the widget can move 382 float offsetY = getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY); 383 final ImageView p = (ImageView) v.findViewById(R.id.widget_preview); 384 AnimatorSet bounce = new AnimatorSet(); 385 ValueAnimator tyuAnim = ObjectAnimator.ofFloat(p, "translationY", offsetY); 386 tyuAnim.setDuration(125); 387 ValueAnimator tydAnim = ObjectAnimator.ofFloat(p, "translationY", 0f); 388 tydAnim.setDuration(100); 389 bounce.play(tyuAnim).before(tydAnim); 390 bounce.setInterpolator(new AccelerateInterpolator()); 391 bounce.start(); 392 } 393 } 394 395 /* 396 * PagedViewWithDraggableItems implementation 397 */ 398 @Override 399 protected void determineDraggingStart(android.view.MotionEvent ev) { 400 // Disable dragging by pulling an app down for now. 401 } 402 403 private void beginDraggingApplication(View v) { 404 mLauncher.getWorkspace().onDragStartedWithItem(v); 405 mLauncher.getWorkspace().beginDragShared(v, this); 406 } 407 408 private void beginDraggingWidget(View v) { 409 // Get the widget preview as the drag representation 410 ImageView image = (ImageView) v.findViewById(R.id.widget_preview); 411 PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag(); 412 413 // Compose the drag image 414 Bitmap b; 415 Drawable preview = image.getDrawable(); 416 int w = preview.getIntrinsicWidth(); 417 int h = preview.getIntrinsicHeight(); 418 if (createItemInfo instanceof PendingAddWidgetInfo) { 419 PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) createItemInfo; 420 int[] spanXY = CellLayout.rectToCell(getResources(), 421 createWidgetInfo.minWidth, createWidgetInfo.minHeight, null); 422 createItemInfo.spanX = spanXY[0]; 423 createItemInfo.spanY = spanXY[1]; 424 425 b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 426 renderDrawableToBitmap(preview, b, 0, 0, w, h, 1, 1, mDragViewMultiplyColor); 427 } else { 428 // Workaround for the fact that we don't keep the original ResolveInfo associated with 429 // the shortcut around. To get the icon, we just render the preview image (which has 430 // the shortcut icon) to a new drag bitmap that clips the non-icon space. 431 b = Bitmap.createBitmap(mWidgetPreviewIconPaddedDimension, 432 mWidgetPreviewIconPaddedDimension, Bitmap.Config.ARGB_8888); 433 mCanvas.setBitmap(b); 434 mCanvas.save(); 435 preview.draw(mCanvas); 436 mCanvas.restore(); 437 mCanvas.drawColor(mDragViewMultiplyColor, PorterDuff.Mode.MULTIPLY); 438 mCanvas.setBitmap(null); 439 createItemInfo.spanX = createItemInfo.spanY = 1; 440 } 441 442 // Start the drag 443 mLauncher.lockScreenOrientation(); 444 mLauncher.getWorkspace().onDragStartedWithItemSpans(createItemInfo.spanX, 445 createItemInfo.spanY, b); 446 mDragController.startDrag(image, b, this, createItemInfo, 447 DragController.DRAG_ACTION_COPY, null); 448 b.recycle(); 449 } 450 @Override 451 protected boolean beginDragging(View v) { 452 if (!super.beginDragging(v)) return false; 453 454 // Go into spring loaded mode (must happen before we startDrag()) 455 int currentPageIndex = mLauncher.getWorkspace().getCurrentPage(); 456 CellLayout currentPage = (CellLayout) mLauncher.getWorkspace().getChildAt(currentPageIndex); 457 mLauncher.enterSpringLoadedDragMode(currentPage); 458 459 if (v instanceof PagedViewIcon) { 460 beginDraggingApplication(v); 461 } else if (v instanceof PagedViewWidget) { 462 beginDraggingWidget(v); 463 } 464 return true; 465 } 466 private void endDragging(View target, boolean success) { 467 mLauncher.getWorkspace().onDragStopped(success); 468 if (!success || (target != mLauncher.getWorkspace() && 469 !(target instanceof DeleteDropTarget))) { 470 // Exit spring loaded mode if we have not successfully dropped or have not handled the 471 // drop in Workspace 472 mLauncher.exitSpringLoadedDragMode(); 473 } 474 mLauncher.unlockScreenOrientation(); 475 476 } 477 478 @Override 479 public void onDropCompleted(View target, DragObject d, boolean success) { 480 endDragging(target, success); 481 482 // Display an error message if the drag failed due to there not being enough space on the 483 // target layout we were dropping on. 484 if (!success) { 485 boolean showOutOfSpaceMessage = false; 486 if (target instanceof Workspace) { 487 int currentScreen = mLauncher.getCurrentWorkspaceScreen(); 488 Workspace workspace = (Workspace) target; 489 CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen); 490 ItemInfo itemInfo = (ItemInfo) d.dragInfo; 491 if (layout != null) { 492 layout.calculateSpans(itemInfo); 493 showOutOfSpaceMessage = 494 !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY); 495 } 496 } 497 // TODO-APPS_CUSTOMIZE: We need to handle this for folders as well later. 498 if (showOutOfSpaceMessage) { 499 mLauncher.showOutOfSpaceMessage(); 500 } 501 } 502 } 503 504 public void setContentType(ContentType type) { 505 mContentType = type; 506 invalidatePageData(0, true); 507 } 508 509 public boolean isContentType(ContentType type) { 510 return (mContentType == type); 511 } 512 513 public void setCurrentPageToWidgets() { 514 invalidatePageData(0); 515 } 516 517 /* 518 * Apps PagedView implementation 519 */ 520 private void setVisibilityOnChildren(ViewGroup layout, int visibility) { 521 int childCount = layout.getChildCount(); 522 for (int i = 0; i < childCount; ++i) { 523 layout.getChildAt(i).setVisibility(visibility); 524 } 525 } 526 private void setupPage(PagedViewCellLayout layout) { 527 layout.setCellCount(mCellCountX, mCellCountY); 528 layout.setGap(mPageLayoutWidthGap, mPageLayoutHeightGap); 529 layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop, 530 mPageLayoutPaddingRight, mPageLayoutPaddingBottom); 531 532 // Note: We force a measure here to get around the fact that when we do layout calculations 533 // immediately after syncing, we don't have a proper width. That said, we already know the 534 // expected page width, so we can actually optimize by hiding all the TextView-based 535 // children that are expensive to measure, and let that happen naturally later. 536 setVisibilityOnChildren(layout, View.GONE); 537 int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST); 538 int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST); 539 layout.setMinimumWidth(getPageContentWidth()); 540 layout.measure(widthSpec, heightSpec); 541 setVisibilityOnChildren(layout, View.VISIBLE); 542 } 543 public void syncAppsPages() { 544 // Ensure that we have the right number of pages 545 Context context = getContext(); 546 int numPages = (int) Math.ceil((float) mApps.size() / (mCellCountX * mCellCountY)); 547 for (int i = 0; i < numPages; ++i) { 548 PagedViewCellLayout layout = new PagedViewCellLayout(context); 549 setupPage(layout); 550 addView(layout); 551 } 552 } 553 public void syncAppsPageItems(int page, boolean immediate) { 554 // ensure that we have the right number of items on the pages 555 int numPages = getPageCount(); 556 int numCells = mCellCountX * mCellCountY; 557 int startIndex = page * numCells; 558 int endIndex = Math.min(startIndex + numCells, mApps.size()); 559 PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page); 560 561 layout.removeAllViewsOnPage(); 562 ArrayList<Object> items = new ArrayList<Object>(); 563 ArrayList<Bitmap> images = new ArrayList<Bitmap>(); 564 for (int i = startIndex; i < endIndex; ++i) { 565 ApplicationInfo info = mApps.get(i); 566 PagedViewIcon icon = (PagedViewIcon) mLayoutInflater.inflate( 567 R.layout.apps_customize_application, layout, false); 568 icon.applyFromApplicationInfo(info, true, mHolographicOutlineHelper); 569 icon.setOnClickListener(this); 570 icon.setOnLongClickListener(this); 571 icon.setOnTouchListener(this); 572 573 int index = i - startIndex; 574 int x = index % mCellCountX; 575 int y = index / mCellCountX; 576 layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1)); 577 578 items.add(info); 579 images.add(info.iconBitmap); 580 } 581 582 // Create the hardware layers 583 layout.allowHardwareLayerCreation(); 584 layout.createHardwareLayers(); 585 586 if (mFadeInAdjacentScreens) { 587 prepareGenerateHoloOutlinesTask(page, items, images); 588 } 589 } 590 591 /** 592 * Return the appropriate thread priority for loading for a given page (we give the current 593 * page much higher priority) 594 */ 595 private int getThreadPriorityForPage(int page) { 596 // TODO-APPS_CUSTOMIZE: detect number of cores and set thread priorities accordingly below 597 int pageDiff = Math.abs(page - mCurrentPage); 598 if (pageDiff <= 0) { 599 // return Process.THREAD_PRIORITY_DEFAULT; 600 return Process.THREAD_PRIORITY_MORE_FAVORABLE; 601 } else if (pageDiff <= 1) { 602 // return Process.THREAD_PRIORITY_BACKGROUND; 603 return Process.THREAD_PRIORITY_DEFAULT; 604 } else { 605 // return Process.THREAD_PRIORITY_LOWEST; 606 return Process.THREAD_PRIORITY_DEFAULT; 607 } 608 } 609 private int getSleepForPage(int page) { 610 int pageDiff = Math.abs(page - mCurrentPage) - 1; 611 return Math.max(0, pageDiff * sPageSleepDelay); 612 } 613 /** 614 * Creates and executes a new AsyncTask to load a page of widget previews. 615 */ 616 private void prepareLoadWidgetPreviewsTask(int page, ArrayList<Object> widgets, 617 int cellWidth, int cellHeight, int cellCountX) { 618 // Prune all tasks that are no longer needed 619 Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator(); 620 while (iter.hasNext()) { 621 AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); 622 int taskPage = task.page; 623 if ((taskPage == page) || 624 taskPage < getAssociatedLowerPageBound(mCurrentPage) || 625 taskPage > getAssociatedUpperPageBound(mCurrentPage)) { 626 task.cancel(false); 627 iter.remove(); 628 } else { 629 task.setThreadPriority(getThreadPriorityForPage(taskPage)); 630 } 631 } 632 633 // We introduce a slight delay to order the loading of side pages so that we don't thrash 634 final int sleepMs = getSleepForPage(page); 635 AsyncTaskPageData pageData = new AsyncTaskPageData(page, widgets, cellWidth, cellHeight, 636 cellCountX, new AsyncTaskCallback() { 637 @Override 638 public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) { 639 try { 640 Thread.sleep(sleepMs); 641 } catch (Exception e) {} 642 loadWidgetPreviewsInBackground(task, data); 643 } 644 }, 645 new AsyncTaskCallback() { 646 @Override 647 public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) { 648 mRunningTasks.remove(task); 649 if (task.isCancelled()) return; 650 if (task.page > getPageCount()) return; 651 if (task.pageContentType != mContentType) return; 652 onSyncWidgetPageItems(data); 653 } 654 }); 655 656 // Ensure that the task is appropriately prioritized and runs in parallel 657 AppsCustomizeAsyncTask t = new AppsCustomizeAsyncTask(page, mContentType, 658 AsyncTaskPageData.Type.LoadWidgetPreviewData); 659 t.setThreadPriority(getThreadPriorityForPage(page)); 660 t.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, pageData); 661 mRunningTasks.add(t); 662 } 663 /** 664 * Creates and executes a new AsyncTask to load the outlines for a page of content. 665 */ 666 private void prepareGenerateHoloOutlinesTask(int page, ArrayList<Object> items, 667 ArrayList<Bitmap> images) { 668 // Prune old tasks for this page 669 Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator(); 670 while (iter.hasNext()) { 671 AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); 672 int taskPage = task.page; 673 if ((taskPage == page) && 674 (task.dataType == AsyncTaskPageData.Type.LoadHolographicIconsData)) { 675 task.cancel(false); 676 iter.remove(); 677 } 678 } 679 680 AsyncTaskPageData pageData = new AsyncTaskPageData(page, items, images, 681 new AsyncTaskCallback() { 682 @Override 683 public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) { 684 // Ensure that this task starts running at the correct priority 685 task.syncThreadPriority(); 686 687 ArrayList<Bitmap> images = data.generatedImages; 688 ArrayList<Bitmap> srcImages = data.sourceImages; 689 int count = srcImages.size(); 690 Canvas c = new Canvas(); 691 for (int i = 0; i < count && !task.isCancelled(); ++i) { 692 // Before work on each item, ensure that this task is running at the correct 693 // priority 694 task.syncThreadPriority(); 695 696 Bitmap b = srcImages.get(i); 697 Bitmap outline = Bitmap.createBitmap(b.getWidth(), b.getHeight(), 698 Bitmap.Config.ARGB_8888); 699 700 c.setBitmap(outline); 701 c.save(); 702 c.drawBitmap(b, 0, 0, null); 703 c.restore(); 704 c.setBitmap(null); 705 706 images.add(outline); 707 } 708 } 709 }, 710 new AsyncTaskCallback() { 711 @Override 712 public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) { 713 mRunningTasks.remove(task); 714 if (task.isCancelled()) return; 715 if (task.page > getPageCount()) return; 716 if (task.pageContentType != mContentType) return; 717 onHolographicPageItemsLoaded(data); 718 } 719 }); 720 721 // Ensure that the outline task always runs in the background, serially 722 AppsCustomizeAsyncTask t = 723 new AppsCustomizeAsyncTask(page, mContentType, 724 AsyncTaskPageData.Type.LoadHolographicIconsData); 725 t.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 726 t.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, pageData); 727 mRunningTasks.add(t); 728 } 729 730 /* 731 * Widgets PagedView implementation 732 */ 733 private void setupPage(PagedViewGridLayout layout) { 734 layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop, 735 mPageLayoutPaddingRight, mPageLayoutPaddingBottom); 736 737 // Note: We force a measure here to get around the fact that when we do layout calculations 738 // immediately after syncing, we don't have a proper width. 739 int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST); 740 int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST); 741 layout.setMinimumWidth(getPageContentWidth()); 742 layout.measure(widthSpec, heightSpec); 743 } 744 private void renderDrawableToBitmap(Drawable d, Bitmap bitmap, int x, int y, int w, int h, 745 float scaleX, float scaleY) { 746 renderDrawableToBitmap(d, bitmap, x, y, w, h, scaleX, scaleY, 0xFFFFFFFF); 747 } 748 private void renderDrawableToBitmap(Drawable d, Bitmap bitmap, int x, int y, int w, int h, 749 float scaleX, float scaleY, int multiplyColor) { 750 if (bitmap != null) { 751 Canvas c = new Canvas(bitmap); 752 c.scale(scaleX, scaleY); 753 Rect oldBounds = d.copyBounds(); 754 d.setBounds(x, y, x + w, y + h); 755 d.draw(c); 756 d.setBounds(oldBounds); // Restore the bounds 757 if (multiplyColor != 0xFFFFFFFF) { 758 c.drawColor(mDragViewMultiplyColor, PorterDuff.Mode.MULTIPLY); 759 } 760 c.setBitmap(null); 761 } 762 } 763 private Bitmap getShortcutPreview(ResolveInfo info, int cellWidth, int cellHeight) { 764 // Render the icon 765 Bitmap preview = Bitmap.createBitmap(cellWidth, mAppIconSize, Config.ARGB_8888); 766 Drawable icon = mIconCache.getFullResIcon(info, mPackageManager); 767 renderDrawableToBitmap(icon, preview, 0, 0, mAppIconSize, mAppIconSize, 1f, 1f); 768 return preview; 769 } 770 private Bitmap getWidgetPreview(AppWidgetProviderInfo info, 771 int cellHSpan, int cellVSpan, int cellWidth, int cellHeight) { 772 773 // Calculate the size of the drawable 774 cellHSpan = Math.max(mMinWidgetSpan, Math.min(mMaxWidgetSpan, cellHSpan)); 775 cellVSpan = Math.max(mMinWidgetSpan, Math.min(mMaxWidgetSpan, cellVSpan)); 776 int expectedWidth = mWidgetSpacingLayout.estimateCellWidth(cellHSpan); 777 int expectedHeight = mWidgetSpacingLayout.estimateCellHeight(cellVSpan); 778 779 // Scale down the bitmap to fit the space 780 float widgetPreviewScale = (float) cellWidth / expectedWidth; 781 expectedWidth = (int) (widgetPreviewScale * expectedWidth); 782 expectedHeight = (int) (widgetPreviewScale * expectedHeight); 783 784 // Load the preview image if possible 785 String packageName = info.provider.getPackageName(); 786 Drawable drawable = null; 787 Bitmap preview = null; 788 if (info.previewImage != 0) { 789 drawable = mPackageManager.getDrawable(packageName, info.previewImage, null); 790 if (drawable == null) { 791 Log.w(LOG_TAG, "Can't load icon drawable 0x" + Integer.toHexString(info.icon) 792 + " for provider: " + info.provider); 793 } else { 794 // Scale down the preview to the dimensions we want 795 int imageWidth = drawable.getIntrinsicWidth(); 796 int imageHeight = drawable.getIntrinsicHeight(); 797 float aspect = (float) imageWidth / imageHeight; 798 int newWidth = imageWidth; 799 int newHeight = imageHeight; 800 if (aspect > 1f) { 801 newWidth = expectedWidth; 802 newHeight = (int) (imageHeight * ((float) expectedWidth / imageWidth)); 803 } else { 804 newHeight = expectedHeight; 805 newWidth = (int) (imageWidth * ((float) expectedHeight / imageHeight)); 806 } 807 808 preview = Bitmap.createBitmap(newWidth, newHeight, Config.ARGB_8888); 809 renderDrawableToBitmap(drawable, preview, 0, 0, newWidth, newHeight, 1f, 1f); 810 } 811 } 812 813 // Generate a preview image if we couldn't load one 814 if (drawable == null) { 815 Resources resources = mLauncher.getResources(); 816 int bitmapWidth; 817 int bitmapHeight; 818 819 // Specify the dimensions of the bitmap (since we are using a default preview bg with 820 // the full icon, we only imply the aspect ratio of the widget) 821 if (cellHSpan == cellVSpan) { 822 bitmapWidth = bitmapHeight = cellWidth; 823 expectedWidth = expectedHeight = mWidgetPreviewIconPaddedDimension; 824 } else if (cellHSpan >= cellVSpan) { 825 bitmapWidth = expectedWidth = cellWidth; 826 bitmapHeight = expectedHeight = mWidgetPreviewIconPaddedDimension; 827 } else { 828 // Note that in vertical widgets, we might not have enough space due to the text 829 // label, so be conservative and use the width as a height bound 830 bitmapWidth = expectedWidth = mWidgetPreviewIconPaddedDimension; 831 bitmapHeight = expectedHeight = cellWidth; 832 } 833 834 preview = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Config.ARGB_8888); 835 renderDrawableToBitmap(mDefaultWidgetBackground, preview, 0, 0, expectedWidth, 836 expectedHeight, 1f, 1f); 837 838 // Draw the icon in the top left corner 839 try { 840 Drawable icon = null; 841 if (info.icon > 0) icon = mPackageManager.getDrawable(packageName, info.icon, null); 842 if (icon == null) icon = resources.getDrawable(R.drawable.ic_launcher_application); 843 844 int offset = (int) (mAppIconSize * sWidgetPreviewIconPaddingPercentage); 845 renderDrawableToBitmap(icon, preview, offset, offset, 846 mAppIconSize, mAppIconSize, 1f, 1f); 847 } catch (Resources.NotFoundException e) {} 848 } 849 return preview; 850 } 851 public void syncWidgetPages() { 852 // Ensure that we have the right number of pages 853 Context context = getContext(); 854 int numPages = (int) Math.ceil(mWidgets.size() / 855 (float) (mWidgetCountX * mWidgetCountY)); 856 for (int j = 0; j < numPages; ++j) { 857 PagedViewGridLayout layout = new PagedViewGridLayout(context, mWidgetCountX, 858 mWidgetCountY); 859 setupPage(layout); 860 addView(layout); 861 } 862 } 863 public void syncWidgetPageItems(int page, boolean immediate) { 864 int numItemsPerPage = mWidgetCountX * mWidgetCountY; 865 int contentWidth = mWidgetSpacingLayout.getContentWidth(); 866 int contentHeight = mWidgetSpacingLayout.getContentHeight(); 867 868 // Calculate the dimensions of each cell we are giving to each widget 869 ArrayList<Object> items = new ArrayList<Object>(); 870 int cellWidth = ((contentWidth - mPageLayoutPaddingLeft - mPageLayoutPaddingRight 871 - ((mWidgetCountX - 1) * mWidgetWidthGap)) / mWidgetCountX); 872 int cellHeight = ((contentHeight - mPageLayoutPaddingTop - mPageLayoutPaddingBottom 873 - ((mWidgetCountY - 1) * mWidgetHeightGap)) / mWidgetCountY); 874 875 int offset = page * numItemsPerPage; 876 for (int i = offset; i < Math.min(offset + numItemsPerPage, mWidgets.size()); ++i) { 877 items.add(mWidgets.get(i)); 878 } 879 880 if (immediate) { 881 AsyncTaskPageData data = new AsyncTaskPageData(page, items, cellWidth, cellHeight, 882 mWidgetCountX, null, null); 883 loadWidgetPreviewsInBackground(null, data); 884 onSyncWidgetPageItems(data); 885 } else { 886 prepareLoadWidgetPreviewsTask(page, items, cellWidth, cellHeight, mWidgetCountX); 887 } 888 } 889 private void loadWidgetPreviewsInBackground(AppsCustomizeAsyncTask task, 890 AsyncTaskPageData data) { 891 if (task != null) { 892 // Ensure that this task starts running at the correct priority 893 task.syncThreadPriority(); 894 } 895 896 // Load each of the widget/shortcut previews 897 ArrayList<Object> items = data.items; 898 ArrayList<Bitmap> images = data.generatedImages; 899 int count = items.size(); 900 int cellWidth = data.cellWidth; 901 int cellHeight = data.cellHeight; 902 for (int i = 0; i < count; ++i) { 903 if (task != null) { 904 // Ensure we haven't been cancelled yet 905 if (task.isCancelled()) break; 906 // Before work on each item, ensure that this task is running at the correct 907 // priority 908 task.syncThreadPriority(); 909 } 910 911 Object rawInfo = items.get(i); 912 if (rawInfo instanceof AppWidgetProviderInfo) { 913 AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo; 914 int[] cellSpans = CellLayout.rectToCell(getResources(), 915 info.minWidth, info.minHeight, null); 916 images.add(getWidgetPreview(info, cellSpans[0],cellSpans[1], 917 cellWidth, cellHeight)); 918 } else if (rawInfo instanceof ResolveInfo) { 919 // Fill in the shortcuts information 920 ResolveInfo info = (ResolveInfo) rawInfo; 921 images.add(getShortcutPreview(info, cellWidth, cellHeight)); 922 } 923 } 924 } 925 private void onSyncWidgetPageItems(AsyncTaskPageData data) { 926 int page = data.page; 927 PagedViewGridLayout layout = (PagedViewGridLayout) getChildAt(page); 928 // Only set the column count once we have items 929 layout.setColumnCount(layout.getCellCountX()); 930 931 ArrayList<Object> items = data.items; 932 int count = items.size(); 933 int cellWidth = data.cellWidth; 934 int cellHeight = data.cellHeight; 935 int cellCountX = data.cellCountX; 936 for (int i = 0; i < count; ++i) { 937 Object rawInfo = items.get(i); 938 PendingAddItemInfo createItemInfo = null; 939 PagedViewWidget widget = (PagedViewWidget) mLayoutInflater.inflate( 940 R.layout.apps_customize_widget, layout, false); 941 if (rawInfo instanceof AppWidgetProviderInfo) { 942 // Fill in the widget information 943 AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo; 944 createItemInfo = new PendingAddWidgetInfo(info, null, null); 945 int[] cellSpans = CellLayout.rectToCell(getResources(), 946 info.minWidth, info.minHeight, null); 947 FastBitmapDrawable preview = new FastBitmapDrawable(data.generatedImages.get(i)); 948 widget.applyFromAppWidgetProviderInfo(info, preview, -1, cellSpans, 949 mHolographicOutlineHelper); 950 widget.setTag(createItemInfo); 951 } else if (rawInfo instanceof ResolveInfo) { 952 // Fill in the shortcuts information 953 ResolveInfo info = (ResolveInfo) rawInfo; 954 createItemInfo = new PendingAddItemInfo(); 955 createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; 956 createItemInfo.componentName = new ComponentName(info.activityInfo.packageName, 957 info.activityInfo.name); 958 FastBitmapDrawable preview = new FastBitmapDrawable(data.generatedImages.get(i)); 959 widget.applyFromResolveInfo(mPackageManager, info, preview, 960 mHolographicOutlineHelper); 961 widget.setTag(createItemInfo); 962 } 963 widget.setOnClickListener(this); 964 widget.setOnLongClickListener(this); 965 widget.setOnTouchListener(this); 966 967 // Layout each widget 968 int ix = i % cellCountX; 969 int iy = i / cellCountX; 970 GridLayout.LayoutParams lp = new GridLayout.LayoutParams( 971 GridLayout.spec(iy, GridLayout.LEFT), 972 GridLayout.spec(ix, GridLayout.TOP)); 973 lp.width = cellWidth; 974 lp.height = cellHeight; 975 lp.setGravity(Gravity.TOP | Gravity.LEFT); 976 if (ix > 0) lp.leftMargin = mWidgetWidthGap; 977 if (iy > 0) lp.topMargin = mWidgetHeightGap; 978 layout.addView(widget, lp); 979 } 980 981 invalidate(); 982 forceUpdateAdjacentPagesAlpha(); 983 984 if (mFadeInAdjacentScreens) { 985 prepareGenerateHoloOutlinesTask(data.page, data.items, data.generatedImages); 986 } 987 } 988 private void onHolographicPageItemsLoaded(AsyncTaskPageData data) { 989 // Invalidate early to short-circuit children invalidates 990 invalidate(); 991 992 int page = data.page; 993 ViewGroup layout = (ViewGroup) getChildAt(page); 994 if (layout instanceof PagedViewCellLayout) { 995 PagedViewCellLayout cl = (PagedViewCellLayout) layout; 996 int count = cl.getPageChildCount(); 997 if (count != data.generatedImages.size()) return; 998 for (int i = 0; i < count; ++i) { 999 PagedViewIcon icon = (PagedViewIcon) cl.getChildOnPageAt(i); 1000 icon.setHolographicOutline(data.generatedImages.get(i)); 1001 } 1002 } else { 1003 int count = layout.getChildCount(); 1004 if (count != data.generatedImages.size()) return; 1005 for (int i = 0; i < count; ++i) { 1006 View v = layout.getChildAt(i); 1007 ((PagedViewWidget) v).setHolographicOutline(data.generatedImages.get(i)); 1008 } 1009 } 1010 } 1011 1012 @Override 1013 public void syncPages() { 1014 removeAllViews(); 1015 1016 // Remove all background asyc tasks if we are loading content anew 1017 Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator(); 1018 while (iter.hasNext()) { 1019 AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); 1020 task.cancel(false); 1021 iter.remove(); 1022 } 1023 1024 switch (mContentType) { 1025 case Applications: 1026 syncAppsPages(); 1027 break; 1028 case Widgets: 1029 syncWidgetPages(); 1030 break; 1031 } 1032 } 1033 @Override 1034 public void syncPageItems(int page, boolean immediate) { 1035 switch (mContentType) { 1036 case Applications: 1037 syncAppsPageItems(page, immediate); 1038 break; 1039 case Widgets: 1040 syncWidgetPageItems(page, immediate); 1041 break; 1042 } 1043 } 1044 1045 /** 1046 * Used by the parent to get the content width to set the tab bar to 1047 * @return 1048 */ 1049 public int getPageContentWidth() { 1050 return mContentWidth; 1051 } 1052 1053 @Override 1054 protected void onPageBeginMoving() { 1055 /* TO BE ENABLED LATER 1056 setChildrenDrawnWithCacheEnabled(true); 1057 for (int i = 0; i < getChildCount(); ++i) { 1058 View v = getChildAt(i); 1059 if (v instanceof PagedViewCellLayout) { 1060 ((PagedViewCellLayout) v).setChildrenDrawingCacheEnabled(true); 1061 } 1062 } 1063 */ 1064 super.onPageBeginMoving(); 1065 } 1066 1067 @Override 1068 protected void onPageEndMoving() { 1069 /* TO BE ENABLED LATER 1070 for (int i = 0; i < getChildCount(); ++i) { 1071 View v = getChildAt(i); 1072 if (v instanceof PagedViewCellLayout) { 1073 ((PagedViewCellLayout) v).setChildrenDrawingCacheEnabled(false); 1074 } 1075 } 1076 setChildrenDrawnWithCacheEnabled(false); 1077 */ 1078 super.onPageEndMoving(); 1079 } 1080 1081 /* 1082 * AllAppsView implementation 1083 */ 1084 @Override 1085 public void setup(Launcher launcher, DragController dragController) { 1086 mLauncher = launcher; 1087 mDragController = dragController; 1088 } 1089 @Override 1090 public void zoom(float zoom, boolean animate) { 1091 // TODO-APPS_CUSTOMIZE: Call back to mLauncher.zoomed() 1092 } 1093 @Override 1094 public boolean isVisible() { 1095 return (getVisibility() == VISIBLE); 1096 } 1097 @Override 1098 public boolean isAnimating() { 1099 return false; 1100 } 1101 @Override 1102 public void setApps(ArrayList<ApplicationInfo> list) { 1103 mApps = list; 1104 Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR); 1105 1106 // The next layout pass will trigger data-ready if both widgets and apps are set, so 1107 // request a layout to do this test and invalidate the page data when ready. 1108 if (testDataReady()) requestLayout(); 1109 } 1110 private void addAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) { 1111 // We add it in place, in alphabetical order 1112 int count = list.size(); 1113 for (int i = 0; i < count; ++i) { 1114 ApplicationInfo info = list.get(i); 1115 int index = Collections.binarySearch(mApps, info, LauncherModel.APP_NAME_COMPARATOR); 1116 if (index < 0) { 1117 mApps.add(-(index + 1), info); 1118 } 1119 } 1120 } 1121 @Override 1122 public void addApps(ArrayList<ApplicationInfo> list) { 1123 addAppsWithoutInvalidate(list); 1124 invalidatePageData(); 1125 } 1126 private int findAppByComponent(List<ApplicationInfo> list, ApplicationInfo item) { 1127 ComponentName removeComponent = item.intent.getComponent(); 1128 int length = list.size(); 1129 for (int i = 0; i < length; ++i) { 1130 ApplicationInfo info = list.get(i); 1131 if (info.intent.getComponent().equals(removeComponent)) { 1132 return i; 1133 } 1134 } 1135 return -1; 1136 } 1137 private void removeAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) { 1138 // loop through all the apps and remove apps that have the same component 1139 int length = list.size(); 1140 for (int i = 0; i < length; ++i) { 1141 ApplicationInfo info = list.get(i); 1142 int removeIndex = findAppByComponent(mApps, info); 1143 if (removeIndex > -1) { 1144 mApps.remove(removeIndex); 1145 } 1146 } 1147 } 1148 @Override 1149 public void removeApps(ArrayList<ApplicationInfo> list) { 1150 removeAppsWithoutInvalidate(list); 1151 invalidatePageData(); 1152 } 1153 @Override 1154 public void updateApps(ArrayList<ApplicationInfo> list) { 1155 // We remove and re-add the updated applications list because it's properties may have 1156 // changed (ie. the title), and this will ensure that the items will be in their proper 1157 // place in the list. 1158 removeAppsWithoutInvalidate(list); 1159 addAppsWithoutInvalidate(list); 1160 invalidatePageData(); 1161 } 1162 1163 @Override 1164 public void reset() { 1165 if (mContentType != ContentType.Applications) { 1166 // Reset to the first page of the Apps pane 1167 AppsCustomizeTabHost tabs = (AppsCustomizeTabHost) 1168 mLauncher.findViewById(R.id.apps_customize_pane); 1169 tabs.selectAppsTab(); 1170 } else if (getCurrentPage() != 0) { 1171 invalidatePageData(0); 1172 } 1173 } 1174 @Override 1175 public void dumpState() { 1176 // TODO: Dump information related to current list of Applications, Widgets, etc. 1177 ApplicationInfo.dumpApplicationInfoList(LOG_TAG, "mApps", mApps); 1178 dumpAppWidgetProviderInfoList(LOG_TAG, "mWidgets", mWidgets); 1179 } 1180 private void dumpAppWidgetProviderInfoList(String tag, String label, 1181 ArrayList<Object> list) { 1182 Log.d(tag, label + " size=" + list.size()); 1183 for (Object i: list) { 1184 if (i instanceof AppWidgetProviderInfo) { 1185 AppWidgetProviderInfo info = (AppWidgetProviderInfo) i; 1186 Log.d(tag, " label=\"" + info.label + "\" previewImage=" + info.previewImage 1187 + " resizeMode=" + info.resizeMode + " configure=" + info.configure 1188 + " initialLayout=" + info.initialLayout 1189 + " minWidth=" + info.minWidth + " minHeight=" + info.minHeight); 1190 } else if (i instanceof ResolveInfo) { 1191 ResolveInfo info = (ResolveInfo) i; 1192 Log.d(tag, " label=\"" + info.loadLabel(mPackageManager) + "\" icon=" 1193 + info.icon); 1194 } 1195 } 1196 } 1197 @Override 1198 public void surrender() { 1199 // TODO: If we are in the middle of any process (ie. for holographic outlines, etc) we 1200 // should stop this now. 1201 } 1202 1203 /* 1204 * We load an extra page on each side to prevent flashes from scrolling and loading of the 1205 * widget previews in the background with the AsyncTasks. 1206 */ 1207 protected int getAssociatedLowerPageBound(int page) { 1208 return Math.max(0, page - 2); 1209 } 1210 protected int getAssociatedUpperPageBound(int page) { 1211 final int count = getChildCount(); 1212 return Math.min(page + 2, count - 1); 1213 } 1214 1215 @Override 1216 protected String getCurrentPageDescription() { 1217 int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage; 1218 int stringId = R.string.default_scroll_format; 1219 switch (mContentType) { 1220 case Applications: 1221 stringId = R.string.apps_customize_apps_scroll_format; 1222 break; 1223 case Widgets: 1224 stringId = R.string.apps_customize_widgets_scroll_format; 1225 break; 1226 } 1227 return String.format(mContext.getString(stringId), page + 1, getChildCount()); 1228 } 1229} 1230