Home | History | Annotate | Download | only in policy
      1 /*
      2  * Copyright (C) 2012 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.ObjectAnimator;
     20 import android.content.Context;
     21 import android.content.res.TypedArray;
     22 import android.graphics.Canvas;
     23 import android.os.SystemClock;
     24 import android.util.AttributeSet;
     25 import android.util.Slog;
     26 import android.view.MotionEvent;
     27 import android.view.View;
     28 
     29 import com.android.systemui.R;
     30 
     31 public class DeadZone extends View {
     32     public static final String TAG = "DeadZone";
     33 
     34     public static final boolean DEBUG = false;
     35     public static final int HORIZONTAL = 0;
     36     public static final int VERTICAL = 1;
     37 
     38     private static final boolean CHATTY = true; // print to logcat when we eat a click
     39 
     40     private boolean mShouldFlash;
     41     private float mFlashFrac = 0f;
     42 
     43     private int mSizeMax;
     44     private int mSizeMin;
     45     // Upon activity elsewhere in the UI, the dead zone will hold steady for
     46     // mHold ms, then move back over the course of mDecay ms
     47     private int mHold, mDecay;
     48     private boolean mVertical;
     49     private long mLastPokeTime;
     50 
     51     private final Runnable mDebugFlash = new Runnable() {
     52         @Override
     53         public void run() {
     54             ObjectAnimator.ofFloat(DeadZone.this, "flash", 1f, 0f).setDuration(150).start();
     55         }
     56     };
     57 
     58     public DeadZone(Context context, AttributeSet attrs) {
     59         this(context, attrs, 0);
     60     }
     61 
     62     public DeadZone(Context context, AttributeSet attrs, int defStyle) {
     63         super(context, attrs);
     64 
     65         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DeadZone,
     66                 defStyle, 0);
     67 
     68         mHold = a.getInteger(R.styleable.DeadZone_holdTime, 0);
     69         mDecay = a.getInteger(R.styleable.DeadZone_decayTime, 0);
     70 
     71         mSizeMin = a.getDimensionPixelSize(R.styleable.DeadZone_minSize, 0);
     72         mSizeMax = a.getDimensionPixelSize(R.styleable.DeadZone_maxSize, 0);
     73 
     74         int index = a.getInt(R.styleable.DeadZone_orientation, -1);
     75         mVertical = (index == VERTICAL);
     76 
     77         if (DEBUG)
     78             Slog.v(TAG, this + " size=[" + mSizeMin + "-" + mSizeMax + "] hold=" + mHold
     79                     + (mVertical ? " vertical" : " horizontal"));
     80 
     81         setFlashOnTouchCapture(context.getResources().getBoolean(R.bool.config_dead_zone_flash));
     82     }
     83 
     84     static float lerp(float a, float b, float f) {
     85         return (b - a) * f + a;
     86     }
     87 
     88     private float getSize(long now) {
     89         if (mSizeMax == 0)
     90             return 0;
     91         long dt = (now - mLastPokeTime);
     92         if (dt > mHold + mDecay)
     93             return mSizeMin;
     94         if (dt < mHold)
     95             return mSizeMax;
     96         return (int) lerp(mSizeMax, mSizeMin, (float) (dt - mHold) / mDecay);
     97     }
     98 
     99     public void setFlashOnTouchCapture(boolean dbg) {
    100         mShouldFlash = dbg;
    101         mFlashFrac = 0f;
    102         postInvalidate();
    103     }
    104 
    105     // I made you a touch event...
    106     @Override
    107     public boolean onTouchEvent(MotionEvent event) {
    108         if (DEBUG) {
    109             Slog.v(TAG, this + " onTouch: " + MotionEvent.actionToString(event.getAction()));
    110         }
    111 
    112         final int action = event.getAction();
    113         if (action == MotionEvent.ACTION_OUTSIDE) {
    114             poke(event);
    115         } else if (action == MotionEvent.ACTION_DOWN) {
    116             if (DEBUG) {
    117                 Slog.v(TAG, this + " ACTION_DOWN: " + event.getX() + "," + event.getY());
    118             }
    119             int size = (int) getSize(event.getEventTime());
    120             if ((mVertical && event.getX() < size) || event.getY() < size) {
    121                 if (CHATTY) {
    122                     Slog.v(TAG, "consuming errant click: (" + event.getX() + "," + event.getY() + ")");
    123                 }
    124                 if (mShouldFlash) {
    125                     post(mDebugFlash);
    126                     postInvalidate();
    127                 }
    128                 return true; // ...but I eated it
    129             }
    130         }
    131         return false;
    132     }
    133 
    134     public void poke(MotionEvent event) {
    135         mLastPokeTime = event.getEventTime();
    136         if (DEBUG)
    137             Slog.v(TAG, "poked! size=" + getSize(mLastPokeTime));
    138         if (mShouldFlash) postInvalidate();
    139     }
    140 
    141     public void setFlash(float f) {
    142         mFlashFrac = f;
    143         postInvalidate();
    144     }
    145 
    146     public float getFlash() {
    147         return mFlashFrac;
    148     }
    149 
    150     @Override
    151     public void onDraw(Canvas can) {
    152         if (!mShouldFlash || mFlashFrac <= 0f) {
    153             return;
    154         }
    155 
    156         final int size = (int) getSize(SystemClock.uptimeMillis());
    157         can.clipRect(0, 0, mVertical ? size : can.getWidth(), mVertical ? can.getHeight() : size);
    158         final float frac = DEBUG ? (mFlashFrac - 0.5f) + 0.5f : mFlashFrac;
    159         can.drawARGB((int) (frac * 0xFF), 0xDD, 0xEE, 0xAA);
    160 
    161         if (DEBUG && size > mSizeMin)
    162             // crazy aggressive redrawing here, for debugging only
    163             postInvalidateDelayed(100);
    164     }
    165 }
    166