NotificationMenuRow.java revision 4ab2820e6c668ae7c55b300f126bb9f70c202ba3
1/* 2 * Copyright (C) 2016 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.systemui.statusbar; 18 19import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION; 20 21import java.util.ArrayList; 22 23import com.android.systemui.Interpolators; 24import com.android.systemui.R; 25import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; 26import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; 27import com.android.systemui.statusbar.NotificationGuts.GutsContent; 28import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; 29 30import android.animation.Animator; 31import android.animation.AnimatorListenerAdapter; 32import android.animation.ValueAnimator; 33import android.app.Notification; 34import android.content.Context; 35import android.content.res.Resources; 36import android.graphics.drawable.Drawable; 37import android.os.Handler; 38import android.os.Looper; 39import android.util.Log; 40import android.service.notification.StatusBarNotification; 41import android.view.LayoutInflater; 42import android.view.MotionEvent; 43import android.view.View; 44import android.view.ViewGroup; 45import android.widget.FrameLayout; 46import android.widget.FrameLayout.LayoutParams; 47 48public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener, 49 ExpandableNotificationRow.LayoutListener { 50 51 private static final boolean DEBUG = false; 52 private static final String TAG = "swipe"; 53 54 private static final int ICON_ALPHA_ANIM_DURATION = 200; 55 private static final long SHOW_MENU_DELAY = 60; 56 private static final long SWIPE_MENU_TIMING = 200; 57 58 // Notification must be swiped at least this fraction of a single menu item to show menu 59 private static final float SWIPED_FAR_ENOUGH_MENU_FRACTION = 0.25f; 60 private static final float SWIPED_FAR_ENOUGH_MENU_UNCLEARABLE_FRACTION = 0.15f; 61 62 // When the menu is displayed, the notification must be swiped within this fraction of a single 63 // menu item to snap back to menu (else it will cover the menu or it'll be dismissed) 64 private static final float SWIPED_BACK_ENOUGH_TO_COVER_FRACTION = 0.2f; 65 66 private ExpandableNotificationRow mParent; 67 68 private Context mContext; 69 private FrameLayout mMenuContainer; 70 private MenuItem mSnoozeItem; 71 private MenuItem mInfoItem; 72 private ArrayList<MenuItem> mMenuItems; 73 private OnMenuEventListener mMenuListener; 74 75 private ValueAnimator mFadeAnimator; 76 private boolean mAnimating; 77 private boolean mMenuFadedIn; 78 79 private boolean mOnLeft; 80 private boolean mIconsPlaced; 81 82 private boolean mDismissing; 83 private boolean mSnapping; 84 private float mTranslation; 85 86 private int[] mIconLocation = new int[2]; 87 private int[] mParentLocation = new int[2]; 88 89 private float mHorizSpaceForIcon; 90 private int mVertSpaceForIcons; 91 private int mIconPadding; 92 93 private float mAlpha = 0f; 94 private float mPrevX; 95 96 private CheckForDrag mCheckForDrag; 97 private Handler mHandler; 98 99 private boolean mMenuSnappedTo; 100 private boolean mMenuSnappedOnLeft; 101 private boolean mShouldShowMenu; 102 103 private NotificationSwipeActionHelper mSwipeHelper; 104 105 public NotificationMenuRow(Context context) { 106 mContext = context; 107 final Resources res = context.getResources(); 108 mShouldShowMenu = res.getBoolean(R.bool.config_showNotificationGear); 109 mHorizSpaceForIcon = res.getDimensionPixelSize(R.dimen.notification_menu_icon_size); 110 mVertSpaceForIcons = res.getDimensionPixelSize(R.dimen.notification_min_height); 111 mIconPadding = res.getDimensionPixelSize(R.dimen.notification_menu_icon_padding); 112 mHandler = new Handler(Looper.getMainLooper()); 113 mMenuItems = new ArrayList<>(); 114 mSnoozeItem = createSnoozeItem(context); 115 mInfoItem = createInfoItem(context); 116 mMenuItems.add(mSnoozeItem); 117 mMenuItems.add(mInfoItem); 118 } 119 120 @Override 121 public ArrayList<MenuItem> getMenuItems(Context context) { 122 return mMenuItems; 123 } 124 125 @Override 126 public MenuItem getLongpressMenuItem(Context context) { 127 return mInfoItem; 128 } 129 130 @Override 131 public void setSwipeActionHelper(NotificationSwipeActionHelper helper) { 132 mSwipeHelper = helper; 133 } 134 135 @Override 136 public void setMenuClickListener(OnMenuEventListener listener) { 137 mMenuListener = listener; 138 } 139 140 @Override 141 public void createMenu(ViewGroup parent, StatusBarNotification sbn) { 142 mParent = (ExpandableNotificationRow) parent; 143 createMenuViews(true /* resetState */); 144 } 145 146 @Override 147 public boolean isMenuVisible() { 148 return mAlpha > 0; 149 } 150 151 @Override 152 public View getMenuView() { 153 return mMenuContainer; 154 } 155 156 @Override 157 public void resetMenu() { 158 resetState(true); 159 } 160 161 @Override 162 public void onNotificationUpdated(StatusBarNotification sbn) { 163 if (mMenuContainer == null) { 164 // Menu hasn't been created yet, no need to do anything. 165 return; 166 } 167 createMenuViews(!isMenuVisible() /* resetState */); 168 } 169 170 @Override 171 public void onConfigurationChanged() { 172 mParent.setLayoutListener(this); 173 } 174 175 @Override 176 public void onLayout() { 177 mIconsPlaced = false; // Force icons to be re-placed 178 setMenuLocation(); 179 mParent.removeListener(); 180 } 181 182 private void createMenuViews(boolean resetState) { 183 // Filter the menu items based on the notification 184 if (mParent != null && mParent.getStatusBarNotification() != null) { 185 int flags = mParent.getStatusBarNotification().getNotification().flags; 186 boolean isForeground = (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0; 187 if (isForeground) { 188 // Don't show snooze for foreground services 189 mMenuItems.remove(mSnoozeItem); 190 } else if (!mMenuItems.contains(mSnoozeItem)) { 191 // Was a foreground service but is no longer, add snooze back 192 mMenuItems.add(mSnoozeItem); 193 } 194 } 195 // Recreate the menu 196 if (mMenuContainer != null) { 197 mMenuContainer.removeAllViews(); 198 } else { 199 mMenuContainer = new FrameLayout(mContext); 200 } 201 for (int i = 0; i < mMenuItems.size(); i++) { 202 addMenuView(mMenuItems.get(i), mMenuContainer); 203 } 204 if (resetState) { 205 resetState(false /* notify */); 206 } else { 207 mIconsPlaced = false; 208 setMenuLocation(); 209 // If the # of items showing changed we need to update the snap position 210 showMenu(mParent, mOnLeft ? getSpaceForMenu() : -getSpaceForMenu(), 0 /* velocity */); 211 } 212 } 213 214 private void resetState(boolean notify) { 215 setMenuAlpha(0f); 216 mIconsPlaced = false; 217 mMenuFadedIn = false; 218 mAnimating = false; 219 mSnapping = false; 220 mDismissing = false; 221 mMenuSnappedTo = false; 222 setMenuLocation(); 223 if (mMenuListener != null && notify) { 224 mMenuListener.onMenuReset(mParent); 225 } 226 } 227 228 @Override 229 public boolean onTouchEvent(View view, MotionEvent ev, float velocity) { 230 final int action = ev.getActionMasked(); 231 switch (action) { 232 case MotionEvent.ACTION_DOWN: 233 mSnapping = false; 234 if (mFadeAnimator != null) { 235 mFadeAnimator.cancel(); 236 } 237 mHandler.removeCallbacks(mCheckForDrag); 238 mCheckForDrag = null; 239 mPrevX = ev.getRawX(); 240 break; 241 242 case MotionEvent.ACTION_MOVE: 243 mSnapping = false; 244 float diffX = ev.getRawX() - mPrevX; 245 mPrevX = ev.getRawX(); 246 if (!isTowardsMenu(diffX) && isMenuLocationChange()) { 247 // Don't consider it "snapped" if location has changed. 248 mMenuSnappedTo = false; 249 250 // Changed directions, make sure we check to fade in icon again. 251 if (!mHandler.hasCallbacks(mCheckForDrag)) { 252 // No check scheduled, set null to schedule a new one. 253 mCheckForDrag = null; 254 } else { 255 // Check scheduled, reset alpha and update location; check will fade it in 256 setMenuAlpha(0f); 257 setMenuLocation(); 258 } 259 } 260 if (mShouldShowMenu 261 && !NotificationStackScrollLayout.isPinnedHeadsUp(view) 262 && !mParent.areGutsExposed() 263 && !mParent.isDark() 264 && (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag))) { 265 // Only show the menu if we're not a heads up view and guts aren't exposed. 266 mCheckForDrag = new CheckForDrag(); 267 mHandler.postDelayed(mCheckForDrag, SHOW_MENU_DELAY); 268 } 269 break; 270 271 case MotionEvent.ACTION_UP: 272 return handleUpEvent(ev, view, velocity); 273 } 274 return false; 275 } 276 277 private boolean handleUpEvent(MotionEvent ev, View animView, float velocity) { 278 // If the menu should not be shown, then there is no need to check if the a swipe 279 // should result in a snapping to the menu. As a result, just check if the swipe 280 // was enough to dismiss the notification. 281 if (!mShouldShowMenu) { 282 if (mSwipeHelper.isDismissGesture(ev)) { 283 dismiss(animView, velocity); 284 } else { 285 snapBack(animView, velocity); 286 } 287 return true; 288 } 289 290 final boolean gestureTowardsMenu = isTowardsMenu(velocity); 291 final boolean gestureFastEnough = 292 mSwipeHelper.getMinDismissVelocity() <= Math.abs(velocity); 293 final boolean gestureFarEnough = 294 mSwipeHelper.swipedFarEnough(mTranslation, mParent.getWidth()); 295 final double timeForGesture = ev.getEventTime() - ev.getDownTime(); 296 final boolean showMenuForSlowOnGoing = !mParent.canViewBeDismissed() 297 && timeForGesture >= SWIPE_MENU_TIMING; 298 final float menuSnapTarget = mOnLeft ? getSpaceForMenu() : -getSpaceForMenu(); 299 300 if (DEBUG) { 301 Log.d(TAG, "mTranslation= " + mTranslation 302 + " mAlpha= " + mAlpha 303 + " velocity= " + velocity 304 + " mMenuSnappedTo= " + mMenuSnappedTo 305 + " mMenuSnappedOnLeft= " + mMenuSnappedOnLeft 306 + " mOnLeft= " + mOnLeft 307 + " minDismissVel= " + mSwipeHelper.getMinDismissVelocity() 308 + " isDismissGesture= " + mSwipeHelper.isDismissGesture(ev) 309 + " gestureTowardsMenu= " + gestureTowardsMenu 310 + " gestureFastEnough= " + gestureFastEnough 311 + " gestureFarEnough= " + gestureFarEnough); 312 } 313 314 if (mMenuSnappedTo && isMenuVisible() && mMenuSnappedOnLeft == mOnLeft) { 315 // Menu was snapped to previously and we're on the same side, figure out if 316 // we should stick to the menu, snap back into place, or dismiss 317 final float maximumSwipeDistance = mHorizSpaceForIcon 318 * SWIPED_BACK_ENOUGH_TO_COVER_FRACTION; 319 final float targetLeft = getSpaceForMenu() - maximumSwipeDistance; 320 final float targetRight = mParent.getWidth() * SWIPED_FAR_ENOUGH_SIZE_FRACTION; 321 boolean withinSnapMenuThreshold = mOnLeft 322 ? mTranslation > targetLeft && mTranslation < targetRight 323 : mTranslation < -targetLeft && mTranslation > -targetRight; 324 boolean shouldSnapTo = mOnLeft ? mTranslation < targetLeft : mTranslation > -targetLeft; 325 if (DEBUG) { 326 Log.d(TAG, " withinSnapMenuThreshold= " + withinSnapMenuThreshold 327 + " shouldSnapTo= " + shouldSnapTo 328 + " targetLeft= " + targetLeft 329 + " targetRight= " + targetRight); 330 } 331 if (withinSnapMenuThreshold && !mSwipeHelper.isDismissGesture(ev)) { 332 // Haven't moved enough to unsnap from the menu 333 showMenu(animView, menuSnapTarget, velocity); 334 } else if (mSwipeHelper.isDismissGesture(ev) && !shouldSnapTo) { 335 // Only dismiss if we're not moving towards the menu 336 dismiss(animView, velocity); 337 } else { 338 snapBack(animView, velocity); 339 } 340 } else if (!mSwipeHelper.isFalseGesture(ev) 341 && (swipedEnoughToShowMenu() && (!gestureFastEnough || showMenuForSlowOnGoing)) 342 || (gestureTowardsMenu && !mSwipeHelper.isDismissGesture(ev))) { 343 // Menu has not been snapped to previously and this is menu revealing gesture 344 showMenu(animView, menuSnapTarget, velocity); 345 } else if (mSwipeHelper.isDismissGesture(ev) && !gestureTowardsMenu) { 346 dismiss(animView, velocity); 347 } else { 348 snapBack(animView, velocity); 349 } 350 return true; 351 } 352 353 private void showMenu(View animView, float targetLeft, float velocity) { 354 mMenuSnappedTo = true; 355 mMenuSnappedOnLeft = mOnLeft; 356 mMenuListener.onMenuShown(animView); 357 mSwipeHelper.snap(animView, targetLeft, velocity); 358 } 359 360 private void snapBack(View animView, float velocity) { 361 if (mFadeAnimator != null) { 362 mFadeAnimator.cancel(); 363 } 364 mHandler.removeCallbacks(mCheckForDrag); 365 mMenuSnappedTo = false; 366 mSnapping = true; 367 mSwipeHelper.snap(animView, 0 /* leftTarget */, velocity); 368 } 369 370 private void dismiss(View animView, float velocity) { 371 if (mFadeAnimator != null) { 372 mFadeAnimator.cancel(); 373 } 374 mHandler.removeCallbacks(mCheckForDrag); 375 mMenuSnappedTo = false; 376 mDismissing = true; 377 mSwipeHelper.dismiss(animView, velocity); 378 } 379 380 /** 381 * @return whether the notification has been translated enough to show the menu and not enough 382 * to be dismissed. 383 */ 384 private boolean swipedEnoughToShowMenu() { 385 final float multiplier = mParent.canViewBeDismissed() 386 ? SWIPED_FAR_ENOUGH_MENU_FRACTION 387 : SWIPED_FAR_ENOUGH_MENU_UNCLEARABLE_FRACTION; 388 final float minimumSwipeDistance = mHorizSpaceForIcon * multiplier; 389 return !mSwipeHelper.swipedFarEnough(0, 0) && isMenuVisible() 390 && (mOnLeft ? mTranslation > minimumSwipeDistance 391 : mTranslation < -minimumSwipeDistance); 392 } 393 394 /** 395 * Returns whether the gesture is towards the menu location or not. 396 */ 397 private boolean isTowardsMenu(float movement) { 398 return isMenuVisible() 399 && ((mOnLeft && movement <= 0) 400 || (!mOnLeft && movement >= 0)); 401 } 402 403 @Override 404 public void setAppName(String appName) { 405 if (appName == null) { 406 return; 407 } 408 Resources res = mContext.getResources(); 409 final int count = mMenuItems.size(); 410 for (int i = 0; i < count; i++) { 411 MenuItem item = mMenuItems.get(i); 412 String description = String.format( 413 res.getString(R.string.notification_menu_accessibility), 414 appName, item.getContentDescription()); 415 View menuView = item.getMenuView(); 416 if (menuView != null) { 417 menuView.setContentDescription(description); 418 } 419 } 420 } 421 422 @Override 423 public void onHeightUpdate() { 424 if (mParent == null || mMenuItems.size() == 0) { 425 return; 426 } 427 int parentHeight = mParent.getCollapsedHeight(); 428 float translationY; 429 if (parentHeight < mVertSpaceForIcons) { 430 translationY = (parentHeight / 2) - (mHorizSpaceForIcon / 2); 431 } else { 432 translationY = (mVertSpaceForIcons - mHorizSpaceForIcon) / 2; 433 } 434 mMenuContainer.setTranslationY(translationY); 435 } 436 437 @Override 438 public void onTranslationUpdate(float translation) { 439 mTranslation = translation; 440 if (mAnimating || !mMenuFadedIn) { 441 // Don't adjust when animating, or if the menu hasn't been shown yet. 442 return; 443 } 444 final float fadeThreshold = mParent.getWidth() * 0.3f; 445 final float absTrans = Math.abs(translation); 446 float desiredAlpha = 0; 447 if (absTrans == 0) { 448 desiredAlpha = 0; 449 } else if (absTrans <= fadeThreshold) { 450 desiredAlpha = 1; 451 } else { 452 desiredAlpha = 1 - ((absTrans - fadeThreshold) / (mParent.getWidth() - fadeThreshold)); 453 } 454 setMenuAlpha(desiredAlpha); 455 } 456 457 @Override 458 public void onClick(View v) { 459 if (mMenuListener == null) { 460 // Nothing to do 461 return; 462 } 463 v.getLocationOnScreen(mIconLocation); 464 mParent.getLocationOnScreen(mParentLocation); 465 final int centerX = (int) (mHorizSpaceForIcon / 2); 466 final int centerY = v.getHeight() / 2; 467 final int x = mIconLocation[0] - mParentLocation[0] + centerX; 468 final int y = mIconLocation[1] - mParentLocation[1] + centerY; 469 final int index = mMenuContainer.indexOfChild(v); 470 mMenuListener.onMenuClicked(mParent, x, y, mMenuItems.get(index)); 471 } 472 473 private boolean isMenuLocationChange() { 474 boolean onLeft = mTranslation > mIconPadding; 475 boolean onRight = mTranslation < -mIconPadding; 476 if ((mOnLeft && onRight) || (!mOnLeft && onLeft)) { 477 return true; 478 } 479 return false; 480 } 481 482 private void setMenuLocation() { 483 boolean showOnLeft = mTranslation > 0; 484 if ((mIconsPlaced && showOnLeft == mOnLeft) || mSnapping 485 || !mMenuContainer.isAttachedToWindow()) { 486 // Do nothing 487 return; 488 } 489 final int count = mMenuContainer.getChildCount(); 490 for (int i = 0; i < count; i++) { 491 final View v = mMenuContainer.getChildAt(i); 492 final float left = i * mHorizSpaceForIcon; 493 final float right = mParent.getWidth() - (mHorizSpaceForIcon * (i + 1)); 494 v.setX(showOnLeft ? left : right); 495 } 496 mOnLeft = showOnLeft; 497 mIconsPlaced = true; 498 } 499 500 private void setMenuAlpha(float alpha) { 501 mAlpha = alpha; 502 if (mMenuContainer == null) { 503 return; 504 } 505 if (alpha == 0) { 506 mMenuFadedIn = false; // Can fade in again once it's gone. 507 mMenuContainer.setVisibility(View.INVISIBLE); 508 } else { 509 mMenuContainer.setVisibility(View.VISIBLE); 510 } 511 final int count = mMenuContainer.getChildCount(); 512 for (int i = 0; i < count; i++) { 513 mMenuContainer.getChildAt(i).setAlpha(mAlpha); 514 } 515 } 516 517 /** 518 * Returns the horizontal space in pixels required to display the menu. 519 */ 520 private float getSpaceForMenu() { 521 return mHorizSpaceForIcon * mMenuContainer.getChildCount(); 522 } 523 524 private final class CheckForDrag implements Runnable { 525 @Override 526 public void run() { 527 final float absTransX = Math.abs(mTranslation); 528 final float bounceBackToMenuWidth = getSpaceForMenu(); 529 final float notiThreshold = mParent.getWidth() * 0.4f; 530 if ((!isMenuVisible() || isMenuLocationChange()) 531 && absTransX >= bounceBackToMenuWidth * 0.4 532 && absTransX < notiThreshold) { 533 fadeInMenu(notiThreshold); 534 } 535 } 536 } 537 538 private void fadeInMenu(final float notiThreshold) { 539 if (mDismissing || mAnimating) { 540 return; 541 } 542 if (isMenuLocationChange()) { 543 setMenuAlpha(0f); 544 } 545 final float transX = mTranslation; 546 final boolean fromLeft = mTranslation > 0; 547 setMenuLocation(); 548 mFadeAnimator = ValueAnimator.ofFloat(mAlpha, 1); 549 mFadeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 550 @Override 551 public void onAnimationUpdate(ValueAnimator animation) { 552 final float absTrans = Math.abs(transX); 553 554 boolean pastMenu = (fromLeft && transX <= notiThreshold) 555 || (!fromLeft && absTrans <= notiThreshold); 556 if (pastMenu && !mMenuFadedIn) { 557 setMenuAlpha((float) animation.getAnimatedValue()); 558 } 559 } 560 }); 561 mFadeAnimator.addListener(new AnimatorListenerAdapter() { 562 @Override 563 public void onAnimationStart(Animator animation) { 564 mAnimating = true; 565 } 566 567 @Override 568 public void onAnimationCancel(Animator animation) { 569 // TODO should animate back to 0f from current alpha 570 setMenuAlpha(0f); 571 } 572 573 @Override 574 public void onAnimationEnd(Animator animation) { 575 mAnimating = false; 576 mMenuFadedIn = mAlpha == 1; 577 } 578 }); 579 mFadeAnimator.setInterpolator(Interpolators.ALPHA_IN); 580 mFadeAnimator.setDuration(ICON_ALPHA_ANIM_DURATION); 581 mFadeAnimator.start(); 582 } 583 584 @Override 585 public void setMenuItems(ArrayList<MenuItem> items) { 586 // Do nothing we use our own for now. 587 // TODO -- handle / allow custom menu items! 588 } 589 590 public static MenuItem createSnoozeItem(Context context) { 591 Resources res = context.getResources(); 592 NotificationSnooze content = (NotificationSnooze) LayoutInflater.from(context) 593 .inflate(R.layout.notification_snooze, null, false); 594 String snoozeDescription = res.getString(R.string.notification_menu_snooze_description); 595 MenuItem snooze = new NotificationMenuItem(context, snoozeDescription, content, 596 R.drawable.ic_snooze); 597 return snooze; 598 } 599 600 public static MenuItem createInfoItem(Context context) { 601 Resources res = context.getResources(); 602 String infoDescription = res.getString(R.string.notification_menu_gear_description); 603 NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate( 604 R.layout.notification_info, null, false); 605 MenuItem info = new NotificationMenuItem(context, infoDescription, infoContent, 606 R.drawable.ic_settings); 607 return info; 608 } 609 610 private void addMenuView(MenuItem item, ViewGroup parent) { 611 View menuView = item.getMenuView(); 612 if (menuView != null) { 613 parent.addView(menuView); 614 menuView.setOnClickListener(this); 615 FrameLayout.LayoutParams lp = (LayoutParams) menuView.getLayoutParams(); 616 lp.width = (int) mHorizSpaceForIcon; 617 lp.height = (int) mHorizSpaceForIcon; 618 menuView.setLayoutParams(lp); 619 } 620 } 621 622 public static class NotificationMenuItem implements MenuItem { 623 View mMenuView; 624 GutsContent mGutsContent; 625 String mContentDescription; 626 627 public NotificationMenuItem(Context context, String s, GutsContent content, int iconResId) { 628 Resources res = context.getResources(); 629 int padding = res.getDimensionPixelSize(R.dimen.notification_menu_icon_padding); 630 int tint = res.getColor(R.color.notification_gear_color); 631 AlphaOptimizedImageView iv = new AlphaOptimizedImageView(context); 632 iv.setPadding(padding, padding, padding, padding); 633 Drawable icon = context.getResources().getDrawable(iconResId); 634 iv.setImageDrawable(icon); 635 iv.setColorFilter(tint); 636 iv.setAlpha(1f); 637 mMenuView = iv; 638 mContentDescription = s; 639 mGutsContent = content; 640 } 641 642 @Override 643 public View getMenuView() { 644 return mMenuView; 645 } 646 647 @Override 648 public View getGutsView() { 649 return mGutsContent.getContentView(); 650 } 651 652 @Override 653 public String getContentDescription() { 654 return mContentDescription; 655 } 656 } 657} 658