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 static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL; 20 21 import android.app.Notification; 22 import android.content.Context; 23 import android.graphics.Color; 24 import android.graphics.Rect; 25 import android.view.MotionEvent; 26 import android.view.View; 27 import android.view.ViewGroup.MarginLayoutParams; 28 import android.widget.TextView; 29 30 import com.android.launcher3.R; 31 import com.android.launcher3.graphics.IconPalette; 32 import com.android.launcher3.popup.PopupContainerWithArrow; 33 import com.android.launcher3.touch.SingleAxisSwipeDetector; 34 import com.android.launcher3.util.Themes; 35 36 import java.util.List; 37 38 /** 39 * Utility class to manage notification UI 40 */ 41 public class NotificationItemView { 42 43 private static final Rect sTempRect = new Rect(); 44 45 private final Context mContext; 46 private final PopupContainerWithArrow mContainer; 47 48 private final TextView mHeaderText; 49 private final TextView mHeaderCount; 50 private final NotificationMainView mMainView; 51 private final NotificationFooterLayout mFooter; 52 private final SingleAxisSwipeDetector mSwipeDetector; 53 private final View mIconView; 54 55 private final View mHeader; 56 private final View mDivider; 57 58 private View mGutter; 59 60 private boolean mIgnoreTouch = false; 61 private boolean mAnimatingNextIcon; 62 private int mNotificationHeaderTextColor = Notification.COLOR_DEFAULT; 63 NotificationItemView(PopupContainerWithArrow container)64 public NotificationItemView(PopupContainerWithArrow container) { 65 mContainer = container; 66 mContext = container.getContext(); 67 68 mHeaderText = container.findViewById(R.id.notification_text); 69 mHeaderCount = container.findViewById(R.id.notification_count); 70 mMainView = container.findViewById(R.id.main_view); 71 mFooter = container.findViewById(R.id.footer); 72 mIconView = container.findViewById(R.id.popup_item_icon); 73 74 mHeader = container.findViewById(R.id.header); 75 mDivider = container.findViewById(R.id.divider); 76 77 mSwipeDetector = new SingleAxisSwipeDetector(mContext, mMainView, HORIZONTAL); 78 mSwipeDetector.setDetectableScrollConditions(SingleAxisSwipeDetector.DIRECTION_BOTH, false); 79 mMainView.setSwipeDetector(mSwipeDetector); 80 mFooter.setContainer(this); 81 } 82 addGutter()83 public void addGutter() { 84 if (mGutter == null) { 85 mGutter = mContainer.inflateAndAdd(R.layout.notification_gutter, mContainer); 86 } 87 } 88 removeFooter()89 public void removeFooter() { 90 if (mContainer.indexOfChild(mFooter) >= 0) { 91 mContainer.removeView(mFooter); 92 mContainer.removeView(mDivider); 93 } 94 } 95 inverseGutterMargin()96 public void inverseGutterMargin() { 97 MarginLayoutParams lp = (MarginLayoutParams) mGutter.getLayoutParams(); 98 int top = lp.topMargin; 99 lp.topMargin = lp.bottomMargin; 100 lp.bottomMargin = top; 101 } 102 removeAllViews()103 public void removeAllViews() { 104 mContainer.removeView(mMainView); 105 mContainer.removeView(mHeader); 106 107 if (mContainer.indexOfChild(mFooter) >= 0) { 108 mContainer.removeView(mFooter); 109 mContainer.removeView(mDivider); 110 } 111 112 if (mGutter != null) { 113 mContainer.removeView(mGutter); 114 } 115 } 116 updateHeader(int notificationCount, int iconColor)117 public void updateHeader(int notificationCount, int iconColor) { 118 mHeaderCount.setText(notificationCount <= 1 ? "" : String.valueOf(notificationCount)); 119 if (Color.alpha(iconColor) > 0) { 120 if (mNotificationHeaderTextColor == Notification.COLOR_DEFAULT) { 121 mNotificationHeaderTextColor = 122 IconPalette.resolveContrastColor(mContext, iconColor, 123 Themes.getAttrColor(mContext, R.attr.popupColorPrimary)); 124 } 125 mHeaderText.setTextColor(mNotificationHeaderTextColor); 126 mHeaderCount.setTextColor(mNotificationHeaderTextColor); 127 } 128 } 129 onInterceptTouchEvent(MotionEvent ev)130 public boolean onInterceptTouchEvent(MotionEvent ev) { 131 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 132 sTempRect.set(mMainView.getLeft(), mMainView.getTop(), 133 mMainView.getRight(), mMainView.getBottom()); 134 mIgnoreTouch = !sTempRect.contains((int) ev.getX(), (int) ev.getY()); 135 if (!mIgnoreTouch) { 136 mContainer.getParent().requestDisallowInterceptTouchEvent(true); 137 } 138 } 139 if (mIgnoreTouch) { 140 return false; 141 } 142 if (mMainView.getNotificationInfo() == null) { 143 // The notification hasn't been populated yet. 144 return false; 145 } 146 147 mSwipeDetector.onTouchEvent(ev); 148 return mSwipeDetector.isDraggingOrSettling(); 149 } 150 onTouchEvent(MotionEvent ev)151 public boolean onTouchEvent(MotionEvent ev) { 152 if (mIgnoreTouch) { 153 return false; 154 } 155 if (mMainView.getNotificationInfo() == null) { 156 // The notification hasn't been populated yet. 157 return false; 158 } 159 return mSwipeDetector.onTouchEvent(ev); 160 } 161 applyNotificationInfos(final List<NotificationInfo> notificationInfos)162 public void applyNotificationInfos(final List<NotificationInfo> notificationInfos) { 163 if (notificationInfos.isEmpty()) { 164 return; 165 } 166 167 NotificationInfo mainNotification = notificationInfos.get(0); 168 mMainView.applyNotificationInfo(mainNotification, false); 169 170 for (int i = 1; i < notificationInfos.size(); i++) { 171 mFooter.addNotificationInfo(notificationInfos.get(i)); 172 } 173 mFooter.commitNotificationInfos(); 174 } 175 trimNotifications(final List<String> notificationKeys)176 public void trimNotifications(final List<String> notificationKeys) { 177 boolean dismissedMainNotification = !notificationKeys.contains( 178 mMainView.getNotificationInfo().notificationKey); 179 if (dismissedMainNotification && !mAnimatingNextIcon) { 180 // Animate the next icon into place as the new main notification. 181 mAnimatingNextIcon = true; 182 mMainView.setContentVisibility(View.INVISIBLE); 183 mMainView.setContentTranslation(0); 184 mIconView.getGlobalVisibleRect(sTempRect); 185 mFooter.animateFirstNotificationTo(sTempRect, (newMainNotification) -> { 186 if (newMainNotification != null) { 187 mMainView.applyNotificationInfo(newMainNotification, true); 188 mMainView.setContentVisibility(View.VISIBLE); 189 } 190 mAnimatingNextIcon = false; 191 }); 192 } else { 193 mFooter.trimNotifications(notificationKeys); 194 } 195 } 196 } 197