GestureTrailsDrawingPreview.java revision 0c01fc6f1c01a2009546a2982818e68c08012ab3
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 setKeyboardViewGeometry(final int[] originCoords, final int width, 99 final int height) { 100 super.setKeyboardViewGeometry(originCoords, width, height); 101 mOffscreenOffsetY = (int)(height 102 * GestureStrokeRecognitionPoints.EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO); 103 mOffscreenWidth = width; 104 mOffscreenHeight = mOffscreenOffsetY + height; 105 } 106 107 @Override 108 public void onDeallocateMemory() { 109 freeOffscreenBuffer(); 110 } 111 112 private void freeOffscreenBuffer() { 113 mOffscreenCanvas.setBitmap(null); 114 mOffscreenCanvas.setMatrix(null); 115 if (mOffscreenBuffer != null) { 116 mOffscreenBuffer.recycle(); 117 mOffscreenBuffer = null; 118 } 119 } 120 121 private void mayAllocateOffscreenBuffer() { 122 if (mOffscreenBuffer != null && mOffscreenBuffer.getWidth() == mOffscreenWidth 123 && mOffscreenBuffer.getHeight() == mOffscreenHeight) { 124 return; 125 } 126 freeOffscreenBuffer(); 127 mOffscreenBuffer = Bitmap.createBitmap( 128 mOffscreenWidth, mOffscreenHeight, Bitmap.Config.ARGB_8888); 129 mOffscreenCanvas.setBitmap(mOffscreenBuffer); 130 mOffscreenCanvas.translate(0, mOffscreenOffsetY); 131 } 132 133 private boolean drawGestureTrails(final Canvas offscreenCanvas, final Paint paint, 134 final Rect dirtyRect) { 135 // Clear previous dirty rectangle. 136 if (!dirtyRect.isEmpty()) { 137 paint.setColor(Color.TRANSPARENT); 138 paint.setStyle(Paint.Style.FILL); 139 offscreenCanvas.drawRect(dirtyRect, paint); 140 } 141 dirtyRect.setEmpty(); 142 boolean needsUpdatingGestureTrail = false; 143 // Draw gesture trails to offscreen buffer. 144 synchronized (mGestureTrails) { 145 // Trails count == fingers count that have ever been active. 146 final int trailsCount = mGestureTrails.size(); 147 for (int index = 0; index < trailsCount; index++) { 148 final GestureTrailDrawingPoints trail = mGestureTrails.valueAt(index); 149 needsUpdatingGestureTrail |= trail.drawGestureTrail(offscreenCanvas, paint, 150 mGestureTrailBoundsRect, mDrawingParams); 151 // {@link #mGestureTrailBoundsRect} has bounding box of the trail. 152 dirtyRect.union(mGestureTrailBoundsRect); 153 } 154 } 155 return needsUpdatingGestureTrail; 156 } 157 158 /** 159 * Draws the preview 160 * @param canvas The canvas where the preview is drawn. 161 */ 162 @Override 163 public void drawPreview(final Canvas canvas) { 164 if (!isPreviewEnabled()) { 165 return; 166 } 167 mayAllocateOffscreenBuffer(); 168 // Draw gesture trails to offscreen buffer. 169 final boolean needsUpdatingGestureTrail = drawGestureTrails( 170 mOffscreenCanvas, mGesturePaint, mDirtyRect); 171 if (needsUpdatingGestureTrail) { 172 mDrawingHandler.postUpdateGestureTrailPreview(); 173 } 174 // Transfer offscreen buffer to screen. 175 if (!mDirtyRect.isEmpty()) { 176 mOffscreenSrcRect.set(mDirtyRect); 177 mOffscreenSrcRect.offset(0, mOffscreenOffsetY); 178 canvas.drawBitmap(mOffscreenBuffer, mOffscreenSrcRect, mDirtyRect, null); 179 // Note: Defer clearing the dirty rectangle here because we will get cleared 180 // rectangle on the canvas. 181 } 182 } 183 184 /** 185 * Set the position of the preview. 186 * @param tracker The new location of the preview is based on the points in PointerTracker. 187 */ 188 @Override 189 public void setPreviewPosition(final PointerTracker tracker) { 190 if (!isPreviewEnabled()) { 191 return; 192 } 193 GestureTrailDrawingPoints trail; 194 synchronized (mGestureTrails) { 195 trail = mGestureTrails.get(tracker.mPointerId); 196 if (trail == null) { 197 trail = new GestureTrailDrawingPoints(); 198 mGestureTrails.put(tracker.mPointerId, trail); 199 } 200 } 201 trail.addStroke(tracker.getGestureStrokeDrawingPoints(), tracker.getDownTime()); 202 203 // TODO: Should narrow the invalidate region. 204 getDrawingView().invalidate(); 205 } 206} 207