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.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 
     73     public NotificationRowLayout(Context context, AttributeSet attrs) {
     74         this(context, attrs, 0);
     75     }
     76 
     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 
    106     public void setLongPressListener(View.OnLongClickListener listener) {
    107         mSwipeHelper.setLongPressListener(listener);
    108     }
    109 
    110     public void setOnSizeChangedListener(OnSizeChangedListener l) {
    111         mOnSizeChangedListener = l;
    112     }
    113 
    114     @Override
    115     public void onWindowFocusChanged(boolean hasWindowFocus) {
    116         super.onWindowFocusChanged(hasWindowFocus);
    117         if (!hasWindowFocus) {
    118             mSwipeHelper.removeLongPressCallback();
    119         }
    120     }
    121 
    122     public void setAnimateBounds(boolean anim) {
    123         mAnimateBounds = anim;
    124     }
    125 
    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
    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
    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 
    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 
    157     public boolean canChildBeExpanded(View v) {
    158         return NotificationData.getIsExpandable(v);
    159     }
    160 
    161     public boolean setUserExpandedChild(View v, boolean userExpanded) {
    162         return NotificationData.setUserExpanded(v, userExpanded);
    163     }
    164 
    165     public boolean setUserLockedChild(View v, boolean userLocked) {
    166         return NotificationData.setUserLocked(v, userLocked);
    167     }
    168 
    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 
    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) Slog.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             //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
    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