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