]> Pileus Git - ~andy/freeotp/blob - src/org/fedorahosted/freeotp/CameraDialogFragment.java
Support camera hardware oreintation offsets
[~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.view.Surface;
34 import android.view.SurfaceHolder;
35 import android.view.SurfaceView;
36 import android.view.View;
37 import android.widget.TextView;
38
39 public class CameraDialogFragment extends BaseAlertDialogFragment implements SurfaceHolder.Callback {
40         public static final String FRAGMENT_TAG = "fragment_camera";
41
42         private final CameraInfo mCameraInfo = new CameraInfo();
43         private final DecodeAsyncTask mDecodeAsyncTask;
44         private final int mCameraId;
45         private Camera mCamera;
46
47         public CameraDialogFragment() {
48                 super(R.string.scan_qr_code, R.layout.camera,
49                         android.R.string.cancel, R.string.manual_entry, 0);
50
51                 // Find a back-facing camera
52                 int cameraId;
53                 for (cameraId = Camera.getNumberOfCameras() - 1; cameraId >= 0; cameraId--) {
54                         Camera.getCameraInfo(cameraId, mCameraInfo);
55                         if (mCameraInfo.facing == CameraInfo.CAMERA_FACING_BACK)
56                                 break;
57                 }
58                 mCameraId = cameraId;
59
60                 // Create the decoder thread
61                 mDecodeAsyncTask = new DecodeAsyncTask() {
62                         @Override
63                         protected void onPostExecute(String result) {
64                                 super.onPostExecute(result);
65                                 if (result != null)
66                                         ((MainActivity) getActivity()).tokenURIReceived(result);
67                                 dismiss();
68                         }
69                 };
70         }
71
72         @Override
73         public void onCreate(Bundle savedInstanceState) {
74                 super.onCreate(savedInstanceState);
75
76                 // If we have no back facing camera, open the manual dialog
77                 if (mCameraId < 0) {
78                         new ManualDialogFragment().show(getFragmentManager(),
79                                         ManualDialogFragment.FRAGMENT_TAG);
80                         dismiss();
81                         return;
82                 }
83
84                 mDecodeAsyncTask.execute();
85         }
86
87         @Override
88         public void onDestroy() {
89                 super.onDestroy();
90                 mDecodeAsyncTask.cancel(true);
91         }
92
93         @Override
94         protected void onViewInflated(View view) {
95                 SurfaceView sv = (SurfaceView) view.findViewById(R.id.camera_surfaceview);
96                 sv.getHolder().addCallback(this);
97         }
98
99         @Override
100         public void onClick(DialogInterface dialog, int which) {
101                 if (which != AlertDialog.BUTTON_NEUTRAL)
102                         return;
103
104                 new ManualDialogFragment().show(getFragmentManager(),
105                                 ManualDialogFragment.FRAGMENT_TAG);
106         }
107
108         @Override
109         public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
110                 if (mCamera == null)
111                         return;
112
113                 int rotation = 0;
114                 switch (getActivity().getWindowManager().getDefaultDisplay().getRotation()) {
115                 case Surface.ROTATION_0:
116                         rotation = 0;
117                         break;
118                 case Surface.ROTATION_90:
119                         rotation = 90;
120                         break;
121                 case Surface.ROTATION_180:
122                         rotation = 180;
123                         break;
124                 case Surface.ROTATION_270:
125                         rotation = 270;
126                         break;
127                 }
128
129                 mCamera.setDisplayOrientation((mCameraInfo.orientation - rotation + 360) % 360);
130                 mCamera.startPreview();
131         }
132
133         @Override
134         @TargetApi(14)
135         public void surfaceCreated(SurfaceHolder holder) {
136                 surfaceDestroyed(holder);
137
138                 try {
139                         // Open the camera
140                         mCamera = Camera.open(mCameraId);
141                         mCamera.setPreviewDisplay(holder);
142                         mCamera.setPreviewCallback(mDecodeAsyncTask);
143                 } catch (Exception e) {
144                         e.printStackTrace();
145                         surfaceDestroyed(holder);
146
147                         // Show error message
148                         Dialog d = getDialog();
149                         SurfaceView sv = (SurfaceView) d.findViewById(R.id.camera_surfaceview);
150                         TextView tv = (TextView) d.findViewById(R.id.camera_textview);
151                         sv.setVisibility(View.INVISIBLE);
152                         tv.setVisibility(View.VISIBLE);
153                         return;
154                 }
155
156                 // Set auto-focus mode
157                 Parameters params = mCamera.getParameters();
158                 List<String> modes = params.getSupportedFocusModes();
159                 if (modes.contains(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
160                         params.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
161                 else if (modes.contains(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO))
162                         params.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
163                 else if (modes.contains(Parameters.FOCUS_MODE_AUTO))
164                         params.setFocusMode(Parameters.FOCUS_MODE_AUTO);
165                 mCamera.setParameters(params);
166         }
167
168         @Override
169         public void surfaceDestroyed(SurfaceHolder holder) {
170                 if (mCamera == null)
171                         return;
172
173                 mCamera.stopPreview();
174                 mCamera.setPreviewCallback(null);
175                 mCamera.release();
176                 mCamera = null;
177         }
178 }