141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li/*
241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * Copyright (C) 2011 The Android Open Source Project
341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li *
441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * Licensed under the Apache License, Version 2.0 (the "License");
541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * you may not use this file except in compliance with the License.
641ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * You may obtain a copy of the License at
741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li *
841ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li *      http://www.apache.org/licenses/LICENSE-2.0
941ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li *
1041ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * Unless required by applicable law or agreed to in writing, software
1141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * distributed under the License is distributed on an "AS IS" BASIS,
1241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * See the License for the specific language governing permissions and
1441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li * limitations under the License.
1541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li */
1641ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
1741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Lipackage com.android.camera.ui;
1841ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
19c347dd2cccb6cbad8caa8404eae1c0ae17204156Ahbong Changimport android.annotation.TargetApi;
2041ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Liimport android.content.Context;
21918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolbimport android.content.res.Resources;
2241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Liimport android.graphics.Canvas;
2341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Liimport android.graphics.Matrix;
24918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolbimport android.graphics.Paint;
25918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolbimport android.graphics.Paint.Style;
2641ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Liimport android.graphics.RectF;
2741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Liimport android.hardware.Camera.Face;
28630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reckimport android.os.Handler;
29630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reckimport android.os.Message;
3041ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Liimport android.util.AttributeSet;
3141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Liimport android.util.Log;
3241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Liimport android.view.View;
3341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
3441d49f0fa2c9c93869bacfd6e7f76299f482e40fMichael Kolbimport com.android.camera.CameraActivity;
3541d49f0fa2c9c93869bacfd6e7f76299f482e40fMichael Kolbimport com.android.camera.CameraScreenNail;
36892781804513066b68b4a2fa6f846e862dac252aOwen Linimport com.android.camera.R;
37892781804513066b68b4a2fa6f846e862dac252aOwen Linimport com.android.camera.Util;
38c347dd2cccb6cbad8caa8404eae1c0ae17204156Ahbong Changimport com.android.gallery3d.common.ApiHelper;
39892781804513066b68b4a2fa6f846e862dac252aOwen Lin
40c347dd2cccb6cbad8caa8404eae1c0ae17204156Ahbong Chang@TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
413ebe49d8f1b4defb7cfd4850a14e795aada2ebd1Chih-yu Huangpublic class FaceView extends View implements FocusIndicator, Rotatable {
4241d49f0fa2c9c93869bacfd6e7f76299f482e40fMichael Kolb    private static final String TAG = "CAM FaceView";
4341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    private final boolean LOGV = false;
44c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li    // The value for android.hardware.Camera.setDisplayOrientation.
4541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    private int mDisplayOrientation;
46c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li    // The orientation compensation for the face indicator to make it look
47c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li    // correctly in all device orientations. Ex: if the value is 90, the
48c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li    // indicator should be rotated 90 degrees counter-clockwise.
49c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li    private int mOrientation;
5041ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    private boolean mMirror;
51d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li    private boolean mPause;
5241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    private Matrix mMatrix = new Matrix();
5341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    private RectF mRect = new RectF();
54630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    // As face detection can be flaky, we add a layer of filtering on top of it
55630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    // to avoid rapid changes in state (eg, flickering between has faces and
56630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    // not having faces)
5741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    private Face[] mFaces;
58630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    private Face[] mPendingFaces;
59918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb    private int mColor;
60918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb    private final int mFocusingColor;
61918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb    private final int mFocusedColor;
62918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb    private final int mFailColor;
63918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb    private Paint mPaint;
648dbb2a65ef1b0404c169e8fada6c0d0f154dd08fMichael Kolb    private volatile boolean mBlocked;
6541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
66630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    private static final int MSG_SWITCH_FACES = 1;
67630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    private static final int SWITCH_DELAY = 70;
68630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    private boolean mStateSwitchPending = false;
69630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    private Handler mHandler = new Handler() {
70630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        @Override
71630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        public void handleMessage(Message msg) {
72630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            switch (msg.what) {
73630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            case MSG_SWITCH_FACES:
74630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                mStateSwitchPending = false;
75630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                mFaces = mPendingFaces;
76630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                invalidate();
77630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                break;
78630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            }
79630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        }
80630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck    };
81630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck
8241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    public FaceView(Context context, AttributeSet attrs) {
8341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        super(context, attrs);
84918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        Resources res = getResources();
85918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mFocusingColor = res.getColor(R.color.face_detect_start);
86918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mFocusedColor = res.getColor(R.color.face_detect_success);
87918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mFailColor = res.getColor(R.color.face_detect_fail);
88918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mColor = mFocusingColor;
89918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mPaint = new Paint();
90918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mPaint.setAntiAlias(true);
91918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mPaint.setStyle(Style.STROKE);
9241d49f0fa2c9c93869bacfd6e7f76299f482e40fMichael Kolb        mPaint.setStrokeWidth(res.getDimension(R.dimen.face_circle_stroke));
9341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
9441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
9541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    public void setFaces(Face[] faces) {
9641ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        if (LOGV) Log.v(TAG, "Num of faces=" + faces.length);
97d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li        if (mPause) return;
98630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        if (mFaces != null) {
99630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            if ((faces.length > 0 && mFaces.length == 0)
100630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                    || (faces.length == 0 && mFaces.length > 0)) {
101630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                mPendingFaces = faces;
102630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                if (!mStateSwitchPending) {
103630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                    mStateSwitchPending = true;
104630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                    mHandler.sendEmptyMessageDelayed(MSG_SWITCH_FACES, SWITCH_DELAY);
105630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                }
106630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck                return;
107630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            }
108630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        }
109630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        if (mStateSwitchPending) {
110630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            mStateSwitchPending = false;
111630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck            mHandler.removeMessages(MSG_SWITCH_FACES);
112630d255bdeeb1a13a71b0ecda0a38d6546c351e2John Reck        }
11341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        mFaces = faces;
11441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        invalidate();
11541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
11641ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
11741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    public void setDisplayOrientation(int orientation) {
11841ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        mDisplayOrientation = orientation;
11941ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        if (LOGV) Log.v(TAG, "mDisplayOrientation=" + orientation);
12041ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
12141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
1222865863f6a1887bda17e0a58a6a0f3aaac237cb6Angus Kong    @Override
1232865863f6a1887bda17e0a58a6a0f3aaac237cb6Angus Kong    public void setOrientation(int orientation, boolean animation) {
124c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li        mOrientation = orientation;
125c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li        invalidate();
126c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li    }
127c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li
12841ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    public void setMirror(boolean mirror) {
12941ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        mMirror = mirror;
13041ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        if (LOGV) Log.v(TAG, "mMirror=" + mirror);
13141ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
13241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
13341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    public boolean faceExists() {
13441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        return (mFaces != null && mFaces.length > 0);
13541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
13641ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
13720b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    @Override
13820b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    public void showStart() {
139918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mColor = mFocusingColor;
14020b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li        invalidate();
14120b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    }
14220b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li
143e75e33cd3de39f1961d3fac8e7a51d126260de45Wu-cheng Li    // Ignore the parameter. No autofocus animation for face detection.
14420b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    @Override
145e75e33cd3de39f1961d3fac8e7a51d126260de45Wu-cheng Li    public void showSuccess(boolean timeout) {
146918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mColor = mFocusedColor;
14720b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li        invalidate();
14820b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    }
14920b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li
150e75e33cd3de39f1961d3fac8e7a51d126260de45Wu-cheng Li    // Ignore the parameter. No autofocus animation for face detection.
15120b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    @Override
152e75e33cd3de39f1961d3fac8e7a51d126260de45Wu-cheng Li    public void showFail(boolean timeout) {
153918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mColor = mFailColor;
15420b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li        invalidate();
15520b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    }
15620b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li
15720b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    @Override
15820b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li    public void clear() {
15920b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li        // Face indicator is displayed during preview. Do not clear the
16020b3c3526ebb8d8a6873f7a1110f23847a843515Wu-cheng Li        // drawable.
161918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb        mColor = mFocusingColor;
16241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        mFaces = null;
16341ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        invalidate();
16441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
16541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
166d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li    public void pause() {
167d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li        mPause = true;
168d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li    }
169d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li
170d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li    public void resume() {
171d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li        mPause = false;
172d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li    }
173d480002ccad05cf992c628c72884091c36cc654cWu-cheng Li
1748dbb2a65ef1b0404c169e8fada6c0d0f154dd08fMichael Kolb    public void setBlockDraw(boolean block) {
1758dbb2a65ef1b0404c169e8fada6c0d0f154dd08fMichael Kolb        mBlocked = block;
1768dbb2a65ef1b0404c169e8fada6c0d0f154dd08fMichael Kolb    }
1778dbb2a65ef1b0404c169e8fada6c0d0f154dd08fMichael Kolb
17841ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    @Override
17941ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    protected void onDraw(Canvas canvas) {
1808dbb2a65ef1b0404c169e8fada6c0d0f154dd08fMichael Kolb        if (!mBlocked && (mFaces != null) && (mFaces.length > 0)) {
18141d49f0fa2c9c93869bacfd6e7f76299f482e40fMichael Kolb            final CameraScreenNail sn = ((CameraActivity) getContext()).getCameraScreenNail();
1828a3640ceaab154f3a5c4c043c6f8db8afd815e6eJohn Reck            int rw = sn.getUncroppedRenderWidth();
1838a3640ceaab154f3a5c4c043c6f8db8afd815e6eJohn Reck            int rh = sn.getUncroppedRenderHeight();
18441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li            // Prepare the matrix.
18540d03901adfba08db6f2f274ded7d60ff4001663Michael Kolb            if (((rh > rw) && ((mDisplayOrientation == 0) || (mDisplayOrientation == 180)))
18640d03901adfba08db6f2f274ded7d60ff4001663Michael Kolb                    || ((rw > rh) && ((mDisplayOrientation == 90) || (mDisplayOrientation == 270)))) {
18741d49f0fa2c9c93869bacfd6e7f76299f482e40fMichael Kolb                int temp = rw;
18841d49f0fa2c9c93869bacfd6e7f76299f482e40fMichael Kolb                rw = rh;
18941d49f0fa2c9c93869bacfd6e7f76299f482e40fMichael Kolb                rh = temp;
19041d49f0fa2c9c93869bacfd6e7f76299f482e40fMichael Kolb            }
19141d49f0fa2c9c93869bacfd6e7f76299f482e40fMichael Kolb            Util.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, rw, rh);
19241d49f0fa2c9c93869bacfd6e7f76299f482e40fMichael Kolb            int dx = (getWidth() - rw) / 2;
19341d49f0fa2c9c93869bacfd6e7f76299f482e40fMichael Kolb            int dy = (getHeight() - rh) / 2;
19441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li
195c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            // Focus indicator is directional. Rotate the matrix and the canvas
196c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            // so it looks correctly in all orientations.
197c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            canvas.save();
198c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            mMatrix.postRotate(mOrientation); // postRotate is clockwise
199c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            canvas.rotate(-mOrientation); // rotate is counter-clockwise (for canvas)
20041ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li            for (int i = 0; i < mFaces.length; i++) {
20159585c89a3f85ea92c06426e5f6cc6038f91e308Wu-cheng Li                // Filter out false positives.
20259585c89a3f85ea92c06426e5f6cc6038f91e308Wu-cheng Li                if (mFaces[i].score < 50) continue;
20359585c89a3f85ea92c06426e5f6cc6038f91e308Wu-cheng Li
20441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li                // Transform the coordinates.
20541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li                mRect.set(mFaces[i].rect);
206048edf2ef22607b85bc4b062e2af71804f5b3530Chih-yu Huang                if (LOGV) Util.dumpRect(mRect, "Original rect");
20741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li                mMatrix.mapRect(mRect);
208048edf2ef22607b85bc4b062e2af71804f5b3530Chih-yu Huang                if (LOGV) Util.dumpRect(mRect, "Transformed rect");
209918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb                mPaint.setColor(mColor);
21041d49f0fa2c9c93869bacfd6e7f76299f482e40fMichael Kolb                mRect.offset(dx, dy);
211918c02ec856ea0e24fb9e57abace7f42120b5987Michael Kolb                canvas.drawOval(mRect, mPaint);
21241ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li            }
213c7e69709baea0cd9e49eed005d85714326b27da1Wu-cheng Li            canvas.restore();
21441ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        }
21541ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li        super.onDraw(canvas);
21641ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li    }
21741ecefdc167ca88d785e6a1d58b706cc7f03c2bdWu-cheng Li}
218