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