| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.launcher3.notification; |
| |
| import android.animation.Animator; |
| import android.animation.AnimatorSet; |
| import android.animation.ObjectAnimator; |
| import android.app.Notification; |
| import android.content.Context; |
| import android.graphics.Rect; |
| import android.support.annotation.Nullable; |
| import android.util.AttributeSet; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.widget.FrameLayout; |
| import android.widget.TextView; |
| |
| import com.android.launcher3.ItemInfo; |
| import com.android.launcher3.LauncherAnimUtils; |
| import com.android.launcher3.R; |
| import com.android.launcher3.anim.PropertyResetListener; |
| import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; |
| import com.android.launcher3.graphics.IconPalette; |
| import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider; |
| import com.android.launcher3.popup.PopupItemView; |
| import com.android.launcher3.touch.SwipeDetector; |
| import com.android.launcher3.userevent.nano.LauncherLogProto; |
| import com.android.launcher3.util.Themes; |
| |
| import java.util.List; |
| |
| /** |
| * A {@link FrameLayout} that contains a header, main view and a footer. |
| * The main view contains the icon and text (title + subtext) of the first notification. |
| * The footer contains: A list of just the icons of all the notifications past the first one. |
| * @see NotificationFooterLayout |
| */ |
| public class NotificationItemView extends PopupItemView implements LogContainerProvider { |
| |
| private static final Rect sTempRect = new Rect(); |
| |
| private TextView mHeaderText; |
| private TextView mHeaderCount; |
| private NotificationMainView mMainView; |
| private NotificationFooterLayout mFooter; |
| private SwipeDetector mSwipeDetector; |
| private boolean mAnimatingNextIcon; |
| private int mNotificationHeaderTextColor = Notification.COLOR_DEFAULT; |
| |
| public NotificationItemView(Context context) { |
| this(context, null, 0); |
| } |
| |
| public NotificationItemView(Context context, AttributeSet attrs) { |
| this(context, attrs, 0); |
| } |
| |
| public NotificationItemView(Context context, AttributeSet attrs, int defStyle) { |
| super(context, attrs, defStyle); |
| } |
| |
| @Override |
| protected void onFinishInflate() { |
| super.onFinishInflate(); |
| mHeaderText = findViewById(R.id.notification_text); |
| mHeaderCount = findViewById(R.id.notification_count); |
| mMainView = findViewById(R.id.main_view); |
| mFooter = findViewById(R.id.footer); |
| |
| mSwipeDetector = new SwipeDetector(getContext(), mMainView, SwipeDetector.HORIZONTAL); |
| mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false); |
| mMainView.setSwipeDetector(mSwipeDetector); |
| } |
| |
| public NotificationMainView getMainView() { |
| return mMainView; |
| } |
| |
| /** |
| * This method is used to calculate the height to remove when dismissing the last notification. |
| * We subtract the height of the footer in this case since the footer should be gone or in the |
| * process of being removed. |
| * @return The height of the entire notification item, minus the footer if it still exists. |
| */ |
| public int getHeightMinusFooter() { |
| if (mFooter.getParent() == null) { |
| return getHeight(); |
| } |
| int excessFooterHeight = mFooter.getHeight() - getResources().getDimensionPixelSize( |
| R.dimen.notification_empty_footer_height); |
| return getHeight() - excessFooterHeight; |
| } |
| |
| public Animator animateHeightRemoval(int heightToRemove, boolean shouldRemoveFromTop) { |
| AnimatorSet anim = LauncherAnimUtils.createAnimatorSet(); |
| |
| Rect startRect = new Rect(mPillRect); |
| Rect endRect = new Rect(mPillRect); |
| if (shouldRemoveFromTop) { |
| endRect.top += heightToRemove; |
| } else { |
| endRect.bottom -= heightToRemove; |
| } |
| anim.play(new RoundedRectRevealOutlineProvider(getBackgroundRadius(), getBackgroundRadius(), |
| startRect, endRect, mRoundedCorners).createRevealAnimator(this, false)); |
| |
| View bottomGutter = findViewById(R.id.gutter_bottom); |
| if (bottomGutter != null && bottomGutter.getVisibility() == VISIBLE) { |
| Animator translateGutter = ObjectAnimator.ofFloat(bottomGutter, TRANSLATION_Y, |
| -heightToRemove); |
| translateGutter.addListener(new PropertyResetListener<>(TRANSLATION_Y, 0f)); |
| anim.play(translateGutter); |
| } |
| |
| return anim; |
| } |
| |
| public void updateHeader(int notificationCount, @Nullable IconPalette palette) { |
| mHeaderCount.setText(notificationCount <= 1 ? "" : String.valueOf(notificationCount)); |
| if (palette != null) { |
| if (mNotificationHeaderTextColor == Notification.COLOR_DEFAULT) { |
| mNotificationHeaderTextColor = |
| IconPalette.resolveContrastColor(getContext(), palette.dominantColor, |
| Themes.getAttrColor(getContext(), R.attr.popupColorPrimary)); |
| } |
| mHeaderText.setTextColor(mNotificationHeaderTextColor); |
| mHeaderCount.setTextColor(mNotificationHeaderTextColor); |
| } |
| } |
| |
| @Override |
| public boolean onInterceptTouchEvent(MotionEvent ev) { |
| if (mMainView.getNotificationInfo() == null) { |
| // The notification hasn't been populated yet. |
| return false; |
| } |
| getParent().requestDisallowInterceptTouchEvent(true); |
| mSwipeDetector.onTouchEvent(ev); |
| return mSwipeDetector.isDraggingOrSettling(); |
| } |
| |
| @Override |
| public boolean onTouchEvent(MotionEvent ev) { |
| if (mMainView.getNotificationInfo() == null) { |
| // The notification hasn't been populated yet. |
| return false; |
| } |
| return mSwipeDetector.onTouchEvent(ev) || super.onTouchEvent(ev); |
| } |
| |
| public void applyNotificationInfos(final List<NotificationInfo> notificationInfos) { |
| if (notificationInfos.isEmpty()) { |
| return; |
| } |
| |
| NotificationInfo mainNotification = notificationInfos.get(0); |
| mMainView.applyNotificationInfo(mainNotification, mIconView); |
| |
| for (int i = 1; i < notificationInfos.size(); i++) { |
| mFooter.addNotificationInfo(notificationInfos.get(i)); |
| } |
| mFooter.commitNotificationInfos(); |
| } |
| |
| public void trimNotifications(final List<String> notificationKeys) { |
| boolean dismissedMainNotification = !notificationKeys.contains( |
| mMainView.getNotificationInfo().notificationKey); |
| if (dismissedMainNotification && !mAnimatingNextIcon) { |
| // Animate the next icon into place as the new main notification. |
| mAnimatingNextIcon = true; |
| mMainView.setVisibility(INVISIBLE); |
| mMainView.setTranslationX(0); |
| mIconView.getGlobalVisibleRect(sTempRect); |
| mFooter.animateFirstNotificationTo(sTempRect, |
| new NotificationFooterLayout.IconAnimationEndListener() { |
| @Override |
| public void onIconAnimationEnd(NotificationInfo newMainNotification) { |
| if (newMainNotification != null) { |
| mMainView.applyNotificationInfo(newMainNotification, mIconView, true); |
| mMainView.setVisibility(VISIBLE); |
| } |
| mAnimatingNextIcon = false; |
| } |
| }); |
| } else { |
| mFooter.trimNotifications(notificationKeys); |
| } |
| } |
| |
| @Override |
| public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target, |
| LauncherLogProto.Target targetParent) { |
| target.itemType = LauncherLogProto.ItemType.NOTIFICATION; |
| targetParent.containerType = LauncherLogProto.ContainerType.DEEPSHORTCUTS; |
| } |
| } |