AppsCustomizePagedView.java revision 55b6550e37da5df2fda44fc712bc082731f2d8e7
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 java.util.ArrayList; 20import java.util.Collections; 21import java.util.Iterator; 22import java.util.List; 23 24import org.xmlpull.v1.XmlPullParser; 25 26import android.animation.Animator; 27import android.animation.AnimatorListenerAdapter; 28import android.animation.AnimatorSet; 29import android.animation.ObjectAnimator; 30import android.animation.PropertyValuesHolder; 31import android.animation.ValueAnimator; 32import android.app.WallpaperManager; 33import android.appwidget.AppWidgetManager; 34import android.appwidget.AppWidgetProviderInfo; 35import android.content.ComponentName; 36import android.content.Context; 37import android.content.Intent; 38import android.content.pm.ActivityInfo; 39import android.content.pm.PackageManager; 40import android.content.pm.ResolveInfo; 41import android.content.res.Resources; 42import android.content.res.TypedArray; 43import android.content.res.XmlResourceParser; 44import android.graphics.Bitmap; 45import android.graphics.Bitmap.Config; 46import android.graphics.Canvas; 47import android.graphics.Rect; 48import android.graphics.drawable.Drawable; 49import android.util.AttributeSet; 50import android.util.Log; 51import android.util.LruCache; 52import android.util.Slog; 53import android.util.TypedValue; 54import android.util.Xml; 55import android.view.LayoutInflater; 56import android.view.View; 57import android.view.ViewGroup; 58import android.view.animation.AccelerateInterpolator; 59import android.view.animation.DecelerateInterpolator; 60import android.view.animation.LinearInterpolator; 61import android.widget.ImageView; 62import android.widget.TextView; 63import android.widget.Toast; 64 65import com.android.launcher.R; 66 67public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements 68 AllAppsView, View.OnClickListener, DragSource { 69 static final String LOG_TAG = "AppsCustomizePagedView"; 70 71 /** 72 * The different content types that this paged view can show. 73 */ 74 public enum ContentType { 75 Applications, 76 Widgets, 77 Wallpapers 78 } 79 80 // Refs 81 private Launcher mLauncher; 82 private DragController mDragController; 83 private final LayoutInflater mLayoutInflater; 84 private final PackageManager mPackageManager; 85 86 // Content 87 private ContentType mContentType; 88 private ArrayList<ApplicationInfo> mApps; 89 private List<Object> mWidgets; 90 private List<ResolveInfo> mWallpapers; 91 92 // Caching 93 private Drawable mDefaultWidgetBackground; 94 private final int sWidgetPreviewCacheSize = 1 * 1024 * 1024; // 1 MiB 95 private LruCache<Object, Bitmap> mWidgetPreviewCache; 96 private IconCache mIconCache; 97 98 // Dimens 99 private int mContentWidth; 100 private int mMaxWidgetSpan, mMinWidgetSpan; 101 private int mCellWidthGap, mCellHeightGap; 102 private int mWidgetCountX, mWidgetCountY; 103 private int mWallpaperCountX, mWallpaperCountY; 104 private final int mWidgetPreviewIconPaddedDimension; 105 private final float sWidgetPreviewIconPaddingPercentage = 0.25f; 106 private PagedViewCellLayout mWidgetSpacingLayout; 107 108 // Animations 109 private final float ANIMATION_SCALE = 0.5f; 110 private final int TRANSLATE_ANIM_DURATION = 400; 111 private final int DROP_ANIM_DURATION = 200; 112 113 public AppsCustomizePagedView(Context context, AttributeSet attrs) { 114 super(context, attrs); 115 mLayoutInflater = LayoutInflater.from(context); 116 mPackageManager = context.getPackageManager(); 117 mContentType = ContentType.Applications; 118 mApps = new ArrayList<ApplicationInfo>(); 119 mWidgets = new ArrayList<Object>(); 120 mWallpapers = new ArrayList<ResolveInfo>(); 121 mIconCache = ((LauncherApplication) context.getApplicationContext()).getIconCache(); 122 mWidgetPreviewCache = new LruCache<Object, Bitmap>(sWidgetPreviewCacheSize) { 123 protected int sizeOf(Object key, Bitmap value) { 124 return value.getByteCount(); 125 } 126 }; 127 128 // Save the default widget preview background 129 Resources resources = context.getResources(); 130 mDefaultWidgetBackground = resources.getDrawable(R.drawable.default_widget_preview); 131 132 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagedView, 0, 0); 133 mCellCountX = a.getInt(R.styleable.PagedView_cellCountX, 6); 134 mCellCountY = a.getInt(R.styleable.PagedView_cellCountY, 4); 135 a.recycle(); 136 a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0); 137 mCellWidthGap = 138 a.getDimensionPixelSize(R.styleable.AppsCustomizePagedView_widgetCellWidthGap, 10); 139 mCellHeightGap = 140 a.getDimensionPixelSize(R.styleable.AppsCustomizePagedView_widgetCellHeightGap, 10); 141 mWidgetCountX = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountX, 2); 142 mWidgetCountY = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountY, 2); 143 mWallpaperCountX = a.getInt(R.styleable.AppsCustomizePagedView_wallpaperCountX, 2); 144 mWallpaperCountY = a.getInt(R.styleable.AppsCustomizePagedView_wallpaperCountY, 2); 145 a.recycle(); 146 147 // Create a dummy page that we can use to approximate the cell dimensions of widgets and 148 // the content width (to be used by our parent) 149 mWidgetSpacingLayout = new PagedViewCellLayout(context); 150 setupPage(mWidgetSpacingLayout); 151 mContentWidth = mWidgetSpacingLayout.getContentWidth(); 152 153 // The max widget span is the length N, such that NxN is the largest bounds that the widget 154 // preview can be before applying the widget scaling 155 mMinWidgetSpan = 1; 156 mMaxWidgetSpan = 3; 157 158 // The padding on the non-matched dimension for the default widget preview icons 159 // (top + bottom) 160 int iconSize = resources.getDimensionPixelSize(R.dimen.app_icon_size); 161 mWidgetPreviewIconPaddedDimension = 162 (int) (iconSize * (1 + (2 * sWidgetPreviewIconPaddingPercentage))); 163 } 164 165 @Override 166 protected void init() { 167 super.init(); 168 mCenterPagesVertically = false; 169 170 Context context = getContext(); 171 Resources r = context.getResources(); 172 setDragSlopeThreshold(r.getInteger(R.integer.config_appsCustomizeDragSlopeThreshold)/100f); 173 } 174 175 /** Removes and returns the ResolveInfo with the specified ComponentName */ 176 private ResolveInfo removeResolveInfoWithComponentName(List<ResolveInfo> list, 177 ComponentName cn) { 178 Iterator<ResolveInfo> iter = list.iterator(); 179 while (iter.hasNext()) { 180 ResolveInfo rinfo = iter.next(); 181 ActivityInfo info = rinfo.activityInfo; 182 ComponentName c = new ComponentName(info.packageName, info.name); 183 if (c.equals(cn)) { 184 iter.remove(); 185 return rinfo; 186 } 187 } 188 return null; 189 } 190 191 public void onPackagesUpdated() { 192 // Get the list of widgets and shortcuts 193 mWidgets.clear(); 194 mWidgets.addAll(AppWidgetManager.getInstance(mLauncher).getInstalledProviders()); 195 Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); 196 mWidgets.addAll(mPackageManager.queryIntentActivities(shortcutsIntent, 0)); 197 Collections.sort(mWidgets, 198 new LauncherModel.WidgetAndShortcutNameComparator(mPackageManager)); 199 200 // Get the list of wallpapers 201 Intent wallpapersIntent = new Intent(Intent.ACTION_SET_WALLPAPER); 202 mWallpapers = mPackageManager.queryIntentActivities(wallpapersIntent, 203 PackageManager.GET_META_DATA); 204 Collections.sort(mWallpapers, 205 new LauncherModel.ShortcutNameComparator(mPackageManager)); 206 // Move Live Wallpapers to the front of the list 207 Context c = getContext(); 208 ResolveInfo liveWallpapers = removeResolveInfoWithComponentName(mWallpapers, 209 new ComponentName(c.getString(R.string.live_wallpaper_picker_package_name), 210 c.getString(R.string.live_wallpaper_picker_class_name))); 211 if (liveWallpapers != null) { 212 mWallpapers.add(0, liveWallpapers); 213 } 214 // Move Wallpapers to the front of the list 215 ResolveInfo wallpapers = removeResolveInfoWithComponentName(mWallpapers, 216 new ComponentName(c.getPackageName(), WallpaperChooser.class.getName())); 217 if (wallpapers != null) { 218 mWallpapers.add(0, wallpapers); 219 } 220 } 221 222 /** 223 * Animates the given item onto the center of a home screen, and then scales the item to 224 * look as though it's disappearing onto that screen. 225 */ 226 private void animateItemOntoScreen(View dragView, 227 final CellLayout layout, final ItemInfo info) { 228 // On the phone, we only want to fade the widget preview out 229 float[] position = new float[2]; 230 position[0] = layout.getWidth() / 2; 231 position[1] = layout.getHeight() / 2; 232 233 mLauncher.getWorkspace().mapPointFromChildToSelf(layout, position); 234 235 int dragViewWidth = dragView.getMeasuredWidth(); 236 int dragViewHeight = dragView.getMeasuredHeight(); 237 float heightOffset = 0; 238 float widthOffset = 0; 239 240 if (dragView instanceof ImageView) { 241 Drawable d = ((ImageView) dragView).getDrawable(); 242 int width = d.getIntrinsicWidth(); 243 int height = d.getIntrinsicHeight(); 244 245 if ((1.0 * width / height) >= (1.0f * dragViewWidth) / dragViewHeight) { 246 float f = (dragViewWidth / (width * 1.0f)); 247 heightOffset = ANIMATION_SCALE * (dragViewHeight - f * height) / 2; 248 } else { 249 float f = (dragViewHeight / (height * 1.0f)); 250 widthOffset = ANIMATION_SCALE * (dragViewWidth - f * width) / 2; 251 } 252 } 253 final float toX = position[0] - dragView.getMeasuredWidth() / 2 + widthOffset; 254 final float toY = position[1] - dragView.getMeasuredHeight() / 2 + heightOffset; 255 256 final DragLayer dragLayer = (DragLayer) mLauncher.findViewById(R.id.drag_layer); 257 final View dragCopy = dragLayer.createDragView(dragView); 258 dragCopy.setAlpha(1.0f); 259 260 // Translate the item to the center of the appropriate home screen 261 animateIntoPosition(dragCopy, toX, toY, null); 262 263 // The drop-onto-screen animation begins a bit later, but ends at the same time. 264 final int startDelay = TRANSLATE_ANIM_DURATION - DROP_ANIM_DURATION; 265 266 // Scale down the icon and fade out the alpha 267 animateDropOntoScreen(dragCopy, info, DROP_ANIM_DURATION, startDelay); 268 } 269 270 /** 271 * Animation which scales the view down and animates its alpha, making it appear to disappear 272 * onto a home screen. 273 */ 274 private void animateDropOntoScreen( 275 final View view, final ItemInfo info, int duration, int delay) { 276 final DragLayer dragLayer = (DragLayer) mLauncher.findViewById(R.id.drag_layer); 277 final CellLayout layout = mLauncher.getWorkspace().getCurrentDropLayout(); 278 279 ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(view, 280 PropertyValuesHolder.ofFloat("alpha", 1.0f, 0.0f), 281 PropertyValuesHolder.ofFloat("scaleX", ANIMATION_SCALE), 282 PropertyValuesHolder.ofFloat("scaleY", ANIMATION_SCALE)); 283 anim.setInterpolator(new LinearInterpolator()); 284 if (delay > 0) { 285 anim.setStartDelay(delay); 286 } 287 anim.setDuration(duration); 288 anim.addListener(new AnimatorListenerAdapter() { 289 public void onAnimationEnd(Animator animation) { 290 dragLayer.removeView(view); 291 mLauncher.addExternalItemToScreen(info, layout); 292 info.dropPos = null; 293 } 294 }); 295 anim.start(); 296 } 297 298 /** 299 * Animates the x,y position of the view, and optionally execute a Runnable on animation end. 300 */ 301 private void animateIntoPosition( 302 View view, float toX, float toY, final Runnable endRunnable) { 303 ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(view, 304 PropertyValuesHolder.ofFloat("x", toX), 305 PropertyValuesHolder.ofFloat("y", toY)); 306 anim.setInterpolator(new DecelerateInterpolator(2.5f)); 307 anim.setDuration(TRANSLATE_ANIM_DURATION); 308 if (endRunnable != null) { 309 anim.addListener(new AnimatorListenerAdapter() { 310 @Override 311 public void onAnimationEnd(Animator animation) { 312 endRunnable.run(); 313 } 314 }); 315 } 316 anim.start(); 317 } 318 319 @Override 320 public void onClick(View v) { 321 if (v instanceof PagedViewIcon) { 322 // Animate some feedback to the click 323 final ApplicationInfo appInfo = (ApplicationInfo) v.getTag(); 324 animateClickFeedback(v, new Runnable() { 325 @Override 326 public void run() { 327 mLauncher.startActivitySafely(appInfo.intent, appInfo); 328 } 329 }); 330 } else if (v instanceof PagedViewWidget) { 331 if (v.getTag() instanceof ResolveInfo) { 332 final ResolveInfo info = (ResolveInfo) v.getTag(); 333 if (mWallpapers.contains(info)) { 334 // Start the wallpaper picker 335 animateClickFeedback(v, new Runnable() { 336 @Override 337 public void run() { 338 // add the shortcut 339 Intent createWallpapersIntent = new Intent(Intent.ACTION_SET_WALLPAPER); 340 ComponentName name = new ComponentName(info.activityInfo.packageName, 341 info.activityInfo.name); 342 createWallpapersIntent.setComponent(name); 343 mLauncher.processWallpaper(createWallpapersIntent); 344 } 345 }); 346 } 347 } else { 348 // Let the user know that they have to long press to add a widget 349 Toast.makeText(getContext(), R.string.long_press_widget_to_add, 350 Toast.LENGTH_SHORT).show(); 351 352 // Create a little animation to show that the widget can move 353 float offsetY = getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY); 354 final ImageView p = (ImageView) v.findViewById(R.id.widget_preview); 355 AnimatorSet bounce = new AnimatorSet(); 356 ValueAnimator tyuAnim = ObjectAnimator.ofFloat(p, "translationY", offsetY); 357 tyuAnim.setDuration(125); 358 ValueAnimator tydAnim = ObjectAnimator.ofFloat(p, "translationY", 0f); 359 tydAnim.setDuration(100); 360 bounce.play(tyuAnim).before(tydAnim); 361 bounce.setInterpolator(new AccelerateInterpolator()); 362 bounce.start(); 363 } 364 } 365 } 366 367 /* 368 * PagedViewWithDraggableItems implementation 369 */ 370 @Override 371 protected void determineDraggingStart(android.view.MotionEvent ev) { 372 // Disable dragging by pulling an app down for now. 373 } 374 private void beginDraggingApplication(View v) { 375 // Make a copy of the ApplicationInfo 376 ApplicationInfo appInfo = new ApplicationInfo((ApplicationInfo) v.getTag()); 377 378 // Show the uninstall button if the app is uninstallable. 379 if ((appInfo.flags & ApplicationInfo.DOWNLOADED_FLAG) != 0) { 380 DeleteZone allAppsDeleteZone = (DeleteZone) 381 mLauncher.findViewById(R.id.all_apps_delete_zone); 382 allAppsDeleteZone.setDragAndDropEnabled(true); 383 384 if ((appInfo.flags & ApplicationInfo.UPDATED_SYSTEM_APP_FLAG) != 0) { 385 allAppsDeleteZone.setText(R.string.delete_zone_label_all_apps_system_app); 386 } else { 387 allAppsDeleteZone.setText(R.string.delete_zone_label_all_apps); 388 } 389 } 390 391 // Show the info button 392 ApplicationInfoDropTarget allAppsInfoButton = 393 (ApplicationInfoDropTarget) mLauncher.findViewById(R.id.all_apps_info_target); 394 allAppsInfoButton.setDragAndDropEnabled(true); 395 396 // Compose the drag image (top compound drawable, index is 1) 397 final TextView tv = (TextView) v; 398 final Drawable icon = tv.getCompoundDrawables()[1]; 399 Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), 400 Bitmap.Config.ARGB_8888); 401 Canvas c = new Canvas(b); 402 c.translate((v.getWidth() - icon.getIntrinsicWidth()) / 2, v.getPaddingTop()); 403 icon.draw(c); 404 405 // Compose the visible rect of the drag image 406 Rect dragRect = null; 407 if (v instanceof TextView) { 408 int iconSize = getResources().getDimensionPixelSize(R.dimen.app_icon_size); 409 int top = v.getPaddingTop(); 410 int left = (b.getWidth() - iconSize) / 2; 411 int right = left + iconSize; 412 int bottom = top + iconSize; 413 dragRect = new Rect(left, top, right, bottom); 414 } 415 416 // Start the drag 417 mLauncher.lockScreenOrientation(); 418 mLauncher.getWorkspace().onDragStartedWithItemSpans(1, 1, b); 419 mDragController.startDrag(v, b, this, appInfo, DragController.DRAG_ACTION_COPY, dragRect); 420 b.recycle(); 421 } 422 private void beginDraggingWidget(View v) { 423 // Get the widget preview as the drag representation 424 ImageView image = (ImageView) v.findViewById(R.id.widget_preview); 425 PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag(); 426 427 // Compose the drag image 428 Bitmap b; 429 Drawable preview = image.getDrawable(); 430 int w = preview.getIntrinsicWidth(); 431 int h = preview.getIntrinsicHeight(); 432 if (createItemInfo instanceof PendingAddWidgetInfo) { 433 PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) createItemInfo; 434 int[] spanXY = CellLayout.rectToCell(getResources(), 435 createWidgetInfo.minWidth, createWidgetInfo.minHeight, null); 436 createItemInfo.spanX = spanXY[0]; 437 createItemInfo.spanY = spanXY[1]; 438 439 b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 440 renderDrawableToBitmap(preview, b, 0, 0, w, h, 1, 1); 441 } else { 442 // Workaround for the fact that we don't keep the original ResolveInfo associated with 443 // the shortcut around. To get the icon, we just render the preview image (which has 444 // the shortcut icon) to a new drag bitmap that clips the non-icon space. 445 b = Bitmap.createBitmap(mWidgetPreviewIconPaddedDimension, 446 mWidgetPreviewIconPaddedDimension, Bitmap.Config.ARGB_8888); 447 Canvas c = new Canvas(b); 448 preview.draw(c); 449 createItemInfo.spanX = createItemInfo.spanY = 1; 450 } 451 452 // Start the drag 453 mLauncher.lockScreenOrientation(); 454 mLauncher.getWorkspace().onDragStartedWithItemSpans(createItemInfo.spanX, 455 createItemInfo.spanY, b); 456 mDragController.startDrag(image, b, this, createItemInfo, 457 DragController.DRAG_ACTION_COPY, null); 458 b.recycle(); 459 } 460 @Override 461 protected boolean beginDragging(View v) { 462 if (!super.beginDragging(v)) return false; 463 464 // Hide the pane so that the user can drop onto the workspace, we must do this first, 465 // due to how the drop target layout is computed when we start dragging to the workspace. 466 mLauncher.showWorkspace(true); 467 468 if (v instanceof PagedViewIcon) { 469 beginDraggingApplication(v); 470 } else if (v instanceof PagedViewWidget) { 471 beginDraggingWidget(v); 472 } 473 474 return true; 475 } 476 private void endDragging(boolean success) { 477 post(new Runnable() { 478 // Once the drag operation has fully completed, hence the post, we want to disable the 479 // deleteZone and the appInfoButton in all apps, and re-enable the instance which 480 // live in the workspace 481 public void run() { 482 // if onDestroy was called on Launcher, we might have already deleted the 483 // all apps delete zone / info button, so check if they are null 484 DeleteZone allAppsDeleteZone = 485 (DeleteZone) mLauncher.findViewById(R.id.all_apps_delete_zone); 486 ApplicationInfoDropTarget allAppsInfoButton = 487 (ApplicationInfoDropTarget) mLauncher.findViewById(R.id.all_apps_info_target); 488 489 if (allAppsDeleteZone != null) allAppsDeleteZone.setDragAndDropEnabled(false); 490 if (allAppsInfoButton != null) allAppsInfoButton.setDragAndDropEnabled(false); 491 } 492 }); 493 mLauncher.getWorkspace().onDragStopped(success); 494 mLauncher.unlockScreenOrientation(); 495 } 496 497 /* 498 * DragSource implementation 499 */ 500 @Override 501 public void onDragViewVisible() {} 502 @Override 503 public void onDropCompleted(View target, Object dragInfo, boolean success) { 504 endDragging(success); 505 506 // Display an error message if the drag failed due to there not being enough space on the 507 // target layout we were dropping on. 508 if (!success) { 509 boolean showOutOfSpaceMessage = false; 510 if (target instanceof Workspace) { 511 int currentScreen = mLauncher.getCurrentWorkspaceScreen(); 512 Workspace workspace = (Workspace) target; 513 CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen); 514 ItemInfo itemInfo = (ItemInfo) dragInfo; 515 if (layout != null) { 516 layout.calculateSpans(itemInfo); 517 showOutOfSpaceMessage = 518 !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY); 519 } 520 } 521 // TODO-APPS_CUSTOMIZE: We need to handle this for folders as well later. 522 if (showOutOfSpaceMessage) { 523 mLauncher.showOutOfSpaceMessage(); 524 } 525 } 526 } 527 528 public void setContentType(ContentType type) { 529 mContentType = type; 530 setCurrentPage(0); 531 invalidatePageData(); 532 } 533 534 /* 535 * Apps PagedView implementation 536 */ 537 private void setVisibilityOnChildren(ViewGroup layout, int visibility) { 538 int childCount = layout.getChildCount(); 539 for (int i = 0; i < childCount; ++i) { 540 layout.getChildAt(i).setVisibility(visibility); 541 } 542 } 543 private void setupPage(PagedViewCellLayout layout) { 544 layout.setCellCount(mCellCountX, mCellCountY); 545 layout.setGap(mPageLayoutWidthGap, mPageLayoutHeightGap); 546 layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop, 547 mPageLayoutPaddingRight, mPageLayoutPaddingBottom); 548 549 // Note: We force a measure here to get around the fact that when we do layout calculations 550 // immediately after syncing, we don't have a proper width. That said, we already know the 551 // expected page width, so we can actually optimize by hiding all the TextView-based 552 // children that are expensive to measure, and let that happen naturally later. 553 setVisibilityOnChildren(layout, View.GONE); 554 int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST); 555 int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST); 556 layout.setMinimumWidth(getPageContentWidth()); 557 layout.measure(widthSpec, heightSpec); 558 setVisibilityOnChildren(layout, View.VISIBLE); 559 } 560 public void syncAppsPages() { 561 // Ensure that we have the right number of pages 562 Context context = getContext(); 563 int numPages = (int) Math.ceil((float) mApps.size() / (mCellCountX * mCellCountY)); 564 for (int i = 0; i < numPages; ++i) { 565 PagedViewCellLayout layout = new PagedViewCellLayout(context); 566 setupPage(layout); 567 addView(layout); 568 } 569 } 570 public void syncAppsPageItems(int page) { 571 // ensure that we have the right number of items on the pages 572 int numPages = getPageCount(); 573 int numCells = mCellCountX * mCellCountY; 574 int startIndex = page * numCells; 575 int endIndex = Math.min(startIndex + numCells, mApps.size()); 576 PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page); 577 layout.removeAllViewsOnPage(); 578 for (int i = startIndex; i < endIndex; ++i) { 579 ApplicationInfo info = mApps.get(i); 580 PagedViewIcon icon = (PagedViewIcon) mLayoutInflater.inflate( 581 R.layout.apps_customize_application, layout, false); 582 icon.applyFromApplicationInfo( 583 info, mPageViewIconCache, true, isHardwareAccelerated() && (numPages > 1)); 584 icon.setOnClickListener(this); 585 icon.setOnLongClickListener(this); 586 icon.setOnTouchListener(this); 587 588 int index = i - startIndex; 589 int x = index % mCellCountX; 590 int y = index / mCellCountX; 591 layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1)); 592 } 593 } 594 /* 595 * Widgets PagedView implementation 596 */ 597 private void setupPage(PagedViewGridLayout layout) { 598 layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop, 599 mPageLayoutPaddingRight, mPageLayoutPaddingBottom); 600 601 // Note: We force a measure here to get around the fact that when we do layout calculations 602 // immediately after syncing, we don't have a proper width. That said, we already know the 603 // expected page width, so we can actually optimize by hiding all the TextView-based 604 // children that are expensive to measure, and let that happen naturally later. 605 setVisibilityOnChildren(layout, View.GONE); 606 int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST); 607 int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST); 608 layout.setMinimumWidth(getPageContentWidth()); 609 layout.measure(widthSpec, heightSpec); 610 setVisibilityOnChildren(layout, View.VISIBLE); 611 } 612 private void renderDrawableToBitmap(Drawable d, Bitmap bitmap, int x, int y, int w, int h, 613 float scaleX, float scaleY) { 614 Canvas c = new Canvas(); 615 if (bitmap != null) c.setBitmap(bitmap); 616 c.save(); 617 c.scale(scaleX, scaleY); 618 Rect oldBounds = d.copyBounds(); 619 d.setBounds(x, y, x + w, y + h); 620 d.draw(c); 621 d.setBounds(oldBounds); // Restore the bounds 622 c.restore(); 623 } 624 private FastBitmapDrawable getShortcutPreview(ResolveInfo info, int cellWidth, int cellHeight) { 625 // Return the cached version if necessary 626 Bitmap cachedBitmap = mWidgetPreviewCache.get(info); 627 if (cachedBitmap != null) { 628 return new FastBitmapDrawable(cachedBitmap); 629 } 630 631 Resources resources = mLauncher.getResources(); 632 int iconSize = resources.getDimensionPixelSize(R.dimen.app_icon_size); 633 // We only need to make it wide enough so as not allow the preview to be scaled 634 int expectedWidth = cellWidth; 635 int expectedHeight = mWidgetPreviewIconPaddedDimension; 636 int offset = (int) (iconSize * sWidgetPreviewIconPaddingPercentage); 637 638 // Render the icon 639 Bitmap preview = Bitmap.createBitmap(expectedWidth, expectedHeight, Config.ARGB_8888); 640 Drawable icon = mIconCache.getFullResIcon(info, mPackageManager); 641 renderDrawableToBitmap(mDefaultWidgetBackground, preview, 0, 0, 642 mWidgetPreviewIconPaddedDimension, mWidgetPreviewIconPaddedDimension, 1f, 1f); 643 renderDrawableToBitmap(icon, preview, offset, offset, iconSize, iconSize, 1f, 1f); 644 FastBitmapDrawable iconDrawable = new FastBitmapDrawable(preview); 645 iconDrawable.setBounds(0, 0, expectedWidth, expectedHeight); 646 mWidgetPreviewCache.put(info, preview); 647 return iconDrawable; 648 } 649 private FastBitmapDrawable getWidgetPreview(AppWidgetProviderInfo info, int cellHSpan, 650 int cellVSpan, int cellWidth, int cellHeight) { 651 // Return the cached version if necessary 652 Bitmap cachedBitmap = mWidgetPreviewCache.get(info); 653 if (cachedBitmap != null) { 654 return new FastBitmapDrawable(cachedBitmap); 655 } 656 657 // Calculate the size of the drawable 658 cellHSpan = Math.max(mMinWidgetSpan, Math.min(mMaxWidgetSpan, cellHSpan)); 659 cellVSpan = Math.max(mMinWidgetSpan, Math.min(mMaxWidgetSpan, cellVSpan)); 660 int expectedWidth = mWidgetSpacingLayout.estimateCellWidth(cellHSpan); 661 int expectedHeight = mWidgetSpacingLayout.estimateCellHeight(cellVSpan); 662 663 // Scale down the bitmap to fit the space 664 float widgetPreviewScale = (float) cellWidth / expectedWidth; 665 expectedWidth = (int) (widgetPreviewScale * expectedWidth); 666 expectedHeight = (int) (widgetPreviewScale * expectedHeight); 667 668 // Load the preview image if possible 669 String packageName = info.provider.getPackageName(); 670 Drawable drawable = null; 671 FastBitmapDrawable newDrawable = null; 672 if (info.previewImage != 0) { 673 drawable = mPackageManager.getDrawable(packageName, info.previewImage, null); 674 if (drawable == null) { 675 Log.w(LOG_TAG, "Can't load icon drawable 0x" + Integer.toHexString(info.icon) 676 + " for provider: " + info.provider); 677 } else { 678 // Scale down the preview to the dimensions we want 679 int imageWidth = drawable.getIntrinsicWidth(); 680 int imageHeight = drawable.getIntrinsicHeight(); 681 float aspect = (float) imageWidth / imageHeight; 682 int newWidth = imageWidth; 683 int newHeight = imageHeight; 684 if (aspect > 1f) { 685 newWidth = expectedWidth; 686 newHeight = (int) (imageHeight * ((float) expectedWidth / imageWidth)); 687 } else { 688 newHeight = expectedHeight; 689 newWidth = (int) (imageWidth * ((float) expectedHeight / imageHeight)); 690 } 691 692 Bitmap preview = Bitmap.createBitmap(newWidth, newHeight, Config.ARGB_8888); 693 renderDrawableToBitmap(drawable, preview, 0, 0, newWidth, newHeight, 1f, 1f); 694 newDrawable = new FastBitmapDrawable(preview); 695 newDrawable.setBounds(0, 0, newWidth, newHeight); 696 mWidgetPreviewCache.put(info, preview); 697 } 698 } 699 700 // Generate a preview image if we couldn't load one 701 if (drawable == null) { 702 Resources resources = mLauncher.getResources(); 703 int iconSize = resources.getDimensionPixelSize(R.dimen.app_icon_size); 704 705 // Specify the dimensions of the bitmap 706 if (info.minWidth >= info.minHeight) { 707 expectedWidth = cellWidth; 708 expectedHeight = mWidgetPreviewIconPaddedDimension; 709 } else { 710 // Note that in vertical widgets, we might not have enough space due to the text 711 // label, so be conservative and use the width as a height bound 712 expectedWidth = mWidgetPreviewIconPaddedDimension; 713 expectedHeight = cellWidth; 714 } 715 716 Bitmap preview = Bitmap.createBitmap(expectedWidth, expectedHeight, Config.ARGB_8888); 717 renderDrawableToBitmap(mDefaultWidgetBackground, preview, 0, 0, expectedWidth, 718 expectedHeight, 1f,1f); 719 720 // Draw the icon in the top left corner 721 try { 722 Drawable icon = null; 723 if (info.icon > 0) icon = mPackageManager.getDrawable(packageName, info.icon, null); 724 if (icon == null) icon = resources.getDrawable(R.drawable.ic_launcher_application); 725 726 int offset = (int) (iconSize * sWidgetPreviewIconPaddingPercentage); 727 renderDrawableToBitmap(icon, preview, offset, offset, iconSize, iconSize, 1f, 1f); 728 } catch (Resources.NotFoundException e) {} 729 730 newDrawable = new FastBitmapDrawable(preview); 731 newDrawable.setBounds(0, 0, expectedWidth, expectedHeight); 732 mWidgetPreviewCache.put(info, preview); 733 } 734 return newDrawable; 735 } 736 public void syncWidgetPages() { 737 // Ensure that we have the right number of pages 738 Context context = getContext(); 739 int numWidgetsPerPage = mWidgetCountX * mWidgetCountY; 740 int numPages = (int) Math.ceil(mWidgets.size() / (float) numWidgetsPerPage); 741 for (int i = 0; i < numPages; ++i) { 742 PagedViewGridLayout layout = new PagedViewGridLayout(context, mWidgetCountX, 743 mWidgetCountY); 744 setupPage(layout); 745 addView(layout); 746 } 747 } 748 public void syncWidgetPageItems(int page) { 749 PagedViewGridLayout layout = (PagedViewGridLayout) getChildAt(page); 750 layout.removeAllViews(); 751 752 // Calculate the dimensions of each cell we are giving to each widget 753 int numWidgetsPerPage = mWidgetCountX * mWidgetCountY; 754 int offset = page * numWidgetsPerPage; 755 int cellWidth = ((mWidgetSpacingLayout.getContentWidth() - mPageLayoutWidthGap 756 - ((mWidgetCountX - 1) * mCellWidthGap)) / mWidgetCountX); 757 int cellHeight = ((mWidgetSpacingLayout.getContentHeight() - mPageLayoutHeightGap 758 - ((mWidgetCountY - 1) * mCellHeightGap)) / mWidgetCountY); 759 for (int i = 0; i < Math.min(numWidgetsPerPage, mWidgets.size() - offset); ++i) { 760 Object rawInfo = mWidgets.get(offset + i); 761 PendingAddItemInfo createItemInfo = null; 762 PagedViewWidget widget = (PagedViewWidget) mLayoutInflater.inflate( 763 R.layout.apps_customize_widget, layout, false); 764 if (rawInfo instanceof AppWidgetProviderInfo) { 765 // Fill in the widget information 766 AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo; 767 createItemInfo = new PendingAddWidgetInfo(info, null, null); 768 final int[] cellSpans = CellLayout.rectToCell(getResources(), info.minWidth, 769 info.minHeight, null); 770 FastBitmapDrawable preview = getWidgetPreview(info, cellSpans[0], cellSpans[1], 771 cellWidth, cellHeight); 772 widget.applyFromAppWidgetProviderInfo(info, preview, -1, cellSpans, null, false); 773 widget.setTag(createItemInfo); 774 } else if (rawInfo instanceof ResolveInfo) { 775 // Fill in the shortcuts information 776 ResolveInfo info = (ResolveInfo) rawInfo; 777 createItemInfo = new PendingAddItemInfo(); 778 createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; 779 createItemInfo.componentName = new ComponentName(info.activityInfo.packageName, 780 info.activityInfo.name); 781 FastBitmapDrawable preview = getShortcutPreview(info, cellWidth, cellHeight); 782 widget.applyFromResolveInfo(mPackageManager, info, preview, null, false); 783 widget.setTag(createItemInfo); 784 } 785 widget.setOnClickListener(this); 786 widget.setOnLongClickListener(this); 787 widget.setOnTouchListener(this); 788 789 // Layout each widget 790 int ix = i % mWidgetCountX; 791 int iy = i / mWidgetCountX; 792 PagedViewGridLayout.LayoutParams lp = new PagedViewGridLayout.LayoutParams(cellWidth, 793 cellHeight); 794 lp.leftMargin = (ix * cellWidth) + (ix * mCellWidthGap); 795 lp.topMargin = (iy * cellHeight) + (iy * mCellHeightGap); 796 layout.addView(widget, lp); 797 } 798 } 799 800 /* 801 * This method fetches an xml file specified in the manifest identified by 802 * WallpaperManager.WALLPAPER_PREVIEW_META_DATA). The xml file specifies 803 * an image which will be used as the wallpaper preview for an activity 804 * which responds to ACTION_SET_WALLPAPER. This image is returned and used 805 * in the customize drawer. 806 */ 807 private Drawable parseWallpaperPreviewXml(ResolveInfo ri) { 808 ActivityInfo activityInfo = ri.activityInfo; 809 XmlResourceParser parser = null; 810 ComponentName component = new ComponentName(ri.activityInfo.packageName, 811 ri.activityInfo.name); 812 try { 813 parser = activityInfo.loadXmlMetaData(mPackageManager, 814 WallpaperManager.WALLPAPER_PREVIEW_META_DATA); 815 if (parser == null) { 816 Slog.w(LOG_TAG, "No " + WallpaperManager.WALLPAPER_PREVIEW_META_DATA 817 + " meta-data for " + "wallpaper provider '" + component + '\''); 818 return null; 819 } 820 821 AttributeSet attrs = Xml.asAttributeSet(parser); 822 823 int type; 824 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 825 && type != XmlPullParser.START_TAG) { 826 // drain whitespace, comments, etc. 827 } 828 829 String nodeName = parser.getName(); 830 if (!"wallpaper-preview".equals(nodeName)) { 831 Slog.w(LOG_TAG, "Meta-data does not start with wallpaper-preview tag for " 832 + "wallpaper provider '" + component + '\''); 833 return null; 834 } 835 836 // If metaData was null, we would have returned earlier when getting 837 // the parser No need to do the check here 838 Resources res = mPackageManager.getResourcesForApplication( 839 activityInfo.applicationInfo); 840 841 TypedArray sa = res.obtainAttributes(attrs, 842 com.android.internal.R.styleable.WallpaperPreviewInfo); 843 844 TypedValue value = sa.peekValue( 845 com.android.internal.R.styleable.WallpaperPreviewInfo_staticWallpaperPreview); 846 if (value == null) return null; 847 848 return res.getDrawable(value.resourceId); 849 } catch (Exception e) { 850 Slog.w(LOG_TAG, "XML parsing failed for wallpaper provider '" + component + '\'', e); 851 return null; 852 } finally { 853 if (parser != null) parser.close(); 854 } 855 } 856 private FastBitmapDrawable getWallpaperPreview(ResolveInfo info, int cellWidth, int cellHeight){ 857 // Return the cached version if necessary 858 Bitmap cachedBitmap = mWidgetPreviewCache.get(info); 859 if (cachedBitmap != null) { 860 return new FastBitmapDrawable(cachedBitmap); 861 } 862 863 // Get the preview 864 Resources resources = getContext().getResources(); 865 Drawable wallpaperPreview = parseWallpaperPreviewXml(info); 866 Drawable wallpaperIcon = null; 867 int expectedWidth; 868 int expectedHeight; 869 if (wallpaperPreview != null) { 870 expectedWidth = wallpaperPreview.getIntrinsicWidth(); 871 expectedHeight = wallpaperPreview.getIntrinsicHeight(); 872 } else { 873 wallpaperPreview = mDefaultWidgetBackground; 874 expectedWidth = expectedHeight = Math.min(cellWidth, cellHeight); 875 876 // Draw the icon in the top left corner 877 String packageName = info.activityInfo.packageName; 878 try { 879 if (info.icon > 0) { 880 wallpaperIcon = mPackageManager.getDrawable(packageName, info.icon, null); 881 } 882 if (wallpaperIcon == null) { 883 wallpaperIcon = resources.getDrawable(R.drawable.ic_launcher_application); 884 } 885 } catch (Resources.NotFoundException e) {} 886 } 887 888 // Create the bitmap 889 Bitmap preview = Bitmap.createBitmap(expectedWidth, expectedHeight, Config.ARGB_8888); 890 renderDrawableToBitmap(wallpaperPreview, preview, 0, 0, expectedWidth, expectedHeight, 891 1f, 1f); 892 if (wallpaperIcon != null) { 893 int iconSize = resources.getDimensionPixelSize(R.dimen.app_icon_size); 894 int offset = (int) (iconSize * sWidgetPreviewIconPaddingPercentage); 895 renderDrawableToBitmap(wallpaperIcon, preview, offset, offset, iconSize, iconSize, 896 1f, 1f); 897 } 898 899 FastBitmapDrawable previewDrawable = new FastBitmapDrawable(preview); 900 previewDrawable.setBounds(0, 0, expectedWidth, expectedHeight); 901 mWidgetPreviewCache.put(info, preview); 902 return previewDrawable; 903 } 904 /* 905 * Wallpapers PagedView implementation 906 */ 907 public void syncWallpaperPages() { 908 // Ensure that we have the right number of pages 909 Context context = getContext(); 910 int numWidgetsPerPage = mWallpaperCountX * mWallpaperCountY; 911 int numPages = (int) Math.ceil(mWallpapers.size() / (float) numWidgetsPerPage); 912 for (int i = 0; i < numPages; ++i) { 913 PagedViewGridLayout layout = new PagedViewGridLayout(context, mWallpaperCountX, 914 mWallpaperCountY); 915 setupPage(layout); 916 addView(layout); 917 } 918 } 919 public void syncWallpaperPageItems(int page) { 920 PagedViewGridLayout layout = (PagedViewGridLayout) getChildAt(page); 921 layout.removeAllViews(); 922 923 // Calculate the dimensions of each cell we are giving to each widget 924 int numWidgetsPerPage = mWallpaperCountX * mWallpaperCountY; 925 int offset = page * numWidgetsPerPage; 926 int cellWidth = ((mWidgetSpacingLayout.getContentWidth() - mPageLayoutWidthGap 927 - ((mWallpaperCountX - 1) * mCellWidthGap)) / mWallpaperCountX); 928 int cellHeight = ((mWidgetSpacingLayout.getContentHeight() - mPageLayoutHeightGap 929 - ((mWallpaperCountY - 1) * mCellHeightGap)) / mWallpaperCountY); 930 for (int i = 0; i < Math.min(numWidgetsPerPage, mWallpapers.size() - offset); ++i) { 931 ResolveInfo info = mWallpapers.get(offset + i); 932 PagedViewWidget widget = (PagedViewWidget) mLayoutInflater.inflate( 933 R.layout.apps_customize_wallpaper, layout, false); 934 935 // Fill in the shortcuts information 936 FastBitmapDrawable preview = getWallpaperPreview(info, cellWidth, cellHeight); 937 widget.applyFromResolveInfo(mPackageManager, info, preview, null, false); 938 widget.setTag(info); 939 widget.setOnClickListener(this); 940 widget.setOnLongClickListener(this); 941 widget.setOnTouchListener(this); 942 943 // Layout each widget 944 int ix = i % mWallpaperCountX; 945 int iy = i / mWallpaperCountX; 946 PagedViewGridLayout.LayoutParams lp = new PagedViewGridLayout.LayoutParams(cellWidth, 947 cellHeight); 948 lp.leftMargin = (ix * cellWidth) + (ix * mCellWidthGap); 949 lp.topMargin = (iy * cellHeight) + (iy * mCellHeightGap); 950 layout.addView(widget, lp); 951 } 952 } 953 954 @Override 955 public void syncPages() { 956 removeAllViews(); 957 switch (mContentType) { 958 case Applications: 959 syncAppsPages(); 960 break; 961 case Widgets: 962 syncWidgetPages(); 963 break; 964 case Wallpapers: 965 syncWallpaperPages(); 966 break; 967 } 968 } 969 @Override 970 public void syncPageItems(int page) { 971 switch (mContentType) { 972 case Applications: 973 syncAppsPageItems(page); 974 break; 975 case Widgets: 976 syncWidgetPageItems(page); 977 break; 978 case Wallpapers: 979 syncWallpaperPageItems(page); 980 break; 981 } 982 } 983 984 /** 985 * Used by the parent to get the content width to set the tab bar to 986 * @return 987 */ 988 public int getPageContentWidth() { 989 return mContentWidth; 990 } 991 992 /* 993 * AllAppsView implementation 994 */ 995 @Override 996 public void setup(Launcher launcher, DragController dragController) { 997 mLauncher = launcher; 998 mDragController = dragController; 999 } 1000 @Override 1001 public void zoom(float zoom, boolean animate) { 1002 // TODO-APPS_CUSTOMIZE: Call back to mLauncher.zoomed() 1003 } 1004 @Override 1005 public boolean isVisible() { 1006 return (getVisibility() == VISIBLE); 1007 } 1008 @Override 1009 public boolean isAnimating() { 1010 return false; 1011 } 1012 @Override 1013 public void setApps(ArrayList<ApplicationInfo> list) { 1014 mApps = list; 1015 Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR); 1016 invalidatePageData(); 1017 } 1018 private void addAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) { 1019 // We add it in place, in alphabetical order 1020 int count = list.size(); 1021 for (int i = 0; i < count; ++i) { 1022 ApplicationInfo info = list.get(i); 1023 int index = Collections.binarySearch(mApps, info, LauncherModel.APP_NAME_COMPARATOR); 1024 if (index < 0) { 1025 mApps.add(-(index + 1), info); 1026 } 1027 } 1028 } 1029 @Override 1030 public void addApps(ArrayList<ApplicationInfo> list) { 1031 addAppsWithoutInvalidate(list); 1032 invalidatePageData(); 1033 } 1034 private int findAppByComponent(List<ApplicationInfo> list, ApplicationInfo item) { 1035 ComponentName removeComponent = item.intent.getComponent(); 1036 int length = list.size(); 1037 for (int i = 0; i < length; ++i) { 1038 ApplicationInfo info = list.get(i); 1039 if (info.intent.getComponent().equals(removeComponent)) { 1040 return i; 1041 } 1042 } 1043 return -1; 1044 } 1045 private void removeAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) { 1046 // loop through all the apps and remove apps that have the same component 1047 int length = list.size(); 1048 for (int i = 0; i < length; ++i) { 1049 ApplicationInfo info = list.get(i); 1050 int removeIndex = findAppByComponent(mApps, info); 1051 if (removeIndex > -1) { 1052 mApps.remove(removeIndex); 1053 mPageViewIconCache.removeOutline(new PagedViewIconCache.Key(info)); 1054 } 1055 } 1056 } 1057 @Override 1058 public void removeApps(ArrayList<ApplicationInfo> list) { 1059 removeAppsWithoutInvalidate(list); 1060 invalidatePageData(); 1061 } 1062 @Override 1063 public void updateApps(ArrayList<ApplicationInfo> list) { 1064 // We remove and re-add the updated applications list because it's properties may have 1065 // changed (ie. the title), and this will ensure that the items will be in their proper 1066 // place in the list. 1067 removeAppsWithoutInvalidate(list); 1068 addAppsWithoutInvalidate(list); 1069 invalidatePageData(); 1070 } 1071 @Override 1072 public void reset() { 1073 if (mContentType != ContentType.Applications) { 1074 // Reset to the first page of the Apps pane 1075 AppsCustomizeTabHost tabs = (AppsCustomizeTabHost) 1076 mLauncher.findViewById(R.id.apps_customize_pane); 1077 tabs.setCurrentTabByTag(tabs.getTabTagForContentType(ContentType.Applications)); 1078 } else { 1079 setCurrentPage(0); 1080 invalidatePageData(); 1081 } 1082 } 1083 @Override 1084 public void dumpState() { 1085 // TODO: Dump information related to current list of Applications, Widgets, etc. 1086 ApplicationInfo.dumpApplicationInfoList(LOG_TAG, "mApps", mApps); 1087 dumpAppWidgetProviderInfoList(LOG_TAG, "mWidgets", mWidgets); 1088 } 1089 private void dumpAppWidgetProviderInfoList(String tag, String label, 1090 List<Object> list) { 1091 Log.d(tag, label + " size=" + list.size()); 1092 for (Object i: list) { 1093 if (i instanceof AppWidgetProviderInfo) { 1094 AppWidgetProviderInfo info = (AppWidgetProviderInfo) i; 1095 Log.d(tag, " label=\"" + info.label + "\" previewImage=" + info.previewImage 1096 + " resizeMode=" + info.resizeMode + " configure=" + info.configure 1097 + " initialLayout=" + info.initialLayout 1098 + " minWidth=" + info.minWidth + " minHeight=" + info.minHeight); 1099 } else if (i instanceof ResolveInfo) { 1100 ResolveInfo info = (ResolveInfo) i; 1101 Log.d(tag, " label=\"" + info.loadLabel(mPackageManager) + "\" icon=" 1102 + info.icon); 1103 } 1104 } 1105 } 1106 @Override 1107 public void surrender() { 1108 // TODO: If we are in the middle of any process (ie. for holographic outlines, etc) we 1109 // should stop this now. 1110 } 1111} 1112