GestureTrailsDrawingPreview.java revision e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.inputmethod.keyboard.internal; 18 19import android.content.res.TypedArray; 20import android.graphics.Bitmap; 21import android.graphics.Canvas; 22import android.graphics.Color; 23import android.graphics.Paint; 24import android.graphics.PorterDuff; 25import android.graphics.PorterDuffXfermode; 26import android.graphics.Rect; 27import android.os.Message; 28import android.util.SparseArray; 29import android.view.View; 30 31import com.android.inputmethod.keyboard.PointerTracker; 32import com.android.inputmethod.latin.utils.CollectionUtils; 33import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; 34 35/** 36 * Draw preview graphics of multiple gesture trails during gesture input. 37 */ 38public final class GestureTrailsDrawingPreview extends AbstractDrawingPreview { 39 private final SparseArray<GestureTrailDrawingPoints> mGestureTrails = 40 CollectionUtils.newSparseArray(); 41 private final GestureTrailDrawingParams mDrawingParams; 42 private final Paint mGesturePaint; 43 private int mOffscreenWidth; 44 private int mOffscreenHeight; 45 private int mOffscreenOffsetY; 46 private Bitmap mOffscreenBuffer; 47 private final Canvas mOffscreenCanvas = new Canvas(); 48 private final Rect mOffscreenSrcRect = new Rect(); 49 private final Rect mDirtyRect = new Rect(); 50 private final Rect mGestureTrailBoundsRect = new Rect(); // per trail 51 52 private final DrawingHandler mDrawingHandler; 53 54 private static final class DrawingHandler 55 extends LeakGuardHandlerWrapper<GestureTrailsDrawingPreview> { 56 private static final int MSG_UPDATE_GESTURE_TRAIL = 0; 57 58 private final GestureTrailDrawingParams mDrawingParams; 59 60 public DrawingHandler(final GestureTrailsDrawingPreview ownerInstance, 61 final GestureTrailDrawingParams drawingParams) { 62 super(ownerInstance); 63 mDrawingParams = drawingParams; 64 } 65 66 @Override 67 public void handleMessage(final Message msg) { 68 final GestureTrailsDrawingPreview preview = getOwnerInstance(); 69 if (preview == null) { 70 return; 71 } 72 switch (msg.what) { 73 case MSG_UPDATE_GESTURE_TRAIL: 74 preview.getDrawingView().invalidate(); 75 break; 76 } 77 } 78 79 public void postUpdateGestureTrailPreview() { 80 removeMessages(MSG_UPDATE_GESTURE_TRAIL); 81 sendMessageDelayed(obtainMessage(MSG_UPDATE_GESTURE_TRAIL), 82 mDrawingParams.mUpdateInterval); 83 } 84 } 85 86 public GestureTrailsDrawingPreview(final View drawingView, 87 final TypedArray mainKeyboardViewAttr) { 88 super(drawingView); 89 mDrawingParams = new GestureTrailDrawingParams(mainKeyboardViewAttr); 90 mDrawingHandler = new DrawingHandler(this, mDrawingParams); 91 final Paint gesturePaint = new Paint(); 92 gesturePaint.setAntiAlias(true); 93 gesturePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); 94 mGesturePaint = gesturePaint; 95 } 96 97 @Override 98 public void setKeyboardGeometry(final int[] originCoords, final int width, final int height) { 99 mOffscreenOffsetY = (int)(height 100 * GestureStrokeRecognitionPoints.EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO); 101 mOffscreenWidth = width; 102 mOffscreenHeight = mOffscreenOffsetY + height; 103 } 104 105 @Override 106 public void onDeallocateMemory() { 107 freeOffscreenBuffer(); 108 } 109 110 private void freeOffscreenBuffer() { 111 mOffscreenCanvas.setBitmap(null); 112 mOffscreenCanvas.setMatrix(null); 113 if (mOffscreenBuffer != null) { 114 mOffscreenBuffer.recycle(); 115 mOffscreenBuffer = null; 116 } 117 } 118 119 private void mayAllocateOffscreenBuffer() { 120 if (mOffscreenBuffer != null && mOffscreenBuffer.getWidth() == mOffscreenWidth 121 && mOffscreenBuffer.getHeight() == mOffscreenHeight) { 122 return; 123 } 124 freeOffscreenBuffer(); 125 mOffscreenBuffer = Bitmap.createBitmap( 126 mOffscreenWidth, mOffscreenHeight, Bitmap.Config.ARGB_8888); 127 mOffscreenCanvas.setBitmap(mOffscreenBuffer); 128 mOffscreenCanvas.translate(0, mOffscreenOffsetY); 129 } 130 131 private boolean drawGestureTrails(final Canvas offscreenCanvas, final Paint paint, 132 final Rect dirtyRect) { 133 // Clear previous dirty rectangle. 134 if (!dirtyRect.isEmpty()) { 135 paint.setColor(Color.TRANSPARENT); 136 paint.setStyle(Paint.Style.FILL); 137 offscreenCanvas.drawRect(dirtyRect, paint); 138 } 139 dirtyRect.setEmpty(); 140 boolean needsUpdatingGestureTrail = false; 141 // Draw gesture trails to offscreen buffer. 142 synchronized (mGestureTrails) { 143 // Trails count == fingers count that have ever been active. 144 final int trailsCount = mGestureTrails.size(); 145 for (int index = 0; index < trailsCount; index++) { 146 final GestureTrailDrawingPoints trail = mGestureTrails.valueAt(index); 147 needsUpdatingGestureTrail |= trail.drawGestureTrail(offscreenCanvas, paint, 148 mGestureTrailBoundsRect, mDrawingParams); 149 // {@link #mGestureTrailBoundsRect} has bounding box of the trail. 150 dirtyRect.union(mGestureTrailBoundsRect); 151 } 152 } 153 return needsUpdatingGestureTrail; 154 } 155 156 /** 157 * Draws the preview 158 * @param canvas The canvas where the preview is drawn. 159 */ 160 @Override 161 public void drawPreview(final Canvas canvas) { 162 if (!isPreviewEnabled()) { 163 return; 164 } 165 mayAllocateOffscreenBuffer(); 166 // Draw gesture trails to offscreen buffer. 167 final boolean needsUpdatingGestureTrail = drawGestureTrails( 168 mOffscreenCanvas, mGesturePaint, mDirtyRect); 169 if (needsUpdatingGestureTrail) { 170 mDrawingHandler.postUpdateGestureTrailPreview(); 171 } 172 // Transfer offscreen buffer to screen. 173 if (!mDirtyRect.isEmpty()) { 174 mOffscreenSrcRect.set(mDirtyRect); 175 mOffscreenSrcRect.offset(0, mOffscreenOffsetY); 176 canvas.drawBitmap(mOffscreenBuffer, mOffscreenSrcRect, mDirtyRect, null); 177 // Note: Defer clearing the dirty rectangle here because we will get cleared 178 // rectangle on the canvas. 179 } 180 } 181 182 /** 183 * Set the position of the preview. 184 * @param tracker The new location of the preview is based on the points in PointerTracker. 185 */ 186 @Override 187 public void setPreviewPosition(final PointerTracker tracker) { 188 if (!isPreviewEnabled()) { 189 return; 190 } 191 GestureTrailDrawingPoints trail; 192 synchronized (mGestureTrails) { 193 trail = mGestureTrails.get(tracker.mPointerId); 194 if (trail == null) { 195 trail = new GestureTrailDrawingPoints(); 196 mGestureTrails.put(tracker.mPointerId, trail); 197 } 198 } 199 trail.addStroke(tracker.getGestureStrokeDrawingPoints(), tracker.getDownTime()); 200 201 // TODO: Should narrow the invalidate region. 202 getDrawingView().invalidate(); 203 } 204} 205