summaryrefslogtreecommitdiffstats
path: root/src/com/android/camera/ActivityBase.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/camera/ActivityBase.java')
-rw-r--r--src/com/android/camera/ActivityBase.java642
1 files changed, 642 insertions, 0 deletions
diff --git a/src/com/android/camera/ActivityBase.java b/src/com/android/camera/ActivityBase.java
new file mode 100644
index 000000000..4e4143ef8
--- /dev/null
+++ b/src/com/android/camera/ActivityBase.java
@@ -0,0 +1,642 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camera;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Rect;
+import android.hardware.Camera.Parameters;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.DecelerateInterpolator;
+
+import com.android.camera.ui.LayoutChangeNotifier;
+import com.android.camera.ui.PopupManager;
+import com.android.gallery3d.app.AbstractGalleryActivity;
+import com.android.gallery3d.app.AppBridge;
+import com.android.gallery3d.app.FilmstripPage;
+import com.android.gallery3d.app.GalleryActionBar;
+import com.android.gallery3d.app.PhotoPage;
+import com.android.gallery3d.common.ApiHelper;
+import com.android.gallery3d.ui.ScreenNail;
+import com.android.gallery3d.util.MediaSetUtils;
+
+/**
+ * Superclass of camera activity.
+ */
+public abstract class ActivityBase extends AbstractGalleryActivity
+ implements LayoutChangeNotifier.Listener {
+
+ private static final String TAG = "ActivityBase";
+ private static final int CAMERA_APP_VIEW_TOGGLE_TIME = 100; // milliseconds
+ private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE =
+ "android.media.action.STILL_IMAGE_CAMERA_SECURE";
+ public static final String ACTION_IMAGE_CAPTURE_SECURE =
+ "android.media.action.IMAGE_CAPTURE_SECURE";
+ // The intent extra for camera from secure lock screen. True if the gallery
+ // should only show newly captured pictures. sSecureAlbumId does not
+ // increment. This is used when switching between camera, camcorder, and
+ // panorama. If the extra is not set, it is in the normal camera mode.
+ public static final String SECURE_CAMERA_EXTRA = "secure_camera";
+
+ private int mResultCodeForTesting;
+ private Intent mResultDataForTesting;
+ private OnScreenHint mStorageHint;
+ private View mSingleTapArea;
+
+ protected boolean mOpenCameraFail;
+ protected boolean mCameraDisabled;
+ protected CameraManager.CameraProxy mCameraDevice;
+ protected Parameters mParameters;
+ // The activity is paused. The classes that extend this class should set
+ // mPaused the first thing in onResume/onPause.
+ protected boolean mPaused;
+ protected GalleryActionBar mActionBar;
+
+ // multiple cameras support
+ protected int mNumberOfCameras;
+ protected int mCameraId;
+ // The activity is going to switch to the specified camera id. This is
+ // needed because texture copy is done in GL thread. -1 means camera is not
+ // switching.
+ protected int mPendingSwitchCameraId = -1;
+
+ protected MyAppBridge mAppBridge;
+ protected ScreenNail mCameraScreenNail; // This shows camera preview.
+ // The view containing only camera related widgets like control panel,
+ // indicator bar, focus indicator and etc.
+ protected View mCameraAppView;
+ protected boolean mShowCameraAppView = true;
+ private Animation mCameraAppViewFadeIn;
+ private Animation mCameraAppViewFadeOut;
+ // Secure album id. This should be incremented every time the camera is
+ // launched from the secure lock screen. The id should be the same when
+ // switching between camera, camcorder, and panorama.
+ protected static int sSecureAlbumId;
+ // True if the camera is started from secure lock screen.
+ protected boolean mSecureCamera;
+ private static boolean sFirstStartAfterScreenOn = true;
+
+ private long mStorageSpace = Storage.LOW_STORAGE_THRESHOLD;
+ private static final int UPDATE_STORAGE_HINT = 0;
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case UPDATE_STORAGE_HINT:
+ updateStorageHint();
+ return;
+ }
+ }
+ };
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_MEDIA_MOUNTED)
+ || action.equals(Intent.ACTION_MEDIA_UNMOUNTED)
+ || action.equals(Intent.ACTION_MEDIA_CHECKING)
+ || action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) {
+ updateStorageSpaceAndHint();
+ }
+ }
+ };
+
+ // close activity when screen turns off
+ private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ finish();
+ }
+ };
+
+ private static BroadcastReceiver sScreenOffReceiver;
+ private static class ScreenOffReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ sFirstStartAfterScreenOn = true;
+ }
+ }
+
+ public static boolean isFirstStartAfterScreenOn() {
+ return sFirstStartAfterScreenOn;
+ }
+
+ public static void resetFirstStartAfterScreenOn() {
+ sFirstStartAfterScreenOn = false;
+ }
+
+ protected class CameraOpenThread extends Thread {
+ @Override
+ public void run() {
+ try {
+ mCameraDevice = Util.openCamera(ActivityBase.this, mCameraId);
+ mParameters = mCameraDevice.getParameters();
+ } catch (CameraHardwareException e) {
+ mOpenCameraFail = true;
+ } catch (CameraDisabledException e) {
+ mCameraDisabled = true;
+ }
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.disableToggleStatusBar();
+ // Set a theme with action bar. It is not specified in manifest because
+ // we want to hide it by default. setTheme must happen before
+ // setContentView.
+ //
+ // This must be set before we call super.onCreate(), where the window's
+ // background is removed.
+ setTheme(R.style.Theme_Gallery);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ if (ApiHelper.HAS_ACTION_BAR) {
+ requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
+ } else {
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ }
+
+ // Check if this is in the secure camera mode.
+ Intent intent = getIntent();
+ String action = intent.getAction();
+ if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) {
+ mSecureCamera = true;
+ // Use a new album when this is started from the lock screen.
+ sSecureAlbumId++;
+ } else if (ACTION_IMAGE_CAPTURE_SECURE.equals(action)) {
+ mSecureCamera = true;
+ } else {
+ mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false);
+ }
+ if (mSecureCamera) {
+ IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
+ registerReceiver(mScreenOffReceiver, filter);
+ if (sScreenOffReceiver == null) {
+ sScreenOffReceiver = new ScreenOffReceiver();
+ getApplicationContext().registerReceiver(sScreenOffReceiver, filter);
+ }
+ }
+ super.onCreate(icicle);
+ }
+
+ public boolean isPanoramaActivity() {
+ return false;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ installIntentFilter();
+ if (updateStorageHintOnResume()) {
+ updateStorageSpace();
+ mHandler.sendEmptyMessageDelayed(UPDATE_STORAGE_HINT, 200);
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ if (mStorageHint != null) {
+ mStorageHint.cancel();
+ mStorageHint = null;
+ }
+
+ unregisterReceiver(mReceiver);
+ }
+
+ @Override
+ public void setContentView(int layoutResID) {
+ super.setContentView(layoutResID);
+ // getActionBar() should be after setContentView
+ mActionBar = new GalleryActionBar(this);
+ mActionBar.hide();
+ }
+
+ @Override
+ public boolean onSearchRequested() {
+ return false;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ // Prevent software keyboard or voice search from showing up.
+ if (keyCode == KeyEvent.KEYCODE_SEARCH
+ || keyCode == KeyEvent.KEYCODE_MENU) {
+ if (event.isLongPress()) return true;
+ }
+ if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraAppView) {
+ return true;
+ }
+
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraAppView) {
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ protected void setResultEx(int resultCode) {
+ mResultCodeForTesting = resultCode;
+ setResult(resultCode);
+ }
+
+ protected void setResultEx(int resultCode, Intent data) {
+ mResultCodeForTesting = resultCode;
+ mResultDataForTesting = data;
+ setResult(resultCode, data);
+ }
+
+ public int getResultCode() {
+ return mResultCodeForTesting;
+ }
+
+ public Intent getResultData() {
+ return mResultDataForTesting;
+ }
+
+ @Override
+ protected void onDestroy() {
+ PopupManager.removeInstance(this);
+ if (mSecureCamera) unregisterReceiver(mScreenOffReceiver);
+ super.onDestroy();
+ }
+
+ protected void installIntentFilter() {
+ // install an intent filter to receive SD card related events.
+ IntentFilter intentFilter =
+ new IntentFilter(Intent.ACTION_MEDIA_MOUNTED);
+ intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
+ intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
+ intentFilter.addAction(Intent.ACTION_MEDIA_CHECKING);
+ intentFilter.addDataScheme("file");
+ registerReceiver(mReceiver, intentFilter);
+ }
+
+ protected void updateStorageSpace() {
+ mStorageSpace = Storage.getAvailableSpace();
+ }
+
+ protected long getStorageSpace() {
+ return mStorageSpace;
+ }
+
+ protected void updateStorageSpaceAndHint() {
+ updateStorageSpace();
+ updateStorageHint(mStorageSpace);
+ }
+
+ protected void updateStorageHint() {
+ updateStorageHint(mStorageSpace);
+ }
+
+ protected boolean updateStorageHintOnResume() {
+ return true;
+ }
+
+ protected void updateStorageHint(long storageSpace) {
+ String message = null;
+ if (storageSpace == Storage.UNAVAILABLE) {
+ message = getString(R.string.no_storage);
+ } else if (storageSpace == Storage.PREPARING) {
+ message = getString(R.string.preparing_sd);
+ } else if (storageSpace == Storage.UNKNOWN_SIZE) {
+ message = getString(R.string.access_sd_fail);
+ } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD) {
+ message = getString(R.string.spaceIsLow_content);
+ }
+
+ if (message != null) {
+ if (mStorageHint == null) {
+ mStorageHint = OnScreenHint.makeText(this, message);
+ } else {
+ mStorageHint.setText(message);
+ }
+ mStorageHint.show();
+ } else if (mStorageHint != null) {
+ mStorageHint.cancel();
+ mStorageHint = null;
+ }
+ }
+
+ protected void gotoGallery() {
+ // Move the next picture with capture animation. "1" means next.
+ mAppBridge.switchWithCaptureAnimation(1);
+ }
+
+ // Call this after setContentView.
+ public ScreenNail createCameraScreenNail(boolean getPictures) {
+ mCameraAppView = findViewById(R.id.camera_app_root);
+ Bundle data = new Bundle();
+ String path;
+ if (getPictures) {
+ if (mSecureCamera) {
+ path = "/secure/all/" + sSecureAlbumId;
+ } else {
+ path = "/local/all/" + MediaSetUtils.CAMERA_BUCKET_ID;
+ }
+ } else {
+ path = "/local/all/0"; // Use 0 so gallery does not show anything.
+ }
+ data.putString(PhotoPage.KEY_MEDIA_SET_PATH, path);
+ data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, path);
+ data.putBoolean(PhotoPage.KEY_SHOW_WHEN_LOCKED, mSecureCamera);
+
+ // Send an AppBridge to gallery to enable the camera preview.
+ if (mAppBridge != null) {
+ mCameraScreenNail.recycle();
+ }
+ mAppBridge = new MyAppBridge();
+ data.putParcelable(PhotoPage.KEY_APP_BRIDGE, mAppBridge);
+ if (getStateManager().getStateCount() == 0) {
+ getStateManager().startState(FilmstripPage.class, data);
+ } else {
+ getStateManager().switchState(getStateManager().getTopState(),
+ FilmstripPage.class, data);
+ }
+ mCameraScreenNail = mAppBridge.getCameraScreenNail();
+ return mCameraScreenNail;
+ }
+
+ // Call this after setContentView.
+ protected ScreenNail reuseCameraScreenNail(boolean getPictures) {
+ mCameraAppView = findViewById(R.id.camera_app_root);
+ Bundle data = new Bundle();
+ String path;
+ if (getPictures) {
+ if (mSecureCamera) {
+ path = "/secure/all/" + sSecureAlbumId;
+ } else {
+ path = "/local/all/" + MediaSetUtils.CAMERA_BUCKET_ID;
+ }
+ } else {
+ path = "/local/all/0"; // Use 0 so gallery does not show anything.
+ }
+ data.putString(PhotoPage.KEY_MEDIA_SET_PATH, path);
+ data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, path);
+ data.putBoolean(PhotoPage.KEY_SHOW_WHEN_LOCKED, mSecureCamera);
+
+ // Send an AppBridge to gallery to enable the camera preview.
+ if (mAppBridge == null) {
+ mAppBridge = new MyAppBridge();
+ }
+ data.putParcelable(PhotoPage.KEY_APP_BRIDGE, mAppBridge);
+ if (getStateManager().getStateCount() == 0) {
+ getStateManager().startState(FilmstripPage.class, data);
+ }
+ mCameraScreenNail = mAppBridge.getCameraScreenNail();
+ return mCameraScreenNail;
+ }
+
+ private class HideCameraAppView implements Animation.AnimationListener {
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ // We cannot set this as GONE because we want to receive the
+ // onLayoutChange() callback even when we are invisible.
+ mCameraAppView.setVisibility(View.INVISIBLE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+ }
+
+ protected void updateCameraAppView() {
+ // Initialize the animation.
+ if (mCameraAppViewFadeIn == null) {
+ mCameraAppViewFadeIn = new AlphaAnimation(0f, 1f);
+ mCameraAppViewFadeIn.setDuration(CAMERA_APP_VIEW_TOGGLE_TIME);
+ mCameraAppViewFadeIn.setInterpolator(new DecelerateInterpolator());
+
+ mCameraAppViewFadeOut = new AlphaAnimation(1f, 0f);
+ mCameraAppViewFadeOut.setDuration(CAMERA_APP_VIEW_TOGGLE_TIME);
+ mCameraAppViewFadeOut.setInterpolator(new DecelerateInterpolator());
+ mCameraAppViewFadeOut.setAnimationListener(new HideCameraAppView());
+ }
+
+ if (mShowCameraAppView) {
+ mCameraAppView.setVisibility(View.VISIBLE);
+ // The "transparent region" is not recomputed when a sibling of
+ // SurfaceView changes visibility (unless it involves GONE). It's
+ // been broken since 1.0. Call requestLayout to work around it.
+ mCameraAppView.requestLayout();
+ mCameraAppView.startAnimation(mCameraAppViewFadeIn);
+ } else {
+ mCameraAppView.startAnimation(mCameraAppViewFadeOut);
+ }
+ }
+
+ protected void onFullScreenChanged(boolean full) {
+ if (mShowCameraAppView == full) return;
+ mShowCameraAppView = full;
+ if (mPaused || isFinishing()) return;
+ updateCameraAppView();
+ }
+
+ @Override
+ public GalleryActionBar getGalleryActionBar() {
+ return mActionBar;
+ }
+
+ // Preview frame layout has changed.
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom) {
+ if (mAppBridge == null) return;
+
+ int width = right - left;
+ int height = bottom - top;
+ if (ApiHelper.HAS_SURFACE_TEXTURE) {
+ CameraScreenNail screenNail = (CameraScreenNail) mCameraScreenNail;
+ if (Util.getDisplayRotation(this) % 180 == 0) {
+ screenNail.setPreviewFrameLayoutSize(width, height);
+ } else {
+ // Swap the width and height. Camera screen nail draw() is based on
+ // natural orientation, not the view system orientation.
+ screenNail.setPreviewFrameLayoutSize(height, width);
+ }
+ notifyScreenNailChanged();
+ }
+ }
+
+ protected void setSingleTapUpListener(View singleTapArea) {
+ mSingleTapArea = singleTapArea;
+ }
+
+ private boolean onSingleTapUp(int x, int y) {
+ // Ignore if listener is null or the camera control is invisible.
+ if (mSingleTapArea == null || !mShowCameraAppView) return false;
+
+ int[] relativeLocation = Util.getRelativeLocation((View) getGLRoot(),
+ mSingleTapArea);
+ x -= relativeLocation[0];
+ y -= relativeLocation[1];
+ if (x >= 0 && x < mSingleTapArea.getWidth() && y >= 0
+ && y < mSingleTapArea.getHeight()) {
+ onSingleTapUp(mSingleTapArea, x, y);
+ return true;
+ }
+ return false;
+ }
+
+ protected void onSingleTapUp(View view, int x, int y) {
+ }
+
+ public void setSwipingEnabled(boolean enabled) {
+ mAppBridge.setSwipingEnabled(enabled);
+ }
+
+ public void notifyScreenNailChanged() {
+ mAppBridge.notifyScreenNailChanged();
+ }
+
+ protected void onPreviewTextureCopied() {
+ }
+
+ protected void onCaptureTextureCopied() {
+ }
+
+ protected void addSecureAlbumItemIfNeeded(boolean isVideo, Uri uri) {
+ if (mSecureCamera) {
+ int id = Integer.parseInt(uri.getLastPathSegment());
+ mAppBridge.addSecureAlbumItem(isVideo, id);
+ }
+ }
+
+ public boolean isSecureCamera() {
+ return mSecureCamera;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // The is the communication interface between the Camera Application and
+ // the Gallery PhotoPage.
+ //////////////////////////////////////////////////////////////////////////
+
+ class MyAppBridge extends AppBridge implements CameraScreenNail.Listener {
+ @SuppressWarnings("hiding")
+ private ScreenNail mCameraScreenNail;
+ private Server mServer;
+
+ @Override
+ public ScreenNail attachScreenNail() {
+ if (mCameraScreenNail == null) {
+ if (ApiHelper.HAS_SURFACE_TEXTURE) {
+ mCameraScreenNail = new CameraScreenNail(this);
+ } else {
+ Bitmap b = BitmapFactory.decodeResource(getResources(),
+ R.drawable.wallpaper_picker_preview);
+ mCameraScreenNail = new StaticBitmapScreenNail(b);
+ }
+ }
+ return mCameraScreenNail;
+ }
+
+ @Override
+ public void detachScreenNail() {
+ mCameraScreenNail = null;
+ }
+
+ public ScreenNail getCameraScreenNail() {
+ return mCameraScreenNail;
+ }
+
+ // Return true if the tap is consumed.
+ @Override
+ public boolean onSingleTapUp(int x, int y) {
+ return ActivityBase.this.onSingleTapUp(x, y);
+ }
+
+ // This is used to notify that the screen nail will be drawn in full screen
+ // or not in next draw() call.
+ @Override
+ public void onFullScreenChanged(boolean full) {
+ ActivityBase.this.onFullScreenChanged(full);
+ }
+
+ @Override
+ public void requestRender() {
+ getGLRoot().requestRenderForced();
+ }
+
+ @Override
+ public void onPreviewTextureCopied() {
+ ActivityBase.this.onPreviewTextureCopied();
+ }
+
+ @Override
+ public void onCaptureTextureCopied() {
+ ActivityBase.this.onCaptureTextureCopied();
+ }
+
+ @Override
+ public void setServer(Server s) {
+ mServer = s;
+ }
+
+ @Override
+ public boolean isPanorama() {
+ return ActivityBase.this.isPanoramaActivity();
+ }
+
+ @Override
+ public boolean isStaticCamera() {
+ return !ApiHelper.HAS_SURFACE_TEXTURE;
+ }
+
+ public void addSecureAlbumItem(boolean isVideo, int id) {
+ if (mServer != null) mServer.addSecureAlbumItem(isVideo, id);
+ }
+
+ private void setCameraRelativeFrame(Rect frame) {
+ if (mServer != null) mServer.setCameraRelativeFrame(frame);
+ }
+
+ private void switchWithCaptureAnimation(int offset) {
+ if (mServer != null) mServer.switchWithCaptureAnimation(offset);
+ }
+
+ private void setSwipingEnabled(boolean enabled) {
+ if (mServer != null) mServer.setSwipingEnabled(enabled);
+ }
+
+ private void notifyScreenNailChanged() {
+ if (mServer != null) mServer.notifyScreenNailChanged();
+ }
+ }
+}