AppsCustomizePagedView.java revision 72d8b393b03f9decba05b0797608ccdc751fecef
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 createItemInfo.spanX = createItemInfo.spanY = 1; 438 } 439 440 // Start the drag 441 mLauncher.lockScreenOrientation(); 442 mLauncher.getWorkspace().onDragStartedWithItemSpans(createItemInfo.spanX, 443 createItemInfo.spanY, b); 444 mDragController.startDrag(image, b, this, createItemInfo, 445 DragController.DRAG_ACTION_COPY, null); 446 b.recycle(); 447 } 448 @Override 449 protected boolean beginDragging(View v) { 450 if (!super.beginDragging(v)) return false; 451 452 // Go into spring loaded mode (must happen before we startDrag()) 453 int currentPageIndex = mLauncher.getWorkspace().getCurrentPage(); 454 CellLayout currentPage = (CellLayout) mLauncher.getWorkspace().getChildAt(currentPageIndex); 455 mLauncher.enterSpringLoadedDragMode(currentPage); 456 457 if (v instanceof PagedViewIcon) { 458 beginDraggingApplication(v); 459 } else if (v instanceof PagedViewWidget) { 460 beginDraggingWidget(v); 461 } 462 return true; 463 } 464 private void endDragging(View target, boolean success) { 465 mLauncher.getWorkspace().onDragStopped(success); 466 if (!success || (target != mLauncher.getWorkspace() && 467 !(target instanceof DeleteDropTarget))) { 468 // Exit spring loaded mode if we have not successfully dropped or have not handled the 469 // drop in Workspace 470 mLauncher.exitSpringLoadedDragMode(); 471 } 472 mLauncher.unlockScreenOrientation(); 473 474 } 475 476 @Override 477 public void onDropCompleted(View target, DragObject d, boolean success) { 478 endDragging(target, success); 479 480 // Display an error message if the drag failed due to there not being enough space on the 481 // target layout we were dropping on. 482 if (!success) { 483 boolean showOutOfSpaceMessage = false; 484 if (target instanceof Workspace) { 485 int currentScreen = mLauncher.getCurrentWorkspaceScreen(); 486 Workspace workspace = (Workspace) target; 487 CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen); 488 ItemInfo itemInfo = (ItemInfo) d.dragInfo; 489 if (layout != null) { 490 layout.calculateSpans(itemInfo); 491 showOutOfSpaceMessage = 492 !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY); 493 } 494 } 495 // TODO-APPS_CUSTOMIZE: We need to handle this for folders as well later. 496 if (showOutOfSpaceMessage) { 497 mLauncher.showOutOfSpaceMessage(); 498 } 499 } 500 } 501 502 public void setContentType(ContentType type) { 503 mContentType = type; 504 invalidatePageData(0); 505 } 506 507 public boolean isContentType(ContentType type) { 508 return (mContentType == type); 509 } 510 511 public void setCurrentPageToWidgets() { 512 invalidatePageData(0); 513 } 514 public void setCurrentPageToShortcuts() { 515 invalidatePageData(mNumWidgetPages); 516 } 517 518 /* 519 * Apps PagedView implementation 520 */ 521 private void setVisibilityOnChildren(ViewGroup layout, int visibility) { 522 int childCount = layout.getChildCount(); 523 for (int i = 0; i < childCount; ++i) { 524 layout.getChildAt(i).setVisibility(visibility); 525 } 526 } 527 private void setupPage(PagedViewCellLayout layout) { 528 layout.setCellCount(mCellCountX, mCellCountY); 529 layout.setGap(mPageLayoutWidthGap, mPageLayoutHeightGap); 530 layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop, 531 mPageLayoutPaddingRight, mPageLayoutPaddingBottom); 532 533 // Note: We force a measure here to get around the fact that when we do layout calculations 534 // immediately after syncing, we don't have a proper width. That said, we already know the 535 // expected page width, so we can actually optimize by hiding all the TextView-based 536 // children that are expensive to measure, and let that happen naturally later. 537 setVisibilityOnChildren(layout, View.GONE); 538 int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST); 539 int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST); 540 layout.setMinimumWidth(getPageContentWidth()); 541 layout.measure(widthSpec, heightSpec); 542 setVisibilityOnChildren(layout, View.VISIBLE); 543 } 544 public void syncAppsPages() { 545 // Ensure that we have the right number of pages 546 Context context = getContext(); 547 int numPages = (int) Math.ceil((float) mApps.size() / (mCellCountX * mCellCountY)); 548 for (int i = 0; i < numPages; ++i) { 549 PagedViewCellLayout layout = new PagedViewCellLayout(context); 550 setupPage(layout); 551 addView(layout); 552 } 553 } 554 public void syncAppsPageItems(int page) { 555 // ensure that we have the right number of items on the pages 556 int numPages = getPageCount(); 557 int numCells = mCellCountX * mCellCountY; 558 int startIndex = page * numCells; 559 int endIndex = Math.min(startIndex + numCells, mApps.size()); 560 PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page); 561 562 layout.removeAllViewsOnPage(); 563 ArrayList<Object> items = new ArrayList<Object>(); 564 ArrayList<Bitmap> images = new ArrayList<Bitmap>(); 565 for (int i = startIndex; i < endIndex; ++i) { 566 ApplicationInfo info = mApps.get(i); 567 PagedViewIcon icon = (PagedViewIcon) mLayoutInflater.inflate( 568 R.layout.apps_customize_application, layout, false); 569 icon.applyFromApplicationInfo(info, true, mHolographicOutlineHelper); 570 icon.setOnClickListener(this); 571 icon.setOnLongClickListener(this); 572 icon.setOnTouchListener(this); 573 574 int index = i - startIndex; 575 int x = index % mCellCountX; 576 int y = index / mCellCountX; 577 layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1)); 578 579 items.add(info); 580 images.add(info.iconBitmap); 581 } 582 583 // Create the hardware layers 584 layout.allowHardwareLayerCreation(); 585 layout.createHardwareLayers(); 586 587 prepareGenerateHoloOutlinesTask(page, items, images); 588 } 589 590 /** 591 * Return the appropriate thread priority for loading for a given page (we give the current 592 * page much higher priority) 593 */ 594 private int getThreadPriorityForPage(int page) { 595 // TODO-APPS_CUSTOMIZE: detect number of cores and set thread priorities accordingly below 596 int pageDiff = Math.abs(page - mCurrentPage); 597 if (pageDiff <= 0) { 598 // return Process.THREAD_PRIORITY_DEFAULT; 599 return Process.THREAD_PRIORITY_MORE_FAVORABLE; 600 } else if (pageDiff <= 1) { 601 // return Process.THREAD_PRIORITY_BACKGROUND; 602 return Process.THREAD_PRIORITY_DEFAULT; 603 } else { 604 // return Process.THREAD_PRIORITY_LOWEST; 605 return Process.THREAD_PRIORITY_DEFAULT; 606 } 607 } 608 /** 609 * Creates and executes a new AsyncTask to load a page of widget previews. 610 */ 611 private void prepareLoadWidgetPreviewsTask(int page, ArrayList<Object> widgets, 612 int cellWidth, int cellHeight, int cellCountX) { 613 // Prune all tasks that are no longer needed 614 Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator(); 615 while (iter.hasNext()) { 616 AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); 617 int taskPage = task.page; 618 if ((taskPage == page) || 619 taskPage < getAssociatedLowerPageBound(mCurrentPage) || 620 taskPage > getAssociatedUpperPageBound(mCurrentPage)) { 621 task.cancel(false); 622 iter.remove(); 623 } else { 624 task.setThreadPriority(getThreadPriorityForPage(taskPage)); 625 } 626 } 627 628 AsyncTaskPageData pageData = new AsyncTaskPageData(page, widgets, cellWidth, cellHeight, 629 cellCountX, new AsyncTaskCallback() { 630 @Override 631 public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) { 632 // Ensure that this task starts running at the correct priority 633 task.syncThreadPriority(); 634 635 // Load each of the widget/shortcut previews 636 ArrayList<Object> items = data.items; 637 ArrayList<Bitmap> images = data.generatedImages; 638 int count = items.size(); 639 int cellWidth = data.cellWidth; 640 int cellHeight = data.cellHeight; 641 for (int i = 0; i < count && !task.isCancelled(); ++i) { 642 // Before work on each item, ensure that this task is running at the correct 643 // priority 644 task.syncThreadPriority(); 645 646 Object rawInfo = items.get(i); 647 if (rawInfo instanceof AppWidgetProviderInfo) { 648 AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo; 649 int[] cellSpans = CellLayout.rectToCell(getResources(), 650 info.minWidth, info.minHeight, null); 651 images.add(getWidgetPreview(info, cellSpans[0],cellSpans[1], 652 cellWidth, cellHeight)); 653 } else if (rawInfo instanceof ResolveInfo) { 654 // Fill in the shortcuts information 655 ResolveInfo info = (ResolveInfo) rawInfo; 656 images.add(getShortcutPreview(info, cellWidth, cellHeight)); 657 } 658 } 659 } 660 }, 661 new AsyncTaskCallback() { 662 @Override 663 public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) { 664 mRunningTasks.remove(task); 665 if (task.isCancelled()) return; 666 if (task.page > getPageCount()) return; 667 if (task.pageContentType != mContentType) return; 668 onSyncWidgetPageItems(data); 669 } 670 }); 671 672 // Ensure that the task is appropriately prioritized and runs in parallel 673 AppsCustomizeAsyncTask t = new AppsCustomizeAsyncTask(page, mContentType, 674 AsyncTaskPageData.Type.LoadWidgetPreviewData); 675 t.setThreadPriority(getThreadPriorityForPage(page)); 676 t.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, pageData); 677 mRunningTasks.add(t); 678 } 679 /** 680 * Creates and executes a new AsyncTask to load the outlines for a page of content. 681 */ 682 private void prepareGenerateHoloOutlinesTask(int page, ArrayList<Object> items, 683 ArrayList<Bitmap> images) { 684 // Prune old tasks for this page 685 Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator(); 686 while (iter.hasNext()) { 687 AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); 688 int taskPage = task.page; 689 if ((taskPage == page) && 690 (task.dataType == AsyncTaskPageData.Type.LoadHolographicIconsData)) { 691 task.cancel(false); 692 iter.remove(); 693 } 694 } 695 696 AsyncTaskPageData pageData = new AsyncTaskPageData(page, items, images, 697 new AsyncTaskCallback() { 698 @Override 699 public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) { 700 // Ensure that this task starts running at the correct priority 701 task.syncThreadPriority(); 702 703 ArrayList<Bitmap> images = data.generatedImages; 704 ArrayList<Bitmap> srcImages = data.sourceImages; 705 int count = srcImages.size(); 706 Canvas c = new Canvas(); 707 for (int i = 0; i < count && !task.isCancelled(); ++i) { 708 // Before work on each item, ensure that this task is running at the correct 709 // priority 710 task.syncThreadPriority(); 711 712 Bitmap b = srcImages.get(i); 713 Bitmap outline = Bitmap.createBitmap(b.getWidth(), b.getHeight(), 714 Bitmap.Config.ARGB_8888); 715 716 c.setBitmap(outline); 717 c.save(); 718 c.drawBitmap(b, 0, 0, null); 719 c.restore(); 720 721 images.add(outline); 722 } 723 } 724 }, 725 new AsyncTaskCallback() { 726 @Override 727 public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) { 728 mRunningTasks.remove(task); 729 if (task.isCancelled()) return; 730 if (task.page > getPageCount()) return; 731 if (task.pageContentType != mContentType) return; 732 onHolographicPageItemsLoaded(data); 733 } 734 }); 735 736 // Ensure that the outline task always runs in the background, serially 737 AppsCustomizeAsyncTask t = 738 new AppsCustomizeAsyncTask(page, mContentType, 739 AsyncTaskPageData.Type.LoadHolographicIconsData); 740 t.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 741 t.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, pageData); 742 mRunningTasks.add(t); 743 } 744 745 /* 746 * Widgets PagedView implementation 747 */ 748 private void setupPage(PagedViewGridLayout layout) { 749 layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop, 750 mPageLayoutPaddingRight, mPageLayoutPaddingBottom); 751 752 // Note: We force a measure here to get around the fact that when we do layout calculations 753 // immediately after syncing, we don't have a proper width. 754 int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST); 755 int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST); 756 layout.setMinimumWidth(getPageContentWidth()); 757 layout.measure(widthSpec, heightSpec); 758 } 759 private void renderDrawableToBitmap(Drawable d, Bitmap bitmap, int x, int y, int w, int h, 760 float scaleX, float scaleY) { 761 if (bitmap != null) { 762 Canvas c = new Canvas(bitmap); 763 c.scale(scaleX, scaleY); 764 Rect oldBounds = d.copyBounds(); 765 d.setBounds(x, y, x + w, y + h); 766 d.draw(c); 767 d.setBounds(oldBounds); // Restore the bounds 768 } 769 } 770 private Bitmap getShortcutPreview(ResolveInfo info, int cellWidth, int cellHeight) { 771 // Render the icon 772 Bitmap preview = Bitmap.createBitmap(cellWidth, mAppIconSize, Config.ARGB_8888); 773 Drawable icon = mIconCache.getFullResIcon(info, mPackageManager); 774 renderDrawableToBitmap(icon, preview, 0, 0, mAppIconSize, mAppIconSize, 1f, 1f); 775 return preview; 776 } 777 private Bitmap getWidgetPreview(AppWidgetProviderInfo info, 778 int cellHSpan, int cellVSpan, int cellWidth, int cellHeight) { 779 780 // Calculate the size of the drawable 781 cellHSpan = Math.max(mMinWidgetSpan, Math.min(mMaxWidgetSpan, cellHSpan)); 782 cellVSpan = Math.max(mMinWidgetSpan, Math.min(mMaxWidgetSpan, cellVSpan)); 783 int expectedWidth = mWidgetSpacingLayout.estimateCellWidth(cellHSpan); 784 int expectedHeight = mWidgetSpacingLayout.estimateCellHeight(cellVSpan); 785 786 // Scale down the bitmap to fit the space 787 float widgetPreviewScale = (float) cellWidth / expectedWidth; 788 expectedWidth = (int) (widgetPreviewScale * expectedWidth); 789 expectedHeight = (int) (widgetPreviewScale * expectedHeight); 790 791 // Load the preview image if possible 792 String packageName = info.provider.getPackageName(); 793 Drawable drawable = null; 794 Bitmap preview = null; 795 if (info.previewImage != 0) { 796 drawable = mPackageManager.getDrawable(packageName, info.previewImage, null); 797 if (drawable == null) { 798 Log.w(LOG_TAG, "Can't load icon drawable 0x" + Integer.toHexString(info.icon) 799 + " for provider: " + info.provider); 800 } else { 801 // Scale down the preview to the dimensions we want 802 int imageWidth = drawable.getIntrinsicWidth(); 803 int imageHeight = drawable.getIntrinsicHeight(); 804 float aspect = (float) imageWidth / imageHeight; 805 int newWidth = imageWidth; 806 int newHeight = imageHeight; 807 if (aspect > 1f) { 808 newWidth = expectedWidth; 809 newHeight = (int) (imageHeight * ((float) expectedWidth / imageWidth)); 810 } else { 811 newHeight = expectedHeight; 812 newWidth = (int) (imageWidth * ((float) expectedHeight / imageHeight)); 813 } 814 815 preview = Bitmap.createBitmap(newWidth, newHeight, Config.ARGB_8888); 816 renderDrawableToBitmap(drawable, preview, 0, 0, newWidth, newHeight, 1f, 1f); 817 } 818 } 819 820 // Generate a preview image if we couldn't load one 821 if (drawable == null) { 822 Resources resources = mLauncher.getResources(); 823 int bitmapWidth; 824 int bitmapHeight; 825 826 // Specify the dimensions of the bitmap (since we are using a default preview bg with 827 // the full icon, we only imply the aspect ratio of the widget) 828 if (cellHSpan == cellVSpan) { 829 bitmapWidth = bitmapHeight = cellWidth; 830 expectedWidth = expectedHeight = mWidgetPreviewIconPaddedDimension; 831 } else if (cellHSpan >= cellVSpan) { 832 bitmapWidth = expectedWidth = cellWidth; 833 bitmapHeight = expectedHeight = mWidgetPreviewIconPaddedDimension; 834 } else { 835 // Note that in vertical widgets, we might not have enough space due to the text 836 // label, so be conservative and use the width as a height bound 837 bitmapWidth = expectedWidth = mWidgetPreviewIconPaddedDimension; 838 bitmapHeight = expectedHeight = cellWidth; 839 } 840 841 preview = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Config.ARGB_8888); 842 renderDrawableToBitmap(mDefaultWidgetBackground, preview, 0, 0, expectedWidth, 843 expectedHeight, 1f,1f); 844 845 // Draw the icon in the top left corner 846 try { 847 Drawable icon = null; 848 if (info.icon > 0) icon = mPackageManager.getDrawable(packageName, info.icon, null); 849 if (icon == null) icon = resources.getDrawable(R.drawable.ic_launcher_application); 850 851 int offset = (int) (mAppIconSize * sWidgetPreviewIconPaddingPercentage); 852 renderDrawableToBitmap(icon, preview, offset, offset, 853 mAppIconSize, mAppIconSize, 1f, 1f); 854 } catch (Resources.NotFoundException e) {} 855 } 856 return preview; 857 } 858 public void syncWidgetPages() { 859 // Ensure that we have the right number of pages 860 Context context = getContext(); 861 int[] countX = { mWidgetCountX, mShortcutCountX }; 862 int[] countY = { mWidgetCountY, mShortcutCountY }; 863 int[] numPages = { mNumWidgetPages, mNumShortcutPages }; 864 for (int i = 0; i < 2; ++i) { 865 for (int j = 0; j < numPages[i]; ++j) { 866 PagedViewGridLayout layout = new PagedViewGridLayout(context, countX[i], countY[i]); 867 setupPage(layout); 868 addView(layout); 869 } 870 } 871 } 872 public void syncWidgetPageItems(int page) { 873 int[] countX = { mWidgetCountX, mShortcutCountX }; 874 int[] countY = { mWidgetCountY, mShortcutCountY }; 875 int[] widthGap = { mWidgetWidthGap, mWidgetWidthGap }; 876 int[] heightGap = { mWidgetHeightGap, mWidgetHeightGap }; 877 int[] numItemsPerPage = { mWidgetCountX * mWidgetCountY, 878 mShortcutCountX * mShortcutCountY }; 879 Object[] collection = { mWidgets, mShortcuts }; 880 int contentWidth = mWidgetSpacingLayout.getContentWidth(); 881 int contentHeight = mWidgetSpacingLayout.getContentHeight(); 882 int numWidgetPages = (int) Math.ceil(mWidgets.size() / (float) numItemsPerPage[0]); 883 int[] offsets = { page * numItemsPerPage[0], (page - numWidgetPages) * numItemsPerPage[1] }; 884 int index = (page < numWidgetPages ? 0 : 1); 885 886 // Calculate the dimensions of each cell we are giving to each widget 887 ArrayList<Object> list = (ArrayList<Object>) collection[index]; 888 ArrayList<Object> items = new ArrayList<Object>(); 889 int cellWidth = ((contentWidth - mPageLayoutPaddingLeft - mPageLayoutPaddingRight 890 - ((countX[index] - 1) * widthGap[index])) / countX[index]); 891 int cellHeight = ((contentHeight - mPageLayoutPaddingTop - mPageLayoutPaddingBottom 892 - ((countY[index] - 1) * heightGap[index])) / countY[index]); 893 894 int offset = offsets[index]; 895 for (int i = offset; i < Math.min(offset + numItemsPerPage[index], list.size()); ++i) { 896 items.add(list.get(i)); 897 } 898 899 prepareLoadWidgetPreviewsTask(page, items, cellWidth, cellHeight, countX[index]); 900 } 901 private void onSyncWidgetPageItems(AsyncTaskPageData data) { 902 int page = data.page; 903 PagedViewGridLayout layout = (PagedViewGridLayout) getChildAt(page); 904 // Only set the column count once we have items 905 layout.setColumnCount(layout.getCellCountX()); 906 907 ArrayList<Object> items = data.items; 908 int count = items.size(); 909 int cellWidth = data.cellWidth; 910 int cellHeight = data.cellHeight; 911 int cellCountX = data.cellCountX; 912 for (int i = 0; i < count; ++i) { 913 Object rawInfo = items.get(i); 914 PendingAddItemInfo createItemInfo = null; 915 PagedViewWidget widget = (PagedViewWidget) mLayoutInflater.inflate( 916 R.layout.apps_customize_widget, layout, false); 917 if (rawInfo instanceof AppWidgetProviderInfo) { 918 // Fill in the widget information 919 AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo; 920 createItemInfo = new PendingAddWidgetInfo(info, null, null); 921 int[] cellSpans = CellLayout.rectToCell(getResources(), 922 info.minWidth, info.minHeight, null); 923 FastBitmapDrawable preview = new FastBitmapDrawable(data.generatedImages.get(i)); 924 widget.applyFromAppWidgetProviderInfo(info, preview, -1, cellSpans, 925 mHolographicOutlineHelper); 926 widget.setTag(createItemInfo); 927 } else if (rawInfo instanceof ResolveInfo) { 928 // Fill in the shortcuts information 929 ResolveInfo info = (ResolveInfo) rawInfo; 930 createItemInfo = new PendingAddItemInfo(); 931 createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; 932 createItemInfo.componentName = new ComponentName(info.activityInfo.packageName, 933 info.activityInfo.name); 934 FastBitmapDrawable preview = new FastBitmapDrawable(data.generatedImages.get(i)); 935 widget.applyFromResolveInfo(mPackageManager, info, preview, 936 mHolographicOutlineHelper); 937 widget.setTag(createItemInfo); 938 } 939 widget.setOnClickListener(this); 940 widget.setOnLongClickListener(this); 941 widget.setOnTouchListener(this); 942 943 // Layout each widget 944 int ix = i % cellCountX; 945 int iy = i / cellCountX; 946 GridLayout.LayoutParams lp = new GridLayout.LayoutParams( 947 GridLayout.spec(iy, GridLayout.LEFT), 948 GridLayout.spec(ix, GridLayout.TOP)); 949 lp.width = cellWidth; 950 lp.height = cellHeight; 951 lp.setGravity(Gravity.TOP | Gravity.LEFT); 952 if (ix > 0) lp.leftMargin = mWidgetWidthGap; 953 if (iy > 0) lp.topMargin = mWidgetHeightGap; 954 layout.addView(widget, lp); 955 } 956 957 invalidate(); 958 forceUpdateAdjacentPagesAlpha(); 959 prepareGenerateHoloOutlinesTask(data.page, data.items, data.generatedImages); 960 } 961 private void onHolographicPageItemsLoaded(AsyncTaskPageData data) { 962 // Invalidate early to short-circuit children invalidates 963 invalidate(); 964 965 int page = data.page; 966 ViewGroup layout = (ViewGroup) getChildAt(page); 967 if (layout instanceof PagedViewCellLayout) { 968 PagedViewCellLayout cl = (PagedViewCellLayout) layout; 969 int count = cl.getPageChildCount(); 970 if (count != data.generatedImages.size()) return; 971 for (int i = 0; i < count; ++i) { 972 PagedViewIcon icon = (PagedViewIcon) cl.getChildOnPageAt(i); 973 icon.setHolographicOutline(data.generatedImages.get(i)); 974 } 975 } else { 976 int count = layout.getChildCount(); 977 if (count != data.generatedImages.size()) return; 978 for (int i = 0; i < count; ++i) { 979 View v = layout.getChildAt(i); 980 ((PagedViewWidget) v).setHolographicOutline(data.generatedImages.get(i)); 981 } 982 } 983 } 984 985 @Override 986 public void syncPages() { 987 removeAllViews(); 988 989 // Remove all background asyc tasks if we are loading content anew 990 Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator(); 991 while (iter.hasNext()) { 992 AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); 993 task.cancel(false); 994 iter.remove(); 995 } 996 997 switch (mContentType) { 998 case Applications: 999 syncAppsPages(); 1000 break; 1001 case Widgets: 1002 syncWidgetPages(); 1003 break; 1004 } 1005 } 1006 @Override 1007 public void syncPageItems(int page) { 1008 switch (mContentType) { 1009 case Applications: 1010 syncAppsPageItems(page); 1011 break; 1012 case Widgets: 1013 syncWidgetPageItems(page); 1014 break; 1015 } 1016 } 1017 1018 /** 1019 * Used by the parent to get the content width to set the tab bar to 1020 * @return 1021 */ 1022 public int getPageContentWidth() { 1023 return mContentWidth; 1024 } 1025 1026 @Override 1027 protected void onPageBeginMoving() { 1028 /* TO BE ENABLED LATER 1029 setChildrenDrawnWithCacheEnabled(true); 1030 for (int i = 0; i < getChildCount(); ++i) { 1031 View v = getChildAt(i); 1032 if (v instanceof PagedViewCellLayout) { 1033 ((PagedViewCellLayout) v).setChildrenDrawingCacheEnabled(true); 1034 } 1035 } 1036 */ 1037 super.onPageBeginMoving(); 1038 } 1039 1040 @Override 1041 protected void onPageEndMoving() { 1042 /* TO BE ENABLED LATER 1043 for (int i = 0; i < getChildCount(); ++i) { 1044 View v = getChildAt(i); 1045 if (v instanceof PagedViewCellLayout) { 1046 ((PagedViewCellLayout) v).setChildrenDrawingCacheEnabled(false); 1047 } 1048 } 1049 setChildrenDrawnWithCacheEnabled(false); 1050 */ 1051 super.onPageEndMoving(); 1052 } 1053 1054 /* 1055 * AllAppsView implementation 1056 */ 1057 @Override 1058 public void setup(Launcher launcher, DragController dragController) { 1059 mLauncher = launcher; 1060 mDragController = dragController; 1061 } 1062 @Override 1063 public void zoom(float zoom, boolean animate) { 1064 // TODO-APPS_CUSTOMIZE: Call back to mLauncher.zoomed() 1065 } 1066 @Override 1067 public boolean isVisible() { 1068 return (getVisibility() == VISIBLE); 1069 } 1070 @Override 1071 public boolean isAnimating() { 1072 return false; 1073 } 1074 @Override 1075 public void setApps(ArrayList<ApplicationInfo> list) { 1076 mApps = list; 1077 Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR); 1078 1079 // The next layout pass will trigger data-ready if both widgets and apps are set, so 1080 // request a layout to do this test and invalidate the page data when ready. 1081 if (testDataReady()) requestLayout(); 1082 } 1083 private void addAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) { 1084 // We add it in place, in alphabetical order 1085 int count = list.size(); 1086 for (int i = 0; i < count; ++i) { 1087 ApplicationInfo info = list.get(i); 1088 int index = Collections.binarySearch(mApps, info, LauncherModel.APP_NAME_COMPARATOR); 1089 if (index < 0) { 1090 mApps.add(-(index + 1), info); 1091 } 1092 } 1093 } 1094 @Override 1095 public void addApps(ArrayList<ApplicationInfo> list) { 1096 addAppsWithoutInvalidate(list); 1097 invalidatePageData(); 1098 } 1099 private int findAppByComponent(List<ApplicationInfo> list, ApplicationInfo item) { 1100 ComponentName removeComponent = item.intent.getComponent(); 1101 int length = list.size(); 1102 for (int i = 0; i < length; ++i) { 1103 ApplicationInfo info = list.get(i); 1104 if (info.intent.getComponent().equals(removeComponent)) { 1105 return i; 1106 } 1107 } 1108 return -1; 1109 } 1110 private void removeAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) { 1111 // loop through all the apps and remove apps that have the same component 1112 int length = list.size(); 1113 for (int i = 0; i < length; ++i) { 1114 ApplicationInfo info = list.get(i); 1115 int removeIndex = findAppByComponent(mApps, info); 1116 if (removeIndex > -1) { 1117 mApps.remove(removeIndex); 1118 } 1119 } 1120 } 1121 @Override 1122 public void removeApps(ArrayList<ApplicationInfo> list) { 1123 removeAppsWithoutInvalidate(list); 1124 invalidatePageData(); 1125 } 1126 @Override 1127 public void updateApps(ArrayList<ApplicationInfo> list) { 1128 // We remove and re-add the updated applications list because it's properties may have 1129 // changed (ie. the title), and this will ensure that the items will be in their proper 1130 // place in the list. 1131 removeAppsWithoutInvalidate(list); 1132 addAppsWithoutInvalidate(list); 1133 invalidatePageData(); 1134 } 1135 1136 @Override 1137 public void reset() { 1138 if (mContentType != ContentType.Applications) { 1139 // Reset to the first page of the Apps pane 1140 AppsCustomizeTabHost tabs = (AppsCustomizeTabHost) 1141 mLauncher.findViewById(R.id.apps_customize_pane); 1142 tabs.selectAppsTab(); 1143 } else if (getCurrentPage() != 0) { 1144 invalidatePageData(0); 1145 } 1146 } 1147 @Override 1148 public void dumpState() { 1149 // TODO: Dump information related to current list of Applications, Widgets, etc. 1150 ApplicationInfo.dumpApplicationInfoList(LOG_TAG, "mApps", mApps); 1151 dumpAppWidgetProviderInfoList(LOG_TAG, "mWidgets", mWidgets); 1152 dumpAppWidgetProviderInfoList(LOG_TAG, "mShortcuts", mShortcuts); 1153 } 1154 private void dumpAppWidgetProviderInfoList(String tag, String label, 1155 ArrayList<Object> list) { 1156 Log.d(tag, label + " size=" + list.size()); 1157 for (Object i: list) { 1158 if (i instanceof AppWidgetProviderInfo) { 1159 AppWidgetProviderInfo info = (AppWidgetProviderInfo) i; 1160 Log.d(tag, " label=\"" + info.label + "\" previewImage=" + info.previewImage 1161 + " resizeMode=" + info.resizeMode + " configure=" + info.configure 1162 + " initialLayout=" + info.initialLayout 1163 + " minWidth=" + info.minWidth + " minHeight=" + info.minHeight); 1164 } else if (i instanceof ResolveInfo) { 1165 ResolveInfo info = (ResolveInfo) i; 1166 Log.d(tag, " label=\"" + info.loadLabel(mPackageManager) + "\" icon=" 1167 + info.icon); 1168 } 1169 } 1170 } 1171 @Override 1172 public void surrender() { 1173 // TODO: If we are in the middle of any process (ie. for holographic outlines, etc) we 1174 // should stop this now. 1175 } 1176 1177 /* 1178 * We load an extra page on each side to prevent flashes from scrolling and loading of the 1179 * widget previews in the background with the AsyncTasks. 1180 */ 1181 protected int getAssociatedLowerPageBound(int page) { 1182 return Math.max(0, page - 2); 1183 } 1184 protected int getAssociatedUpperPageBound(int page) { 1185 final int count = getChildCount(); 1186 return Math.min(page + 2, count - 1); 1187 } 1188 1189 @Override 1190 protected String getCurrentPageDescription() { 1191 int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage; 1192 int stringId = R.string.default_scroll_format; 1193 switch (mContentType) { 1194 case Applications: 1195 stringId = R.string.apps_customize_apps_scroll_format; 1196 break; 1197 case Widgets: 1198 stringId = R.string.apps_customize_widgets_scroll_format; 1199 break; 1200 } 1201 return String.format(mContext.getString(stringId), page + 1, getChildCount()); 1202 } 1203} 1204