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