1 /* 2 * Copyright (C) 2017 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.launcher3.notification; 18 19 import android.animation.Animator; 20 import android.app.Notification; 21 import android.content.Context; 22 import android.graphics.Rect; 23 import android.support.annotation.Nullable; 24 import android.support.v4.content.ContextCompat; 25 import android.util.AttributeSet; 26 import android.view.MotionEvent; 27 import android.view.View; 28 import android.widget.FrameLayout; 29 import android.widget.TextView; 30 31 import com.android.launcher3.ItemInfo; 32 import com.android.launcher3.R; 33 import com.android.launcher3.anim.PillHeightRevealOutlineProvider; 34 import com.android.launcher3.graphics.IconPalette; 35 import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider; 36 import com.android.launcher3.popup.PopupItemView; 37 import com.android.launcher3.userevent.nano.LauncherLogProto; 38 39 import java.util.List; 40 41 /** 42 * A {@link FrameLayout} that contains a header, main view and a footer. 43 * The main view contains the icon and text (title + subtext) of the first notification. 44 * The footer contains: A list of just the icons of all the notifications past the first one. 45 * @see NotificationFooterLayout 46 */ 47 public class NotificationItemView extends PopupItemView implements LogContainerProvider { 48 49 private static final Rect sTempRect = new Rect(); 50 51 private TextView mHeaderCount; 52 private NotificationMainView mMainView; 53 private NotificationFooterLayout mFooter; 54 private SwipeHelper mSwipeHelper; 55 private boolean mAnimatingNextIcon; 56 private int mNotificationHeaderTextColor = Notification.COLOR_DEFAULT; 57 58 public NotificationItemView(Context context) { 59 this(context, null, 0); 60 } 61 62 public NotificationItemView(Context context, AttributeSet attrs) { 63 this(context, attrs, 0); 64 } 65 66 public NotificationItemView(Context context, AttributeSet attrs, int defStyle) { 67 super(context, attrs, defStyle); 68 } 69 70 @Override 71 protected void onFinishInflate() { 72 super.onFinishInflate(); 73 mHeaderCount = (TextView) findViewById(R.id.notification_count); 74 mMainView = (NotificationMainView) findViewById(R.id.main_view); 75 mFooter = (NotificationFooterLayout) findViewById(R.id.footer); 76 mSwipeHelper = new SwipeHelper(SwipeHelper.X, mMainView, getContext()); 77 mSwipeHelper.setDisableHardwareLayers(true); 78 } 79 80 public NotificationMainView getMainView() { 81 return mMainView; 82 } 83 84 public int getHeightMinusFooter() { 85 int footerHeight = mFooter.getParent() == null ? 0 : mFooter.getHeight(); 86 return getHeight() - footerHeight; 87 } 88 89 public Animator animateHeightRemoval(int heightToRemove) { 90 final int newHeight = getHeight() - heightToRemove; 91 return new PillHeightRevealOutlineProvider(mPillRect, 92 getBackgroundRadius(), newHeight).createRevealAnimator(this, true /* isReversed */); 93 } 94 95 public void updateHeader(int notificationCount, @Nullable IconPalette palette) { 96 mHeaderCount.setText(notificationCount <= 1 ? "" : String.valueOf(notificationCount)); 97 if (palette != null) { 98 if (mNotificationHeaderTextColor == Notification.COLOR_DEFAULT) { 99 mNotificationHeaderTextColor = 100 IconPalette.resolveContrastColor(getContext(), palette.dominantColor, 101 getResources().getColor(R.color.popup_header_background_color)); 102 } 103 mHeaderCount.setTextColor(mNotificationHeaderTextColor); 104 } 105 } 106 107 @Override 108 public boolean onInterceptTouchEvent(MotionEvent ev) { 109 if (mMainView.getNotificationInfo() == null) { 110 // The notification hasn't been populated yet. 111 return false; 112 } 113 getParent().requestDisallowInterceptTouchEvent(true); 114 return mSwipeHelper.onInterceptTouchEvent(ev); 115 } 116 117 @Override 118 public boolean onTouchEvent(MotionEvent ev) { 119 if (mMainView.getNotificationInfo() == null) { 120 // The notification hasn't been populated yet. 121 return false; 122 } 123 return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev); 124 } 125 126 public void applyNotificationInfos(final List<NotificationInfo> notificationInfos) { 127 if (notificationInfos.isEmpty()) { 128 return; 129 } 130 131 NotificationInfo mainNotification = notificationInfos.get(0); 132 mMainView.applyNotificationInfo(mainNotification, mIconView); 133 134 for (int i = 1; i < notificationInfos.size(); i++) { 135 mFooter.addNotificationInfo(notificationInfos.get(i)); 136 } 137 mFooter.commitNotificationInfos(); 138 } 139 140 public void trimNotifications(final List<String> notificationKeys) { 141 boolean dismissedMainNotification = !notificationKeys.contains( 142 mMainView.getNotificationInfo().notificationKey); 143 if (dismissedMainNotification && !mAnimatingNextIcon) { 144 // Animate the next icon into place as the new main notification. 145 mAnimatingNextIcon = true; 146 mMainView.setVisibility(INVISIBLE); 147 mMainView.setTranslationX(0); 148 mIconView.getGlobalVisibleRect(sTempRect); 149 mFooter.animateFirstNotificationTo(sTempRect, 150 new NotificationFooterLayout.IconAnimationEndListener() { 151 @Override 152 public void onIconAnimationEnd(NotificationInfo newMainNotification) { 153 if (newMainNotification != null) { 154 mMainView.applyNotificationInfo(newMainNotification, mIconView, true); 155 mMainView.setVisibility(VISIBLE); 156 } 157 mAnimatingNextIcon = false; 158 } 159 }); 160 } else { 161 mFooter.trimNotifications(notificationKeys); 162 } 163 } 164 165 @Override 166 public int getArrowColor(boolean isArrowAttachedToBottom) { 167 return ContextCompat.getColor(getContext(), isArrowAttachedToBottom 168 ? R.color.popup_background_color 169 : R.color.popup_header_background_color); 170 } 171 172 @Override 173 public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target, 174 LauncherLogProto.Target targetParent) { 175 target.itemType = LauncherLogProto.ItemType.NOTIFICATION; 176 targetParent.containerType = LauncherLogProto.ContainerType.DEEPSHORTCUTS; 177 } 178 } 179