Home | History | Annotate | Download | only in sensors
      1 /*
      2  * Copyright (C) 2013 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.cts.verifier.sensors;
     18 
     19 import android.content.Context;
     20 import android.graphics.Canvas;
     21 import android.graphics.Color;
     22 import android.graphics.Paint;
     23 import android.graphics.PorterDuff;
     24 import android.graphics.PorterDuffXfermode;
     25 import android.graphics.RectF;
     26 import android.hardware.SensorManager;
     27 import android.util.AttributeSet;
     28 import android.util.Log;
     29 import android.view.View;
     30 
     31 /**
     32  * A view class that draws the user prompt
     33  *
     34  * The following piece of code should show how to use this view.
     35  *
     36  *  public void testUI()  {
     37  *     final int MAX_TILT_ANGLE = 70; // +/- 70
     38  *
     39  *     final int TILT_ANGLE_STEP = 5; // 5 degree(s) per step
     40  *     final int YAW_ANGLE_STEP = 10; // 10 degree(s) per step
     41  *
     42  *     RangeCoveredRegister xCovered, yCovered, zCovered;
     43  *     xCovered = new RangeCoveredRegister(-MAX_TILT_ANGLE, +MAX_TILT_ANGLE, TILT_ANGLE_STEP);
     44  *
     45  *     yCovered = new RangeCoveredRegister(-MAX_TILT_ANGLE, +MAX_TILT_ANGLE, TILT_ANGLE_STEP);
     46  *     zCovered = new RangeCoveredRegister(YAW_ANGLE_STEP);
     47  *
     48  *     xCovered.update(40);
     49  *     xCovered.update(-40);
     50  *     xCovered.update(12);
     51  *
     52  *     yCovered.update(50);
     53  *     yCovered.update(-51);
     54  *
     55  *     zCovered.update(150);
     56  *     zCovered.update(42);
     57  *
     58  *     setDataProvider(xCovered, yCovered, zCovered);
     59  *     enableAxis(RVCVRecordActivity.AXIS_ALL); //debug mode, show all three axis
     60  * }
     61  */
     62 public class MotionIndicatorView extends View {
     63     private final String TAG = "MotionIndicatorView";
     64     private final boolean LOCAL_LOGV = false;
     65 
     66     private Paint mCursorPaint;
     67     private Paint mLimitPaint;
     68     private Paint mCoveredPaint;
     69     private Paint mRangePaint;
     70     private Paint mEraserPaint;
     71 
     72     // UI settings
     73     private final int XBAR_WIDTH = 50;
     74     private final int XBAR_MARGIN = 50;
     75     private final int XBAR_CURSOR_ADD = 20;
     76 
     77     private final int YBAR_WIDTH = 50;
     78     private final int YBAR_MARGIN = 50;
     79     private final int YBAR_CURSOR_ADD = 20;
     80 
     81     private final int ZRING_WIDTH = 50;
     82     private final int ZRING_CURSOR_ADD = 30;
     83 
     84 
     85     private int mXSize, mYSize;
     86     private RectF mZBoundOut, mZBoundOut2, mZBoundIn, mZBoundIn2;
     87 
     88     private RangeCoveredRegister mXCovered, mYCovered, mZCovered;
     89 
     90     private boolean mXEnabled, mYEnabled, mZEnabled;
     91 
     92     /**
     93      * Constructor
     94      * @param context
     95      */
     96     public MotionIndicatorView(Context context) {
     97         super(context);
     98         init();
     99     }
    100 
    101     /**
    102      * Constructor
    103      * @param context Application context
    104      * @param attrs
    105      */
    106     public MotionIndicatorView(Context context, AttributeSet attrs) {
    107         super(context, attrs);
    108         init();
    109     }
    110 
    111     /**
    112      * Initialize the Paint objects
    113      */
    114     private void init() {
    115 
    116         mCursorPaint = new Paint();
    117         mCursorPaint.setColor(Color.BLUE);
    118 
    119         mLimitPaint = new Paint();
    120         mLimitPaint.setColor(Color.YELLOW);
    121 
    122         mCoveredPaint = new Paint();
    123         mCoveredPaint.setColor(Color.CYAN);
    124 
    125         mRangePaint = new Paint();
    126         mRangePaint.setColor(Color.DKGRAY);
    127 
    128         mEraserPaint = new Paint();
    129         mEraserPaint.setColor(Color.TRANSPARENT);
    130         // ensure the erasing effect
    131         mEraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
    132     }
    133 
    134     /**
    135      * Connect the view to certain data provider objects
    136      * @param x Data provider for x direction tilt angle
    137      * @param y Data provider for y direction tilt angle
    138      * @param z Data provider for z rotation
    139      */
    140     public void setDataProvider(RangeCoveredRegister x,
    141                                 RangeCoveredRegister y,
    142                                 RangeCoveredRegister z)    {
    143         mXCovered = x;
    144         mYCovered = y;
    145         mZCovered = z;
    146     }
    147 
    148     /**
    149      * Set the active axis for display
    150      *
    151      * @param axis AXIS_X, AXIS_Y, AXIS_Z for x, y, z axis indicators, or AXIS_ALL for all three.
    152      */
    153     public void enableAxis(int axis)  {
    154         mXEnabled = mYEnabled = mZEnabled = false;
    155 
    156         switch(axis)
    157         {
    158             case SensorManager.AXIS_X:
    159                 mXEnabled = true;
    160                 break;
    161             case SensorManager.AXIS_Y:
    162                 mYEnabled = true;
    163                 break;
    164             case SensorManager.AXIS_Z:
    165                 mZEnabled = true;
    166                 break;
    167             case RVCVRecordActivity.AXIS_ALL:
    168                 mXEnabled = mYEnabled = mZEnabled = true;
    169         }
    170     }
    171 
    172     /**
    173      * Doing some pre-calculation that only changes when view dimensions are changed.
    174      * @param w
    175      * @param h
    176      * @param oldw
    177      * @param oldh
    178      */
    179     @Override
    180     protected void onSizeChanged (int w, int h, int oldw, int oldh) {
    181         mXSize = w;
    182         mYSize = h;
    183 
    184         mZBoundOut = new RectF(w/2-w/2.5f, h/2-w/2.5f, w/2+w/2.5f, h/2+w/2.5f);
    185         mZBoundOut2 = new RectF(
    186                 w/2-w/2.5f-ZRING_CURSOR_ADD, h/2-w/2.5f-ZRING_CURSOR_ADD,
    187                 w/2+w/2.5f+ZRING_CURSOR_ADD, h/2+w/2.5f+ZRING_CURSOR_ADD);
    188         mZBoundIn = new RectF(
    189                 w/2-w/2.5f+ZRING_WIDTH, h/2-w/2.5f+ZRING_WIDTH,
    190                 w/2+w/2.5f-ZRING_WIDTH, h/2+w/2.5f-ZRING_WIDTH);
    191         mZBoundIn2 = new RectF(
    192                 w/2-w/2.5f+ZRING_WIDTH+ZRING_CURSOR_ADD, h/2-w/2.5f+ZRING_WIDTH+ZRING_CURSOR_ADD,
    193                 w/2+w/2.5f-ZRING_WIDTH-ZRING_CURSOR_ADD, h/2+w/2.5f-ZRING_WIDTH-ZRING_CURSOR_ADD);
    194 
    195         if (LOCAL_LOGV) Log.v(TAG, "New view size = ("+w+", "+h+")");
    196     }
    197 
    198     /**
    199      * Draw UI depends on the selected axis and registered value
    200      *
    201      * @param canvas the canvas to draw on
    202      */
    203     @Override
    204     protected void onDraw(Canvas canvas) {
    205         super.onDraw(canvas);
    206         int i,t;
    207 
    208         Paint p = new Paint();
    209         p.setColor(Color.YELLOW);
    210         canvas.drawRect(10,10, 50, 50, p);
    211 
    212         if (mXEnabled && mXCovered != null) {
    213             int xNStep = mXCovered.getNSteps() + 4; // two on each side as a buffer
    214             int xStepSize = mXSize * 3/4 / xNStep;
    215             int xLeft = mXSize * 1/8 + (mXSize * 3/4 % xNStep)/2;
    216 
    217             // base bar
    218             canvas.drawRect(xLeft, XBAR_MARGIN,
    219                     xLeft+xStepSize*xNStep-1, XBAR_WIDTH+XBAR_MARGIN, mRangePaint);
    220 
    221             // covered range
    222             for (i=0; i<mXCovered.getNSteps(); ++i) {
    223                 if (mXCovered.isCovered(i)) {
    224                     canvas.drawRect(
    225                             xLeft+xStepSize*(i+2), XBAR_MARGIN,
    226                             xLeft+xStepSize*(i+3)-1, XBAR_WIDTH + XBAR_MARGIN,
    227                             mCoveredPaint);
    228                 }
    229             }
    230 
    231             // limit
    232             canvas.drawRect(xLeft+xStepSize*2-4, XBAR_MARGIN,
    233                     xLeft+xStepSize*2+3, XBAR_WIDTH+XBAR_MARGIN, mLimitPaint);
    234             canvas.drawRect(xLeft+xStepSize*(xNStep-2)-4, XBAR_MARGIN,
    235                     xLeft+xStepSize*(xNStep-2)+3, XBAR_WIDTH+XBAR_MARGIN, mLimitPaint);
    236 
    237             // cursor
    238             t = (int)(xLeft+xStepSize*(mXCovered.getLastValue()+2));
    239             canvas.drawRect(t-4, XBAR_MARGIN-XBAR_CURSOR_ADD, t+3,
    240                     XBAR_WIDTH+XBAR_MARGIN+XBAR_CURSOR_ADD, mCursorPaint);
    241         }
    242         if (mYEnabled && mYCovered != null) {
    243             int yNStep = mYCovered.getNSteps() + 4; // two on each side as a buffer
    244             int yStepSize = mYSize * 3/4 / yNStep;
    245             int yLeft = mYSize * 1/8 + (mYSize * 3/4 % yNStep)/2;
    246 
    247             // base bar
    248             canvas.drawRect(YBAR_MARGIN, yLeft,
    249                     YBAR_WIDTH+YBAR_MARGIN, yLeft+yStepSize*yNStep-1, mRangePaint);
    250 
    251             // covered range
    252             for (i=0; i<mYCovered.getNSteps(); ++i) {
    253                 if (mYCovered.isCovered(i)) {
    254                     canvas.drawRect(
    255                             YBAR_MARGIN, yLeft+yStepSize*(i+2),
    256                             YBAR_WIDTH + YBAR_MARGIN, yLeft+yStepSize*(i+3)-1,
    257                             mCoveredPaint);
    258                 }
    259             }
    260 
    261             // limit
    262             canvas.drawRect(YBAR_MARGIN, yLeft + yStepSize * 2 - 4,
    263                     YBAR_WIDTH + YBAR_MARGIN, yLeft + yStepSize * 2 + 3, mLimitPaint);
    264             canvas.drawRect(YBAR_MARGIN, yLeft + yStepSize * (yNStep - 2) - 4,
    265                     YBAR_WIDTH + YBAR_MARGIN, yLeft + yStepSize * (yNStep - 2) + 3, mLimitPaint);
    266 
    267             // cursor
    268             t = (int)(yLeft+yStepSize*(mYCovered.getLastValue()+2));
    269             canvas.drawRect( YBAR_MARGIN-YBAR_CURSOR_ADD, t-4,
    270                     YBAR_WIDTH+YBAR_MARGIN+YBAR_CURSOR_ADD, t+3, mCursorPaint);
    271         }
    272 
    273         if (mZEnabled && mZCovered != null) {
    274             float stepSize  = 360.0f/mZCovered.getNSteps();
    275 
    276             // base bar
    277             canvas.drawArc(mZBoundOut,0, 360, true, mRangePaint);
    278 
    279             // covered range
    280             for (i=0; i<mZCovered.getNSteps(); ++i) {
    281                 if (mZCovered.isCovered(i)) {
    282                     canvas.drawArc(mZBoundOut,i*stepSize-0.2f, stepSize+0.4f,
    283                             true, mCoveredPaint);
    284                 }
    285             }
    286             // clear center
    287             canvas.drawArc(mZBoundIn, 0, 360, true, mEraserPaint);
    288             // cursor
    289             canvas.drawArc(mZBoundOut2, mZCovered.getLastValue()*stepSize- 1, 2,
    290                     true, mCursorPaint);
    291             canvas.drawArc(mZBoundIn2, mZCovered.getLastValue()*stepSize-1.5f, 3,
    292                     true, mEraserPaint);
    293         }
    294     }
    295 }
    296 
    297 /**
    298  *  A range register class for the RVCVRecord Activity
    299  */
    300 class RangeCoveredRegister {
    301     enum MODE {
    302         LINEAR,
    303         ROTATE2D
    304     }
    305 
    306     private boolean[] mCovered;
    307     private MODE mMode;
    308     private int mStep;
    309     private int mLow, mHigh;
    310     private int mLastData;
    311 
    312     // high is not inclusive
    313     RangeCoveredRegister(int low, int high, int step) {
    314         mMode = MODE.LINEAR;
    315         mStep = step;
    316         mLow = low;
    317         mHigh = high;
    318         init();
    319     }
    320 
    321     RangeCoveredRegister(int step) {
    322         mMode = MODE.ROTATE2D;
    323         mStep = step;
    324         mLow = 0;
    325         mHigh = 360;
    326         init();
    327     }
    328 
    329     private void init() {
    330         if (mMode == MODE.LINEAR) {
    331             mCovered = new boolean[(mHigh-mLow)/mStep];
    332         }else {
    333             mCovered = new boolean[360/mStep];
    334         }
    335     }
    336 
    337     /**
    338      * Test if the range specified by (low, high) is covered.
    339      *
    340      * If it is LINEAR mode, the range will be quantized to nearest step boundary. If it is the
    341      * ROTATE2D mode, it is the same as isFullyCovered().
    342      *
    343      * @param low The low end of the range.
    344      * @param high The high end of the range.
    345      * @return if the specified range is covered, return true; otherwise false.
    346      */
    347     public boolean isRangeCovered(int low, int high) {
    348         if (mMode == MODE.LINEAR) {
    349             int iLow = Math.max(Math.round((low - mLow) / mStep), 0);
    350             int iHigh = Math.min(Math.round((high - mLow) / mStep), mCovered.length-1);
    351 
    352             for (int i = iLow; i <= iHigh; ++i) {
    353                 if (!mCovered[i]) {
    354                     return false;
    355                 }
    356             }
    357             return true;
    358 
    359         } else {
    360             return isFullyCovered();
    361         }
    362     }
    363 
    364     /**
    365      * Test if the range defined is fully covered.
    366      *
    367      * @return if the range is fully covered, return true; otherwise false.
    368      */
    369     public boolean isFullyCovered() {
    370         for (boolean i : mCovered) {
    371             if (!i) return false;
    372         }
    373         return true;
    374     }
    375 
    376     /**
    377      * Test if a specific step is covered.
    378      *
    379      * @param i the step number
    380      * @return if the step specified is covered, return true; otherwise false.
    381      */
    382     public boolean isCovered(int i) {
    383         return mCovered[i];
    384     }
    385 
    386     /**
    387      *
    388      *
    389      * @param data
    390      * @return if this update changes the status of
    391      */
    392     public boolean update(int data) {
    393         mLastData = data;
    394 
    395         if (mMode == MODE.ROTATE2D) {
    396             data %= 360;
    397         }
    398 
    399         int iStep = (data - mLow)/mStep;
    400 
    401         if (iStep>=0 && iStep<getNSteps()) {
    402             // only record valid data
    403             mLastData = data;
    404 
    405             if (mCovered[iStep]) {
    406                 return false;
    407             } else {
    408                 mCovered[iStep] = true;
    409                 return true;
    410             }
    411         }
    412         return false;
    413     }
    414 
    415     /**
    416      * Get the number of steps in this register
    417      *
    418      * @return The number of steps in this register
    419      */
    420     public int getNSteps() {
    421         //if (mCovered == null) {
    422         //return 0;
    423         //}
    424         return mCovered.length;
    425     }
    426 
    427     /**
    428      * Get the last value updated
    429      *
    430      * @return The last value updated
    431      */
    432     public float getLastValue() {
    433         // ensure float division
    434         return ((float)(mLastData - mLow))/mStep;
    435     }
    436 }
    437