]> Pileus Git - ~andy/freeotp/blob - src/org/fedorahosted/freeotp/CameraDialogFragment.java
Fix an anonymous class leak in the autofocus code
[~andy/freeotp] / src / org / fedorahosted / freeotp / CameraDialogFragment.java
1 /*
2  * FreeOTP
3  *
4  * Authors: Nathaniel McCallum <npmccallum@redhat.com>
5  *
6  * Copyright (C) 2013  Nathaniel McCallum, Red Hat
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20
21 package org.fedorahosted.freeotp;
22
23 import java.util.List;
24
25 import android.annotation.TargetApi;
26 import android.app.AlertDialog;
27 import android.app.Dialog;
28 import android.content.DialogInterface;
29 import android.hardware.Camera;
30 import android.hardware.Camera.CameraInfo;
31 import android.hardware.Camera.Parameters;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.os.Message;
35 import android.view.Surface;
36 import android.view.SurfaceHolder;
37 import android.view.SurfaceView;
38 import android.view.View;
39 import android.widget.TextView;
40
41 public class CameraDialogFragment extends BaseAlertDialogFragment
42                         implements SurfaceHolder.Callback  {
43         public static final String FRAGMENT_TAG = "fragment_camera";
44
45         private final CameraInfo mCameraInfo = new CameraInfo();
46         private final DecodeAsyncTask mDecodeAsyncTask;
47         private final int mCameraId;
48         private Handler mHandler;
49         private Camera mCamera;
50
51         private static class AutoFocusHandler extends Handler
52                         implements Camera.AutoFocusCallback {
53                 private final Camera mCamera;
54
55                 public AutoFocusHandler(Camera camera) {
56                         mCamera = camera;
57                 }
58
59                 @Override
60                 public void handleMessage(Message msg) {
61                         super.handleMessage(msg);
62                         mCamera.autoFocus(this);
63                 }
64
65                 @Override
66                 public void onAutoFocus(boolean success, Camera camera) {
67                         sendEmptyMessageDelayed(0, 1000);
68                 }
69         }
70
71         public CameraDialogFragment() {
72                 super(R.string.scan_qr_code, R.layout.camera,
73                         android.R.string.cancel, R.string.manual_entry, 0);
74
75                 // Find a back-facing camera, otherwise use the first camera.
76                 int cameraId;
77                 for (cameraId = Camera.getNumberOfCameras() - 1; cameraId > 0; cameraId--) {
78                         Camera.getCameraInfo(cameraId, mCameraInfo);
79                         if (mCameraInfo.facing == CameraInfo.CAMERA_FACING_BACK)
80                                 break;
81                 }
82                 mCameraId = cameraId;
83
84                 // Create the decoder thread
85                 mDecodeAsyncTask = new DecodeAsyncTask() {
86                         @Override
87                         protected void onPostExecute(String result) {
88                                 super.onPostExecute(result);
89                                 if (result != null)
90                                         ((MainActivity) getActivity()).tokenURIReceived(result);
91                                 dismiss();
92                         }
93                 };
94         }
95
96         @Override
97         public void onCreate(Bundle savedInstanceState) {
98                 super.onCreate(savedInstanceState);
99
100                 // If we have no cameras, open the manual dialog
101                 if (mCameraId < 0) {
102                         new ManualDialogFragment().show(getFragmentManager(),
103                                         ManualDialogFragment.FRAGMENT_TAG);
104                         dismiss();
105                         return;
106                 }
107
108                 mDecodeAsyncTask.execute();
109         }
110
111         @Override
112         public void onDestroy() {
113                 super.onDestroy();
114                 mDecodeAsyncTask.cancel(true);
115         }
116
117         @Override
118         protected void onViewInflated(View view) {
119                 SurfaceView sv = (SurfaceView) view.findViewById(R.id.camera_surfaceview);
120                 sv.getHolder().addCallback(this);
121         }
122
123         @Override
124         public void onClick(DialogInterface dialog, int which) {
125                 if (which != AlertDialog.BUTTON_NEUTRAL)
126                         return;
127
128                 new ManualDialogFragment().show(getFragmentManager(),
129                                 ManualDialogFragment.FRAGMENT_TAG);
130         }
131
132         @Override
133         public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
134                 if (mCamera == null)
135                         return;
136
137                 // The code in this section comes from the developer docs. See:
138                 //    http://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)
139
140                 int rotation = 0;
141                 switch (getActivity().getWindowManager().getDefaultDisplay().getRotation()) {
142                 case Surface.ROTATION_0:
143                         rotation = 0;
144                         break;
145                 case Surface.ROTATION_90:
146                         rotation = 90;
147                         break;
148                 case Surface.ROTATION_180:
149                         rotation = 180;
150                         break;
151                 case Surface.ROTATION_270:
152                         rotation = 270;
153                         break;
154                 }
155
156                 int result = 0;
157                 switch (mCameraInfo.facing) {
158                 case Camera.CameraInfo.CAMERA_FACING_FRONT:
159                         result = (mCameraInfo.orientation + rotation) % 360;
160                         result = (360 - result) % 360; // compensate the mirror
161                         break;
162
163                 case Camera.CameraInfo.CAMERA_FACING_BACK:
164                         result = (mCameraInfo.orientation - rotation + 360) % 360;
165                         break;
166                 }
167
168                 mCamera.setDisplayOrientation(result);
169                 mCamera.startPreview();
170
171                 if (mHandler != null)
172                         mHandler.sendEmptyMessageDelayed(0, 100);
173         }
174
175         @Override
176         @TargetApi(14)
177         public void surfaceCreated(SurfaceHolder holder) {
178                 surfaceDestroyed(holder);
179
180                 try {
181                         // Open the camera
182                         mCamera = Camera.open(mCameraId);
183                         mCamera.setPreviewDisplay(holder);
184                         mCamera.setPreviewCallback(mDecodeAsyncTask);
185                 } catch (Exception e) {
186                         e.printStackTrace();
187                         surfaceDestroyed(holder);
188
189                         // Show error message
190                         Dialog d = getDialog();
191                         SurfaceView sv = (SurfaceView) d.findViewById(R.id.camera_surfaceview);
192                         TextView tv = (TextView) d.findViewById(R.id.camera_textview);
193                         sv.setVisibility(View.INVISIBLE);
194                         tv.setVisibility(View.VISIBLE);
195                         return;
196                 }
197
198                 // Set auto-focus mode
199                 Parameters params = mCamera.getParameters();
200                 List<String> modes = params.getSupportedFocusModes();
201                 if (modes.contains(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
202                         params.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
203                 else if (modes.contains(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO))
204                         params.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
205                 else if (modes.contains(Parameters.FOCUS_MODE_AUTO)) {
206                         params.setFocusMode(Parameters.FOCUS_MODE_AUTO);
207                         mHandler = new AutoFocusHandler(mCamera);
208                 }
209                 mCamera.setParameters(params);
210         }
211
212         @Override
213         public void surfaceDestroyed(SurfaceHolder holder) {
214                 if (mCamera == null)
215                         return;
216
217                 if (mHandler != null) {
218                         mCamera.cancelAutoFocus();
219                         mHandler.removeMessages(0);
220                         mHandler = null;
221                 }
222
223                 mCamera.stopPreview();
224                 mCamera.setPreviewCallback(null);
225                 mCamera.release();
226                 mCamera = null;
227         }
228 }