1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package com.android.tools.sdkcontroller.views; 18 19 import java.io.InputStream; 20 21 import android.content.Context; 22 import android.graphics.Bitmap; 23 import android.graphics.BitmapFactory; 24 import android.graphics.Canvas; 25 import android.graphics.Matrix; 26 import android.graphics.Paint; 27 import android.util.AttributeSet; 28 import android.util.Log; 29 import android.view.MotionEvent; 30 import android.view.View; 31 32 /** 33 * Implements a main view for the application providing multi-touch emulation. 34 */ 35 public class MultiTouchView extends View { 36 /** Tag for logging messages. */ 37 private static final String TAG = MultiTouchView.class.getSimpleName(); 38 /** 39 * Back-end bitmap. Initialized in onSizeChanged(), updated in 40 * onTouchEvent() and drawn in onDraw(). 41 */ 42 private Bitmap mBitmap; 43 /** Default Paint instance for drawing the bitmap. */ 44 private final Paint mPaint = new Paint(); 45 /** Canvas instance for this view. */ 46 private Canvas mCanvas; 47 /** Emulator screen width to this view width ratio. */ 48 private float mDx = 1; 49 /** Emulator screen height to this view height ratio. */ 50 private float mDy = 1; 51 /** 52 * Flags whether or not image received from the emulator should be rotated. 53 * Rotation is required when display orientation state of the emulator and 54 * the device doesn't match. 55 */ 56 private boolean mRotateDisplay; 57 /** Base matrix that keep emulator->device display scaling */ 58 private Matrix mBaseMatrix = new Matrix(); 59 /** Matrix that is used to draw emulator's screen on the device. */ 60 private Matrix mDrawMatrix = new Matrix(); 61 62 /** 63 * Simple constructor to use when creating a view from code. 64 * 65 * @see View#View(Context) 66 */ MultiTouchView(Context context)67 public MultiTouchView(Context context) { 68 this(context, null); 69 } 70 71 /** 72 * Constructor that is called when inflating a view from XML. 73 * 74 * @see View#View(Context, AttributeSet) 75 */ MultiTouchView(Context context, AttributeSet attrs)76 public MultiTouchView(Context context, AttributeSet attrs) { 77 this(context, attrs, 0); 78 } 79 80 /** 81 * Perform inflation from XML and apply a class-specific base style. 82 * 83 * @see View#View(Context, AttributeSet, int) 84 */ MultiTouchView(Context context, AttributeSet attrs, int defStyle)85 public MultiTouchView(Context context, AttributeSet attrs, int defStyle) { 86 super(context, attrs, defStyle); 87 88 // TODO Add constructor-time code here. 89 } 90 91 @Override onSizeChanged(int w, int h, int oldw, int oldh)92 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 93 super.onSizeChanged(w, h, oldw, oldh); 94 95 mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 96 mCanvas = new Canvas(mBitmap); 97 } 98 99 @Override onDraw(Canvas canvas)100 protected void onDraw(Canvas canvas) { 101 super.onDraw(canvas); 102 // Just draw the back-end bitmap without zooming or scaling. 103 if (mBitmap != null) { 104 canvas.drawBitmap(mBitmap, 0, 0, null); 105 } 106 } 107 108 /** 109 * Sets emulator screen width and height to this view width and height 110 * ratio. 111 * 112 * @param dx Emulator screen width to this view width ratio. 113 * @param dy Emulator screen height to this view height ratio. 114 * @param rotateDisplay Flags whether image received from the emulator 115 * should be rotated when drawn on the device. 116 */ setDxDy(float dx, float dy, boolean rotateDisplay)117 public void setDxDy(float dx, float dy, boolean rotateDisplay) { 118 mDx = dx; 119 mDy = dy; 120 mRotateDisplay = rotateDisplay; 121 122 mBaseMatrix.setScale(dx, dy); 123 if (mRotateDisplay) { 124 mBaseMatrix.postRotate(90); 125 mBaseMatrix.postTranslate(getWidth(), 0); 126 } 127 } 128 129 /** 130 * Computes draw matrix for the emulator screen update. 131 * 132 * @param x Left screen coordinate of the bitmap on emulator screen. 133 * @param y Top screen coordinate of the bitmap on emulator screen. 134 */ computeDrawMatrix(int x, int y)135 private void computeDrawMatrix(int x, int y) { 136 mDrawMatrix.set(mBaseMatrix); 137 if (mRotateDisplay) { 138 mDrawMatrix.postTranslate(-y * mDy, x * mDx); 139 } else { 140 mDrawMatrix.postTranslate(x * mDx, y * mDy); 141 } 142 } 143 144 /** 145 * Draws a bitmap on the screen. 146 * 147 * @param x Left screen coordinate of the bitmap on emulator screen. 148 * @param y Top screen coordinate of the bitmap on emulator screen. 149 * @param w Width of the bitmap on the emulator screen. 150 * @param h Height of the bitmap on the emulator screen. 151 * @param colors Bitmap to draw. 152 */ drawBitmap(int x, int y, int w, int h, int[] colors)153 public void drawBitmap(int x, int y, int w, int h, int[] colors) { 154 if (mCanvas != null) { 155 final Bitmap bmp = Bitmap.createBitmap(colors, 0, w, w, h, Bitmap.Config.ARGB_8888); 156 157 computeDrawMatrix(x, y); 158 159 /* Draw the bitmap and invalidate the updated region. */ 160 mCanvas.drawBitmap(bmp, mDrawMatrix, mPaint); 161 invalidate(); 162 } 163 } 164 165 /** 166 * Draws a JPEG bitmap on the screen. 167 * 168 * @param x Left screen coordinate of the bitmap on emulator screen. 169 * @param y Top screen coordinate of the bitmap on emulator screen. 170 * @param w Width of the bitmap on the emulator screen. 171 * @param h Height of the bitmap on the emulator screen. 172 * @param jpeg JPEG bitmap to draw. 173 */ drawJpeg(int x, int y, int w, int h, InputStream jpeg)174 public void drawJpeg(int x, int y, int w, int h, InputStream jpeg) { 175 if (mCanvas != null) { 176 final Bitmap bmp = BitmapFactory.decodeStream(jpeg); 177 178 computeDrawMatrix(x, y); 179 180 /* Draw the bitmap and invalidate the updated region. */ 181 mCanvas.drawBitmap(bmp, mDrawMatrix, mPaint); 182 invalidate(); 183 } 184 } 185 186 /** 187 * Constructs touch event message to be send to emulator. 188 * 189 * @param sb String builder where to construct the message. 190 * @param event Event for which to construct the message. 191 * @param ptr_index Index of the motion pointer for which to construct the 192 * message. 193 */ constructEventMessage(StringBuilder sb, MotionEvent event, int ptr_index)194 public void constructEventMessage(StringBuilder sb, MotionEvent event, int ptr_index) { 195 sb.append(" pid=").append(event.getPointerId(ptr_index)); 196 if (mRotateDisplay == false) { 197 sb.append(" x=").append((int) (event.getX(ptr_index) / mDx)); 198 sb.append(" y=").append((int) (event.getY(ptr_index) / mDy)); 199 } else { 200 sb.append(" x=").append((int) (event.getY(ptr_index) / mDy)); 201 sb.append(" y=").append((int) (getWidth() - event.getX(ptr_index) / mDx)); 202 } 203 // At the system level the input reader takes integers in the range 204 // 0 - 100 for the pressure. 205 int pressure = (int) (event.getPressure(ptr_index) * 100); 206 // Make sure it doesn't exceed 100... 207 if (pressure > 100) { 208 pressure = 100; 209 } 210 sb.append(" pressure=").append(pressure); 211 } 212 213 /*************************************************************************** 214 * Logging wrappers 215 **************************************************************************/ 216 217 @SuppressWarnings("unused") Loge(String log)218 private void Loge(String log) { 219 Log.e(TAG, log); 220 } 221 222 @SuppressWarnings("unused") Logw(String log)223 private void Logw(String log) { 224 Log.w(TAG, log); 225 } 226 227 @SuppressWarnings("unused") Logv(String log)228 private void Logv(String log) { 229 Log.v(TAG, log); 230 } 231 } 232