blob: 0f185b173790195e569cbb146d4198d569f2fc0b [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.tools.sdkcontroller.views;
import java.io.InputStream;
import java.nio.ByteBuffer;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
/**
* Implements a main view for the application providing multi-touch emulation.
*/
public class MultiTouchView extends View {
/** Tag for logging messages. */
private static final String TAG = MultiTouchView.class.getSimpleName();
/**
* Back-end bitmap. Initialized in onSizeChanged(), updated in
* onTouchEvent() and drawn in onDraw().
*/
private Bitmap mBitmap;
/** Default Paint instance for drawing the bitmap. */
private final Paint mPaint = new Paint();
/** Canvas instance for this view. */
private Canvas mCanvas;
/** Emulator screen width to this view width ratio. */
private float mDx = 1;
/** Emulator screen height to this view height ratio. */
private float mDy = 1;
/**
* Flags whether or not image received from the emulator should be rotated.
* Rotation is required when display orientation state of the emulator and
* the device doesn't match.
*/
private boolean mRotateDisplay;
/** Base matrix that keep emulator->device display scaling */
private Matrix mBaseMatrix = new Matrix();
/** Matrix that is used to draw emulator's screen on the device. */
private Matrix mDrawMatrix = new Matrix();
/**
* Simple constructor to use when creating a view from code.
*
* @see View#View(Context)
*/
public MultiTouchView(Context context) {
this(context, null);
}
/**
* Constructor that is called when inflating a view from XML.
*
* @see View#View(Context, AttributeSet)
*/
public MultiTouchView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* Perform inflation from XML and apply a class-specific base style.
*
* @see View#View(Context, AttributeSet, int)
*/
public MultiTouchView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Add constructor-time code here.
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Just draw the back-end bitmap without zooming or scaling.
if (mBitmap != null) {
canvas.drawBitmap(mBitmap, 0, 0, null);
}
}
/**
* Sets emulator screen width and height to this view width and height
* ratio.
*
* @param dx Emulator screen width to this view width ratio.
* @param dy Emulator screen height to this view height ratio.
* @param rotateDisplay Flags whether image received from the emulator
* should be rotated when drawn on the device.
*/
public void setDxDy(float dx, float dy, boolean rotateDisplay) {
mDx = dx;
mDy = dy;
mRotateDisplay = rotateDisplay;
mBaseMatrix.setScale(dx, dy);
if (mRotateDisplay) {
mBaseMatrix.postRotate(90);
mBaseMatrix.postTranslate(getWidth(), 0);
}
}
/**
* Computes draw matrix for the emulator screen update.
*
* @param x Left screen coordinate of the bitmap on emulator screen.
* @param y Top screen coordinate of the bitmap on emulator screen.
*/
private void computeDrawMatrix(int x, int y) {
mDrawMatrix.set(mBaseMatrix);
if (mRotateDisplay) {
mDrawMatrix.postTranslate(-y * mDy, x * mDx);
} else {
mDrawMatrix.postTranslate(x * mDx, y * mDy);
}
}
/**
* Draws a bitmap on the screen.
*
* @param x Left screen coordinate of the bitmap on emulator screen.
* @param y Top screen coordinate of the bitmap on emulator screen.
* @param w Width of the bitmap on the emulator screen.
* @param h Height of the bitmap on the emulator screen.
* @param colors Bitmap to draw.
*/
public void drawBitmap(int x, int y, int w, int h, int[] colors) {
if (mCanvas != null) {
final Bitmap bmp = Bitmap.createBitmap(colors, 0, w, w, h, Bitmap.Config.ARGB_8888);
computeDrawMatrix(x, y);
/* Draw the bitmap and invalidate the updated region. */
mCanvas.drawBitmap(bmp, mDrawMatrix, mPaint);
invalidate();
}
}
/**
* Draws a JPEG bitmap on the screen.
*
* @param x Left screen coordinate of the bitmap on emulator screen.
* @param y Top screen coordinate of the bitmap on emulator screen.
* @param w Width of the bitmap on the emulator screen.
* @param h Height of the bitmap on the emulator screen.
* @param jpeg JPEG bitmap to draw.
*/
public void drawJpeg(int x, int y, int w, int h, InputStream jpeg) {
if (mCanvas != null) {
final Bitmap bmp = BitmapFactory.decodeStream(jpeg);
computeDrawMatrix(x, y);
/* Draw the bitmap and invalidate the updated region. */
mCanvas.drawBitmap(bmp, mDrawMatrix, mPaint);
invalidate();
}
}
/**
* Constructs touch event message to be send to emulator.
*
* @param bb ByteBuffer where to construct the message.
* @param event Event for which to construct the message.
* @param ptr_index Index of the motion pointer for which to construct the
* message.
*/
public void constructEventMessage(ByteBuffer bb, MotionEvent event, int ptr_index) {
bb.putInt(event.getPointerId(ptr_index));
if (mRotateDisplay == false) {
bb.putInt((int) (event.getX(ptr_index) / mDx));
bb.putInt((int) (event.getY(ptr_index) / mDy));
} else {
bb.putInt((int) (event.getY(ptr_index) / mDy));
bb.putInt((int) (getWidth() - event.getX(ptr_index) / mDx));
}
// At the system level the input reader takes integers in the range
// 0 - 100 for the pressure.
int pressure = (int) (event.getPressure(ptr_index) * 100);
// Make sure it doesn't exceed 100...
if (pressure > 100) {
pressure = 100;
}
bb.putInt(pressure);
}
/***************************************************************************
* Logging wrappers
**************************************************************************/
@SuppressWarnings("unused")
private void Loge(String log) {
Log.e(TAG, log);
}
@SuppressWarnings("unused")
private void Logw(String log) {
Log.w(TAG, log);
}
@SuppressWarnings("unused")
private void Logv(String log) {
Log.v(TAG, log);
}
}