• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.inputmethod.keyboard.internal;
18 
19 import android.content.res.TypedArray;
20 import android.graphics.Bitmap;
21 import android.graphics.Canvas;
22 import android.graphics.Color;
23 import android.graphics.Paint;
24 import android.graphics.PorterDuff;
25 import android.graphics.PorterDuffXfermode;
26 import android.graphics.Rect;
27 import android.os.Message;
28 import android.util.SparseArray;
29 import android.view.View;
30 
31 import com.android.inputmethod.keyboard.PointerTracker;
32 import com.android.inputmethod.keyboard.internal.GestureTrail.Params;
33 import com.android.inputmethod.latin.utils.CollectionUtils;
34 import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper;
35 
36 /**
37  * Draw gesture trail preview graphics during gesture.
38  */
39 public final class GestureTrailsPreview extends AbstractDrawingPreview {
40     private final SparseArray<GestureTrail> mGestureTrails = CollectionUtils.newSparseArray();
41     private final Params mGestureTrailParams;
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 StaticInnerHandlerWrapper<GestureTrailsPreview> {
56         private static final int MSG_UPDATE_GESTURE_TRAIL = 0;
57 
58         private final Params mGestureTrailParams;
59 
DrawingHandler(final GestureTrailsPreview outerInstance, final Params gestureTrailParams)60         public DrawingHandler(final GestureTrailsPreview outerInstance,
61                 final Params gestureTrailParams) {
62             super(outerInstance);
63             mGestureTrailParams = gestureTrailParams;
64         }
65 
66         @Override
handleMessage(final Message msg)67         public void handleMessage(final Message msg) {
68             final GestureTrailsPreview preview = getOuterInstance();
69             if (preview == null) return;
70             switch (msg.what) {
71             case MSG_UPDATE_GESTURE_TRAIL:
72                 preview.getDrawingView().invalidate();
73                 break;
74             }
75         }
76 
postUpdateGestureTrailPreview()77         public void postUpdateGestureTrailPreview() {
78             removeMessages(MSG_UPDATE_GESTURE_TRAIL);
79             sendMessageDelayed(obtainMessage(MSG_UPDATE_GESTURE_TRAIL),
80                     mGestureTrailParams.mUpdateInterval);
81         }
82     }
83 
GestureTrailsPreview(final View drawingView, final TypedArray mainKeyboardViewAttr)84     public GestureTrailsPreview(final View drawingView, final TypedArray mainKeyboardViewAttr) {
85         super(drawingView);
86         mGestureTrailParams = new Params(mainKeyboardViewAttr);
87         mDrawingHandler = new DrawingHandler(this, mGestureTrailParams);
88         final Paint gesturePaint = new Paint();
89         gesturePaint.setAntiAlias(true);
90         gesturePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
91         mGesturePaint = gesturePaint;
92     }
93 
94     @Override
setKeyboardGeometry(final int[] originCoords, final int width, final int height)95     public void setKeyboardGeometry(final int[] originCoords, final int width, final int height) {
96         mOffscreenOffsetY = (int)(
97                 height * GestureStroke.EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO);
98         mOffscreenWidth = width;
99         mOffscreenHeight = mOffscreenOffsetY + height;
100     }
101 
102     @Override
onDetachFromWindow()103     public void onDetachFromWindow() {
104         freeOffscreenBuffer();
105     }
106 
deallocateMemory()107     public void deallocateMemory() {
108         freeOffscreenBuffer();
109     }
110 
freeOffscreenBuffer()111     private void freeOffscreenBuffer() {
112         mOffscreenCanvas.setBitmap(null);
113         mOffscreenCanvas.setMatrix(null);
114         if (mOffscreenBuffer != null) {
115             mOffscreenBuffer.recycle();
116             mOffscreenBuffer = null;
117         }
118     }
119 
mayAllocateOffscreenBuffer()120     private void mayAllocateOffscreenBuffer() {
121         if (mOffscreenBuffer != null && mOffscreenBuffer.getWidth() == mOffscreenWidth
122                 && mOffscreenBuffer.getHeight() == mOffscreenHeight) {
123             return;
124         }
125         freeOffscreenBuffer();
126         mOffscreenBuffer = Bitmap.createBitmap(
127                 mOffscreenWidth, mOffscreenHeight, Bitmap.Config.ARGB_8888);
128         mOffscreenCanvas.setBitmap(mOffscreenBuffer);
129         mOffscreenCanvas.translate(0, mOffscreenOffsetY);
130     }
131 
drawGestureTrails(final Canvas offscreenCanvas, final Paint paint, final Rect dirtyRect)132     private boolean drawGestureTrails(final Canvas offscreenCanvas, final Paint paint,
133             final Rect dirtyRect) {
134         // Clear previous dirty rectangle.
135         if (!dirtyRect.isEmpty()) {
136             paint.setColor(Color.TRANSPARENT);
137             paint.setStyle(Paint.Style.FILL);
138             offscreenCanvas.drawRect(dirtyRect, paint);
139         }
140         dirtyRect.setEmpty();
141         boolean needsUpdatingGestureTrail = false;
142         // Draw gesture trails to offscreen buffer.
143         synchronized (mGestureTrails) {
144             // Trails count == fingers count that have ever been active.
145             final int trailsCount = mGestureTrails.size();
146             for (int index = 0; index < trailsCount; index++) {
147                 final GestureTrail trail = mGestureTrails.valueAt(index);
148                 needsUpdatingGestureTrail |= trail.drawGestureTrail(offscreenCanvas, paint,
149                         mGestureTrailBoundsRect, mGestureTrailParams);
150                 // {@link #mGestureTrailBoundsRect} has bounding box of the trail.
151                 dirtyRect.union(mGestureTrailBoundsRect);
152             }
153         }
154         return needsUpdatingGestureTrail;
155     }
156 
157     /**
158      * Draws the preview
159      * @param canvas The canvas where the preview is drawn.
160      */
161     @Override
drawPreview(final Canvas canvas)162     public void drawPreview(final Canvas canvas) {
163         if (!isPreviewEnabled()) {
164             return;
165         }
166         mayAllocateOffscreenBuffer();
167         // Draw gesture trails to offscreen buffer.
168         final boolean needsUpdatingGestureTrail = drawGestureTrails(
169                 mOffscreenCanvas, mGesturePaint, mDirtyRect);
170         if (needsUpdatingGestureTrail) {
171             mDrawingHandler.postUpdateGestureTrailPreview();
172         }
173         // Transfer offscreen buffer to screen.
174         if (!mDirtyRect.isEmpty()) {
175             mOffscreenSrcRect.set(mDirtyRect);
176             mOffscreenSrcRect.offset(0, mOffscreenOffsetY);
177             canvas.drawBitmap(mOffscreenBuffer, mOffscreenSrcRect, mDirtyRect, null);
178             // Note: Defer clearing the dirty rectangle here because we will get cleared
179             // rectangle on the canvas.
180         }
181     }
182 
183     /**
184      * Set the position of the preview.
185      * @param tracker The new location of the preview is based on the points in PointerTracker.
186      */
187     @Override
setPreviewPosition(final PointerTracker tracker)188     public void setPreviewPosition(final PointerTracker tracker) {
189         if (!isPreviewEnabled()) {
190             return;
191         }
192         GestureTrail trail;
193         synchronized (mGestureTrails) {
194             trail = mGestureTrails.get(tracker.mPointerId);
195             if (trail == null) {
196                 trail = new GestureTrail();
197                 mGestureTrails.put(tracker.mPointerId, trail);
198             }
199         }
200         trail.addStroke(tracker.getGestureStrokeWithPreviewPoints(), tracker.getDownTime());
201 
202         // TODO: Should narrow the invalidate region.
203         getDrawingView().invalidate();
204     }
205 }
206