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