• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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.policy;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.LayoutTransition;
22 import android.animation.ObjectAnimator;
23 import android.animation.ValueAnimator;
24 import android.content.Context;
25 import android.content.res.Configuration;
26 import android.content.res.TypedArray;
27 import android.graphics.Rect;
28 import android.util.AttributeSet;
29 import android.util.Log;
30 import android.util.Slog;
31 import android.view.MotionEvent;
32 import android.view.View;
33 import android.view.ViewConfiguration;
34 import android.view.ViewGroup;
35 import android.view.inputmethod.InputMethodManager;
36 import android.widget.LinearLayout;
37 
38 import com.android.systemui.ExpandHelper;
39 import com.android.systemui.R;
40 import com.android.systemui.SwipeHelper;
41 import com.android.systemui.statusbar.NotificationData;
42 
43 import java.util.HashMap;
44 
45 public class NotificationRowLayout
46         extends LinearLayout
47         implements SwipeHelper.Callback, ExpandHelper.Callback
48 {
49     private static final String TAG = "NotificationRowLayout";
50     private static final boolean DEBUG = false;
51     private static final boolean SLOW_ANIMATIONS = DEBUG;
52 
53     private static final int APPEAR_ANIM_LEN = SLOW_ANIMATIONS ? 5000 : 250;
54     private static final int DISAPPEAR_ANIM_LEN = APPEAR_ANIM_LEN;
55 
56     boolean mAnimateBounds = true;
57 
58     Rect mTmpRect = new Rect();
59 
60     HashMap<View, ValueAnimator> mAppearingViews = new HashMap<View, ValueAnimator>();
61     HashMap<View, ValueAnimator> mDisappearingViews = new HashMap<View, ValueAnimator>();
62 
63     private SwipeHelper mSwipeHelper;
64 
65     private OnSizeChangedListener mOnSizeChangedListener;
66 
67     // Flag set during notification removal animation to avoid causing too much work until
68     // animation is done
69     boolean mRemoveViews = true;
70 
71     private LayoutTransition mRealLayoutTransition;
72 
NotificationRowLayout(Context context, AttributeSet attrs)73     public NotificationRowLayout(Context context, AttributeSet attrs) {
74         this(context, attrs, 0);
75     }
76 
NotificationRowLayout(Context context, AttributeSet attrs, int defStyle)77     public NotificationRowLayout(Context context, AttributeSet attrs, int defStyle) {
78         super(context, attrs, defStyle);
79 
80         mRealLayoutTransition = new LayoutTransition();
81         mRealLayoutTransition.setAnimateParentHierarchy(true);
82         setLayoutTransitionsEnabled(true);
83 
84         setOrientation(LinearLayout.VERTICAL);
85 
86         if (DEBUG) {
87             setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
88                 @Override
89                 public void onChildViewAdded(View parent, View child) {
90                     Slog.d(TAG, "view added: " + child + "; new count: " + getChildCount());
91                 }
92                 @Override
93                 public void onChildViewRemoved(View parent, View child) {
94                     Slog.d(TAG, "view removed: " + child + "; new count: " + (getChildCount() - 1));
95                 }
96             });
97 
98             setBackgroundColor(0x80FF8000);
99         }
100 
101         float densityScale = getResources().getDisplayMetrics().density;
102         float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
103         mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
104     }
105 
setLongPressListener(View.OnLongClickListener listener)106     public void setLongPressListener(View.OnLongClickListener listener) {
107         mSwipeHelper.setLongPressListener(listener);
108     }
109 
setOnSizeChangedListener(OnSizeChangedListener l)110     public void setOnSizeChangedListener(OnSizeChangedListener l) {
111         mOnSizeChangedListener = l;
112     }
113 
114     @Override
onWindowFocusChanged(boolean hasWindowFocus)115     public void onWindowFocusChanged(boolean hasWindowFocus) {
116         super.onWindowFocusChanged(hasWindowFocus);
117         if (!hasWindowFocus) {
118             mSwipeHelper.removeLongPressCallback();
119         }
120     }
121 
setAnimateBounds(boolean anim)122     public void setAnimateBounds(boolean anim) {
123         mAnimateBounds = anim;
124     }
125 
logLayoutTransition()126     private void logLayoutTransition() {
127         Log.v(TAG, "layout " +
128               (mRealLayoutTransition.isChangingLayout() ? "is " : "is not ") +
129               "in transition and animations " +
130               (mRealLayoutTransition.isRunning() ? "are " : "are not ") +
131               "running.");
132     }
133 
134     @Override
onInterceptTouchEvent(MotionEvent ev)135     public boolean onInterceptTouchEvent(MotionEvent ev) {
136         if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
137         if (DEBUG) logLayoutTransition();
138 
139         return mSwipeHelper.onInterceptTouchEvent(ev) ||
140                 super.onInterceptTouchEvent(ev);
141     }
142 
143     @Override
onTouchEvent(MotionEvent ev)144     public boolean onTouchEvent(MotionEvent ev) {
145         if (DEBUG) Log.v(TAG, "onTouchEvent()");
146         if (DEBUG) logLayoutTransition();
147 
148         return mSwipeHelper.onTouchEvent(ev) ||
149                 super.onTouchEvent(ev);
150     }
151 
canChildBeDismissed(View v)152     public boolean canChildBeDismissed(View v) {
153         final View veto = v.findViewById(R.id.veto);
154         return (veto != null && veto.getVisibility() != View.GONE);
155     }
156 
canChildBeExpanded(View v)157     public boolean canChildBeExpanded(View v) {
158         return NotificationData.getIsExpandable(v);
159     }
160 
setUserExpandedChild(View v, boolean userExpanded)161     public boolean setUserExpandedChild(View v, boolean userExpanded) {
162         return NotificationData.setUserExpanded(v, userExpanded);
163     }
164 
setUserLockedChild(View v, boolean userLocked)165     public boolean setUserLockedChild(View v, boolean userLocked) {
166         return NotificationData.setUserLocked(v, userLocked);
167     }
168 
onChildDismissed(View v)169     public void onChildDismissed(View v) {
170         if (DEBUG) Slog.v(TAG, "onChildDismissed: " + v + " mRemoveViews=" + mRemoveViews);
171         final View veto = v.findViewById(R.id.veto);
172         if (veto != null && veto.getVisibility() != View.GONE && mRemoveViews) {
173             veto.performClick();
174         }
175     }
176 
onBeginDrag(View v)177     public void onBeginDrag(View v) {
178         // We need to prevent the surrounding ScrollView from intercepting us now;
179         // the scroll position will be locked while we swipe
180         requestDisallowInterceptTouchEvent(true);
181     }
182 
onDragCancelled(View v)183     public void onDragCancelled(View v) {
184     }
185 
getChildAtPosition(MotionEvent ev)186     public View getChildAtPosition(MotionEvent ev) {
187         return getChildAtPosition(ev.getX(), ev.getY());
188     }
189 
getChildAtRawPosition(float touchX, float touchY)190     public View getChildAtRawPosition(float touchX, float touchY) {
191         int[] location = new int[2];
192         getLocationOnScreen(location);
193         return getChildAtPosition((float) (touchX - location[0]), (float) (touchY - location[1]));
194     }
195 
getChildAtPosition(float touchX, float touchY)196     public View getChildAtPosition(float touchX, float touchY) {
197         // find the view under the pointer, accounting for GONE views
198         final int count = getChildCount();
199         int y = 0;
200         int childIdx = 0;
201         View slidingChild;
202         for (; childIdx < count; childIdx++) {
203             slidingChild = getChildAt(childIdx);
204             if (slidingChild.getVisibility() == GONE) {
205                 continue;
206             }
207             y += slidingChild.getMeasuredHeight();
208             if (touchY < y) return slidingChild;
209         }
210         return null;
211     }
212 
getChildContentView(View v)213     public View getChildContentView(View v) {
214         return v;
215     }
216 
217     @Override
onConfigurationChanged(Configuration newConfig)218     protected void onConfigurationChanged(Configuration newConfig) {
219         super.onConfigurationChanged(newConfig);
220         float densityScale = getResources().getDisplayMetrics().density;
221         mSwipeHelper.setDensityScale(densityScale);
222         float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
223         mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
224     }
225 
226 
227     /**
228      * Sets a flag to tell us whether to actually remove views. Removal is delayed by setting this
229      * to false during some animations to smooth out performance. Callers should restore the
230      * flag to true after the animation is done, and then they should make sure that the views
231      * get removed properly.
232      */
setViewRemoval(boolean removeViews)233     public void setViewRemoval(boolean removeViews) {
234         if (DEBUG) Slog.v(TAG, "setViewRemoval: " + removeViews);
235         mRemoveViews = removeViews;
236     }
237 
238     // Suppress layout transitions for a little while.
setLayoutTransitionsEnabled(boolean b)239     public void setLayoutTransitionsEnabled(boolean b) {
240         if (b) {
241             setLayoutTransition(mRealLayoutTransition);
242         } else {
243             if (mRealLayoutTransition.isRunning()) {
244                 mRealLayoutTransition.cancel();
245             }
246             setLayoutTransition(null);
247         }
248     }
249 
dismissRowAnimated(View child)250     public void dismissRowAnimated(View child) {
251         dismissRowAnimated(child, 0);
252     }
253 
dismissRowAnimated(View child, int vel)254     public void dismissRowAnimated(View child, int vel) {
255         mSwipeHelper.dismissChild(child, vel);
256     }
257 
258     @Override
onFinishInflate()259     public void onFinishInflate() {
260         super.onFinishInflate();
261         if (DEBUG) setWillNotDraw(false);
262     }
263 
264     @Override
onDraw(android.graphics.Canvas c)265     public void onDraw(android.graphics.Canvas c) {
266         super.onDraw(c);
267         if (DEBUG) logLayoutTransition();
268         if (DEBUG) {
269             //Slog.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: "
270             //        + getMeasuredHeight() + "px");
271             c.save();
272             c.clipRect(6, 6, c.getWidth() - 6, getMeasuredHeight() - 6,
273                     android.graphics.Region.Op.DIFFERENCE);
274             c.drawColor(0xFFFF8000);
275             c.restore();
276         }
277     }
278 
279     @Override
onSizeChanged(int w, int h, int oldw, int oldh)280     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
281         if (mOnSizeChangedListener != null) {
282             mOnSizeChangedListener.onSizeChanged(this, w, h, oldw, oldh);
283         }
284     }
285 }
286