OSDN Git Service

Import translations. DO NOT MERGE
[android-x86/packages-apps-Launcher3.git] / src / com / android / launcher3 / notification / NotificationFooterLayout.java
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.animation.AnimatorListenerAdapter;
21 import android.animation.AnimatorSet;
22 import android.animation.ObjectAnimator;
23 import android.content.Context;
24 import android.content.res.Resources;
25 import android.graphics.Rect;
26 import android.graphics.drawable.ColorDrawable;
27 import android.util.AttributeSet;
28 import android.view.Gravity;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.widget.FrameLayout;
32 import android.widget.LinearLayout;
33
34 import com.android.launcher3.Launcher;
35 import com.android.launcher3.LauncherAnimUtils;
36 import com.android.launcher3.R;
37 import com.android.launcher3.Utilities;
38 import com.android.launcher3.anim.PropertyListBuilder;
39 import com.android.launcher3.anim.PropertyResetListener;
40 import com.android.launcher3.popup.PopupContainerWithArrow;
41
42 import java.util.ArrayList;
43 import java.util.Iterator;
44 import java.util.List;
45
46 /**
47  * A {@link FrameLayout} that contains only icons of notifications.
48  * If there are more than {@link #MAX_FOOTER_NOTIFICATIONS} icons, we add a "..." overflow.
49  */
50 public class NotificationFooterLayout extends FrameLayout {
51
52     public interface IconAnimationEndListener {
53         void onIconAnimationEnd(NotificationInfo animatedNotification);
54     }
55
56     private static final int MAX_FOOTER_NOTIFICATIONS = 5;
57
58     private static final Rect sTempRect = new Rect();
59
60     private final List<NotificationInfo> mNotifications = new ArrayList<>();
61     private final List<NotificationInfo> mOverflowNotifications = new ArrayList<>();
62     private final boolean mRtl;
63
64     FrameLayout.LayoutParams mIconLayoutParams;
65     private View mOverflowEllipsis;
66     private LinearLayout mIconRow;
67     private int mBackgroundColor;
68
69     public NotificationFooterLayout(Context context) {
70         this(context, null, 0);
71     }
72
73     public NotificationFooterLayout(Context context, AttributeSet attrs) {
74         this(context, attrs, 0);
75     }
76
77     public NotificationFooterLayout(Context context, AttributeSet attrs, int defStyle) {
78         super(context, attrs, defStyle);
79
80         Resources res = getResources();
81         mRtl = Utilities.isRtl(res);
82
83         int iconSize = res.getDimensionPixelSize(R.dimen.notification_footer_icon_size);
84         mIconLayoutParams = new LayoutParams(iconSize, iconSize);
85         mIconLayoutParams.gravity = Gravity.CENTER_VERTICAL;
86         // Compute margin start for each icon such that the icons between the first one
87         // and the ellipsis are evenly spaced out.
88         int paddingEnd = res.getDimensionPixelSize(R.dimen.notification_footer_icon_row_padding);
89         int ellipsisSpace = res.getDimensionPixelSize(R.dimen.horizontal_ellipsis_offset)
90                 + res.getDimensionPixelSize(R.dimen.horizontal_ellipsis_size);
91         int footerWidth = res.getDimensionPixelSize(R.dimen.bg_popup_item_width);
92         int availableIconRowSpace = footerWidth - paddingEnd - ellipsisSpace
93                 - iconSize * MAX_FOOTER_NOTIFICATIONS;
94         mIconLayoutParams.setMarginStart(availableIconRowSpace / MAX_FOOTER_NOTIFICATIONS);
95     }
96
97     @Override
98     protected void onFinishInflate() {
99         super.onFinishInflate();
100         mOverflowEllipsis = findViewById(R.id.overflow);
101         mIconRow = (LinearLayout) findViewById(R.id.icon_row);
102         mBackgroundColor = ((ColorDrawable) getBackground()).getColor();
103     }
104
105     /**
106      * Keep track of the NotificationInfo, and then update the UI when
107      * {@link #commitNotificationInfos()} is called.
108      */
109     public void addNotificationInfo(final NotificationInfo notificationInfo) {
110         if (mNotifications.size() < MAX_FOOTER_NOTIFICATIONS) {
111             mNotifications.add(notificationInfo);
112         } else {
113             mOverflowNotifications.add(notificationInfo);
114         }
115     }
116
117     /**
118      * Adds icons and potentially overflow text for all of the NotificationInfo's
119      * added using {@link #addNotificationInfo(NotificationInfo)}.
120      */
121     public void commitNotificationInfos() {
122         mIconRow.removeAllViews();
123
124         for (int i = 0; i < mNotifications.size(); i++) {
125             NotificationInfo info = mNotifications.get(i);
126             addNotificationIconForInfo(info);
127         }
128         updateOverflowEllipsisVisibility();
129     }
130
131     private void updateOverflowEllipsisVisibility() {
132         mOverflowEllipsis.setVisibility(mOverflowNotifications.isEmpty() ? GONE : VISIBLE);
133     }
134
135     /**
136      * Creates an icon for the given NotificationInfo, and adds it to the icon row.
137      * @return the icon view that was added
138      */
139     private View addNotificationIconForInfo(NotificationInfo info) {
140         View icon = new View(getContext());
141         icon.setBackground(info.getIconForBackground(getContext(), mBackgroundColor));
142         icon.setOnClickListener(info);
143         icon.setTag(info);
144         mIconRow.addView(icon, 0, mIconLayoutParams);
145         return icon;
146     }
147
148     public void animateFirstNotificationTo(Rect toBounds,
149             final IconAnimationEndListener callback) {
150         AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
151         final View firstNotification = mIconRow.getChildAt(mIconRow.getChildCount() - 1);
152
153         Rect fromBounds = sTempRect;
154         firstNotification.getGlobalVisibleRect(fromBounds);
155         float scale = (float) toBounds.height() / fromBounds.height();
156         Animator moveAndScaleIcon = LauncherAnimUtils.ofPropertyValuesHolder(firstNotification,
157                 new PropertyListBuilder().scale(scale).translationY(toBounds.top - fromBounds.top
158                         + (fromBounds.height() * scale - fromBounds.height()) / 2).build());
159         moveAndScaleIcon.addListener(new AnimatorListenerAdapter() {
160             @Override
161             public void onAnimationEnd(Animator animation) {
162                 callback.onIconAnimationEnd((NotificationInfo) firstNotification.getTag());
163                 removeViewFromIconRow(firstNotification);
164             }
165         });
166         animation.play(moveAndScaleIcon);
167
168         // Shift all notifications (not the overflow) over to fill the gap.
169         int gapWidth = mIconLayoutParams.width + mIconLayoutParams.getMarginStart();
170         if (mRtl) {
171             gapWidth = -gapWidth;
172         }
173         if (!mOverflowNotifications.isEmpty()) {
174             NotificationInfo notification = mOverflowNotifications.remove(0);
175             mNotifications.add(notification);
176             View iconFromOverflow = addNotificationIconForInfo(notification);
177             animation.play(ObjectAnimator.ofFloat(iconFromOverflow, ALPHA, 0, 1));
178         }
179         int numIcons = mIconRow.getChildCount() - 1; // All children besides the one leaving.
180         // We have to reset the translation X to 0 when the new main notification
181         // is removed from the footer.
182         PropertyResetListener<View, Float> propertyResetListener
183                 = new PropertyResetListener<>(TRANSLATION_X, 0f);
184         for (int i = 0; i < numIcons; i++) {
185             final View child = mIconRow.getChildAt(i);
186             Animator shiftChild = ObjectAnimator.ofFloat(child, TRANSLATION_X, gapWidth);
187             shiftChild.addListener(propertyResetListener);
188             animation.play(shiftChild);
189         }
190         animation.start();
191     }
192
193     private void removeViewFromIconRow(View child) {
194         mIconRow.removeView(child);
195         mNotifications.remove((NotificationInfo) child.getTag());
196         updateOverflowEllipsisVisibility();
197         if (mIconRow.getChildCount() == 0) {
198             // There are no more icons in the footer, so hide it.
199             PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(
200                     Launcher.getLauncher(getContext()));
201             if (popup != null) {
202                 Animator collapseFooter = popup.reduceNotificationViewHeight(getHeight(),
203                         getResources().getInteger(R.integer.config_removeNotificationViewDuration));
204                 collapseFooter.addListener(new AnimatorListenerAdapter() {
205                     @Override
206                     public void onAnimationEnd(Animator animation) {
207                         ((ViewGroup) getParent()).removeView(NotificationFooterLayout.this);
208                     }
209                 });
210                 collapseFooter.start();
211             }
212         }
213     }
214
215     public void trimNotifications(List<String> notifications) {
216         if (!isAttachedToWindow() || mIconRow.getChildCount() == 0) {
217             return;
218         }
219         Iterator<NotificationInfo> overflowIterator = mOverflowNotifications.iterator();
220         while (overflowIterator.hasNext()) {
221             if (!notifications.contains(overflowIterator.next().notificationKey)) {
222                 overflowIterator.remove();
223             }
224         }
225         for (int i = mIconRow.getChildCount() - 1; i >= 0; i--) {
226             View child = mIconRow.getChildAt(i);
227             NotificationInfo childInfo = (NotificationInfo) child.getTag();
228             if (!notifications.contains(childInfo.notificationKey)) {
229                 removeViewFromIconRow(child);
230             }
231         }
232     }
233 }