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      */
MultiTouchView(Context context)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      */
MultiTouchView(Context context, AttributeSet attrs)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      */
MultiTouchView(Context context, AttributeSet attrs, int defStyle)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
onSizeChanged(int w, int h, int oldw, int oldh)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
onDraw(Canvas canvas)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      */
setDxDy(float dx, float dy, boolean rotateDisplay)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      */
computeDrawMatrix(int x, int y)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      */
drawBitmap(int x, int y, int w, int h, int[] colors)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      */
drawJpeg(int x, int y, int w, int h, InputStream jpeg)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      */
constructEventMessage(ByteBuffer bb, MotionEvent event, int ptr_index)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")
Loge(String log)219     private void Loge(String log) {
220         Log.e(TAG, log);
221     }
222 
223     @SuppressWarnings("unused")
Logw(String log)224     private void Logw(String log) {
225         Log.w(TAG, log);
226     }
227 
228     @SuppressWarnings("unused")
Logv(String log)229     private void Logv(String log) {
230         Log.v(TAG, log);
231     }
232 }
233