Home | History | Annotate | Download | only in camera
      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.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.animation.ObjectAnimator;
     22 import android.content.ComponentName;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.ServiceConnection;
     26 import android.content.pm.ActivityInfo;
     27 import android.content.res.Configuration;
     28 import android.graphics.drawable.Drawable;
     29 import android.os.Bundle;
     30 import android.os.IBinder;
     31 import android.provider.MediaStore;
     32 import android.provider.Settings;
     33 import android.view.KeyEvent;
     34 import android.view.MotionEvent;
     35 import android.view.OrientationEventListener;
     36 import android.view.View;
     37 import android.view.Window;
     38 import android.view.WindowManager;
     39 import android.widget.FrameLayout;
     40 
     41 import com.android.camera.ui.CameraSwitcher;
     42 import com.android.gallery3d.R;
     43 import com.android.gallery3d.app.PhotoPage;
     44 import com.android.gallery3d.common.ApiHelper;
     45 import com.android.gallery3d.util.LightCycleHelper;
     46 
     47 public class CameraActivity extends ActivityBase
     48         implements CameraSwitcher.CameraSwitchListener {
     49     public static final int PHOTO_MODULE_INDEX = 0;
     50     public static final int VIDEO_MODULE_INDEX = 1;
     51     public static final int PANORAMA_MODULE_INDEX = 2;
     52     public static final int LIGHTCYCLE_MODULE_INDEX = 3;
     53 
     54     CameraModule mCurrentModule;
     55     private FrameLayout mFrame;
     56     private ShutterButton mShutter;
     57     private CameraSwitcher mSwitcher;
     58     private View mCameraControls;
     59     private View mControlsBackground;
     60     private View mPieMenuButton;
     61     private Drawable[] mDrawables;
     62     private int mCurrentModuleIndex;
     63     private MotionEvent mDown;
     64     private boolean mAutoRotateScreen;
     65     private int mHeightOrWidth = -1;
     66 
     67     private MyOrientationEventListener mOrientationListener;
     68     // The degrees of the device rotated clockwise from its natural orientation.
     69     private int mLastRawOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
     70 
     71     private MediaSaveService mMediaSaveService;
     72     private ServiceConnection mConnection = new ServiceConnection() {
     73             @Override
     74             public void onServiceConnected(ComponentName className, IBinder b) {
     75                 mMediaSaveService = ((MediaSaveService.LocalBinder) b).getService();
     76                 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService);
     77             }
     78             @Override
     79             public void onServiceDisconnected(ComponentName className) {
     80                 mMediaSaveService = null;
     81             }};
     82 
     83     private static final String TAG = "CAM_activity";
     84 
     85     private static final int[] DRAW_IDS = {
     86             R.drawable.ic_switch_camera,
     87             R.drawable.ic_switch_video,
     88             R.drawable.ic_switch_pan,
     89             R.drawable.ic_switch_photosphere
     90     };
     91 
     92     @Override
     93     public void onCreate(Bundle state) {
     94         super.onCreate(state);
     95         setContentView(R.layout.camera_main);
     96         mFrame = (FrameLayout) findViewById(R.id.camera_app_root);
     97         mDrawables = new Drawable[DRAW_IDS.length];
     98         for (int i = 0; i < DRAW_IDS.length; i++) {
     99             mDrawables[i] = getResources().getDrawable(DRAW_IDS[i]);
    100         }
    101         init();
    102         if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction())
    103                 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) {
    104             mCurrentModule = new VideoModule();
    105             mCurrentModuleIndex = VIDEO_MODULE_INDEX;
    106         } else {
    107             mCurrentModule = new PhotoModule();
    108             mCurrentModuleIndex = PHOTO_MODULE_INDEX;
    109         }
    110         mCurrentModule.init(this, mFrame, true);
    111         mSwitcher.setCurrentIndex(mCurrentModuleIndex);
    112         mOrientationListener = new MyOrientationEventListener(this);
    113         bindMediaSaveService();
    114     }
    115 
    116     public void init() {
    117         boolean landscape = Util.getDisplayRotation(this) % 180 == 90;
    118         mControlsBackground = findViewById(R.id.blocker);
    119         mCameraControls = findViewById(R.id.camera_controls);
    120         mShutter = (ShutterButton) findViewById(R.id.shutter_button);
    121         mSwitcher = (CameraSwitcher) findViewById(R.id.camera_switcher);
    122         mPieMenuButton = findViewById(R.id.menu);
    123         int totaldrawid = (LightCycleHelper.hasLightCycleCapture(this)
    124                                 ? DRAW_IDS.length : DRAW_IDS.length - 1);
    125         if (!ApiHelper.HAS_OLD_PANORAMA) totaldrawid--;
    126 
    127         int[] drawids = new int[totaldrawid];
    128         int[] moduleids = new int[totaldrawid];
    129         int ix = 0;
    130         for (int i = 0; i < mDrawables.length; i++) {
    131             if (i == PANORAMA_MODULE_INDEX && !ApiHelper.HAS_OLD_PANORAMA) {
    132                 continue; // not enabled, so don't add to UI
    133             }
    134             if (i == LIGHTCYCLE_MODULE_INDEX && !LightCycleHelper.hasLightCycleCapture(this)) {
    135                 continue; // not enabled, so don't add to UI
    136             }
    137             moduleids[ix] = i;
    138             drawids[ix++] = DRAW_IDS[i];
    139         }
    140         mSwitcher.setIds(moduleids, drawids);
    141         mSwitcher.setSwitchListener(this);
    142         mSwitcher.setCurrentIndex(mCurrentModuleIndex);
    143     }
    144 
    145     @Override
    146     public void onDestroy() {
    147         unbindMediaSaveService();
    148         super.onDestroy();
    149     }
    150 
    151     // Return whether the auto-rotate screen in system settings
    152     // is turned on.
    153     public boolean isAutoRotateScreen() {
    154         return mAutoRotateScreen;
    155     }
    156 
    157     private class MyOrientationEventListener
    158             extends OrientationEventListener {
    159         public MyOrientationEventListener(Context context) {
    160             super(context);
    161         }
    162 
    163         @Override
    164         public void onOrientationChanged(int orientation) {
    165             // We keep the last known orientation. So if the user first orient
    166             // the camera then point the camera to floor or sky, we still have
    167             // the correct orientation.
    168             if (orientation == ORIENTATION_UNKNOWN) return;
    169             mLastRawOrientation = orientation;
    170             mCurrentModule.onOrientationChanged(orientation);
    171         }
    172     }
    173 
    174     private ObjectAnimator mCameraSwitchAnimator;
    175 
    176     @Override
    177     public void onCameraSelected(final int i) {
    178         if (mPaused) return;
    179         if (i != mCurrentModuleIndex) {
    180             mPaused = true;
    181             CameraScreenNail screenNail = getCameraScreenNail();
    182             if (screenNail != null) {
    183                 if (mCameraSwitchAnimator != null && mCameraSwitchAnimator.isRunning()) {
    184                     mCameraSwitchAnimator.cancel();
    185                 }
    186                 mCameraSwitchAnimator = ObjectAnimator.ofFloat(
    187                         screenNail, "alpha", screenNail.getAlpha(), 0f);
    188                 mCameraSwitchAnimator.addListener(new AnimatorListenerAdapter() {
    189                     @Override
    190                     public void onAnimationEnd(Animator animation) {
    191                         super.onAnimationEnd(animation);
    192                         doChangeCamera(i);
    193                     }
    194                 });
    195                 mCameraSwitchAnimator.start();
    196             } else {
    197                 doChangeCamera(i);
    198             }
    199         }
    200     }
    201 
    202     private void doChangeCamera(int i) {
    203         boolean canReuse = canReuseScreenNail();
    204         CameraHolder.instance().keep();
    205         closeModule(mCurrentModule);
    206         mCurrentModuleIndex = i;
    207         switch (i) {
    208             case VIDEO_MODULE_INDEX:
    209                 mCurrentModule = new VideoModule();
    210                 break;
    211             case PHOTO_MODULE_INDEX:
    212                 mCurrentModule = new PhotoModule();
    213                 break;
    214             case PANORAMA_MODULE_INDEX:
    215                 mCurrentModule = new PanoramaModule();
    216                 break;
    217             case LIGHTCYCLE_MODULE_INDEX:
    218                 mCurrentModule = LightCycleHelper.createPanoramaModule();
    219                 break;
    220         }
    221         showPieMenuButton(mCurrentModule.needsPieMenu());
    222 
    223         openModule(mCurrentModule, canReuse);
    224         mCurrentModule.onOrientationChanged(mLastRawOrientation);
    225         if (mMediaSaveService != null) {
    226             mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService);
    227         }
    228         getCameraScreenNail().setAlpha(0f);
    229         getCameraScreenNail().setOnFrameDrawnOneShot(mOnFrameDrawn);
    230     }
    231 
    232     public void showPieMenuButton(boolean show) {
    233         if (show) {
    234             findViewById(R.id.blocker).setVisibility(View.VISIBLE);
    235             findViewById(R.id.menu).setVisibility(View.VISIBLE);
    236             findViewById(R.id.on_screen_indicators).setVisibility(View.VISIBLE);
    237         } else {
    238             findViewById(R.id.blocker).setVisibility(View.INVISIBLE);
    239             findViewById(R.id.menu).setVisibility(View.INVISIBLE);
    240             findViewById(R.id.on_screen_indicators).setVisibility(View.INVISIBLE);
    241         }
    242     }
    243 
    244     private Runnable mOnFrameDrawn = new Runnable() {
    245 
    246         @Override
    247         public void run() {
    248             runOnUiThread(mFadeInCameraScreenNail);
    249         }
    250     };
    251 
    252     private Runnable mFadeInCameraScreenNail = new Runnable() {
    253 
    254         @Override
    255         public void run() {
    256             mCameraSwitchAnimator = ObjectAnimator.ofFloat(
    257                     getCameraScreenNail(), "alpha", 0f, 1f);
    258             mCameraSwitchAnimator.setStartDelay(50);
    259             mCameraSwitchAnimator.start();
    260         }
    261     };
    262 
    263     @Override
    264     public void onShowSwitcherPopup() {
    265         mCurrentModule.onShowSwitcherPopup();
    266     }
    267 
    268     private void openModule(CameraModule module, boolean canReuse) {
    269         module.init(this, mFrame, canReuse && canReuseScreenNail());
    270         mPaused = false;
    271         module.onResumeBeforeSuper();
    272         module.onResumeAfterSuper();
    273     }
    274 
    275     private void closeModule(CameraModule module) {
    276         module.onPauseBeforeSuper();
    277         module.onPauseAfterSuper();
    278         mFrame.removeAllViews();
    279     }
    280 
    281     public ShutterButton getShutterButton() {
    282         return mShutter;
    283     }
    284 
    285     public void hideUI() {
    286         mCameraControls.setVisibility(View.INVISIBLE);
    287         hideSwitcher();
    288         mShutter.setVisibility(View.GONE);
    289     }
    290 
    291     public void showUI() {
    292         mCameraControls.setVisibility(View.VISIBLE);
    293         showSwitcher();
    294         mShutter.setVisibility(View.VISIBLE);
    295         // Force a layout change to show shutter button
    296         mShutter.requestLayout();
    297     }
    298 
    299     public void hideSwitcher() {
    300         mSwitcher.closePopup();
    301         mSwitcher.setVisibility(View.INVISIBLE);
    302     }
    303 
    304     public void showSwitcher() {
    305         if (mCurrentModule.needsSwitcher()) {
    306             mSwitcher.setVisibility(View.VISIBLE);
    307         }
    308     }
    309 
    310     public boolean isInCameraApp() {
    311         return mShowCameraAppView;
    312     }
    313 
    314     @Override
    315     public void onConfigurationChanged(Configuration config) {
    316         super.onConfigurationChanged(config);
    317         mCurrentModule.onConfigurationChanged(config);
    318     }
    319 
    320     @Override
    321     public void onPause() {
    322         mPaused = true;
    323         mOrientationListener.disable();
    324         mCurrentModule.onPauseBeforeSuper();
    325         super.onPause();
    326         mCurrentModule.onPauseAfterSuper();
    327     }
    328 
    329     @Override
    330     public void onResume() {
    331         mPaused = false;
    332         if (Settings.System.getInt(getContentResolver(),
    333                 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {// auto-rotate off
    334             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
    335             mAutoRotateScreen = false;
    336         } else {
    337             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
    338             mAutoRotateScreen = true;
    339         }
    340         mOrientationListener.enable();
    341         mCurrentModule.onResumeBeforeSuper();
    342         super.onResume();
    343         mCurrentModule.onResumeAfterSuper();
    344     }
    345 
    346     private void bindMediaSaveService() {
    347         Intent intent = new Intent(this, MediaSaveService.class);
    348         bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    349     }
    350 
    351     private void unbindMediaSaveService() {
    352         if (mMediaSaveService != null) {
    353             mMediaSaveService.setListener(null);
    354         }
    355         if (mConnection != null) {
    356             unbindService(mConnection);
    357         }
    358     }
    359 
    360     @Override
    361     protected void onFullScreenChanged(boolean full) {
    362         if (full) {
    363             showUI();
    364         } else {
    365             hideUI();
    366         }
    367         super.onFullScreenChanged(full);
    368         if (ApiHelper.HAS_ROTATION_ANIMATION) {
    369             setRotationAnimation(full);
    370         }
    371         mCurrentModule.onFullScreenChanged(full);
    372     }
    373 
    374     private void setRotationAnimation(boolean fullscreen) {
    375         int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
    376         if (fullscreen) {
    377             rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
    378         }
    379         Window win = getWindow();
    380         WindowManager.LayoutParams winParams = win.getAttributes();
    381         winParams.rotationAnimation = rotationAnimation;
    382         win.setAttributes(winParams);
    383     }
    384 
    385     @Override
    386     protected void onStop() {
    387         super.onStop();
    388         mCurrentModule.onStop();
    389         getStateManager().clearTasks();
    390     }
    391 
    392     @Override
    393     protected void onNewIntent(Intent intent) {
    394         super.onNewIntent(intent);
    395         getStateManager().clearActivityResult();
    396     }
    397 
    398     @Override
    399     protected void installIntentFilter() {
    400         super.installIntentFilter();
    401         mCurrentModule.installIntentFilter();
    402     }
    403 
    404     @Override
    405     protected void onActivityResult(
    406             int requestCode, int resultCode, Intent data) {
    407         // Only PhotoPage understands ProxyLauncher.RESULT_USER_CANCELED
    408         if (resultCode == ProxyLauncher.RESULT_USER_CANCELED
    409                 && !(getStateManager().getTopState() instanceof PhotoPage)) {
    410             resultCode = RESULT_CANCELED;
    411         }
    412         super.onActivityResult(requestCode, resultCode, data);
    413         // Unmap cancel vs. reset
    414         if (resultCode == ProxyLauncher.RESULT_USER_CANCELED) {
    415             resultCode = RESULT_CANCELED;
    416         }
    417         mCurrentModule.onActivityResult(requestCode, resultCode, data);
    418     }
    419 
    420     // Preview area is touched. Handle touch focus.
    421     // Touch to focus is handled by PreviewGestures, this function call
    422     // is no longer needed. TODO: Clean it up in the next refactor
    423     @Override
    424     protected void onSingleTapUp(View view, int x, int y) {
    425     }
    426 
    427     @Override
    428     public void onBackPressed() {
    429         if (!mCurrentModule.onBackPressed()) {
    430             super.onBackPressed();
    431         }
    432     }
    433 
    434     @Override
    435     public boolean onKeyDown(int keyCode, KeyEvent event) {
    436         return mCurrentModule.onKeyDown(keyCode,  event)
    437                 || super.onKeyDown(keyCode, event);
    438     }
    439 
    440     @Override
    441     public boolean onKeyUp(int keyCode, KeyEvent event) {
    442         return mCurrentModule.onKeyUp(keyCode,  event)
    443                 || super.onKeyUp(keyCode, event);
    444     }
    445 
    446     public void cancelActivityTouchHandling() {
    447         if (mDown != null) {
    448             MotionEvent cancel = MotionEvent.obtain(mDown);
    449             cancel.setAction(MotionEvent.ACTION_CANCEL);
    450             super.dispatchTouchEvent(cancel);
    451         }
    452     }
    453 
    454     @Override
    455     public boolean dispatchTouchEvent(MotionEvent m) {
    456         if (m.getActionMasked() == MotionEvent.ACTION_DOWN) {
    457             mDown = m;
    458         }
    459         if ((mSwitcher != null) && mSwitcher.showsPopup() && !mSwitcher.isInsidePopup(m)) {
    460             return mSwitcher.onTouch(null, m);
    461         } else if ((mSwitcher != null) && mSwitcher.isInsidePopup(m)) {
    462             return superDispatchTouchEvent(m);
    463         } else {
    464             return mCurrentModule.dispatchTouchEvent(m);
    465         }
    466     }
    467 
    468     @Override
    469     public void startActivityForResult(Intent intent, int requestCode) {
    470         Intent proxyIntent = new Intent(this, ProxyLauncher.class);
    471         proxyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
    472         proxyIntent.putExtra(Intent.EXTRA_INTENT, intent);
    473         super.startActivityForResult(proxyIntent, requestCode);
    474     }
    475 
    476     public boolean superDispatchTouchEvent(MotionEvent m) {
    477         return super.dispatchTouchEvent(m);
    478     }
    479 
    480     // Preview texture has been copied. Now camera can be released and the
    481     // animation can be started.
    482     @Override
    483     public void onPreviewTextureCopied() {
    484         mCurrentModule.onPreviewTextureCopied();
    485     }
    486 
    487     @Override
    488     public void onCaptureTextureCopied() {
    489         mCurrentModule.onCaptureTextureCopied();
    490     }
    491 
    492     @Override
    493     public void onUserInteraction() {
    494         super.onUserInteraction();
    495         mCurrentModule.onUserInteraction();
    496     }
    497 
    498     @Override
    499     protected boolean updateStorageHintOnResume() {
    500         return mCurrentModule.updateStorageHintOnResume();
    501     }
    502 
    503     @Override
    504     public void updateCameraAppView() {
    505         super.updateCameraAppView();
    506         mCurrentModule.updateCameraAppView();
    507     }
    508 
    509     private boolean canReuseScreenNail() {
    510         return mCurrentModuleIndex == PHOTO_MODULE_INDEX
    511                 || mCurrentModuleIndex == VIDEO_MODULE_INDEX
    512                 || mCurrentModuleIndex == LIGHTCYCLE_MODULE_INDEX;
    513     }
    514 
    515     @Override
    516     public boolean isPanoramaActivity() {
    517         return (mCurrentModuleIndex == PANORAMA_MODULE_INDEX);
    518     }
    519 
    520     // Accessor methods for getting latency times used in performance testing
    521     public long getAutoFocusTime() {
    522         return (mCurrentModule instanceof PhotoModule) ?
    523                 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
    524     }
    525 
    526     public long getShutterLag() {
    527         return (mCurrentModule instanceof PhotoModule) ?
    528                 ((PhotoModule) mCurrentModule).mShutterLag : -1;
    529     }
    530 
    531     public long getShutterToPictureDisplayedTime() {
    532         return (mCurrentModule instanceof PhotoModule) ?
    533                 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
    534     }
    535 
    536     public long getPictureDisplayedToJpegCallbackTime() {
    537         return (mCurrentModule instanceof PhotoModule) ?
    538                 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
    539     }
    540 
    541     public long getJpegCallbackFinishTime() {
    542         return (mCurrentModule instanceof PhotoModule) ?
    543                 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
    544     }
    545 
    546     public long getCaptureStartTime() {
    547         return (mCurrentModule instanceof PhotoModule) ?
    548                 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
    549     }
    550 
    551     public boolean isRecording() {
    552         return (mCurrentModule instanceof VideoModule) ?
    553                 ((VideoModule) mCurrentModule).isRecording() : false;
    554     }
    555 
    556     public CameraScreenNail getCameraScreenNail() {
    557         return (CameraScreenNail) mCameraScreenNail;
    558     }
    559 
    560     public MediaSaveService getMediaSaveService() {
    561         return mMediaSaveService;
    562     }
    563 }
    564