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