Home | History | Annotate | Download | only in policy
      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.LayoutTransition;
     20 import android.animation.ValueAnimator;
     21 import android.content.Context;
     22 import android.content.res.Configuration;
     23 import android.graphics.Rect;
     24 import android.util.AttributeSet;
     25 import android.util.Log;
     26 import android.view.MotionEvent;
     27 import android.view.View;
     28 import android.view.ViewConfiguration;
     29 import android.view.ViewGroup;
     30 import android.widget.LinearLayout;
     31 
     32 import com.android.systemui.ExpandHelper;
     33 import com.android.systemui.R;
     34 import com.android.systemui.SwipeHelper;
     35 import com.android.systemui.statusbar.ExpandableNotificationRow;
     36 import com.android.systemui.statusbar.NotificationData;
     37 
     38 import java.util.HashMap;
     39 
     40 public class NotificationRowLayout
     41         extends LinearLayout
     42         implements SwipeHelper.Callback, ExpandHelper.Callback
     43 {
     44     private static final String TAG = "NotificationRowLayout";
     45     private static final boolean DEBUG = false;
     46     private static final boolean SLOW_ANIMATIONS = DEBUG;
     47 
     48     private static final int APPEAR_ANIM_LEN = SLOW_ANIMATIONS ? 5000 : 250;
     49     private static final int DISAPPEAR_ANIM_LEN = APPEAR_ANIM_LEN;
     50 
     51     boolean mAnimateBounds = true;
     52 
     53     Rect mTmpRect = new Rect();
     54 
     55     HashMap<View, ValueAnimator> mAppearingViews = new HashMap<View, ValueAnimator>();
     56     HashMap<View, ValueAnimator> mDisappearingViews = new HashMap<View, ValueAnimator>();
     57 
     58     private SwipeHelper mSwipeHelper;
     59 
     60     private OnSizeChangedListener mOnSizeChangedListener;
     61 
     62     // Flag set during notification removal animation to avoid causing too much work until
     63     // animation is done
     64     boolean mRemoveViews = true;
     65 
     66     private LayoutTransition mRealLayoutTransition;
     67 
     68     public NotificationRowLayout(Context context, AttributeSet attrs) {
     69         this(context, attrs, 0);
     70     }
     71 
     72     public NotificationRowLayout(Context context, AttributeSet attrs, int defStyle) {
     73         super(context, attrs, defStyle);
     74 
     75         mRealLayoutTransition = new LayoutTransition();
     76         mRealLayoutTransition.setAnimateParentHierarchy(true);
     77         setLayoutTransitionsEnabled(true);
     78 
     79         setOrientation(LinearLayout.VERTICAL);
     80 
     81         if (DEBUG) {
     82             setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
     83                 @Override
     84                 public void onChildViewAdded(View parent, View child) {
     85                     Log.d(TAG, "view added: " + child + "; new count: " + getChildCount());
     86                 }
     87                 @Override
     88                 public void onChildViewRemoved(View parent, View child) {
     89                     Log.d(TAG, "view removed: " + child + "; new count: " + (getChildCount() - 1));
     90                 }
     91             });
     92 
     93             setBackgroundColor(0x80FF8000);
     94         }
     95 
     96         float densityScale = getResources().getDisplayMetrics().density;
     97         float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
     98         mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
     99     }
    100 
    101     public void setLongPressListener(View.OnLongClickListener listener) {
    102         mSwipeHelper.setLongPressListener(listener);
    103     }
    104 
    105     public void setOnSizeChangedListener(OnSizeChangedListener l) {
    106         mOnSizeChangedListener = l;
    107     }
    108 
    109     @Override
    110     public void onWindowFocusChanged(boolean hasWindowFocus) {
    111         super.onWindowFocusChanged(hasWindowFocus);
    112         if (!hasWindowFocus) {
    113             mSwipeHelper.removeLongPressCallback();
    114         }
    115     }
    116 
    117     public void setAnimateBounds(boolean anim) {
    118         mAnimateBounds = anim;
    119     }
    120 
    121     private void logLayoutTransition() {
    122         Log.v(TAG, "layout " +
    123               (mRealLayoutTransition.isChangingLayout() ? "is " : "is not ") +
    124               "in transition and animations " +
    125               (mRealLayoutTransition.isRunning() ? "are " : "are not ") +
    126               "running.");
    127     }
    128 
    129     @Override
    130     public boolean onInterceptTouchEvent(MotionEvent ev) {
    131         if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
    132         if (DEBUG) logLayoutTransition();
    133 
    134         return mSwipeHelper.onInterceptTouchEvent(ev) ||
    135                 super.onInterceptTouchEvent(ev);
    136     }
    137 
    138     @Override
    139     public boolean onTouchEvent(MotionEvent ev) {
    140         if (DEBUG) Log.v(TAG, "onTouchEvent()");
    141         if (DEBUG) logLayoutTransition();
    142 
    143         return mSwipeHelper.onTouchEvent(ev) ||
    144                 super.onTouchEvent(ev);
    145     }
    146 
    147     public boolean canChildBeDismissed(View v) {
    148         final View veto = v.findViewById(R.id.veto);
    149         return (veto != null && veto.getVisibility() != View.GONE);
    150     }
    151 
    152     public boolean canChildBeExpanded(View v) {
    153         return v instanceof ExpandableNotificationRow
    154                 && ((ExpandableNotificationRow) v).isExpandable();
    155     }
    156 
    157     public void setUserExpandedChild(View v, boolean userExpanded) {
    158         if (v instanceof ExpandableNotificationRow) {
    159             ((ExpandableNotificationRow) v).setUserExpanded(userExpanded);
    160         }
    161     }
    162 
    163     public void setUserLockedChild(View v, boolean userLocked) {
    164         if (v instanceof ExpandableNotificationRow) {
    165             ((ExpandableNotificationRow) v).setUserLocked(userLocked);
    166         }
    167     }
    168 
    169     public void onChildDismissed(View v) {
    170         if (DEBUG) Log.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 
    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 
    183     public void onDragCancelled(View v) {
    184     }
    185 
    186     public View getChildAtPosition(MotionEvent ev) {
    187         return getChildAtPosition(ev.getX(), ev.getY());
    188     }
    189 
    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 
    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 
    213     public View getChildContentView(View v) {
    214         return v;
    215     }
    216 
    217     @Override
    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      */
    233     public void setViewRemoval(boolean removeViews) {
    234         if (DEBUG) Log.v(TAG, "setViewRemoval: " + removeViews);
    235         mRemoveViews = removeViews;
    236     }
    237 
    238     // Suppress layout transitions for a little while.
    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 
    250     public void dismissRowAnimated(View child) {
    251         dismissRowAnimated(child, 0);
    252     }
    253 
    254     public void dismissRowAnimated(View child, int vel) {
    255         mSwipeHelper.dismissChild(child, vel);
    256     }
    257 
    258     @Override
    259     public void onFinishInflate() {
    260         super.onFinishInflate();
    261         if (DEBUG) setWillNotDraw(false);
    262     }
    263 
    264     @Override
    265     public void onDraw(android.graphics.Canvas c) {
    266         super.onDraw(c);
    267         if (DEBUG) logLayoutTransition();
    268         if (DEBUG) {
    269             //Log.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
    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