1470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka/* 2470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka * Copyright (C) 2013 The Android Open Source Project 3470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka * 4470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); 5470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka * you may not use this file except in compliance with the License. 6470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka * You may obtain a copy of the License at 7470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka * 8470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0 9470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka * 10470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software 11470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, 12470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka * See the License for the specific language governing permissions and 14470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka * limitations under the License. 15470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka */ 16470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka 17470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaokapackage com.android.inputmethod.keyboard.internal; 18470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka 19470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaokaimport android.content.res.TypedArray; 20470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaokaimport android.graphics.Bitmap; 21470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaokaimport android.graphics.Canvas; 22470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaokaimport android.graphics.Color; 23470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaokaimport android.graphics.Paint; 24470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaokaimport android.graphics.PorterDuff; 25470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaokaimport android.graphics.PorterDuffXfermode; 26470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaokaimport android.graphics.Rect; 277fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaokaimport android.os.Handler; 28470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaokaimport android.util.SparseArray; 29470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka 30470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker; 31470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka 32470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka/** 33e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka * Draw preview graphics of multiple gesture trails during gesture input. 34470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka */ 357fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaokapublic final class GestureTrailsDrawingPreview extends AbstractDrawingPreview implements Runnable { 36a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka private final SparseArray<GestureTrailDrawingPoints> mGestureTrails = new SparseArray<>(); 37e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka private final GestureTrailDrawingParams mDrawingParams; 38470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka private final Paint mGesturePaint; 39470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka private int mOffscreenWidth; 40470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka private int mOffscreenHeight; 41470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka private int mOffscreenOffsetY; 42470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka private Bitmap mOffscreenBuffer; 43470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka private final Canvas mOffscreenCanvas = new Canvas(); 44470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka private final Rect mOffscreenSrcRect = new Rect(); 45470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka private final Rect mDirtyRect = new Rect(); 46cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa private final Rect mGestureTrailBoundsRect = new Rect(); // per trail 47470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka 487fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaoka private final Handler mDrawingHandler = new Handler(); 49470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka 507fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaoka public GestureTrailsDrawingPreview(final TypedArray mainKeyboardViewAttr) { 51e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka mDrawingParams = new GestureTrailDrawingParams(mainKeyboardViewAttr); 52470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka final Paint gesturePaint = new Paint(); 53470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka gesturePaint.setAntiAlias(true); 54470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka gesturePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); 55470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka mGesturePaint = gesturePaint; 56470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 57470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka 58470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka @Override 590c01fc6f1c01a2009546a2982818e68c08012ab3Tadashi G. Takaoka public void setKeyboardViewGeometry(final int[] originCoords, final int width, 600c01fc6f1c01a2009546a2982818e68c08012ab3Tadashi G. Takaoka final int height) { 610c01fc6f1c01a2009546a2982818e68c08012ab3Tadashi G. Takaoka super.setKeyboardViewGeometry(originCoords, width, height); 62e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka mOffscreenOffsetY = (int)(height 63e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka * GestureStrokeRecognitionPoints.EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO); 64470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka mOffscreenWidth = width; 65470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka mOffscreenHeight = mOffscreenOffsetY + height; 66470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 67470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka 68470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka @Override 69afca1ddd233c03d79433931a0b6ba97ed22663edTadashi G. Takaoka public void onDeallocateMemory() { 70c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa freeOffscreenBuffer(); 71c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa } 72c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa 73470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka private void freeOffscreenBuffer() { 74c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa mOffscreenCanvas.setBitmap(null); 7582018f99727a104aa77ab4d48f8b9a9858479453Ken Wakasa mOffscreenCanvas.setMatrix(null); 76470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka if (mOffscreenBuffer != null) { 77470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka mOffscreenBuffer.recycle(); 78470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka mOffscreenBuffer = null; 79470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 80470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 81470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka 82470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka private void mayAllocateOffscreenBuffer() { 83470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka if (mOffscreenBuffer != null && mOffscreenBuffer.getWidth() == mOffscreenWidth 84470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka && mOffscreenBuffer.getHeight() == mOffscreenHeight) { 85470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka return; 86470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 87470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka freeOffscreenBuffer(); 88470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka mOffscreenBuffer = Bitmap.createBitmap( 89470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka mOffscreenWidth, mOffscreenHeight, Bitmap.Config.ARGB_8888); 90470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka mOffscreenCanvas.setBitmap(mOffscreenBuffer); 91470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka mOffscreenCanvas.translate(0, mOffscreenOffsetY); 92470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 93470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka 94470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka private boolean drawGestureTrails(final Canvas offscreenCanvas, final Paint paint, 95470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka final Rect dirtyRect) { 96470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka // Clear previous dirty rectangle. 97470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka if (!dirtyRect.isEmpty()) { 98470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka paint.setColor(Color.TRANSPARENT); 99470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka paint.setStyle(Paint.Style.FILL); 100470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka offscreenCanvas.drawRect(dirtyRect, paint); 101470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 102470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka dirtyRect.setEmpty(); 103cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa boolean needsUpdatingGestureTrail = false; 104470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka // Draw gesture trails to offscreen buffer. 105cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa synchronized (mGestureTrails) { 106470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka // Trails count == fingers count that have ever been active. 107cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa final int trailsCount = mGestureTrails.size(); 108470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka for (int index = 0; index < trailsCount; index++) { 109e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka final GestureTrailDrawingPoints trail = mGestureTrails.valueAt(index); 110cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa needsUpdatingGestureTrail |= trail.drawGestureTrail(offscreenCanvas, paint, 111e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka mGestureTrailBoundsRect, mDrawingParams); 112cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa // {@link #mGestureTrailBoundsRect} has bounding box of the trail. 113cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa dirtyRect.union(mGestureTrailBoundsRect); 114470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 115470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 116cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa return needsUpdatingGestureTrail; 117470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 118470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka 1197fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaoka @Override 1207fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaoka public void run() { 1217fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaoka // Update preview. 1227fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaoka invalidateDrawingView(); 1237fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaoka } 1247fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaoka 125470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka /** 126470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka * Draws the preview 127470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka * @param canvas The canvas where the preview is drawn. 128470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka */ 129470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka @Override 130470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka public void drawPreview(final Canvas canvas) { 131470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka if (!isPreviewEnabled()) { 132470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka return; 133470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 134470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka mayAllocateOffscreenBuffer(); 135470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka // Draw gesture trails to offscreen buffer. 136cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa final boolean needsUpdatingGestureTrail = drawGestureTrails( 137470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka mOffscreenCanvas, mGesturePaint, mDirtyRect); 138cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa if (needsUpdatingGestureTrail) { 1397fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaoka mDrawingHandler.removeCallbacks(this); 1407fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaoka mDrawingHandler.postDelayed(this, mDrawingParams.mUpdateInterval); 141470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 142470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka // Transfer offscreen buffer to screen. 143470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka if (!mDirtyRect.isEmpty()) { 144470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka mOffscreenSrcRect.set(mDirtyRect); 145470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka mOffscreenSrcRect.offset(0, mOffscreenOffsetY); 146470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka canvas.drawBitmap(mOffscreenBuffer, mOffscreenSrcRect, mDirtyRect, null); 147470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka // Note: Defer clearing the dirty rectangle here because we will get cleared 148470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka // rectangle on the canvas. 149470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 150470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 151470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka 152470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka /** 153470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka * Set the position of the preview. 154470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka * @param tracker The new location of the preview is based on the points in PointerTracker. 155470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka */ 156470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka @Override 157470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka public void setPreviewPosition(final PointerTracker tracker) { 158470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka if (!isPreviewEnabled()) { 159470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka return; 160470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 161e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka GestureTrailDrawingPoints trail; 162cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa synchronized (mGestureTrails) { 163cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa trail = mGestureTrails.get(tracker.mPointerId); 164470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka if (trail == null) { 165e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka trail = new GestureTrailDrawingPoints(); 166cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa mGestureTrails.put(tracker.mPointerId, trail); 167470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 168470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 169e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka trail.addStroke(tracker.getGestureStrokeDrawingPoints(), tracker.getDownTime()); 170470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka 171470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka // TODO: Should narrow the invalidate region. 1727fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaoka invalidateDrawingView(); 173470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka } 174470a5805e125c32f1ed300bb0c064babb651923cTadashi G. Takaoka} 175