OSDN Git Service

Adding ability to zoom in accessibility mode using TalkBack
[android-x86/packages-apps-Camera2.git] / src / com / android / camera / PhotoUI.java
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.camera;
18
19 import android.app.Dialog;
20 import android.content.DialogInterface;
21 import android.graphics.Bitmap;
22 import android.graphics.RectF;
23 import android.graphics.SurfaceTexture;
24 import android.hardware.Camera.Face;
25 import android.os.AsyncTask;
26 import android.view.GestureDetector;
27 import android.view.MotionEvent;
28 import android.view.View;
29 import android.view.ViewGroup;
30 import android.widget.FrameLayout;
31 import android.widget.ImageView;
32
33 import com.android.camera.captureintent.PictureDecoder;
34 import com.android.camera.debug.DebugPropertyHelper;
35 import com.android.camera.debug.Log;
36 import com.android.camera.ui.CountDownView;
37 import com.android.camera.ui.FaceView;
38 import com.android.camera.ui.PreviewOverlay;
39 import com.android.camera.ui.PreviewStatusListener;
40 import com.android.camera.ui.focus.FocusRing;
41 import com.android.camera2.R;
42 import com.android.ex.camera2.portability.CameraAgent;
43 import com.android.ex.camera2.portability.CameraCapabilities;
44 import com.android.ex.camera2.portability.CameraSettings;
45
46 public class PhotoUI implements PreviewStatusListener,
47     CameraAgent.CameraFaceDetectionCallback, PreviewStatusListener.PreviewAreaChangedListener {
48
49     private static final Log.Tag TAG = new Log.Tag("PhotoUI");
50     private static final int DOWN_SAMPLE_FACTOR = 4;
51     private static final float UNSET = 0f;
52
53     private final PreviewOverlay mPreviewOverlay;
54     private final FocusRing mFocusRing;
55     private final CameraActivity mActivity;
56     private final PhotoController mController;
57
58     private final View mRootView;
59     private Dialog mDialog = null;
60
61     // TODO: Remove face view logic if UX does not bring it back within a month.
62     private final FaceView mFaceView;
63     private DecodeImageForReview mDecodeTaskForReview = null;
64
65     private float mZoomMax;
66
67     private int mPreviewWidth = 0;
68     private int mPreviewHeight = 0;
69     private float mAspectRatio = UNSET;
70
71     private ImageView mIntentReviewImageView;
72
73     private final GestureDetector.OnGestureListener mPreviewGestureListener
74             = new GestureDetector.SimpleOnGestureListener() {
75         @Override
76         public boolean onSingleTapUp(MotionEvent ev) {
77             mController.onSingleTapUp(null, (int) ev.getX(), (int) ev.getY());
78             return true;
79         }
80     };
81     private final DialogInterface.OnDismissListener mOnDismissListener
82             = new DialogInterface.OnDismissListener() {
83         @Override
84         public void onDismiss(DialogInterface dialog) {
85             mDialog = null;
86         }
87     };
88     private final CountDownView mCountdownView;
89
90     @Override
91     public GestureDetector.OnGestureListener getGestureListener() {
92         return mPreviewGestureListener;
93     }
94
95     @Override
96     public View.OnTouchListener getTouchListener() {
97         return null;
98     }
99
100     @Override
101     public void onPreviewLayoutChanged(View v, int left, int top, int right,
102             int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
103         int width = right - left;
104         int height = bottom - top;
105         if (mPreviewWidth != width || mPreviewHeight != height) {
106             mPreviewWidth = width;
107             mPreviewHeight = height;
108         }
109     }
110
111     @Override
112     public boolean shouldAutoAdjustTransformMatrixOnLayout() {
113         return true;
114     }
115
116     @Override
117     public void onPreviewFlipped() {
118         mController.updateCameraOrientation();
119     }
120
121     /**
122      * Starts the countdown timer.
123      *
124      * @param sec seconds to countdown
125      */
126     public void startCountdown(int sec) {
127         mCountdownView.startCountDown(sec);
128     }
129
130     /**
131      * Sets a listener that gets notified when the countdown is finished.
132      */
133     public void setCountdownFinishedListener(CountDownView.OnCountDownStatusListener listener) {
134         mCountdownView.setCountDownStatusListener(listener);
135     }
136
137     /**
138      * Returns whether the countdown is on-going.
139      */
140     public boolean isCountingDown() {
141         return mCountdownView.isCountingDown();
142     }
143
144     /**
145      * Cancels the on-going countdown, if any.
146      */
147     public void cancelCountDown() {
148         mCountdownView.cancelCountDown();
149     }
150
151     @Override
152     public void onPreviewAreaChanged(RectF previewArea) {
153         if (mFaceView != null) {
154             mFaceView.onPreviewAreaChanged(previewArea);
155         }
156         mCountdownView.onPreviewAreaChanged(previewArea);
157     }
158
159     private class DecodeTask extends AsyncTask<Void, Void, Bitmap> {
160         private final byte [] mData;
161         private final int mOrientation;
162         private final boolean mMirror;
163
164         public DecodeTask(byte[] data, int orientation, boolean mirror) {
165             mData = data;
166             mOrientation = orientation;
167             mMirror = mirror;
168         }
169
170         @Override
171         protected Bitmap doInBackground(Void... params) {
172             // Decode image in background.
173             return PictureDecoder.decode(mData, DOWN_SAMPLE_FACTOR, mOrientation, mMirror);
174         }
175     }
176
177     private class DecodeImageForReview extends DecodeTask {
178         public DecodeImageForReview(byte[] data, int orientation, boolean mirror) {
179             super(data, orientation, mirror);
180         }
181
182         @Override
183         protected void onPostExecute(Bitmap bitmap) {
184             if (isCancelled()) {
185                 return;
186             }
187
188             mIntentReviewImageView.setImageBitmap(bitmap);
189             showIntentReviewImageView();
190
191             mDecodeTaskForReview = null;
192         }
193     }
194
195     public PhotoUI(CameraActivity activity, PhotoController controller, View parent) {
196         mActivity = activity;
197         mController = controller;
198         mRootView = parent;
199
200         ViewGroup moduleRoot = (ViewGroup) mRootView.findViewById(R.id.module_layout);
201         mActivity.getLayoutInflater().inflate(R.layout.photo_module,
202                  moduleRoot, true);
203         initIndicators();
204         mFocusRing = (FocusRing) mRootView.findViewById(R.id.focus_ring);
205         mPreviewOverlay = (PreviewOverlay) mRootView.findViewById(R.id.preview_overlay);
206         mCountdownView = (CountDownView) mRootView.findViewById(R.id.count_down_view);
207         // Show faces if we are in debug mode.
208         if (DebugPropertyHelper.showCaptureDebugUI()) {
209             mFaceView = (FaceView) mRootView.findViewById(R.id.face_view);
210         } else {
211             mFaceView = null;
212         }
213
214         if (mController.isImageCaptureIntent()) {
215             initIntentReviewImageView();
216         }
217     }
218
219     private void initIntentReviewImageView() {
220         mIntentReviewImageView = (ImageView) mRootView.findViewById(R.id.intent_review_imageview);
221         mActivity.getCameraAppUI().addPreviewAreaChangedListener(
222                 new PreviewStatusListener.PreviewAreaChangedListener() {
223                     @Override
224                     public void onPreviewAreaChanged(RectF previewArea) {
225                         FrameLayout.LayoutParams params =
226                             (FrameLayout.LayoutParams) mIntentReviewImageView.getLayoutParams();
227                         params.width = (int) previewArea.width();
228                         params.height = (int) previewArea.height();
229                         params.setMargins((int) previewArea.left, (int) previewArea.top, 0, 0);
230                         mIntentReviewImageView.setLayoutParams(params);
231                     }
232                 });
233     }
234
235     /**
236      * Show the image review over the live preview for intent captures.
237      */
238     public void showIntentReviewImageView() {
239         if (mIntentReviewImageView != null) {
240             mIntentReviewImageView.setVisibility(View.VISIBLE);
241         }
242     }
243
244     /**
245      * Hide the image review over the live preview for intent captures.
246      */
247     public void hideIntentReviewImageView() {
248         if (mIntentReviewImageView != null) {
249             mIntentReviewImageView.setVisibility(View.INVISIBLE);
250         }
251     }
252
253
254     public FocusRing getFocusRing() {
255         return mFocusRing;
256     }
257
258     public void updatePreviewAspectRatio(float aspectRatio) {
259         if (aspectRatio <= 0) {
260             Log.e(TAG, "Invalid aspect ratio: " + aspectRatio);
261             return;
262         }
263         if (aspectRatio < 1f) {
264             aspectRatio = 1f / aspectRatio;
265         }
266
267         if (mAspectRatio != aspectRatio) {
268             mAspectRatio = aspectRatio;
269             // Update transform matrix with the new aspect ratio.
270             mController.updatePreviewAspectRatio(mAspectRatio);
271         }
272     }
273
274     @Override
275     public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
276         mController.onPreviewUIReady();
277     }
278
279     @Override
280     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
281         // Ignored, Camera does all the work for us
282     }
283
284     @Override
285     public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
286         mController.onPreviewUIDestroyed();
287         return true;
288     }
289
290     @Override
291     public void onSurfaceTextureUpdated(SurfaceTexture surface) {
292     }
293
294     private void initIndicators() {
295         // TODO init toggle buttons on bottom bar here
296     }
297
298     public void onCameraOpened(CameraCapabilities capabilities, CameraSettings settings) {
299         initializeZoom(capabilities, settings);
300     }
301
302     public void animateCapture(final byte[] jpegData, int orientation, boolean mirror) {
303         // Decode jpeg byte array and then animate the jpeg
304         DecodeTask task = new DecodeTask(jpegData, orientation, mirror);
305         task.execute();
306     }
307
308     // called from onResume but only the first time
309     public void initializeFirstTime() {
310
311     }
312
313     // called from onResume every other time
314     public void initializeSecondTime(CameraCapabilities capabilities, CameraSettings settings) {
315         initializeZoom(capabilities, settings);
316         if (mController.isImageCaptureIntent()) {
317             hidePostCaptureAlert();
318         }
319     }
320
321     public void initializeZoom(CameraCapabilities capabilities, CameraSettings settings) {
322         if ((capabilities == null) || settings == null ||
323                 !capabilities.supports(CameraCapabilities.Feature.ZOOM)) {
324             return;
325         }
326         mZoomMax = capabilities.getMaxZoomRatio();
327         // Currently we use immediate zoom for fast zooming to get better UX and
328         // there is no plan to take advantage of the smooth zoom.
329         // TODO: Need to setup a path to AppUI to do this
330         mPreviewOverlay.setupZoom(mZoomMax, settings.getCurrentZoomRatio(),
331                 new ZoomChangeListener());
332     }
333
334     public void animateFlash() {
335         mController.startPreCaptureAnimation();
336     }
337
338     public boolean onBackPressed() {
339         // In image capture mode, back button should:
340         // 1) if there is any popup, dismiss them, 2) otherwise, get out of
341         // image capture
342         if (mController.isImageCaptureIntent()) {
343             mController.onCaptureCancelled();
344             return true;
345         } else if (!mController.isCameraIdle()) {
346             // ignore backs while we're taking a picture
347             return true;
348         } else {
349             return false;
350         }
351     }
352
353     protected void showCapturedImageForReview(byte[] jpegData, int orientation, boolean mirror) {
354         mDecodeTaskForReview = new DecodeImageForReview(jpegData, orientation, mirror);
355         mDecodeTaskForReview.execute();
356
357         mActivity.getCameraAppUI().transitionToIntentReviewLayout();
358         pauseFaceDetection();
359     }
360
361     protected void hidePostCaptureAlert() {
362         if (mDecodeTaskForReview != null) {
363             mDecodeTaskForReview.cancel(true);
364         }
365         resumeFaceDetection();
366     }
367
368     public void setDisplayOrientation(int orientation) {
369         if (mFaceView != null) {
370             mFaceView.setDisplayOrientation(orientation);
371         }
372     }
373
374     private class ZoomChangeListener implements PreviewOverlay.OnZoomChangedListener {
375         @Override
376         public void onZoomValueChanged(float ratio) {
377             mController.onZoomChanged(ratio);
378         }
379
380         @Override
381         public void onZoomStart() {
382         }
383
384         @Override
385         public void onZoomEnd() {
386         }
387     }
388
389     public void setSwipingEnabled(boolean enable) {
390         mActivity.setSwipingEnabled(enable);
391     }
392
393     public void onPause() {
394         if (mFaceView != null) {
395             mFaceView.clear();
396         }
397         if (mDialog != null) {
398             mDialog.dismiss();
399         }
400         // recalculate aspect ratio when restarting.
401         mAspectRatio = 0.0f;
402     }
403
404     public void clearFaces() {
405         if (mFaceView != null) {
406             mFaceView.clear();
407         }
408     }
409
410     public void pauseFaceDetection() {
411         if (mFaceView != null) {
412             mFaceView.pause();
413         }
414     }
415
416     public void resumeFaceDetection() {
417         if (mFaceView != null) {
418             mFaceView.resume();
419         }
420     }
421
422     public void onStartFaceDetection(int orientation, boolean mirror) {
423         if (mFaceView != null) {
424             mFaceView.clear();
425             mFaceView.setVisibility(View.VISIBLE);
426             mFaceView.setDisplayOrientation(orientation);
427             mFaceView.setMirror(mirror);
428             mFaceView.resume();
429         }
430     }
431
432     @Override
433     public void onFaceDetection(Face[] faces, CameraAgent.CameraProxy camera) {
434         if (mFaceView != null) {
435             mFaceView.setFaces(faces);
436         }
437     }
438
439 }