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