1 /* 2 * Copyright (C) 2008 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 17 package com.android.launcher2; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.ValueAnimator; 22 import android.animation.ValueAnimator.AnimatorUpdateListener; 23 import android.content.Context; 24 import android.content.res.Resources; 25 import android.graphics.Canvas; 26 import android.graphics.Color; 27 import android.graphics.PorterDuff; 28 import android.graphics.Rect; 29 import android.graphics.drawable.Drawable; 30 import android.os.Parcelable; 31 import android.util.AttributeSet; 32 import android.view.LayoutInflater; 33 import android.view.View; 34 import android.view.ViewGroup; 35 import android.view.animation.AccelerateInterpolator; 36 import android.view.animation.DecelerateInterpolator; 37 import android.widget.ImageView; 38 import android.widget.LinearLayout; 39 import android.widget.TextView; 40 41 import com.android.launcher.R; 42 import com.android.launcher2.DropTarget.DragObject; 43 import com.android.launcher2.FolderInfo.FolderListener; 44 45 import java.util.ArrayList; 46 47 /** 48 * An icon that can appear on in the workspace representing an {@link UserFolder}. 49 */ 50 public class FolderIcon extends LinearLayout implements FolderListener { 51 private Launcher mLauncher; 52 Folder mFolder; 53 FolderInfo mInfo; 54 private static boolean sStaticValuesDirty = true; 55 56 // The number of icons to display in the 57 private static final int NUM_ITEMS_IN_PREVIEW = 3; 58 private static final int CONSUMPTION_ANIMATION_DURATION = 100; 59 private static final int DROP_IN_ANIMATION_DURATION = 400; 60 private static final int INITIAL_ITEM_ANIMATION_DURATION = 350; 61 62 // The degree to which the inner ring grows when accepting drop 63 private static final float INNER_RING_GROWTH_FACTOR = 0.15f; 64 65 // The degree to which the outer ring is scaled in its natural state 66 private static final float OUTER_RING_GROWTH_FACTOR = 0.3f; 67 68 // The amount of vertical spread between items in the stack [0...1] 69 private static final float PERSPECTIVE_SHIFT_FACTOR = 0.24f; 70 71 // The degree to which the item in the back of the stack is scaled [0...1] 72 // (0 means it's not scaled at all, 1 means it's scaled to nothing) 73 private static final float PERSPECTIVE_SCALE_FACTOR = 0.35f; 74 75 public static Drawable sSharedFolderLeaveBehind = null; 76 77 private ImageView mPreviewBackground; 78 private BubbleTextView mFolderName; 79 80 FolderRingAnimator mFolderRingAnimator = null; 81 82 // These variables are all associated with the drawing of the preview; they are stored 83 // as member variables for shared usage and to avoid computation on each frame 84 private int mIntrinsicIconSize; 85 private float mBaselineIconScale; 86 private int mBaselineIconSize; 87 private int mAvailableSpaceInPreview; 88 private int mTotalWidth = -1; 89 private int mPreviewOffsetX; 90 private int mPreviewOffsetY; 91 private float mMaxPerspectiveShift; 92 boolean mAnimating = false; 93 private PreviewItemDrawingParams mParams = new PreviewItemDrawingParams(0, 0, 0, 0); 94 private PreviewItemDrawingParams mAnimParams = new PreviewItemDrawingParams(0, 0, 0, 0); 95 FolderIcon(Context context, AttributeSet attrs)96 public FolderIcon(Context context, AttributeSet attrs) { 97 super(context, attrs); 98 } 99 FolderIcon(Context context)100 public FolderIcon(Context context) { 101 super(context); 102 } 103 isDropEnabled()104 public boolean isDropEnabled() { 105 final ViewGroup cellLayoutChildren = (ViewGroup) getParent(); 106 final ViewGroup cellLayout = (ViewGroup) cellLayoutChildren.getParent(); 107 final Workspace workspace = (Workspace) cellLayout.getParent(); 108 return !workspace.isSmall(); 109 } 110 fromXml(int resId, Launcher launcher, ViewGroup group, FolderInfo folderInfo, IconCache iconCache)111 static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group, 112 FolderInfo folderInfo, IconCache iconCache) { 113 114 if (INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION) { 115 throw new IllegalStateException("DROP_IN_ANIMATION_DURATION must be greater than " + 116 "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " + 117 "is dependent on this"); 118 } 119 120 FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false); 121 122 icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name); 123 icon.mFolderName.setText(folderInfo.title); 124 icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background); 125 icon.mPreviewBackground.setContentDescription(folderInfo.title); 126 127 icon.setTag(folderInfo); 128 icon.setOnClickListener(launcher); 129 icon.mInfo = folderInfo; 130 icon.mLauncher = launcher; 131 132 Folder folder = Folder.fromXml(launcher); 133 folder.setDragController(launcher.getDragController()); 134 folder.setFolderIcon(icon); 135 folder.bind(folderInfo); 136 icon.mFolder = folder; 137 Resources res = launcher.getResources(); 138 139 icon.mFolderRingAnimator = new FolderRingAnimator(launcher, icon); 140 folderInfo.addListener(icon); 141 142 return icon; 143 } 144 145 @Override onSaveInstanceState()146 protected Parcelable onSaveInstanceState() { 147 sStaticValuesDirty = true; 148 return super.onSaveInstanceState(); 149 } 150 151 public static class FolderRingAnimator { 152 public int mCellX; 153 public int mCellY; 154 private CellLayout mCellLayout; 155 public float mOuterRingSize; 156 public float mInnerRingSize; 157 public FolderIcon mFolderIcon = null; 158 public Drawable mOuterRingDrawable = null; 159 public Drawable mInnerRingDrawable = null; 160 public static Drawable sSharedOuterRingDrawable = null; 161 public static Drawable sSharedInnerRingDrawable = null; 162 public static int sPreviewSize = -1; 163 public static int sPreviewPadding = -1; 164 165 private ValueAnimator mAcceptAnimator; 166 private ValueAnimator mNeutralAnimator; 167 FolderRingAnimator(Launcher launcher, FolderIcon folderIcon)168 public FolderRingAnimator(Launcher launcher, FolderIcon folderIcon) { 169 mFolderIcon = folderIcon; 170 Resources res = launcher.getResources(); 171 mOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo); 172 mInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo); 173 174 // We need to reload the static values when configuration changes in case they are 175 // different in another configuration 176 if (sStaticValuesDirty) { 177 sPreviewSize = res.getDimensionPixelSize(R.dimen.folder_preview_size); 178 sPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding); 179 sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo); 180 sSharedInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo); 181 sSharedFolderLeaveBehind = res.getDrawable(R.drawable.portal_ring_rest); 182 sStaticValuesDirty = false; 183 } 184 } 185 animateToAcceptState()186 public void animateToAcceptState() { 187 if (mNeutralAnimator != null) { 188 mNeutralAnimator.cancel(); 189 } 190 mAcceptAnimator = ValueAnimator.ofFloat(0f, 1f); 191 mAcceptAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION); 192 mAcceptAnimator.addUpdateListener(new AnimatorUpdateListener() { 193 public void onAnimationUpdate(ValueAnimator animation) { 194 final float percent = (Float) animation.getAnimatedValue(); 195 mOuterRingSize = (1 + percent * OUTER_RING_GROWTH_FACTOR) * sPreviewSize; 196 mInnerRingSize = (1 + percent * INNER_RING_GROWTH_FACTOR) * sPreviewSize; 197 if (mCellLayout != null) { 198 mCellLayout.invalidate(); 199 } 200 } 201 }); 202 mAcceptAnimator.addListener(new AnimatorListenerAdapter() { 203 @Override 204 public void onAnimationStart(Animator animation) { 205 if (mFolderIcon != null) { 206 mFolderIcon.mPreviewBackground.setVisibility(INVISIBLE); 207 } 208 } 209 }); 210 mAcceptAnimator.start(); 211 } 212 animateToNaturalState()213 public void animateToNaturalState() { 214 if (mAcceptAnimator != null) { 215 mAcceptAnimator.cancel(); 216 } 217 mNeutralAnimator = ValueAnimator.ofFloat(0f, 1f); 218 mNeutralAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION); 219 mNeutralAnimator.addUpdateListener(new AnimatorUpdateListener() { 220 public void onAnimationUpdate(ValueAnimator animation) { 221 final float percent = (Float) animation.getAnimatedValue(); 222 mOuterRingSize = (1 + (1 - percent) * OUTER_RING_GROWTH_FACTOR) * sPreviewSize; 223 mInnerRingSize = (1 + (1 - percent) * INNER_RING_GROWTH_FACTOR) * sPreviewSize; 224 if (mCellLayout != null) { 225 mCellLayout.invalidate(); 226 } 227 } 228 }); 229 mNeutralAnimator.addListener(new AnimatorListenerAdapter() { 230 @Override 231 public void onAnimationEnd(Animator animation) { 232 if (mCellLayout != null) { 233 mCellLayout.hideFolderAccept(FolderRingAnimator.this); 234 } 235 if (mFolderIcon != null) { 236 mFolderIcon.mPreviewBackground.setVisibility(VISIBLE); 237 } 238 } 239 }); 240 mNeutralAnimator.start(); 241 } 242 243 // Location is expressed in window coordinates getCell(int[] loc)244 public void getCell(int[] loc) { 245 loc[0] = mCellX; 246 loc[1] = mCellY; 247 } 248 249 // Location is expressed in window coordinates setCell(int x, int y)250 public void setCell(int x, int y) { 251 mCellX = x; 252 mCellY = y; 253 } 254 setCellLayout(CellLayout layout)255 public void setCellLayout(CellLayout layout) { 256 mCellLayout = layout; 257 } 258 getOuterRingSize()259 public float getOuterRingSize() { 260 return mOuterRingSize; 261 } 262 getInnerRingSize()263 public float getInnerRingSize() { 264 return mInnerRingSize; 265 } 266 } 267 willAcceptItem(ItemInfo item)268 private boolean willAcceptItem(ItemInfo item) { 269 final int itemType = item.itemType; 270 return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || 271 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) && 272 !mFolder.isFull() && item != mInfo && !mInfo.opened); 273 } 274 acceptDrop(Object dragInfo)275 public boolean acceptDrop(Object dragInfo) { 276 final ItemInfo item = (ItemInfo) dragInfo; 277 return willAcceptItem(item); 278 } 279 addItem(ShortcutInfo item)280 public void addItem(ShortcutInfo item) { 281 mInfo.add(item); 282 LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, item.cellX, item.cellY); 283 } 284 onDragEnter(Object dragInfo)285 public void onDragEnter(Object dragInfo) { 286 if (!willAcceptItem((ItemInfo) dragInfo)) return; 287 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); 288 CellLayout layout = (CellLayout) getParent().getParent(); 289 mFolderRingAnimator.setCell(lp.cellX, lp.cellY); 290 mFolderRingAnimator.setCellLayout(layout); 291 mFolderRingAnimator.animateToAcceptState(); 292 layout.showFolderAccept(mFolderRingAnimator); 293 } 294 onDragOver(Object dragInfo)295 public void onDragOver(Object dragInfo) { 296 } 297 performCreateAnimation(final ShortcutInfo destInfo, final View destView, final ShortcutInfo srcInfo, final View srcView, Rect dstRect, float scaleRelativeToDragLayer, Runnable postAnimationRunnable)298 public void performCreateAnimation(final ShortcutInfo destInfo, final View destView, 299 final ShortcutInfo srcInfo, final View srcView, Rect dstRect, 300 float scaleRelativeToDragLayer, Runnable postAnimationRunnable) { 301 302 Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1]; 303 computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(), destView.getMeasuredWidth()); 304 305 // This will animate the dragView (srcView) into the new folder 306 onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable); 307 308 // This will animate the first item from it's position as an icon into its 309 // position as the first item in the preview 310 animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION); 311 312 postDelayed(new Runnable() { 313 public void run() { 314 addItem(destInfo); 315 } 316 }, INITIAL_ITEM_ANIMATION_DURATION); 317 } 318 onDragExit(Object dragInfo)319 public void onDragExit(Object dragInfo) { 320 if (!willAcceptItem((ItemInfo) dragInfo)) return; 321 mFolderRingAnimator.animateToNaturalState(); 322 } 323 onDrop(final ShortcutInfo item, View animateView, Rect finalRect, float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable)324 private void onDrop(final ShortcutInfo item, View animateView, Rect finalRect, 325 float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable) { 326 item.cellX = -1; 327 item.cellY = -1; 328 329 // Typically, the animateView corresponds to the DragView; however, if this is being done 330 // after a configuration activity (ie. for a Shortcut being dragged from AllApps) we 331 // will not have a view to animate 332 if (animateView != null) { 333 DragLayer dragLayer = mLauncher.getDragLayer(); 334 Rect from = new Rect(); 335 dragLayer.getViewRectRelativeToSelf(animateView, from); 336 Rect to = finalRect; 337 if (to == null) { 338 to = new Rect(); 339 Workspace workspace = mLauncher.getWorkspace(); 340 // Set cellLayout and this to it's final state to compute final animation locations 341 workspace.setFinalTransitionTransform((CellLayout) getParent().getParent()); 342 float scaleX = getScaleX(); 343 float scaleY = getScaleY(); 344 setScaleX(1.0f); 345 setScaleY(1.0f); 346 scaleRelativeToDragLayer = dragLayer.getDescendantRectRelativeToSelf(this, to); 347 // Finished computing final animation locations, restore current state 348 setScaleX(scaleX); 349 setScaleY(scaleY); 350 workspace.resetTransitionTransform((CellLayout) getParent().getParent()); 351 } 352 353 int[] center = new int[2]; 354 float scale = getLocalCenterForIndex(index, center); 355 center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]); 356 center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]); 357 358 to.offset(center[0] - animateView.getMeasuredWidth() / 2, 359 center[1] - animateView.getMeasuredHeight() / 2); 360 361 float finalAlpha = index < NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f; 362 363 dragLayer.animateView(animateView, from, to, finalAlpha, 364 scale * scaleRelativeToDragLayer, DROP_IN_ANIMATION_DURATION, 365 new DecelerateInterpolator(2), new AccelerateInterpolator(2), 366 postAnimationRunnable, false); 367 postDelayed(new Runnable() { 368 public void run() { 369 addItem(item); 370 } 371 }, DROP_IN_ANIMATION_DURATION); 372 } else { 373 addItem(item); 374 } 375 } 376 377 public void onDrop(DragObject d) { 378 ShortcutInfo item; 379 if (d.dragInfo instanceof ApplicationInfo) { 380 // Came from all apps -- make a copy 381 item = ((ApplicationInfo) d.dragInfo).makeShortcut(); 382 } else { 383 item = (ShortcutInfo) d.dragInfo; 384 } 385 mFolder.notifyDrop(); 386 onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable); 387 } 388 389 public DropTarget getDropTargetDelegate(DragObject d) { 390 return null; 391 } 392 393 private void computePreviewDrawingParams(int drawableSize, int totalSize) { 394 if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize) { 395 mIntrinsicIconSize = drawableSize; 396 mTotalWidth = totalSize; 397 398 final int previewSize = FolderRingAnimator.sPreviewSize; 399 final int previewPadding = FolderRingAnimator.sPreviewPadding; 400 401 mAvailableSpaceInPreview = (previewSize - 2 * previewPadding); 402 // cos(45) = 0.707 + ~= 0.1) = 0.8f 403 int adjustedAvailableSpace = (int) ((mAvailableSpaceInPreview / 2) * (1 + 0.8f)); 404 405 int unscaledHeight = (int) (mIntrinsicIconSize * (1 + PERSPECTIVE_SHIFT_FACTOR)); 406 mBaselineIconScale = (1.0f * adjustedAvailableSpace / unscaledHeight); 407 408 mBaselineIconSize = (int) (mIntrinsicIconSize * mBaselineIconScale); 409 mMaxPerspectiveShift = mBaselineIconSize * PERSPECTIVE_SHIFT_FACTOR; 410 411 mPreviewOffsetX = (mTotalWidth - mAvailableSpaceInPreview) / 2; 412 mPreviewOffsetY = previewPadding; 413 } 414 } 415 416 private void computePreviewDrawingParams(Drawable d) { 417 computePreviewDrawingParams(d.getIntrinsicWidth(), getMeasuredWidth()); 418 } 419 420 class PreviewItemDrawingParams { 421 PreviewItemDrawingParams(float transX, float transY, float scale, int overlayAlpha) { 422 this.transX = transX; 423 this.transY = transY; 424 this.scale = scale; 425 this.overlayAlpha = overlayAlpha; 426 } 427 float transX; 428 float transY; 429 float scale; 430 int overlayAlpha; 431 Drawable drawable; 432 } 433 434 private float getLocalCenterForIndex(int index, int[] center) { 435 mParams = computePreviewItemDrawingParams(Math.min(NUM_ITEMS_IN_PREVIEW, index), mParams); 436 437 mParams.transX += mPreviewOffsetX; 438 mParams.transY += mPreviewOffsetY; 439 float offsetX = mParams.transX + (mParams.scale * mIntrinsicIconSize) / 2; 440 float offsetY = mParams.transY + (mParams.scale * mIntrinsicIconSize) / 2; 441 442 center[0] = (int) Math.round(offsetX); 443 center[1] = (int) Math.round(offsetY); 444 return mParams.scale; 445 } 446 447 private PreviewItemDrawingParams computePreviewItemDrawingParams(int index, 448 PreviewItemDrawingParams params) { 449 index = NUM_ITEMS_IN_PREVIEW - index - 1; 450 float r = (index * 1.0f) / (NUM_ITEMS_IN_PREVIEW - 1); 451 float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r)); 452 453 float offset = (1 - r) * mMaxPerspectiveShift; 454 float scaledSize = scale * mBaselineIconSize; 455 float scaleOffsetCorrection = (1 - scale) * mBaselineIconSize; 456 457 // We want to imagine our coordinates from the bottom left, growing up and to the 458 // right. This is natural for the x-axis, but for the y-axis, we have to invert things. 459 float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection); 460 float transX = offset + scaleOffsetCorrection; 461 float totalScale = mBaselineIconScale * scale; 462 final int overlayAlpha = (int) (80 * (1 - r)); 463 464 if (params == null) { 465 params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha); 466 } else { 467 params.transX = transX; 468 params.transY = transY; 469 params.scale = totalScale; 470 params.overlayAlpha = overlayAlpha; 471 } 472 return params; 473 } 474 475 private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) { 476 canvas.save(); 477 canvas.translate(params.transX + mPreviewOffsetX, params.transY + mPreviewOffsetY); 478 canvas.scale(params.scale, params.scale); 479 Drawable d = params.drawable; 480 481 if (d != null) { 482 d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize); 483 d.setFilterBitmap(true); 484 d.setColorFilter(Color.argb(params.overlayAlpha, 0, 0, 0), PorterDuff.Mode.SRC_ATOP); 485 d.draw(canvas); 486 d.clearColorFilter(); 487 d.setFilterBitmap(false); 488 } 489 canvas.restore(); 490 } 491 492 @Override 493 protected void dispatchDraw(Canvas canvas) { 494 super.dispatchDraw(canvas); 495 496 if (mFolder == null) return; 497 if (mFolder.getItemCount() == 0 && !mAnimating) return; 498 499 ArrayList<View> items = mFolder.getItemsInReadingOrder(false); 500 Drawable d; 501 TextView v; 502 503 // Update our drawing parameters if necessary 504 if (mAnimating) { 505 computePreviewDrawingParams(mAnimParams.drawable); 506 } else { 507 v = (TextView) items.get(0); 508 d = v.getCompoundDrawables()[1]; 509 computePreviewDrawingParams(d); 510 } 511 512 int nItemsInPreview = Math.min(items.size(), NUM_ITEMS_IN_PREVIEW); 513 if (!mAnimating) { 514 for (int i = nItemsInPreview - 1; i >= 0; i--) { 515 v = (TextView) items.get(i); 516 d = v.getCompoundDrawables()[1]; 517 518 mParams = computePreviewItemDrawingParams(i, mParams); 519 mParams.drawable = d; 520 drawPreviewItem(canvas, mParams); 521 } 522 } else { 523 drawPreviewItem(canvas, mAnimParams); 524 } 525 } 526 animateFirstItem(final Drawable d, int duration)527 private void animateFirstItem(final Drawable d, int duration) { 528 computePreviewDrawingParams(d); 529 final PreviewItemDrawingParams finalParams = computePreviewItemDrawingParams(0, null); 530 531 final float scale0 = 1.0f; 532 final float transX0 = (mAvailableSpaceInPreview - d.getIntrinsicWidth()) / 2; 533 final float transY0 = (mAvailableSpaceInPreview - d.getIntrinsicHeight()) / 2; 534 mAnimParams.drawable = d; 535 536 ValueAnimator va = ValueAnimator.ofFloat(0f, 1.0f); 537 va.addUpdateListener(new AnimatorUpdateListener(){ 538 public void onAnimationUpdate(ValueAnimator animation) { 539 float progress = (Float) animation.getAnimatedValue(); 540 541 mAnimParams.transX = transX0 + progress * (finalParams.transX - transX0); 542 mAnimParams.transY = transY0 + progress * (finalParams.transY - transY0); 543 mAnimParams.scale = scale0 + progress * (finalParams.scale - scale0); 544 invalidate(); 545 } 546 }); 547 va.addListener(new AnimatorListenerAdapter() { 548 @Override 549 public void onAnimationStart(Animator animation) { 550 mAnimating = true; 551 } 552 @Override 553 public void onAnimationEnd(Animator animation) { 554 mAnimating = false; 555 } 556 }); 557 va.setDuration(duration); 558 va.start(); 559 } 560 setTextVisible(boolean visible)561 public void setTextVisible(boolean visible) { 562 if (visible) { 563 mFolderName.setVisibility(VISIBLE); 564 } else { 565 mFolderName.setVisibility(INVISIBLE); 566 } 567 } 568 getTextVisible()569 public boolean getTextVisible() { 570 return mFolderName.getVisibility() == VISIBLE; 571 } 572 onItemsChanged()573 public void onItemsChanged() { 574 invalidate(); 575 requestLayout(); 576 } 577 onAdd(ShortcutInfo item)578 public void onAdd(ShortcutInfo item) { 579 invalidate(); 580 requestLayout(); 581 } 582 onRemove(ShortcutInfo item)583 public void onRemove(ShortcutInfo item) { 584 invalidate(); 585 requestLayout(); 586 } 587 onTitleChanged(CharSequence title)588 public void onTitleChanged(CharSequence title) { 589 mFolderName.setText(title.toString()); 590 mPreviewBackground.setContentDescription(title.toString()); 591 } 592 } 593