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.content.Context;
20 import android.content.Intent;
21 import android.content.res.Configuration;
22 import android.graphics.drawable.Drawable;
23 import android.os.Bundle;
24 import android.provider.MediaStore;
25 import android.view.KeyEvent;
26 import android.view.LayoutInflater;
27 import android.view.MotionEvent;
28 import android.view.OrientationEventListener;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.widget.FrameLayout;
32 
33 import com.android.camera.ui.CameraSwitcher;
34 import com.android.gallery3d.app.PhotoPage;
35 import com.android.gallery3d.common.ApiHelper;
36 import com.android.gallery3d.util.LightCycleHelper;
37 
38 public class CameraActivity extends ActivityBase
39         implements CameraSwitcher.CameraSwitchListener {
40     public static final int PHOTO_MODULE_INDEX = 0;
41     public static final int VIDEO_MODULE_INDEX = 1;
42     public static final int PANORAMA_MODULE_INDEX = 2;
43     public static final int LIGHTCYCLE_MODULE_INDEX = 3;
44 
45     CameraModule mCurrentModule;
46     private FrameLayout mFrame;
47     private ShutterButton mShutter;
48     private CameraSwitcher mSwitcher;
49     private View mShutterSwitcher;
50     private View mControlsBackground;
51     private Drawable[] mDrawables;
52     private int mCurrentModuleIndex;
53     private MotionEvent mDown;
54 
55     private MyOrientationEventListener mOrientationListener;
56     // The degrees of the device rotated clockwise from its natural orientation.
57     private int mLastRawOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
58 
59     private static final String TAG = "CAM_activity";
60 
61     private static final int[] DRAW_IDS = {
62             R.drawable.ic_switch_camera,
63             R.drawable.ic_switch_video,
64             R.drawable.ic_switch_pan,
65             R.drawable.ic_switch_photosphere
66     };
67 
68     @Override
onCreate(Bundle state)69     public void onCreate(Bundle state) {
70         super.onCreate(state);
71         setContentView(R.layout.camera_main);
72         mFrame = (FrameLayout) findViewById(R.id.main_content);
73         mDrawables = new Drawable[DRAW_IDS.length];
74         for (int i = 0; i < DRAW_IDS.length; i++) {
75             mDrawables[i] = getResources().getDrawable(DRAW_IDS[i]);
76         }
77         init();
78         if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction())
79                 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) {
80             mCurrentModule = new VideoModule();
81             mCurrentModuleIndex = VIDEO_MODULE_INDEX;
82         } else {
83             mCurrentModule = new PhotoModule();
84             mCurrentModuleIndex = PHOTO_MODULE_INDEX;
85         }
86         mCurrentModule.init(this, mFrame, true);
87         mSwitcher.setCurrentIndex(mCurrentModuleIndex);
88         mOrientationListener = new MyOrientationEventListener(this);
89     }
90 
init()91     public void init() {
92         mControlsBackground = findViewById(R.id.controls);
93         mShutterSwitcher = findViewById(R.id.camera_shutter_switcher);
94         mShutter = (ShutterButton) findViewById(R.id.shutter_button);
95         mSwitcher = (CameraSwitcher) findViewById(R.id.camera_switcher);
96         int totaldrawid = (LightCycleHelper.hasLightCycleCapture(this)
97                                 ? DRAW_IDS.length : DRAW_IDS.length - 1);
98         if (!ApiHelper.HAS_OLD_PANORAMA) totaldrawid--;
99 
100         int[] drawids = new int[totaldrawid];
101         int[] moduleids = new int[totaldrawid];
102         int ix = 0;
103         for (int i = 0; i < mDrawables.length; i++) {
104             if (i == PANORAMA_MODULE_INDEX && !ApiHelper.HAS_OLD_PANORAMA) {
105                 continue; // not enabled, so don't add to UI
106             }
107             if (i == LIGHTCYCLE_MODULE_INDEX && !LightCycleHelper.hasLightCycleCapture(this)) {
108                 continue; // not enabled, so don't add to UI
109             }
110             moduleids[ix] = i;
111             drawids[ix++] = DRAW_IDS[i];
112         }
113         mSwitcher.setIds(moduleids, drawids);
114         mSwitcher.setSwitchListener(this);
115         mSwitcher.setCurrentIndex(mCurrentModuleIndex);
116     }
117 
118     private class MyOrientationEventListener
119             extends OrientationEventListener {
MyOrientationEventListener(Context context)120         public MyOrientationEventListener(Context context) {
121             super(context);
122         }
123 
124         @Override
onOrientationChanged(int orientation)125         public void onOrientationChanged(int orientation) {
126             // We keep the last known orientation. So if the user first orient
127             // the camera then point the camera to floor or sky, we still have
128             // the correct orientation.
129             if (orientation == ORIENTATION_UNKNOWN) return;
130             mLastRawOrientation = orientation;
131             mCurrentModule.onOrientationChanged(orientation);
132         }
133     }
134 
135     @Override
onCameraSelected(int i)136     public void onCameraSelected(int i) {
137         if (mPaused) return;
138         if (i != mCurrentModuleIndex) {
139             mPaused = true;
140             boolean canReuse = canReuseScreenNail();
141             CameraHolder.instance().keep();
142             closeModule(mCurrentModule);
143             mCurrentModuleIndex = i;
144             switch (i) {
145                 case VIDEO_MODULE_INDEX:
146                     mCurrentModule = new VideoModule();
147                     break;
148                 case PHOTO_MODULE_INDEX:
149                     mCurrentModule = new PhotoModule();
150                     break;
151                 case PANORAMA_MODULE_INDEX:
152                     mCurrentModule = new PanoramaModule();
153                     break;
154                 case LIGHTCYCLE_MODULE_INDEX:
155                     mCurrentModule = LightCycleHelper.createPanoramaModule();
156                     break;
157             }
158             openModule(mCurrentModule, canReuse);
159             mCurrentModule.onOrientationChanged(mLastRawOrientation);
160         }
161     }
162 
163     @Override
onShowSwitcherPopup()164     public void onShowSwitcherPopup() {
165         mCurrentModule.onShowSwitcherPopup();
166     }
167 
openModule(CameraModule module, boolean canReuse)168     private void openModule(CameraModule module, boolean canReuse) {
169         module.init(this, mFrame, canReuse && canReuseScreenNail());
170         mPaused = false;
171         module.onResumeBeforeSuper();
172         module.onResumeAfterSuper();
173     }
174 
closeModule(CameraModule module)175     private void closeModule(CameraModule module) {
176         module.onPauseBeforeSuper();
177         module.onPauseAfterSuper();
178         mFrame.removeAllViews();
179     }
180 
getShutterButton()181     public ShutterButton getShutterButton() {
182         return mShutter;
183     }
184 
hideUI()185     public void hideUI() {
186         mControlsBackground.setVisibility(View.INVISIBLE);
187         hideSwitcher();
188         mShutter.setVisibility(View.GONE);
189     }
190 
showUI()191     public void showUI() {
192         mControlsBackground.setVisibility(View.VISIBLE);
193         showSwitcher();
194         mShutter.setVisibility(View.VISIBLE);
195         // Force a layout change to show shutter button
196         mShutter.requestLayout();
197     }
198 
hideSwitcher()199     public void hideSwitcher() {
200         mSwitcher.closePopup();
201         mSwitcher.setVisibility(View.INVISIBLE);
202     }
203 
showSwitcher()204     public void showSwitcher() {
205         if (mCurrentModule.needsSwitcher()) {
206             mSwitcher.setVisibility(View.VISIBLE);
207         }
208     }
209 
isInCameraApp()210     public boolean isInCameraApp() {
211         return mShowCameraAppView;
212     }
213 
214     @Override
onConfigurationChanged(Configuration config)215     public void onConfigurationChanged(Configuration config) {
216         super.onConfigurationChanged(config);
217 
218         ViewGroup appRoot = (ViewGroup) findViewById(R.id.content);
219         // remove old switcher, shutter and shutter icon
220         View cameraControlsView = findViewById(R.id.camera_shutter_switcher);
221         appRoot.removeView(cameraControlsView);
222 
223         // create new layout with the current orientation
224         LayoutInflater inflater = getLayoutInflater();
225         inflater.inflate(R.layout.camera_shutter_switcher, appRoot);
226         init();
227 
228         if (mShowCameraAppView) {
229             showUI();
230         } else {
231             hideUI();
232         }
233         mCurrentModule.onConfigurationChanged(config);
234     }
235 
236     @Override
onPause()237     public void onPause() {
238         mPaused = true;
239         mOrientationListener.disable();
240         mCurrentModule.onPauseBeforeSuper();
241         super.onPause();
242         mCurrentModule.onPauseAfterSuper();
243     }
244 
245     @Override
onResume()246     public void onResume() {
247         mPaused = false;
248         mOrientationListener.enable();
249         mCurrentModule.onResumeBeforeSuper();
250         super.onResume();
251         mCurrentModule.onResumeAfterSuper();
252     }
253 
254     @Override
onFullScreenChanged(boolean full)255     protected void onFullScreenChanged(boolean full) {
256         if (full) {
257             showUI();
258         } else {
259             hideUI();
260         }
261         super.onFullScreenChanged(full);
262         mCurrentModule.onFullScreenChanged(full);
263     }
264 
265     @Override
onStop()266     protected void onStop() {
267         super.onStop();
268         mCurrentModule.onStop();
269         getStateManager().clearTasks();
270     }
271 
272     @Override
onNewIntent(Intent intent)273     protected void onNewIntent(Intent intent) {
274         super.onNewIntent(intent);
275         getStateManager().clearActivityResult();
276     }
277 
278     @Override
installIntentFilter()279     protected void installIntentFilter() {
280         super.installIntentFilter();
281         mCurrentModule.installIntentFilter();
282     }
283 
284     @Override
onActivityResult( int requestCode, int resultCode, Intent data)285     protected void onActivityResult(
286             int requestCode, int resultCode, Intent data) {
287         // Only PhotoPage understands ProxyLauncher.RESULT_USER_CANCELED
288         if (resultCode == ProxyLauncher.RESULT_USER_CANCELED
289                 && !(getStateManager().getTopState() instanceof PhotoPage)) {
290             resultCode = RESULT_CANCELED;
291         }
292         super.onActivityResult(requestCode, resultCode, data);
293         // Unmap cancel vs. reset
294         if (resultCode == ProxyLauncher.RESULT_USER_CANCELED) {
295             resultCode = RESULT_CANCELED;
296         }
297         mCurrentModule.onActivityResult(requestCode, resultCode, data);
298     }
299 
300     // Preview area is touched. Handle touch focus.
301     @Override
onSingleTapUp(View view, int x, int y)302     protected void onSingleTapUp(View view, int x, int y) {
303         mCurrentModule.onSingleTapUp(view, x, y);
304     }
305 
306     @Override
onBackPressed()307     public void onBackPressed() {
308         if (!mCurrentModule.onBackPressed()) {
309             super.onBackPressed();
310         }
311     }
312 
313     @Override
onKeyDown(int keyCode, KeyEvent event)314     public boolean onKeyDown(int keyCode, KeyEvent event) {
315         return mCurrentModule.onKeyDown(keyCode,  event)
316                 || super.onKeyDown(keyCode, event);
317     }
318 
319     @Override
onKeyUp(int keyCode, KeyEvent event)320     public boolean onKeyUp(int keyCode, KeyEvent event) {
321         return mCurrentModule.onKeyUp(keyCode,  event)
322                 || super.onKeyUp(keyCode, event);
323     }
324 
cancelActivityTouchHandling()325     public void cancelActivityTouchHandling() {
326         if (mDown != null) {
327             MotionEvent cancel = MotionEvent.obtain(mDown);
328             cancel.setAction(MotionEvent.ACTION_CANCEL);
329             super.dispatchTouchEvent(cancel);
330         }
331     }
332 
333     @Override
dispatchTouchEvent(MotionEvent m)334     public boolean dispatchTouchEvent(MotionEvent m) {
335         if (m.getActionMasked() == MotionEvent.ACTION_DOWN) {
336             mDown = m;
337         }
338         if ((mSwitcher != null) && mSwitcher.showsPopup() && !mSwitcher.isInsidePopup(m)) {
339             return mSwitcher.onTouch(null, m);
340         } else {
341             return mShutterSwitcher.dispatchTouchEvent(m)
342                     || mCurrentModule.dispatchTouchEvent(m);
343         }
344     }
345 
346     @Override
startActivityForResult(Intent intent, int requestCode)347     public void startActivityForResult(Intent intent, int requestCode) {
348         Intent proxyIntent = new Intent(this, ProxyLauncher.class);
349         proxyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
350         proxyIntent.putExtra(Intent.EXTRA_INTENT, intent);
351         super.startActivityForResult(proxyIntent, requestCode);
352     }
353 
superDispatchTouchEvent(MotionEvent m)354     public boolean superDispatchTouchEvent(MotionEvent m) {
355         return super.dispatchTouchEvent(m);
356     }
357 
358     // Preview texture has been copied. Now camera can be released and the
359     // animation can be started.
360     @Override
onPreviewTextureCopied()361     public void onPreviewTextureCopied() {
362         mCurrentModule.onPreviewTextureCopied();
363     }
364 
365     @Override
onCaptureTextureCopied()366     public void onCaptureTextureCopied() {
367         mCurrentModule.onCaptureTextureCopied();
368     }
369 
370     @Override
onUserInteraction()371     public void onUserInteraction() {
372         super.onUserInteraction();
373         mCurrentModule.onUserInteraction();
374     }
375 
376     @Override
updateStorageHintOnResume()377     protected boolean updateStorageHintOnResume() {
378         return mCurrentModule.updateStorageHintOnResume();
379     }
380 
381     @Override
updateCameraAppView()382     public void updateCameraAppView() {
383         super.updateCameraAppView();
384         mCurrentModule.updateCameraAppView();
385     }
386 
canReuseScreenNail()387     private boolean canReuseScreenNail() {
388         return mCurrentModuleIndex == PHOTO_MODULE_INDEX
389                 || mCurrentModuleIndex == VIDEO_MODULE_INDEX
390                 || mCurrentModuleIndex == LIGHTCYCLE_MODULE_INDEX;
391     }
392 
393     @Override
isPanoramaActivity()394     public boolean isPanoramaActivity() {
395         return (mCurrentModuleIndex == PANORAMA_MODULE_INDEX);
396     }
397 
398     // Accessor methods for getting latency times used in performance testing
getAutoFocusTime()399     public long getAutoFocusTime() {
400         return (mCurrentModule instanceof PhotoModule) ?
401                 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
402     }
403 
getShutterLag()404     public long getShutterLag() {
405         return (mCurrentModule instanceof PhotoModule) ?
406                 ((PhotoModule) mCurrentModule).mShutterLag : -1;
407     }
408 
getShutterToPictureDisplayedTime()409     public long getShutterToPictureDisplayedTime() {
410         return (mCurrentModule instanceof PhotoModule) ?
411                 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
412     }
413 
getPictureDisplayedToJpegCallbackTime()414     public long getPictureDisplayedToJpegCallbackTime() {
415         return (mCurrentModule instanceof PhotoModule) ?
416                 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
417     }
418 
getJpegCallbackFinishTime()419     public long getJpegCallbackFinishTime() {
420         return (mCurrentModule instanceof PhotoModule) ?
421                 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
422     }
423 
getCaptureStartTime()424     public long getCaptureStartTime() {
425         return (mCurrentModule instanceof PhotoModule) ?
426                 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
427     }
428 
isRecording()429     public boolean isRecording() {
430         return (mCurrentModule instanceof VideoModule) ?
431                 ((VideoModule) mCurrentModule).isRecording() : false;
432     }
433 
getCameraScreenNail()434     public CameraScreenNail getCameraScreenNail() {
435         return (CameraScreenNail) mCameraScreenNail;
436     }
437 }
438