OSDN Git Service

Spring cleaning
authorMichael Kolb <kolby@google.com>
Sat, 9 Mar 2013 04:43:01 +0000 (20:43 -0800)
committerMichael Kolb <kolby@google.com>
Mon, 11 Mar 2013 22:24:59 +0000 (15:24 -0700)
Change-Id: I00d7176301501784a6a400bc9ea2cb23ff9ec90a

src/com/android/camera/CameraModule.java
src/com/android/camera/FocusOverlayManager.java
src/com/android/camera/MediaSaveService.java
src/com/android/camera/PanoramaModule.java
src/com/android/camera/PhotoController.java
src/com/android/camera/PhotoMenu.java [new file with mode: 0644]
src/com/android/camera/PhotoModule.java
src/com/android/camera/PhotoUI.java [new file with mode: 0644]
src/com/android/camera/PreviewGestures.java
src/com/android/camera/VideoMenu.java [moved from src/com/android/camera/VideoController.java with 98% similarity]
src/com/android/camera/VideoModule.java

index aa057b9..3275d5f 100644 (file)
@@ -64,8 +64,6 @@ public interface CameraModule {
 
     public void updateCameraAppView();
 
-    public boolean collapseCameraControls();
-
     public boolean needsSwitcher();
 
     public boolean needsPieMenu();
index a3726ed..8bcb52f 100644 (file)
@@ -27,9 +27,6 @@ import android.os.Looper;
 import android.os.Message;
 import android.util.Log;
 
-import com.android.camera.ui.FaceView;
-import com.android.camera.ui.FocusIndicator;
-import com.android.camera.ui.PieRenderer;
 import com.android.gallery3d.common.ApiHelper;
 
 import java.util.ArrayList;
@@ -78,13 +75,10 @@ public class FocusOverlayManager {
     private boolean mAeAwbLock;
     private Matrix mMatrix;
 
-    private PieRenderer mPieRenderer;
-
     private int mPreviewWidth; // The width of the preview frame layout.
     private int mPreviewHeight; // The height of the preview frame layout.
     private boolean mMirror; // true if the camera is front-facing.
     private int mDisplayOrientation;
-    private FaceView mFaceView;
     private List<Object> mFocusArea; // focus area in driver format
     private List<Object> mMeteringArea; // metering area in driver format
     private String mFocusMode;
@@ -97,6 +91,19 @@ public class FocusOverlayManager {
     private boolean mPreviousMoving;
     private boolean mFocusDefault;
 
+    private FocusUI mUI;
+
+    public  interface FocusUI {
+        public boolean hasFaces();
+        public void clearFocus();
+        public void setFocusPosition(int x, int y);
+        public void onFocusStarted();
+        public void onFocusSucceeded(boolean timeOut);
+        public void onFocusFailed(boolean timeOut);
+        public void pauseFaceDetection();
+        public void resumeFaceDetection();
+    }
+
     public interface Listener {
         public void autoFocus();
         public void cancelAutoFocus();
@@ -125,7 +132,7 @@ public class FocusOverlayManager {
 
     public FocusOverlayManager(ComboPreferences preferences, String[] defaultFocusModes,
             Parameters parameters, Listener listener,
-            boolean mirror, Looper looper) {
+            boolean mirror, Looper looper, FocusUI ui) {
         mHandler = new MainHandler(looper);
         mMatrix = new Matrix();
         mPreferences = preferences;
@@ -134,11 +141,7 @@ public class FocusOverlayManager {
         mListener = listener;
         setMirror(mirror);
         mFocusDefault = true;
-    }
-
-    public void setFocusRenderer(PieRenderer renderer) {
-        mPieRenderer = renderer;
-        mInitialized = (mMatrix != null);
+        mUI = ui;
     }
 
     public void setParameters(Parameters parameters) {
@@ -172,10 +175,6 @@ public class FocusOverlayManager {
         setMatrix();
     }
 
-    public void setFaceView(FaceView faceView) {
-        mFaceView = faceView;
-    }
-
     private void setMatrix() {
         if (mPreviewWidth != 0 && mPreviewHeight != 0) {
             Matrix matrix = new Matrix();
@@ -185,7 +184,7 @@ public class FocusOverlayManager {
             // coordinates. In tap focus, the inverted matrix converts the UI
             // coordinates to driver coordinates.
             matrix.invert(mMatrix);
-            mInitialized = (mPieRenderer != null);
+            mInitialized = true;
         }
     }
 
@@ -295,9 +294,11 @@ public class FocusOverlayManager {
 
     public void onAutoFocusMoving(boolean moving) {
         if (!mInitialized) return;
+
+
         // Ignore if the camera has detected some faces.
-        if (mFaceView != null && mFaceView.faceExists()) {
-            mPieRenderer.clear();
+        if (mUI.hasFaces()) {
+            mUI.clearFocus();
             return;
         }
 
@@ -307,9 +308,9 @@ public class FocusOverlayManager {
 
         // animate on false->true trasition only b/8219520
         if (moving && !mPreviousMoving) {
-            mPieRenderer.showStart();
+            mUI.onFocusStarted();
         } else if (!moving) {
-            mPieRenderer.showSuccess(true);
+            mUI.onFocusSucceeded(true);
         }
         mPreviousMoving = moving;
     }
@@ -358,7 +359,7 @@ public class FocusOverlayManager {
         }
 
         // Use margin to set the focus indicator to the touched area.
-        mPieRenderer.setFocus(x, y);
+        mUI.setFocusPosition(x, y);
 
         // Stop face detection because we want to specify focus and metering area.
         mListener.stopFaceDetection();
@@ -396,7 +397,7 @@ public class FocusOverlayManager {
         mState = STATE_FOCUSING;
         // Pause the face view because the driver will keep sending face
         // callbacks after the focus completes.
-        if (mFaceView != null) mFaceView.pause();
+        mUI.pauseFaceDetection();
         updateFocusUI();
         mHandler.removeMessages(RESET_TOUCH_FOCUS);
     }
@@ -409,7 +410,7 @@ public class FocusOverlayManager {
         // driver is not reset.
         resetTouchFocus();
         mListener.cancelAutoFocus();
-        if (mFaceView != null) mFaceView.resume();
+        mUI.resumeFaceDetection();
         mState = STATE_IDLE;
         updateFocusUI();
         mHandler.removeMessages(RESET_TOUCH_FOCUS);
@@ -470,28 +471,26 @@ public class FocusOverlayManager {
     public void updateFocusUI() {
         if (!mInitialized) return;
         // Show only focus indicator or face indicator.
-        boolean faceExists = (mFaceView != null && mFaceView.faceExists());
-        FocusIndicator focusIndicator = (faceExists) ? mFaceView : mPieRenderer;
 
         if (mState == STATE_IDLE) {
             if (mFocusDefault) {
-                focusIndicator.clear();
+                mUI.clearFocus();
             } else {
                 // Users touch on the preview and the indicator represents the
                 // metering area. Either focus area is not supported or
                 // autoFocus call is not required.
-                focusIndicator.showStart();
+                mUI.onFocusStarted();
             }
         } else if (mState == STATE_FOCUSING || mState == STATE_FOCUSING_SNAP_ON_FINISH) {
-            focusIndicator.showStart();
+            mUI.onFocusStarted();
         } else {
             if (Util.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)) {
                 // TODO: check HAL behavior and decide if this can be removed.
-                focusIndicator.showSuccess(false);
+                mUI.onFocusSucceeded(false);
             } else if (mState == STATE_SUCCESS) {
-                focusIndicator.showSuccess(false);
+                mUI.onFocusSucceeded(false);
             } else if (mState == STATE_FAIL) {
-                focusIndicator.showFail(false);
+                mUI.onFocusFailed(false);
             }
         }
     }
@@ -500,7 +499,7 @@ public class FocusOverlayManager {
         if (!mInitialized) return;
 
         // Put focus indicator to the center. clear reset position
-        mPieRenderer.clear();
+        mUI.clearFocus();
         // Initialize mFocusArea.
         if (mFocusAreaSupported) {
             initializeFocusAreas(mPreviewWidth / 2, mPreviewHeight / 2);
index d0e02f1..48fb629 100644 (file)
@@ -38,8 +38,8 @@ public class MediaSaveService extends Service {
     private Listener mListener;
 
     interface Listener {
-        public void onQueueAvailable();
-        public void onQueueFull();
+
+        public void onQueueStatus(boolean full);
     }
 
     interface OnMediaSavedListener {
@@ -96,19 +96,15 @@ public class MediaSaveService extends Service {
     public void setListener(Listener l) {
         mListener = l;
         if (l == null) return;
-        if (isQueueFull()) {
-            l.onQueueFull();
-        } else {
-            l.onQueueAvailable();
-        }
+        l.onQueueStatus(isQueueFull());
     }
 
     private void onQueueFull() {
-        if (mListener != null) mListener.onQueueFull();
+        if (mListener != null) mListener.onQueueStatus(true);
     }
 
     private void onQueueAvailable() {
-        if (mListener != null) mListener.onQueueAvailable();
+        if (mListener != null) mListener.onQueueStatus(false);
     }
 
     private class SaveTask extends AsyncTask <Void, Void, Uri> {
index d12c828..2703c35 100644 (file)
@@ -1298,11 +1298,6 @@ public class PanoramaModule implements CameraModule,
     }
 
     @Override
-    public boolean collapseCameraControls() {
-        return false;
-    }
-
-    @Override
     public boolean needsSwitcher() {
         return true;
     }
index c2501c7..b76022e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2013 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.
 
 package com.android.camera;
 
-import android.content.Context;
-import android.hardware.Camera.Parameters;
-import android.view.LayoutInflater;
-
-import com.android.camera.ui.AbstractSettingPopup;
-import com.android.camera.ui.ListPrefSettingPopup;
-import com.android.camera.ui.MoreSettingPopup;
-import com.android.camera.ui.PieItem;
-import com.android.camera.ui.PieItem.OnClickListener;
-import com.android.camera.ui.PieRenderer;
-import com.android.camera.ui.TimerSettingPopup;
-import com.android.gallery3d.R;
-
-public class PhotoController extends PieController
-        implements MoreSettingPopup.Listener,
-        TimerSettingPopup.Listener,
-        ListPrefSettingPopup.Listener {
-    private static String TAG = "CAM_photocontrol";
-    private static float FLOAT_PI_DIVIDED_BY_TWO = (float) Math.PI / 2;
-    private final String mSettingOff;
-
-    private PhotoModule mModule;
-    private String[] mOtherKeys;
-    // First level popup
-    private MoreSettingPopup mPopup;
-    // Second level popup
-    private AbstractSettingPopup mSecondPopup;
-
-    public PhotoController(CameraActivity activity, PhotoModule module, PieRenderer pie) {
-        super(activity, pie);
-        mModule = module;
-        mSettingOff = activity.getString(R.string.setting_off_value);
-    }
-
-    public void initialize(PreferenceGroup group) {
-        super.initialize(group);
-        mPopup = null;
-        mSecondPopup = null;
-        float sweep = FLOAT_PI_DIVIDED_BY_TWO / 2;
-        addItem(CameraSettings.KEY_FLASH_MODE, FLOAT_PI_DIVIDED_BY_TWO - sweep, sweep);
-        addItem(CameraSettings.KEY_EXPOSURE, 3 * FLOAT_PI_DIVIDED_BY_TWO - sweep, sweep);
-        addItem(CameraSettings.KEY_WHITE_BALANCE, 3 * FLOAT_PI_DIVIDED_BY_TWO + sweep, sweep);
-        if (group.findPreference(CameraSettings.KEY_CAMERA_ID) != null) {
-            PieItem item = makeItem(R.drawable.ic_switch_photo_facing_holo_light);
-            item.setFixedSlice(FLOAT_PI_DIVIDED_BY_TWO + sweep, sweep);
-            item.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(PieItem item) {
-                    // Find the index of next camera.
-                    ListPreference camPref = mPreferenceGroup
-                            .findPreference(CameraSettings.KEY_CAMERA_ID);
-                    if (camPref != null) {
-                        int index = camPref.findIndexOfValue(camPref.getValue());
-                        CharSequence[] values = camPref.getEntryValues();
-                        index = (index + 1) % values.length;
-                        int newCameraId = Integer
-                                .parseInt((String) values[index]);
-                        mListener.onCameraPickerClicked(newCameraId);
-                    }
-                }
-            });
-            mRenderer.addItem(item);
-        }
-        if (group.findPreference(CameraSettings.KEY_CAMERA_HDR) != null) {
-            PieItem hdr = makeItem(R.drawable.ic_hdr);
-            hdr.setFixedSlice(FLOAT_PI_DIVIDED_BY_TWO, sweep);
-            hdr.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(PieItem item) {
-                    // Find the index of next camera.
-                    ListPreference pref = mPreferenceGroup
-                            .findPreference(CameraSettings.KEY_CAMERA_HDR);
-                    if (pref != null) {
-                        // toggle hdr value
-                        int index = (pref.findIndexOfValue(pref.getValue()) + 1) % 2;
-                        pref.setValueIndex(index);
-                        onSettingChanged(pref);
-                    }
-                }
-            });
-            mRenderer.addItem(hdr);
-        }
-        mOtherKeys = new String[] {
-                CameraSettings.KEY_SCENE_MODE,
-                CameraSettings.KEY_RECORD_LOCATION,
-                CameraSettings.KEY_PICTURE_SIZE,
-                CameraSettings.KEY_FOCUS_MODE,
-                CameraSettings.KEY_TIMER,
-                CameraSettings.KEY_TIMER_SOUND_EFFECTS,
-                };
-        PieItem item = makeItem(R.drawable.ic_settings_holo_light);
-        item.setFixedSlice(FLOAT_PI_DIVIDED_BY_TWO * 3, sweep);
-        item.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(PieItem item) {
-                if (mPopup == null) {
-                    initializePopup();
-                }
-                mModule.showPopup(mPopup);
-            }
-        });
-        mRenderer.addItem(item);
-    }
-
-    protected void setCameraId(int cameraId) {
-        ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CAMERA_ID);
-        pref.setValue("" + cameraId);
-    }
-
-    @Override
-    public void reloadPreferences() {
-        super.reloadPreferences();
-        if (mPopup != null) {
-            mPopup.reloadPreference();
-        }
-    }
-
-    @Override
-    // Hit when an item in the second-level popup gets selected
-    public void onListPrefChanged(ListPreference pref) {
-        if (mPopup != null && mSecondPopup != null) {
-                mModule.dismissPopup(true);
-                mPopup.reloadPreference();
-        }
-        onSettingChanged(pref);
-    }
-
-    @Override
-    public void overrideSettings(final String ... keyvalues) {
-        super.overrideSettings(keyvalues);
-        if (mPopup == null) initializePopup();
-        mPopup.overrideSettings(keyvalues);
-    }
-
-    protected void initializePopup() {
-        LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService(
-                Context.LAYOUT_INFLATER_SERVICE);
-
-        MoreSettingPopup popup = (MoreSettingPopup) inflater.inflate(
-                R.layout.more_setting_popup, null, false);
-        popup.setSettingChangedListener(this);
-        popup.initialize(mPreferenceGroup, mOtherKeys);
-        if (mActivity.isSecureCamera()) {
-            // Prevent location preference from getting changed in secure camera mode
-            popup.setPreferenceEnabled(CameraSettings.KEY_RECORD_LOCATION, false);
-        }
-        mPopup = popup;
-    }
-
-    public void popupDismissed(boolean topPopupOnly) {
-        // if the 2nd level popup gets dismissed
-        if (mSecondPopup != null) {
-            mSecondPopup = null;
-            if (topPopupOnly) mModule.showPopup(mPopup);
-        }
-    }
-
-    // Return true if the preference has the specified key but not the value.
-    private static boolean notSame(ListPreference pref, String key, String value) {
-        return (key.equals(pref.getKey()) && !value.equals(pref.getValue()));
-    }
-
-    private void setPreference(String key, String value) {
-        ListPreference pref = mPreferenceGroup.findPreference(key);
-        if (pref != null && !value.equals(pref.getValue())) {
-            pref.setValue(value);
-            reloadPreferences();
-        }
-    }
-
-    @Override
-    public void onSettingChanged(ListPreference pref) {
-        // Reset the scene mode if HDR is set to on. Reset HDR if scene mode is
-        // set to non-auto.
-        if (notSame(pref, CameraSettings.KEY_CAMERA_HDR, mSettingOff)) {
-            setPreference(CameraSettings.KEY_SCENE_MODE, Parameters.SCENE_MODE_AUTO);
-        } else if (notSame(pref, CameraSettings.KEY_SCENE_MODE, Parameters.SCENE_MODE_AUTO)) {
-            setPreference(CameraSettings.KEY_CAMERA_HDR, mSettingOff);
-        }
-        super.onSettingChanged(pref);
-    }
-
-    @Override
-    // Hit when an item in the first-level popup gets selected, then bring up
-    // the second-level popup
-    public void onPreferenceClicked(ListPreference pref) {
-        if (mSecondPopup != null) return;
-
-        LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService(
-                Context.LAYOUT_INFLATER_SERVICE);
-        if (CameraSettings.KEY_TIMER.equals(pref.getKey())) {
-            TimerSettingPopup timerPopup = (TimerSettingPopup) inflater.inflate(
-                    R.layout.timer_setting_popup, null, false);
-            timerPopup.initialize(pref);
-            timerPopup.setSettingChangedListener(this);
-            mModule.dismissPopup(true);
-            mSecondPopup = timerPopup;
-        } else {
-            ListPrefSettingPopup basic = (ListPrefSettingPopup) inflater.inflate(
-                    R.layout.list_pref_setting_popup, null, false);
-            basic.initialize(pref);
-            basic.setSettingChangedListener(this);
-            mModule.dismissPopup(true);
-            mSecondPopup = basic;
-        }
-        mModule.showPopup(mSecondPopup);
-    }
+import android.view.SurfaceHolder;
+import android.view.View;
+
+import com.android.camera.ShutterButton.OnShutterButtonListener;
+
+
+public interface PhotoController extends OnShutterButtonListener {
+
+    public static final int PREVIEW_STOPPED = 0;
+    public static final int IDLE = 1;  // preview is active
+    // Focus is in progress. The exact focus state is in Focus.java.
+    public static final int FOCUSING = 2;
+    public static final int SNAPSHOT_IN_PROGRESS = 3;
+    // Switching between cameras.
+    public static final int SWITCHING_CAMERA = 4;
+
+    // returns the actual set zoom value
+    public int onZoomChanged(int requestedZoom);
+
+    public boolean isImageCaptureIntent();
+
+    public boolean isCameraIdle();
+
+    public void onCaptureDone();
+
+    public void onCaptureCancelled();
+
+    public void onCaptureRetake();
+
+    public void cancelAutoFocus();
+
+    public void stopPreview();
+
+    public int getCameraState();
+
+    public void onSingleTapUp(View view, int x, int y);
+
+    public void onSurfaceCreated(SurfaceHolder holder);
+
+    public void onCountDownFinished();
+
+    public void onScreenSizeChanged(int width, int height, int previewWidth, int previewHeight);
+
 }
diff --git a/src/com/android/camera/PhotoMenu.java b/src/com/android/camera/PhotoMenu.java
new file mode 100644 (file)
index 0000000..2535a0c
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2012 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.Context;
+import android.hardware.Camera.Parameters;
+import android.view.LayoutInflater;
+
+import com.android.camera.ui.AbstractSettingPopup;
+import com.android.camera.ui.ListPrefSettingPopup;
+import com.android.camera.ui.MoreSettingPopup;
+import com.android.camera.ui.PieItem;
+import com.android.camera.ui.PieItem.OnClickListener;
+import com.android.camera.ui.PieRenderer;
+import com.android.camera.ui.TimerSettingPopup;
+import com.android.gallery3d.R;
+
+public class PhotoMenu extends PieController
+        implements MoreSettingPopup.Listener,
+        TimerSettingPopup.Listener,
+        ListPrefSettingPopup.Listener {
+    private static String TAG = "CAM_photomenu";
+    private static float FLOAT_PI_DIVIDED_BY_TWO = (float) Math.PI / 2;
+    private final String mSettingOff;
+
+    private PhotoUI mUI;
+    private String[] mOtherKeys;
+    // First level popup
+    private MoreSettingPopup mPopup;
+    // Second level popup
+    private AbstractSettingPopup mSecondPopup;
+
+    public PhotoMenu(CameraActivity activity, PhotoUI ui, PieRenderer pie) {
+        super(activity, pie);
+        mUI = ui;
+        mSettingOff = activity.getString(R.string.setting_off_value);
+    }
+
+    public void initialize(PreferenceGroup group) {
+        super.initialize(group);
+        mPopup = null;
+        mSecondPopup = null;
+        float sweep = FLOAT_PI_DIVIDED_BY_TWO / 2;
+        addItem(CameraSettings.KEY_FLASH_MODE, FLOAT_PI_DIVIDED_BY_TWO - sweep, sweep);
+        addItem(CameraSettings.KEY_EXPOSURE, 3 * FLOAT_PI_DIVIDED_BY_TWO - sweep, sweep);
+        addItem(CameraSettings.KEY_WHITE_BALANCE, 3 * FLOAT_PI_DIVIDED_BY_TWO + sweep, sweep);
+        if (group.findPreference(CameraSettings.KEY_CAMERA_ID) != null) {
+            PieItem item = makeItem(R.drawable.ic_switch_photo_facing_holo_light);
+            item.setFixedSlice(FLOAT_PI_DIVIDED_BY_TWO + sweep, sweep);
+            item.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(PieItem item) {
+                    // Find the index of next camera.
+                    ListPreference camPref = mPreferenceGroup
+                            .findPreference(CameraSettings.KEY_CAMERA_ID);
+                    if (camPref != null) {
+                        int index = camPref.findIndexOfValue(camPref.getValue());
+                        CharSequence[] values = camPref.getEntryValues();
+                        index = (index + 1) % values.length;
+                        int newCameraId = Integer
+                                .parseInt((String) values[index]);
+                        mListener.onCameraPickerClicked(newCameraId);
+                    }
+                }
+            });
+            mRenderer.addItem(item);
+        }
+        if (group.findPreference(CameraSettings.KEY_CAMERA_HDR) != null) {
+            PieItem hdr = makeItem(R.drawable.ic_hdr);
+            hdr.setFixedSlice(FLOAT_PI_DIVIDED_BY_TWO, sweep);
+            hdr.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(PieItem item) {
+                    // Find the index of next camera.
+                    ListPreference pref = mPreferenceGroup
+                            .findPreference(CameraSettings.KEY_CAMERA_HDR);
+                    if (pref != null) {
+                        // toggle hdr value
+                        int index = (pref.findIndexOfValue(pref.getValue()) + 1) % 2;
+                        pref.setValueIndex(index);
+                        onSettingChanged(pref);
+                    }
+                }
+            });
+            mRenderer.addItem(hdr);
+        }
+        mOtherKeys = new String[] {
+                CameraSettings.KEY_SCENE_MODE,
+                CameraSettings.KEY_RECORD_LOCATION,
+                CameraSettings.KEY_PICTURE_SIZE,
+                CameraSettings.KEY_FOCUS_MODE,
+                CameraSettings.KEY_TIMER,
+                CameraSettings.KEY_TIMER_SOUND_EFFECTS,
+                };
+        PieItem item = makeItem(R.drawable.ic_settings_holo_light);
+        item.setFixedSlice(FLOAT_PI_DIVIDED_BY_TWO * 3, sweep);
+        item.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(PieItem item) {
+                if (mPopup == null) {
+                    initializePopup();
+                }
+                mUI.showPopup(mPopup);
+            }
+        });
+        mRenderer.addItem(item);
+    }
+
+    @Override
+    public void reloadPreferences() {
+        super.reloadPreferences();
+        if (mPopup != null) {
+            mPopup.reloadPreference();
+        }
+    }
+
+    @Override
+    // Hit when an item in the second-level popup gets selected
+    public void onListPrefChanged(ListPreference pref) {
+        if (mPopup != null && mSecondPopup != null) {
+                mUI.dismissPopup(true);
+                mPopup.reloadPreference();
+        }
+        onSettingChanged(pref);
+    }
+
+    @Override
+    public void overrideSettings(final String ... keyvalues) {
+        super.overrideSettings(keyvalues);
+        if (mPopup == null) initializePopup();
+        mPopup.overrideSettings(keyvalues);
+    }
+
+    protected void initializePopup() {
+        LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
+
+        MoreSettingPopup popup = (MoreSettingPopup) inflater.inflate(
+                R.layout.more_setting_popup, null, false);
+        popup.setSettingChangedListener(this);
+        popup.initialize(mPreferenceGroup, mOtherKeys);
+        if (mActivity.isSecureCamera()) {
+            // Prevent location preference from getting changed in secure camera mode
+            popup.setPreferenceEnabled(CameraSettings.KEY_RECORD_LOCATION, false);
+        }
+        mPopup = popup;
+    }
+
+    public void popupDismissed(boolean topPopupOnly) {
+        // if the 2nd level popup gets dismissed
+        if (mSecondPopup != null) {
+            mSecondPopup = null;
+            if (topPopupOnly) mUI.showPopup(mPopup);
+        }
+    }
+
+    // Return true if the preference has the specified key but not the value.
+    private static boolean notSame(ListPreference pref, String key, String value) {
+        return (key.equals(pref.getKey()) && !value.equals(pref.getValue()));
+    }
+
+    private void setPreference(String key, String value) {
+        ListPreference pref = mPreferenceGroup.findPreference(key);
+        if (pref != null && !value.equals(pref.getValue())) {
+            pref.setValue(value);
+            reloadPreferences();
+        }
+    }
+
+    @Override
+    public void onSettingChanged(ListPreference pref) {
+        // Reset the scene mode if HDR is set to on. Reset HDR if scene mode is
+        // set to non-auto.
+        if (notSame(pref, CameraSettings.KEY_CAMERA_HDR, mSettingOff)) {
+            setPreference(CameraSettings.KEY_SCENE_MODE, Parameters.SCENE_MODE_AUTO);
+        } else if (notSame(pref, CameraSettings.KEY_SCENE_MODE, Parameters.SCENE_MODE_AUTO)) {
+            setPreference(CameraSettings.KEY_CAMERA_HDR, mSettingOff);
+        }
+        super.onSettingChanged(pref);
+    }
+
+    @Override
+    // Hit when an item in the first-level popup gets selected, then bring up
+    // the second-level popup
+    public void onPreferenceClicked(ListPreference pref) {
+        if (mSecondPopup != null) return;
+
+        LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
+        if (CameraSettings.KEY_TIMER.equals(pref.getKey())) {
+            TimerSettingPopup timerPopup = (TimerSettingPopup) inflater.inflate(
+                    R.layout.timer_setting_popup, null, false);
+            timerPopup.initialize(pref);
+            timerPopup.setSettingChangedListener(this);
+            mUI.dismissPopup(true);
+            mSecondPopup = timerPopup;
+        } else {
+            ListPrefSettingPopup basic = (ListPrefSettingPopup) inflater.inflate(
+                    R.layout.list_pref_setting_popup, null, false);
+            basic.initialize(pref);
+            basic.setSettingChangedListener(this);
+            mUI.dismissPopup(true);
+            mSecondPopup = basic;
+        }
+        mUI.showPopup(mSecondPopup);
+    }
+}
index 4049aa5..4c627c9 100644 (file)
@@ -28,8 +28,6 @@ import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.SurfaceTexture;
 import android.hardware.Camera.CameraInfo;
-import android.hardware.Camera.Face;
-import android.hardware.Camera.FaceDetectionListener;
 import android.hardware.Camera.Parameters;
 import android.hardware.Camera.PictureCallback;
 import android.hardware.Camera.Size;
@@ -45,32 +43,17 @@ import android.os.MessageQueue;
 import android.os.SystemClock;
 import android.provider.MediaStore;
 import android.util.Log;
-import android.view.Gravity;
 import android.view.KeyEvent;
-import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.OrientationEventListener;
 import android.view.SurfaceHolder;
 import android.view.View;
-import android.view.ViewStub;
-import android.view.View.OnClickListener;
-import android.view.View.OnLayoutChangeListener;
-import android.view.ViewGroup;
 import android.view.WindowManager;
-import android.widget.FrameLayout;
-import android.widget.FrameLayout.LayoutParams;
-import android.widget.ImageView;
-import android.widget.Toast;
 
 import com.android.camera.CameraManager.CameraProxy;
-import com.android.camera.ui.AbstractSettingPopup;
-import com.android.camera.ui.CountDownView;
-import com.android.camera.ui.FaceView;
-import com.android.camera.ui.PieRenderer;
+import com.android.camera.ui.CountDownView.OnCountDownFinishedListener;
 import com.android.camera.ui.PopupManager;
-import com.android.camera.ui.RenderOverlay;
 import com.android.camera.ui.RotateTextToast;
-import com.android.camera.ui.ZoomRenderer;
 import com.android.gallery3d.R;
 import com.android.gallery3d.common.ApiHelper;
 import com.android.gallery3d.filtershow.CropExtras;
@@ -88,14 +71,12 @@ import java.util.List;
 
 public class PhotoModule
     implements CameraModule,
+    PhotoController,
     FocusOverlayManager.Listener,
     CameraPreference.OnPreferenceChangedListener,
-    LocationManager.Listener,
     ShutterButton.OnShutterButtonListener,
-    SurfaceHolder.Callback,
-    PieRenderer.PieListener,
-    CountDownView.OnCountDownFinishedListener,
-    MediaSaveService.Listener {
+    MediaSaveService.Listener,
+    OnCountDownFinishedListener {
 
     private static final String TAG = "CAM_PhotoModule";
 
@@ -127,12 +108,12 @@ public class PhotoModule
 
     // copied from Camera hierarchy
     private CameraActivity mActivity;
-    private View mRootView;
     private CameraProxy mCameraDevice;
     private int mCameraId;
     private Parameters mParameters;
     private boolean mPaused;
-    private AbstractSettingPopup mPopup;
+
+    private PhotoUI mUI;
 
     // these are only used by Camera
 
@@ -150,8 +131,6 @@ public class PhotoModule
     private static final int SCREEN_DELAY = 2 * 60 * 1000;
 
     private int mZoomValue;  // The current zoom value.
-    private int mZoomMax;
-    private List<Integer> mZoomRatios;
 
     private Parameters mInitialParams;
     private boolean mFocusAreaSupported;
@@ -167,35 +146,12 @@ public class PhotoModule
     private static final String sTempCropFilename = "crop-temp";
 
     private ContentProviderClient mMediaProviderClient;
-    private ShutterButton mShutterButton;
     private boolean mFaceDetectionStarted = false;
 
-    private Object mSurfaceTexture;
-    private CountDownView mCountDownView;
-
-    private volatile SurfaceHolder mCameraSurfaceHolder;
-
-    private FaceView mFaceView;
-    private RenderOverlay mRenderOverlay;
-    private View mReviewCancelButton;
-    private View mReviewDoneButton;
-    private View mReviewRetakeButton;
-
     // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
     private String mCropValue;
     private Uri mSaveUri;
 
-    private View mMenu;
-    private View mBlocker;
-
-    // Small indicators which show the camera settings in the viewfinder.
-    private ImageView mExposureIndicator;
-    private ImageView mFlashIndicator;
-    private ImageView mSceneIndicator;
-    private ImageView mHdrIndicator;
-    // A view group that contains all the small indicators.
-    private View mOnScreenIndicators;
-
     // We use a queue to generated names of the images to be used later
     // when the image is ready to be saved.
     private NamedImages mNamedImages;
@@ -207,16 +163,6 @@ public class PhotoModule
         }
     };
 
-    private final View.OnLayoutChangeListener mLayoutChangeListener =
-            new View.OnLayoutChangeListener() {
-        @Override
-        public void onLayoutChange(View v, int left, int top, int right,
-                int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
-            onScreenSizeChanged(right - left, bottom - top);
-        }
-    };
-    private int mPreviewWidth = 0;
-    private int mPreviewHeight = 0;
     private final StringBuilder mBuilder = new StringBuilder();
     private final Formatter mFormatter = new Formatter(mBuilder);
     private final Object[] mFormatterArgs = new Object[1];
@@ -242,13 +188,6 @@ public class PhotoModule
     private boolean mFirstTimeInitialized;
     private boolean mIsImageCaptureIntent;
 
-    private static final int PREVIEW_STOPPED = 0;
-    private static final int IDLE = 1;  // preview is active
-    // Focus is in progress. The exact focus state is in Focus.java.
-    private static final int FOCUSING = 2;
-    private static final int SNAPSHOT_IN_PROGRESS = 3;
-    // Switching between cameras.
-    private static final int SWITCHING_CAMERA = 4;
     private int mCameraState = PREVIEW_STOPPED;
     private boolean mSnapshotOnIdle = false;
 
@@ -289,12 +228,7 @@ public class PhotoModule
     // This handles everything about focus.
     private FocusOverlayManager mFocusManager;
 
-    private PieRenderer mPieRenderer;
-    private PhotoController mPhotoControl;
-
-    private ZoomRenderer mZoomRenderer;
     private String mSceneMode;
-    private Toast mNotSelectableToast;
 
     private final Handler mHandler = new MainHandler();
     private PreferenceGroup mPreferenceGroup;
@@ -304,8 +238,6 @@ public class PhotoModule
     CameraStartUpThread mCameraStartUpThread;
     ConditionVariable mStartPreviewPrerequisiteReady = new ConditionVariable();
 
-    private PreviewGestures mGestures;
-
     private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
             new MediaSaveService.OnMediaSavedListener() {
                 @Override
@@ -420,19 +352,12 @@ public class PhotoModule
                 }
 
                 case CAMERA_OPEN_DONE: {
-                    initializeAfterCameraOpen();
+                    onCameraOpened();
                     break;
                 }
 
                 case START_PREVIEW_DONE: {
-                    mCameraStartUpThread = null;
-                    setCameraState(IDLE);
-                    if (!ApiHelper.HAS_SURFACE_TEXTURE) {
-                        // This may happen if surfaceCreated has arrived.
-                        mCameraDevice.setPreviewDisplayAsync(mCameraSurfaceHolder);
-                    }
-                    startFaceDetection();
-                    locationFirstRun();
+                    onPreviewStarted();
                     break;
                 }
 
@@ -458,7 +383,7 @@ public class PhotoModule
     @Override
     public void init(CameraActivity activity, View parent, boolean reuseNail) {
         mActivity = activity;
-        mRootView = parent;
+        mUI = new PhotoUI(activity, this, parent);
         mPreferences = new ComboPreferences(mActivity);
         CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
         mCameraId = getPreferredCameraId(mPreferences);
@@ -470,17 +395,6 @@ public class PhotoModule
         mCameraStartUpThread = new CameraStartUpThread();
         mCameraStartUpThread.start();
 
-        mActivity.getLayoutInflater().inflate(R.layout.photo_module,
-                (ViewGroup) mRootView, true);
-        mRootView.addOnLayoutChangeListener(mLayoutChangeListener);
-        if (ApiHelper.HAS_FACE_DETECTION) {
-            ViewStub faceViewStub = (ViewStub) mRootView
-                    .findViewById(R.id.face_view_stub);
-            if (faceViewStub != null) {
-                faceViewStub.inflate();
-                mFaceView = (FaceView) mRootView.findViewById(R.id.face_view);
-            }
-        }
 
         // Surface texture is from camera screen nail and startPreview needs it.
         // This must be done before startPreview.
@@ -501,10 +415,25 @@ public class PhotoModule
 
         initializeControlByIntent();
         mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
-        mLocationManager = new LocationManager(mActivity, this);
-        initOnScreenIndicator();
-        mCountDownView = (CountDownView) (mRootView.findViewById(R.id.count_down_to_capture));
-        mCountDownView.setCountDownFinishedListener(this);
+        mLocationManager = new LocationManager(mActivity, mUI);
+    }
+
+    private void initializeControlByIntent() {
+        mUI.initializeControlByIntent();
+        if (mIsImageCaptureIntent) {
+            setupCaptureParams();
+        }
+    }
+
+    private void onPreviewStarted() {
+        mCameraStartUpThread = null;
+        setCameraState(IDLE);
+        if (!ApiHelper.HAS_SURFACE_TEXTURE) {
+            // This may happen if surfaceCreated has arrived.
+            mCameraDevice.setPreviewDisplayAsync(mUI.getSurfaceHolder());
+        }
+        startFaceDetection();
+        locationFirstRun();
     }
 
     // Prompt the user to pick to record location for the very first run of
@@ -554,52 +483,12 @@ public class PhotoModule
         onSharedPreferenceChanged();
     }
 
-    private void initializeRenderOverlay() {
-        if (mPieRenderer != null) {
-            mRenderOverlay.addRenderer(mPieRenderer);
-            mFocusManager.setFocusRenderer(mPieRenderer);
-        }
-        if (mZoomRenderer != null) {
-            mRenderOverlay.addRenderer(mZoomRenderer);
-        }
-        if (mGestures != null) {
-            mGestures.clearTouchReceivers();
-            mGestures.setRenderOverlay(mRenderOverlay);
-            mGestures.addTouchReceiver(mMenu);
-            mGestures.addTouchReceiver(mBlocker);
-
-            if (isImageCaptureIntent()) {
-                if (mReviewCancelButton != null) {
-                    mGestures.addTouchReceiver(mReviewCancelButton);
-                }
-                if (mReviewDoneButton != null) {
-                    mGestures.addTouchReceiver(mReviewDoneButton);
-                }
-            }
-        }
-        mRenderOverlay.requestLayout();
-    }
-
-    private void initializeAfterCameraOpen() {
-        if (mPieRenderer == null) {
-            mPieRenderer = new PieRenderer(mActivity);
-            mPhotoControl = new PhotoController(mActivity, this, mPieRenderer);
-            mPhotoControl.setListener(this);
-            mPieRenderer.setPieListener(this);
-        }
-        if (mZoomRenderer == null) {
-            mZoomRenderer = new ZoomRenderer(mActivity);
-        }
-        if (mGestures == null) {
-            // this will handle gesture disambiguation and dispatching
-            mGestures = new PreviewGestures(mActivity, this, mZoomRenderer, mPieRenderer);
-        }
-        initializeRenderOverlay();
-        initializePhotoControl();
-
+    private void onCameraOpened() {
+        View root = mUI.getRootView();
         // These depend on camera parameters.
-        int width = mRootView.getWidth();
-        int height = mRootView.getHeight();
+
+        int width = root.getWidth();
+        int height = root.getHeight();
         mFocusManager.setPreviewSize(width, height);
         // Full-screen screennail
         if (Util.getDisplayRotation(mActivity) % 180 == 0) {
@@ -608,37 +497,76 @@ public class PhotoModule
             ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(height, width);
         }
         // Set touch focus listener.
-        mActivity.setSingleTapUpListener(mRootView);
-        loadCameraPreferences();
-        initializeZoom();
-        updateOnScreenIndicators();
-        showTapToFocusToastIfNeeded();
+        mActivity.setSingleTapUpListener(root);
+        openCameraCommon();
         onFullScreenChanged(mActivity.isInCameraApp());
     }
 
-    public void onScreenSizeChanged(int width, int height) {
-        // Full-screen screennail
-        int w = width;
-        int h = height;
-        if (Util.getDisplayRotation(mActivity) % 180 != 0) {
-            w = height;
-            h = width;
+    private void switchCamera() {
+        if (mPaused) return;
+
+        Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
+        mCameraId = mPendingSwitchCameraId;
+        mPendingSwitchCameraId = -1;
+        setCameraId(mCameraId);
+
+        // from onPause
+        closeCamera();
+        mUI.collapseCameraControls();
+        mUI.clearFaces();
+        if (mFocusManager != null) mFocusManager.removeMessages();
+
+        // Restart the camera and initialize the UI. From onCreate.
+        mPreferences.setLocalId(mActivity, mCameraId);
+        CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
+        try {
+            mCameraDevice = Util.openCamera(mActivity, mCameraId);
+            mParameters = mCameraDevice.getParameters();
+        } catch (CameraHardwareException e) {
+            Util.showErrorAndFinish(mActivity, R.string.cannot_connect_camera);
+            return;
+        } catch (CameraDisabledException e) {
+            Util.showErrorAndFinish(mActivity, R.string.camera_disabled);
+            return;
         }
-        if (mPreviewWidth != w || mPreviewHeight != h) {
-            Log.d(TAG, "Preview size changed.");
-            if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
-            ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(w, h);
+        initializeCapabilities();
+        CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
+        boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
+        mFocusManager.setMirror(mirror);
+        mFocusManager.setParameters(mInitialParams);
+        setupPreview();
+
+        openCameraCommon();
+
+        if (ApiHelper.HAS_SURFACE_TEXTURE) {
+            // Start switch camera animation. Post a message because
+            // onFrameAvailable from the old camera may already exist.
+            mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION);
         }
     }
 
-    private void initializePhotoControl() {
+    protected void setCameraId(int cameraId) {
+        ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CAMERA_ID);
+        pref.setValue("" + cameraId);
+    }
+
+    // either open a new camera or switch cameras
+    private void openCameraCommon() {
         loadCameraPreferences();
-        if (mPhotoControl != null) {
-            mPhotoControl.initialize(mPreferenceGroup);
-        }
-        updateSceneModeUI();
+
+        mUI.onCameraOpened(mPreferenceGroup, mPreferences, mParameters, this);
+        updateSceneMode();
+        showTapToFocusToastIfNeeded();
+
+
     }
 
+    public void onScreenSizeChanged(int width, int height, int previewWidth, int previewHeight) {
+        Log.d(TAG, "Preview size changed.");
+        if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
+        ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(
+                previewWidth, previewHeight);
+    }
 
     private void resetExposureCompensation() {
         String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
@@ -673,11 +601,7 @@ public class PhotoModule
 
         keepMediaProviderInstance();
 
-        // Initialize shutter button.
-        mShutterButton = mActivity.getShutterButton();
-        mShutterButton.setImageResource(R.drawable.btn_new_shutter);
-        mShutterButton.setOnShutterButtonListener(this);
-        mShutterButton.setVisibility(View.VISIBLE);
+        mUI.initializeFirstTime();
         MediaSaveService s = mActivity.getMediaSaveService();
         // We set the listener only when both service and shutterbutton
         // are initialized.
@@ -693,93 +617,54 @@ public class PhotoModule
         mActivity.updateStorageSpaceAndHint();
     }
 
-    private void showTapToFocusToastIfNeeded() {
-        // Show the tap to focus toast if this is the first start.
-        if (mFocusAreaSupported &&
-                mPreferences.getBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, true)) {
-            // Delay the toast for one second to wait for orientation.
-            mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000);
-        }
-    }
-
-    private void addIdleHandler() {
-        MessageQueue queue = Looper.myQueue();
-        queue.addIdleHandler(new MessageQueue.IdleHandler() {
-            @Override
-            public boolean queueIdle() {
-                Storage.ensureOSXCompatible();
-                return false;
-            }
-        });
-    }
-
     // If the activity is paused and resumed, this method will be called in
     // onResume.
     private void initializeSecondTime() {
-
         // Start location update if needed.
         boolean recordLocation = RecordLocationPreference.get(
                 mPreferences, mContentResolver);
         mLocationManager.recordLocation(recordLocation);
-
         MediaSaveService s = mActivity.getMediaSaveService();
         if (s != null) {
             s.setListener(this);
         }
         mNamedImages = new NamedImages();
-        initializeZoom();
+        mUI.initializeSecondTime(mParameters);
         keepMediaProviderInstance();
-        hidePostCaptureAlert();
-
-        if (mPhotoControl != null) {
-            mPhotoControl.reloadPreferences();
-        }
     }
 
-    private class ZoomChangeListener implements ZoomRenderer.OnZoomChangedListener {
-        @Override
-        public void onZoomValueChanged(int index) {
-            // Not useful to change zoom value when the activity is paused.
-            if (mPaused) return;
-            mZoomValue = index;
-            if (mParameters == null || mCameraDevice == null) return;
-            // Set zoom parameters asynchronously
-            mParameters.setZoom(mZoomValue);
-            mCameraDevice.setParametersAsync(mParameters);
-            if (mZoomRenderer != null) {
-                Parameters p = mCameraDevice.getParameters();
-                mZoomRenderer.setZoomValue(mZoomRatios.get(p.getZoom()));
-            }
-        }
+    @Override
+    public void onSurfaceCreated(SurfaceHolder holder) {
+        // Do not access the camera if camera start up thread is not finished.
+        if (mCameraDevice == null || mCameraStartUpThread != null)
+            return;
 
-        @Override
-        public void onZoomStart() {
-            if (mPieRenderer != null) {
-                mPieRenderer.setBlockFocus(true);
-            }
+        mCameraDevice.setPreviewDisplayAsync(holder);
+        // This happens when onConfigurationChanged arrives, surface has been
+        // destroyed, and there is no onFullScreenChanged.
+        if (mCameraState == PREVIEW_STOPPED) {
+            setupPreview();
         }
+    }
 
-        @Override
-        public void onZoomEnd() {
-            if (mPieRenderer != null) {
-                mPieRenderer.setBlockFocus(false);
-            }
+    private void showTapToFocusToastIfNeeded() {
+        // Show the tap to focus toast if this is the first start.
+        if (mFocusAreaSupported &&
+                mPreferences.getBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, true)) {
+            // Delay the toast for one second to wait for orientation.
+            mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000);
         }
     }
 
-    private void initializeZoom() {
-        if ((mParameters == null) || !mParameters.isZoomSupported()
-                || (mZoomRenderer == null)) return;
-        mZoomMax = mParameters.getMaxZoom();
-        mZoomRatios = mParameters.getZoomRatios();
-        // Currently we use immediate zoom for fast zooming to get better UX and
-        // there is no plan to take advantage of the smooth zoom.
-        if (mZoomRenderer != null) {
-            mZoomRenderer.setZoomMax(mZoomMax);
-            mZoomRenderer.setZoom(mParameters.getZoom());
-            mZoomRenderer.setZoomValue(mZoomRatios.get(mParameters.getZoom()));
-            mZoomRenderer.setOnZoomChangeListener(new ZoomChangeListener());
-        }
+    private void addIdleHandler() {
+        MessageQueue queue = Looper.myQueue();
+        queue.addIdleHandler(new MessageQueue.IdleHandler() {
+            @Override
+            public boolean queueIdle() {
+                Storage.ensureOSXCompatible();
+                return false;
+            }
+        });
     }
 
     @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
@@ -789,19 +674,10 @@ public class PhotoModule
         if (mFaceDetectionStarted) return;
         if (mParameters.getMaxNumDetectedFaces() > 0) {
             mFaceDetectionStarted = true;
-            mFaceView.clear();
-            mFaceView.setVisibility(View.VISIBLE);
-            mFaceView.setDisplayOrientation(mDisplayOrientation);
             CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
-            mFaceView.setMirror(info.facing == CameraInfo.CAMERA_FACING_FRONT);
-            mFaceView.resume();
-            mFocusManager.setFaceView(mFaceView);
-            mCameraDevice.setFaceDetectionListener(new FaceDetectionListener() {
-                @Override
-                public void onFaceDetection(Face[] faces, android.hardware.Camera camera) {
-                    mFaceView.setFaces(faces);
-                }
-            });
+            mUI.onStartFaceDetection(mDisplayOrientation,
+                    (info.facing == CameraInfo.CAMERA_FACING_FRONT));
+            mCameraDevice.setFaceDetectionListener(mUI);
             mCameraDevice.startFaceDetection();
         }
     }
@@ -815,115 +691,14 @@ public class PhotoModule
             mFaceDetectionStarted = false;
             mCameraDevice.setFaceDetectionListener(null);
             mCameraDevice.stopFaceDetection();
-            if (mFaceView != null) mFaceView.clear();
+            mUI.clearFaces();
         }
     }
 
     @Override
     public boolean dispatchTouchEvent(MotionEvent m) {
         if (mCameraState == SWITCHING_CAMERA) return true;
-        if (mPopup != null) {
-            return mActivity.superDispatchTouchEvent(m);
-        } else if (mGestures != null && mRenderOverlay != null) {
-            return mGestures.dispatchTouch(m);
-        }
-        return false;
-    }
-
-    private void initOnScreenIndicator() {
-        mOnScreenIndicators = mActivity.findViewById(R.id.on_screen_indicators);
-        mExposureIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_exposure_indicator);
-        mFlashIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_flash_indicator);
-        mSceneIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_scenemode_indicator);
-        mHdrIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_hdr_indicator);
-    }
-
-    @Override
-    public void showGpsOnScreenIndicator(boolean hasSignal) { }
-
-    @Override
-    public void hideGpsOnScreenIndicator() { }
-
-    private void updateExposureOnScreenIndicator(int value) {
-        if (mExposureIndicator == null) {
-            return;
-        }
-        int id = 0;
-        float step = mParameters.getExposureCompensationStep();
-        value = (int) Math.round(value * step);
-        switch(value) {
-        case -3:
-            id = R.drawable.ic_indicator_ev_n3;
-            break;
-        case -2:
-            id = R.drawable.ic_indicator_ev_n2;
-            break;
-        case -1:
-            id = R.drawable.ic_indicator_ev_n1;
-            break;
-        case 0:
-            id = R.drawable.ic_indicator_ev_0;
-            break;
-        case 1:
-            id = R.drawable.ic_indicator_ev_p1;
-            break;
-        case 2:
-            id = R.drawable.ic_indicator_ev_p2;
-            break;
-        case 3:
-            id = R.drawable.ic_indicator_ev_p3;
-            break;
-        }
-        mExposureIndicator.setImageResource(id);
-
-    }
-
-    private void updateFlashOnScreenIndicator(String value) {
-        if (mFlashIndicator == null) {
-            return;
-        }
-        if (value == null || Parameters.FLASH_MODE_OFF.equals(value)) {
-            mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_off);
-        } else {
-            if (Parameters.FLASH_MODE_AUTO.equals(value)) {
-                mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_auto);
-            } else if (Parameters.FLASH_MODE_ON.equals(value)) {
-                mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_on);
-            } else {
-                mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_off);
-            }
-        }
-    }
-
-    private void updateSceneOnScreenIndicator(String value) {
-        if (mSceneIndicator == null) {
-            return;
-        }
-        if ((value == null) || Parameters.SCENE_MODE_AUTO.equals(value)
-                || Parameters.SCENE_MODE_HDR.equals(value)) {
-            mSceneIndicator.setImageResource(R.drawable.ic_indicator_sce_off);
-        } else {
-            mSceneIndicator.setImageResource(R.drawable.ic_indicator_sce_on);
-        }
-    }
-
-    private void updateHdrOnScreenIndicator(String value) {
-        if (mHdrIndicator == null) {
-            return;
-        }
-        if ((value != null) && Parameters.SCENE_MODE_HDR.equals(value)) {
-            mHdrIndicator.setImageResource(R.drawable.ic_indicator_hdr_on);
-        } else {
-            mHdrIndicator.setImageResource(R.drawable.ic_indicator_hdr_off);
-        }
-    }
-
-    private void updateOnScreenIndicators() {
-        if (mParameters == null) return;
-        updateSceneOnScreenIndicator(mParameters.getSceneMode());
-        updateExposureOnScreenIndicator(CameraSettings.readExposure(mPreferences));
-        updateFlashOnScreenIndicator(mParameters.getFlashMode());
-        updateHdrOnScreenIndicator(mParameters.getSceneMode());
+        return mUI.dispatchTouchEvent(m);
     }
 
     private final class ShutterCallback
@@ -1038,9 +813,9 @@ public class PhotoModule
             } else {
                 mJpegImageData = jpegData;
                 if (!mQuickCapture) {
-                    showPostCaptureAlert();
+                    mUI.showPostCaptureAlert();
                 } else {
-                    doAttach();
+                    onCaptureDone();
                 }
             }
 
@@ -1068,7 +843,7 @@ public class PhotoModule
             mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
             Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
             setCameraState(IDLE);
-            mFocusManager.onAutoFocus(focused, mShutterButton.isPressed());
+            mFocusManager.onAutoFocus(focused, mUI.isShutterPressed());
         }
     }
 
@@ -1124,18 +899,17 @@ public class PhotoModule
     private void setCameraState(int state) {
         mCameraState = state;
         switch (state) {
-            case PREVIEW_STOPPED:
-            case SNAPSHOT_IN_PROGRESS:
-            case FOCUSING:
-            case SWITCHING_CAMERA:
-                if (mGestures != null) mGestures.setEnabled(false);
-                break;
-            case IDLE:
-                if (mGestures != null && mActivity.mShowCameraAppView) {
-                    // Enable gestures only when the camera app view is visible
-                    mGestures.setEnabled(true);
-                }
-                break;
+        case PhotoController.PREVIEW_STOPPED:
+        case PhotoController.SNAPSHOT_IN_PROGRESS:
+        case PhotoController.FOCUSING:
+        case PhotoController.SWITCHING_CAMERA:
+            mUI.enableGestures(false);
+            break;
+        case PhotoController.IDLE:
+            if (mActivity.isInCameraApp()) {
+                mUI.enableGestures(true);
+            }
+            break;
         }
     }
 
@@ -1215,38 +989,9 @@ public class PhotoModule
         }
     }
 
-    private void setShowMenu(boolean show) {
-        if (mOnScreenIndicators != null) {
-            mOnScreenIndicators.setVisibility(show ? View.VISIBLE : View.GONE);
-        }
-        if (mMenu != null) {
-            mMenu.setVisibility(show ? View.VISIBLE : View.GONE);
-        }
-    }
-
     @Override
     public void onFullScreenChanged(boolean full) {
-        if (mFaceView != null) {
-            mFaceView.setBlockDraw(!full);
-        }
-        if (mPopup != null) {
-            dismissPopup(false, full);
-        }
-        if (mGestures != null) {
-            mGestures.setEnabled(full);
-        }
-        if (mRenderOverlay != null) {
-            // this can not happen in capture mode
-            mRenderOverlay.setVisibility(full ? View.VISIBLE : View.GONE);
-        }
-        if (mPieRenderer != null) {
-            mPieRenderer.setBlockFocus(!full);
-        }
-        setShowMenu(full);
-        if (mBlocker != null) {
-            mBlocker.setVisibility(full ? View.VISIBLE : View.GONE);
-        }
-        if (!full && mCountDownView != null) mCountDownView.cancelCountDown();
+        mUI.onFullScreenChanged(full);
         if (ApiHelper.HAS_SURFACE_TEXTURE) {
             if (mActivity.mCameraScreenNail != null) {
                 ((CameraScreenNail) mActivity.mCameraScreenNail).setFullScreen(full);
@@ -1255,35 +1000,7 @@ public class PhotoModule
         }
     }
 
-    @Override
-    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
-        Log.v(TAG, "surfaceChanged:" + holder + " width=" + width + ". height="
-                + height);
-    }
-
-    @Override
-    public void surfaceCreated(SurfaceHolder holder) {
-        Log.v(TAG, "surfaceCreated: " + holder);
-        mCameraSurfaceHolder = holder;
-        // Do not access the camera if camera start up thread is not finished.
-        if (mCameraDevice == null || mCameraStartUpThread != null) return;
-
-        mCameraDevice.setPreviewDisplayAsync(holder);
-        // This happens when onConfigurationChanged arrives, surface has been
-        // destroyed, and there is no onFullScreenChanged.
-        if (mCameraState == PREVIEW_STOPPED) {
-            setupPreview();
-        }
-    }
-
-    @Override
-    public void surfaceDestroyed(SurfaceHolder holder) {
-        Log.v(TAG, "surfaceDestroyed: " + holder);
-        mCameraSurfaceHolder = null;
-        stopPreview();
-    }
-
-    private void updateSceneModeUI() {
+    private void updateSceneMode() {
         // If scene mode is set, we cannot set flash mode, white balance, and
         // focus mode, instead, we read it from driver
         if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
@@ -1296,14 +1013,10 @@ public class PhotoModule
 
     private void overrideCameraSettings(final String flashMode,
             final String whiteBalance, final String focusMode) {
-        if (mPhotoControl != null) {
-//            mPieControl.enableFilter(true);
-            mPhotoControl.overrideSettings(
-                    CameraSettings.KEY_FLASH_MODE, flashMode,
-                    CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
-                    CameraSettings.KEY_FOCUS_MODE, focusMode);
-//            mPieControl.enableFilter(false);
-        }
+        mUI.overrideSettings(
+                CameraSettings.KEY_FLASH_MODE, flashMode,
+                CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
+                CameraSettings.KEY_FOCUS_MODE, focusMode);
     }
 
     private void loadCameraPreferences() {
@@ -1313,26 +1026,6 @@ public class PhotoModule
     }
 
     @Override
-    public boolean collapseCameraControls() {
-        // Remove all the popups/dialog boxes
-        boolean ret = false;
-        if (mPopup != null) {
-            dismissPopup(false);
-            ret = true;
-        }
-        return ret;
-    }
-
-    public boolean removeTopLevelPopup() {
-        // Remove the top level popup or dialog box and return true if there's any
-        if (mPopup != null) {
-            dismissPopup(true);
-            return true;
-        }
-        return false;
-    }
-
-    @Override
     public void onOrientationChanged(int orientation) {
         // We keep the last known orientation. So if the user first orient
         // the camera then point the camera to floor or sky, we still have
@@ -1355,29 +1048,22 @@ public class PhotoModule
         }
     }
 
-    // onClick handler for R.id.btn_done
-    @OnClickAttr
-    public void onReviewDoneClicked(View v) {
-        doAttach();
-    }
-
-    // onClick handler for R.id.btn_cancel
-    @OnClickAttr
-    public void onReviewCancelClicked(View v) {
-        doCancel();
+    @Override
+    public void onCaptureCancelled() {
+        mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
+        mActivity.finish();
     }
 
-    // onClick handler for R.id.btn_retake
-    @OnClickAttr
-    public void onReviewRetakeClicked(View v) {
+    @Override
+    public void onCaptureRetake() {
         if (mPaused)
             return;
-
-        hidePostCaptureAlert();
+        mUI.hidePostCaptureAlert();
         setupPreview();
     }
 
-    private void doAttach() {
+    @Override
+    public void onCaptureDone() {
         if (mPaused) {
             return;
         }
@@ -1456,14 +1142,9 @@ public class PhotoModule
         }
     }
 
-    private void doCancel() {
-        mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
-        mActivity.finish();
-    }
-
     @Override
     public void onShutterButtonFocus(boolean pressed) {
-        if (mPaused || collapseCameraControls()
+        if (mPaused || mUI.collapseCameraControls()
                 || (mCameraState == SNAPSHOT_IN_PROGRESS)
                 || (mCameraState == PREVIEW_STOPPED)) return;
 
@@ -1479,7 +1160,7 @@ public class PhotoModule
         } else {
             // for countdown mode, we need to postpone the shutter release
             // i.e. lock the focus during countdown.
-            if (!mCountDownView.isCountingDown()) {
+            if (!mUI.isCountingDown()) {
                 mFocusManager.onShutterUp();
             }
         }
@@ -1487,7 +1168,7 @@ public class PhotoModule
 
     @Override
     public void onShutterButtonClick() {
-        if (mPaused || collapseCameraControls()
+        if (mPaused || mUI.collapseCameraControls()
                 || (mCameraState == SWITCHING_CAMERA)
                 || (mCameraState == PREVIEW_STOPPED)) return;
 
@@ -1520,11 +1201,11 @@ public class PhotoModule
         int seconds = Integer.parseInt(timer);
         // When shutter button is pressed, check whether the previous countdown is
         // finished. If not, cancel the previous countdown and start a new one.
-        if (mCountDownView.isCountingDown()) {
-            mCountDownView.cancelCountDown();
-            mCountDownView.startCountDown(seconds, playSound);
-        } else if (seconds > 0) {
-            mCountDownView.startCountDown(seconds, playSound);
+        if (mUI.isCountingDown()) {
+            mUI.cancelCountDown();
+        }
+        if (seconds > 0) {
+            mUI.startCountDown(seconds, playSound);
         } else {
            mSnapshotOnIdle = false;
            mFocusManager.doSnap();
@@ -1614,17 +1295,8 @@ public class PhotoModule
             mCameraDevice.cancelAutoFocus();
         }
         stopPreview();
-        mCountDownView.cancelCountDown();
-        // Close the camera now because other activities may need to use it.
-        closeCamera();
         // Release surface texture.
         ((CameraScreenNail) mActivity.mCameraScreenNail).releaseSurfaceTexture();
-        mSurfaceTexture = null;
-        resetScreenOn();
-
-        // Clear UI.
-        collapseCameraControls();
-        if (mFaceView != null) mFaceView.clear();
 
         mNamedImages = null;
 
@@ -1645,9 +1317,11 @@ public class PhotoModule
         mHandler.removeMessages(OPEN_CAMERA_FAIL);
         mHandler.removeMessages(CAMERA_DISABLED);
 
-        mRootView.removeOnLayoutChangeListener(mLayoutChangeListener);
-        mPreviewWidth = 0;
-        mPreviewHeight = 0;
+        closeCamera();
+
+        resetScreenOn();
+        mUI.onPause();
+
         mPendingSwitchCameraId = -1;
         if (mFocusManager != null) mFocusManager.removeMessages();
         MediaSaveService s = mActivity.getMediaSaveService();
@@ -1656,61 +1330,12 @@ public class PhotoModule
         }
     }
 
-    private void initializeControlByIntent() {
-        mBlocker = mActivity.findViewById(R.id.blocker);
-        mMenu = mActivity.findViewById(R.id.menu);
-        mMenu.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (mPieRenderer != null) {
-                    // If autofocus is not finished, cancel autofocus so that the
-                    // subsequent touch can be handled by PreviewGestures
-                    if (mCameraState == FOCUSING) cancelAutoFocus();
-                    mPieRenderer.showInCenter();
-                }
-            }
-        });
-        if (mIsImageCaptureIntent) {
-            mActivity.hideSwitcher();
-            ViewGroup cameraControls = (ViewGroup) mActivity.findViewById(R.id.camera_controls);
-            mActivity.getLayoutInflater().inflate(R.layout.review_module_control, cameraControls);
-
-            mReviewDoneButton = mActivity.findViewById(R.id.btn_done);
-            mReviewCancelButton = mActivity.findViewById(R.id.btn_cancel);
-            mReviewRetakeButton = mActivity.findViewById(R.id.btn_retake);
-            mReviewCancelButton.setVisibility(View.VISIBLE);
-
-            mReviewDoneButton.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    onReviewDoneClicked(v);
-                }
-            });
-            mReviewCancelButton.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    onReviewCancelClicked(v);
-                }
-            });
-
-            mReviewRetakeButton.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    onReviewRetakeClicked(v);
-                }
-            });
-
-            setupCaptureParams();
-        }
-    }
-
     /**
      * The focus manager is the first UI related element to get initialized,
      * and it requires the RenderOverlay, so initialize it here
      */
     private void initializeFocusManager() {
         // Create FocusManager object. startPreview needs it.
-        mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay);
         // if mFocusManager not null, reuse it
         // otherwise create a new instance
         if (mFocusManager != null) {
@@ -1722,7 +1347,7 @@ public class PhotoModule
                     R.array.pref_camera_focusmode_default_array);
             mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes,
                     mInitialParams, this, mirror,
-                    mActivity.getMainLooper());
+                    mActivity.getMainLooper(), mUI);
         }
     }
 
@@ -1784,7 +1409,7 @@ public class PhotoModule
         }
 
         // Do not trigger touch focus if popup window is opened.
-        if (removeTopLevelPopup()) return;
+        if (mUI.removeTopLevelPopup()) return;
 
         // Check if metering area or focus area is supported.
         if (!mFocusAreaSupported && !mMeteringAreaSupported) return;
@@ -1793,24 +1418,7 @@ public class PhotoModule
 
     @Override
     public boolean onBackPressed() {
-        if (mPieRenderer != null && mPieRenderer.showsItems()) {
-            mPieRenderer.hide();
-            return true;
-        }
-        // In image capture mode, back button should:
-        // 1) if there is any popup, dismiss them, 2) otherwise, get out of image capture
-        if (mIsImageCaptureIntent) {
-            if (!removeTopLevelPopup()) {
-                // no popup to dismiss, cancel image capture
-                doCancel();
-            }
-            return true;
-        } else if (!isCameraIdle()) {
-            // ignore backs while we're taking a picture
-            return true;
-        } else {
-            return removeTopLevelPopup();
-        }
+        return mUI.onBackPressed();
     }
 
     @Override
@@ -1838,14 +1446,9 @@ public class PhotoModule
                 // Start auto-focus immediately to reduce shutter lag. After
                 // the shutter button gets the focus, onShutterButtonFocus()
                 // will be called again but it is fine.
-                if (removeTopLevelPopup()) return true;
+                if (mUI.removeTopLevelPopup()) return true;
                 onShutterButtonFocus(true);
-                if (mShutterButton.isInTouchMode()) {
-                    mShutterButton.requestFocusFromTouch();
-                } else {
-                    mShutterButton.requestFocus();
-                }
-                mShutterButton.setPressed(true);
+                mUI.pressShutterButton();
             }
             return true;
         }
@@ -1891,9 +1494,7 @@ public class PhotoModule
         mDisplayRotation = Util.getDisplayRotation(mActivity);
         mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, mCameraId);
         mCameraDisplayOrientation = Util.getDisplayOrientation(0, mCameraId);
-        if (mFaceView != null) {
-            mFaceView.setDisplayOrientation(mDisplayOrientation);
-        }
+        mUI.setDisplayOrientation(mDisplayOrientation);
         if (mFocusManager != null) {
             mFocusManager.setDisplayOrientation(mDisplayOrientation);
         }
@@ -1933,7 +1534,7 @@ public class PhotoModule
 
         if (ApiHelper.HAS_SURFACE_TEXTURE) {
             CameraScreenNail screenNail = (CameraScreenNail) mActivity.mCameraScreenNail;
-            if (mSurfaceTexture == null) {
+            if (mUI.getSurfaceTexture() == null) {
                 Size size = mParameters.getPreviewSize();
                 if (mCameraDisplayOrientation % 180 == 0) {
                     screenNail.setSize(size.width, size.height);
@@ -1947,15 +1548,16 @@ public class PhotoModule
                 if (t != null && t.isCanceled()) {
                     return; // Exiting, so no need to get the surface texture.
                 }
-                mSurfaceTexture = screenNail.getSurfaceTexture();
+                mUI.setSurfaceTexture(screenNail.getSurfaceTexture());
             }
             mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
-            if (mSurfaceTexture != null) {
-                mCameraDevice.setPreviewTextureAsync((SurfaceTexture) mSurfaceTexture);
+            Object st = mUI.getSurfaceTexture();
+            if (st != null) {
+                mCameraDevice.setPreviewTextureAsync((SurfaceTexture) st);
             }
         } else {
             mCameraDevice.setDisplayOrientation(mDisplayOrientation);
-            mCameraDevice.setPreviewDisplayAsync(mCameraSurfaceHolder);
+            mCameraDevice.setPreviewDisplayAsync(mUI.getSurfaceHolder());
         }
 
         Log.v(TAG, "startPreview");
@@ -1968,7 +1570,8 @@ public class PhotoModule
         }
     }
 
-    private void stopPreview() {
+    @Override
+    public void stopPreview() {
         if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
             Log.v(TAG, "stopPreview");
             mCameraDevice.stopPreview();
@@ -2197,7 +1800,7 @@ public class PhotoModule
             return;
         } else if (isCameraIdle()) {
             setCameraParameters(mUpdateSet);
-            updateSceneModeUI();
+            updateSceneMode();
             mUpdateSet = 0;
         } else {
             if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
@@ -2207,14 +1810,14 @@ public class PhotoModule
         }
     }
 
-    private boolean isCameraIdle() {
+    public boolean isCameraIdle() {
         return (mCameraState == IDLE) ||
                 (mCameraState == PREVIEW_STOPPED) ||
                 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
                         && (mCameraState != SWITCHING_CAMERA));
     }
 
-    private boolean isImageCaptureIntent() {
+    public boolean isImageCaptureIntent() {
         String action = mActivity.getIntent().getAction();
         return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
                 || ActivityBase.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
@@ -2228,26 +1831,6 @@ public class PhotoModule
         }
     }
 
-    private void showPostCaptureAlert() {
-        if (mIsImageCaptureIntent) {
-            mOnScreenIndicators.setVisibility(View.GONE);
-            mMenu.setVisibility(View.GONE);
-            Util.fadeIn(mReviewDoneButton);
-            mShutterButton.setVisibility(View.INVISIBLE);
-            Util.fadeIn(mReviewRetakeButton);
-        }
-    }
-
-    private void hidePostCaptureAlert() {
-        if (mIsImageCaptureIntent) {
-            mOnScreenIndicators.setVisibility(View.VISIBLE);
-            mMenu.setVisibility(View.VISIBLE);
-            Util.fadeOut(mReviewDoneButton);
-            mShutterButton.setVisibility(View.VISIBLE);
-            Util.fadeOut(mReviewRetakeButton);
-        }
-    }
-
     @Override
     public void onSharedPreferenceChanged() {
         // ignore the events after "onPause()"
@@ -2258,7 +1841,7 @@ public class PhotoModule
         mLocationManager.recordLocation(recordLocation);
 
         setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
-        updateOnScreenIndicators();
+        mUI.updateOnScreenIndicators(mParameters, mPreferences);
     }
 
     @Override
@@ -2278,71 +1861,6 @@ public class PhotoModule
         }
     }
 
-    private void switchCamera() {
-        if (mPaused) return;
-
-        Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
-        mCameraId = mPendingSwitchCameraId;
-        mPendingSwitchCameraId = -1;
-        mPhotoControl.setCameraId(mCameraId);
-
-        // from onPause
-        closeCamera();
-        collapseCameraControls();
-        if (mFaceView != null) mFaceView.clear();
-        if (mFocusManager != null) mFocusManager.removeMessages();
-
-        // Restart the camera and initialize the UI. From onCreate.
-        mPreferences.setLocalId(mActivity, mCameraId);
-        CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
-        try {
-            mCameraDevice = Util.openCamera(mActivity, mCameraId);
-            mParameters = mCameraDevice.getParameters();
-        } catch (CameraHardwareException e) {
-            Util.showErrorAndFinish(mActivity, R.string.cannot_connect_camera);
-            return;
-        } catch (CameraDisabledException e) {
-            Util.showErrorAndFinish(mActivity, R.string.camera_disabled);
-            return;
-        }
-        initializeCapabilities();
-        CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
-        boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
-        mFocusManager.setMirror(mirror);
-        mFocusManager.setParameters(mInitialParams);
-        setupPreview();
-        loadCameraPreferences();
-        initializePhotoControl();
-
-        // from initializeFirstTime
-        initializeZoom();
-        updateOnScreenIndicators();
-        showTapToFocusToastIfNeeded();
-
-        if (ApiHelper.HAS_SURFACE_TEXTURE) {
-            // Start switch camera animation. Post a message because
-            // onFrameAvailable from the old camera may already exist.
-            mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION);
-        }
-    }
-
-    @Override
-    public void onPieOpened(int centerX, int centerY) {
-        mActivity.cancelActivityTouchHandling();
-        mActivity.setSwipingEnabled(false);
-        if (mFaceView != null) {
-            mFaceView.setBlockDraw(true);
-        }
-    }
-
-    @Override
-    public void onPieClosed() {
-        mActivity.setSwipingEnabled(true);
-        if (mFaceView != null) {
-            mFaceView.setBlockDraw(false);
-        }
-    }
-
     // Preview texture has been copied. Now camera can be released and the
     // animation can be started.
     @Override
@@ -2378,11 +1896,7 @@ public class PhotoModule
     @Override
     public void onOverriddenPreferencesClicked() {
         if (mPaused) return;
-        if (mNotSelectableToast == null) {
-            String str = mActivity.getResources().getString(R.string.not_selectable_in_scene_mode);
-            mNotSelectableToast = Toast.makeText(mActivity, str, Toast.LENGTH_SHORT);
-        }
-        mNotSelectableToast.show();
+        mUI.showPreferencesToast();
     }
 
     private void showTapToFocusToast() {
@@ -2421,56 +1935,41 @@ public class PhotoModule
         return true;
     }
 
-    public void showPopup(AbstractSettingPopup popup) {
-        mActivity.hideUI();
-        mBlocker.setVisibility(View.INVISIBLE);
-        setShowMenu(false);
-        mPopup = popup;
-        mPopup.setVisibility(View.VISIBLE);
-        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
-                LayoutParams.WRAP_CONTENT);
-        lp.gravity = Gravity.CENTER;
-        ((FrameLayout) mRootView).addView(mPopup, lp);
-    }
-
-    public void dismissPopup(boolean topPopupOnly) {
-        dismissPopup(topPopupOnly, true);
-    }
-
-    private void dismissPopup(boolean topOnly, boolean fullScreen) {
-        if (fullScreen) {
-            mActivity.showUI();
-            mBlocker.setVisibility(View.VISIBLE);
-        }
-        setShowMenu(fullScreen);
-        if (mPopup != null) {
-            ((FrameLayout) mRootView).removeView(mPopup);
-            mPopup = null;
-        }
-        mPhotoControl.popupDismissed(topOnly);
+    @Override
+    public void onShowSwitcherPopup() {
+        mUI.onShowSwitcherPopup();
     }
 
     @Override
-    public void onShowSwitcherPopup() {
-        if (mPieRenderer != null && mPieRenderer.showsItems()) {
-            mPieRenderer.hide();
-        }
+    public int onZoomChanged(int index) {
+        // Not useful to change zoom value when the activity is paused.
+        if (mPaused) return index;
+        mZoomValue = index;
+        if (mParameters == null || mCameraDevice == null) return index;
+        // Set zoom parameters asynchronously
+        mParameters.setZoom(mZoomValue);
+        mCameraDevice.setParametersAsync(mParameters);
+        Parameters p = mCameraDevice.getParameters();
+        if (p != null) return p.getZoom();
+        return index;
     }
 
     @Override
-    public void onQueueAvailable() {
-        if (mShutterButton != null) mShutterButton.enableTouch(true);
+    public int getCameraState() {
+        return mCameraState;
     }
 
     @Override
-    public void onQueueFull() {
-        if (mShutterButton != null) mShutterButton.enableTouch(false);
+    public void onQueueStatus(boolean full) {
+        mUI.enableShutter(!full);
     }
 
     @Override
     public void onMediaSaveServiceConnected(MediaSaveService s) {
         // We set the listener only when both service and shutterbutton
         // are initialized.
-        if (mShutterButton != null) s.setListener(this);
+        if (mFirstTimeInitialized) {
+            s.setListener(this);
+        }
     }
 }
diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java
new file mode 100644 (file)
index 0000000..9959964
--- /dev/null
@@ -0,0 +1,725 @@
+/*
+ * Copyright (C) 2013 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.hardware.Camera;
+import android.hardware.Camera.Face;
+import android.hardware.Camera.FaceDetectionListener;
+import android.hardware.Camera.Parameters;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLayoutChangeListener;
+import android.view.ViewGroup;
+import android.view.ViewStub;
+import android.widget.FrameLayout;
+import android.widget.FrameLayout.LayoutParams;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+import com.android.camera.CameraPreference.OnPreferenceChangedListener;
+import com.android.camera.FocusOverlayManager.FocusUI;
+import com.android.camera.ui.AbstractSettingPopup;
+import com.android.camera.ui.CountDownView;
+import com.android.camera.ui.CountDownView.OnCountDownFinishedListener;
+import com.android.camera.ui.FaceView;
+import com.android.camera.ui.FocusIndicator;
+import com.android.camera.ui.PieRenderer;
+import com.android.camera.ui.PieRenderer.PieListener;
+import com.android.camera.ui.RenderOverlay;
+import com.android.camera.ui.ZoomRenderer;
+import com.android.gallery3d.R;
+import com.android.gallery3d.common.ApiHelper;
+
+import java.util.List;
+
+public class PhotoUI implements PieListener,
+    SurfaceHolder.Callback,
+    PreviewGestures.SingleTapListener,
+    FocusUI,
+    LocationManager.Listener,
+    FaceDetectionListener {
+
+    private static final String TAG = "CAM_UI";
+
+    private CameraActivity mActivity;
+    private PhotoController mController;
+    private PreviewGestures mGestures;
+
+    private View mRootView;
+    private Object mSurfaceTexture;
+    private volatile SurfaceHolder mSurfaceHolder;
+
+    private AbstractSettingPopup mPopup;
+    private ShutterButton mShutterButton;
+    private CountDownView mCountDownView;
+
+    private FaceView mFaceView;
+    private RenderOverlay mRenderOverlay;
+    private View mReviewCancelButton;
+    private View mReviewDoneButton;
+    private View mReviewRetakeButton;
+
+    private View mMenuButton;
+    private View mBlocker;
+    private PhotoMenu mMenu;
+
+    // Small indicators which show the camera settings in the viewfinder.
+    private ImageView mExposureIndicator;
+    private ImageView mFlashIndicator;
+    private ImageView mSceneIndicator;
+    private ImageView mHdrIndicator;
+    // A view group that contains all the small indicators.
+    private View mOnScreenIndicators;
+
+    private PieRenderer mPieRenderer;
+    private ZoomRenderer mZoomRenderer;
+    private Toast mNotSelectableToast;
+
+    private int mZoomMax;
+    private List<Integer> mZoomRatios;
+
+    private int mPreviewWidth = 0;
+    private int mPreviewHeight = 0;
+
+    private OnLayoutChangeListener mLayoutListener = new OnLayoutChangeListener() {
+        @Override
+        public void onLayoutChange(View v, int left, int top, int right,
+                int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+            int width = right - left;
+            int height = bottom - top;
+            // Full-screen screennail
+            int w = width;
+            int h = height;
+            if (Util.getDisplayRotation(mActivity) % 180 != 0) {
+                w = height;
+                h = width;
+            }
+            if (mPreviewWidth != w || mPreviewHeight != h) {
+                mController.onScreenSizeChanged(width, height, w, h);
+            }
+        }
+    };
+
+    public PhotoUI(CameraActivity activity, PhotoController controller, View parent) {
+        mActivity = activity;
+        mController = controller;
+        mRootView = parent;
+
+        mActivity.getLayoutInflater().inflate(R.layout.photo_module,
+                (ViewGroup) mRootView, true);
+        mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay);
+
+        initIndicators();
+        mCountDownView = (CountDownView) (mRootView.findViewById(R.id.count_down_to_capture));
+        mCountDownView.setCountDownFinishedListener((OnCountDownFinishedListener) mController);
+
+        if (ApiHelper.HAS_FACE_DETECTION) {
+            ViewStub faceViewStub = (ViewStub) mRootView
+                    .findViewById(R.id.face_view_stub);
+            if (faceViewStub != null) {
+                faceViewStub.inflate();
+                mFaceView = (FaceView) mRootView.findViewById(R.id.face_view);
+            }
+        }
+
+        mRootView.addOnLayoutChangeListener(mLayoutListener);
+    }
+
+    public View getRootView() {
+        return mRootView;
+    }
+
+    private void initIndicators() {
+        mOnScreenIndicators = mActivity.findViewById(R.id.on_screen_indicators);
+        mExposureIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_exposure_indicator);
+        mFlashIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_flash_indicator);
+        mSceneIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_scenemode_indicator);
+        mHdrIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_hdr_indicator);
+    }
+
+    public void onCameraOpened(PreferenceGroup prefGroup, ComboPreferences prefs,
+            Camera.Parameters params, OnPreferenceChangedListener listener) {
+        if (mPieRenderer == null) {
+            mPieRenderer = new PieRenderer(mActivity);
+            mPieRenderer.setPieListener(this);
+        }
+        if (mMenu == null) {
+            mMenu = new PhotoMenu(mActivity, this, mPieRenderer);
+            mMenu.setListener(listener);
+        }
+        mMenu.initialize(prefGroup);
+
+        if (mZoomRenderer == null) {
+            mZoomRenderer = new ZoomRenderer(mActivity);
+        }
+        mRenderOverlay.addRenderer(mPieRenderer);
+        mRenderOverlay.addRenderer(mZoomRenderer);
+        if (mGestures == null) {
+            // this will handle gesture disambiguation and dispatching
+            mGestures = new PreviewGestures(mActivity, this, mZoomRenderer, mPieRenderer);
+        }
+        mGestures.clearTouchReceivers();
+        mGestures.setRenderOverlay(mRenderOverlay);
+        mGestures.addTouchReceiver(mMenuButton);
+        mGestures.addTouchReceiver(mBlocker);
+        // make sure to add touch targets for image capture
+        if (mController.isImageCaptureIntent()) {
+            if (mReviewCancelButton != null) {
+                mGestures.addTouchReceiver(mReviewCancelButton);
+            }
+            if (mReviewDoneButton != null) {
+                mGestures.addTouchReceiver(mReviewDoneButton);
+            }
+        }
+        mRenderOverlay.requestLayout();
+
+        initializeZoom(params);
+        updateOnScreenIndicators(params, prefs);
+    }
+
+    public void initializeControlByIntent() {
+        mBlocker = mActivity.findViewById(R.id.blocker);
+        mMenuButton = mActivity.findViewById(R.id.menu);
+        mMenuButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mPieRenderer != null) {
+                    // If autofocus is not finished, cancel autofocus so that the
+                    // subsequent touch can be handled by PreviewGestures
+                    if (mController.getCameraState() == PhotoController.FOCUSING) {
+                            mController.cancelAutoFocus();
+                    }
+                    mPieRenderer.showInCenter();
+                }
+            }
+        });
+        if (mController.isImageCaptureIntent()) {
+            mActivity.hideSwitcher();
+            ViewGroup cameraControls = (ViewGroup) mActivity.findViewById(R.id.camera_controls);
+            mActivity.getLayoutInflater().inflate(R.layout.review_module_control, cameraControls);
+
+            mReviewDoneButton = mActivity.findViewById(R.id.btn_done);
+            mReviewCancelButton = mActivity.findViewById(R.id.btn_cancel);
+            mReviewRetakeButton = mActivity.findViewById(R.id.btn_retake);
+            mReviewCancelButton.setVisibility(View.VISIBLE);
+
+            mReviewDoneButton.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    mController.onCaptureDone();
+                }
+            });
+            mReviewCancelButton.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    mController.onCaptureCancelled();
+                }
+            });
+
+            mReviewRetakeButton.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    mController.onCaptureRetake();
+                }
+            });
+        }
+    }
+
+    // called from onResume but only the first time
+    public  void initializeFirstTime() {
+        // Initialize shutter button.
+        mShutterButton = mActivity.getShutterButton();
+        mShutterButton.setImageResource(R.drawable.btn_new_shutter);
+        mShutterButton.setOnShutterButtonListener(mController);
+        mShutterButton.setVisibility(View.VISIBLE);
+    }
+
+    // called from onResume every other time
+    public void initializeSecondTime(Camera.Parameters params) {
+        initializeZoom(params);
+        if (mController.isImageCaptureIntent()) {
+            hidePostCaptureAlert();
+        }
+        if (mMenu != null) {
+            mMenu.reloadPreferences();
+        }
+    }
+
+    public void initializeZoom(Camera.Parameters params) {
+        if ((params == null) || !params.isZoomSupported()
+                || (mZoomRenderer == null)) return;
+        mZoomMax = params.getMaxZoom();
+        mZoomRatios = params.getZoomRatios();
+        // Currently we use immediate zoom for fast zooming to get better UX and
+        // there is no plan to take advantage of the smooth zoom.
+        if (mZoomRenderer != null) {
+            mZoomRenderer.setZoomMax(mZoomMax);
+            mZoomRenderer.setZoom(params.getZoom());
+            mZoomRenderer.setZoomValue(mZoomRatios.get(params.getZoom()));
+            mZoomRenderer.setOnZoomChangeListener(new ZoomChangeListener());
+        }
+    }
+
+    public void enableGestures(boolean enable) {
+        if (mGestures != null) {
+            mGestures.setEnabled(enable);
+        }
+    }
+
+    public void showGpsOnScreenIndicator(boolean hasSignal) { }
+
+    public void hideGpsOnScreenIndicator() { }
+
+    public void overrideSettings(final String ... keyvalues) {
+        mMenu.overrideSettings(keyvalues);
+    }
+
+    public void updateOnScreenIndicators(Camera.Parameters params,
+            ComboPreferences prefs) {
+        if (params == null) return;
+        updateSceneOnScreenIndicator(params.getSceneMode());
+        updateExposureOnScreenIndicator(params,
+                CameraSettings.readExposure(prefs));
+        updateFlashOnScreenIndicator(params.getFlashMode());
+        updateHdrOnScreenIndicator(params.getSceneMode());
+    }
+
+    private void updateExposureOnScreenIndicator(Camera.Parameters params, int value) {
+        if (mExposureIndicator == null) {
+            return;
+        }
+        int id = 0;
+        float step = params.getExposureCompensationStep();
+        value = (int) Math.round(value * step);
+        switch(value) {
+        case -3:
+            id = R.drawable.ic_indicator_ev_n3;
+            break;
+        case -2:
+            id = R.drawable.ic_indicator_ev_n2;
+            break;
+        case -1:
+            id = R.drawable.ic_indicator_ev_n1;
+            break;
+        case 0:
+            id = R.drawable.ic_indicator_ev_0;
+            break;
+        case 1:
+            id = R.drawable.ic_indicator_ev_p1;
+            break;
+        case 2:
+            id = R.drawable.ic_indicator_ev_p2;
+            break;
+        case 3:
+            id = R.drawable.ic_indicator_ev_p3;
+            break;
+        }
+        mExposureIndicator.setImageResource(id);
+    }
+
+    private void updateFlashOnScreenIndicator(String value) {
+        if (mFlashIndicator == null) {
+            return;
+        }
+        if (value == null || Parameters.FLASH_MODE_OFF.equals(value)) {
+            mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_off);
+        } else {
+            if (Parameters.FLASH_MODE_AUTO.equals(value)) {
+                mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_auto);
+            } else if (Parameters.FLASH_MODE_ON.equals(value)) {
+                mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_on);
+            } else {
+                mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_off);
+            }
+        }
+    }
+
+    private void updateSceneOnScreenIndicator(String value) {
+        if (mSceneIndicator == null) {
+            return;
+        }
+        if ((value == null) || Parameters.SCENE_MODE_AUTO.equals(value)
+                || Parameters.SCENE_MODE_HDR.equals(value)) {
+            mSceneIndicator.setImageResource(R.drawable.ic_indicator_sce_off);
+        } else {
+            mSceneIndicator.setImageResource(R.drawable.ic_indicator_sce_on);
+        }
+    }
+
+    private void updateHdrOnScreenIndicator(String value) {
+        if (mHdrIndicator == null) {
+            return;
+        }
+        if ((value != null) && Parameters.SCENE_MODE_HDR.equals(value)) {
+            mHdrIndicator.setImageResource(R.drawable.ic_indicator_hdr_on);
+        } else {
+            mHdrIndicator.setImageResource(R.drawable.ic_indicator_hdr_off);
+        }
+    }
+
+    public void setCameraState(int state) {
+    }
+
+    public boolean dispatchTouchEvent(MotionEvent m) {
+        if (mPopup != null) {
+            return mActivity.superDispatchTouchEvent(m);
+        } else if (mGestures != null && mRenderOverlay != null) {
+            return mGestures.dispatchTouch(m);
+        }
+        return false;
+    }
+
+    public boolean onBackPressed() {
+        if (mPieRenderer != null && mPieRenderer.showsItems()) {
+            mPieRenderer.hide();
+            return true;
+        }
+        // In image capture mode, back button should:
+        // 1) if there is any popup, dismiss them, 2) otherwise, get out of
+        // image capture
+        if (mController.isImageCaptureIntent()) {
+            if (!removeTopLevelPopup()) {
+                // no popup to dismiss, cancel image capture
+                mController.onCaptureCancelled();
+            }
+            return true;
+        } else if (!mController.isCameraIdle()) {
+            // ignore backs while we're taking a picture
+            return true;
+        } else {
+            return removeTopLevelPopup();
+        }
+    }
+
+    public void onFullScreenChanged(boolean full) {
+        if (mFaceView != null) {
+            mFaceView.setBlockDraw(!full);
+        }
+        if (mPopup != null) {
+            dismissPopup(false, full);
+        }
+        if (mGestures != null) {
+            mGestures.setEnabled(full);
+        }
+        if (mRenderOverlay != null) {
+            // this can not happen in capture mode
+            mRenderOverlay.setVisibility(full ? View.VISIBLE : View.GONE);
+        }
+        if (mPieRenderer != null) {
+            mPieRenderer.setBlockFocus(!full);
+        }
+        setShowMenu(full);
+        if (mBlocker != null) {
+            mBlocker.setVisibility(full ? View.VISIBLE : View.GONE);
+        }
+        if (!full && mCountDownView != null) mCountDownView.cancelCountDown();
+    }
+
+    public boolean removeTopLevelPopup() {
+        // Remove the top level popup or dialog box and return true if there's any
+        if (mPopup != null) {
+            dismissPopup(true);
+            return true;
+        }
+        return false;
+    }
+
+    public void showPopup(AbstractSettingPopup popup) {
+        mActivity.hideUI();
+        mBlocker.setVisibility(View.INVISIBLE);
+        setShowMenu(false);
+        mPopup = popup;
+        mPopup.setVisibility(View.VISIBLE);
+        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
+                LayoutParams.WRAP_CONTENT);
+        lp.gravity = Gravity.CENTER;
+        ((FrameLayout) mRootView).addView(mPopup, lp);
+    }
+
+    public void dismissPopup(boolean topPopupOnly) {
+        dismissPopup(topPopupOnly, true);
+    }
+
+    private void dismissPopup(boolean topOnly, boolean fullScreen) {
+        if (fullScreen) {
+            mActivity.showUI();
+            mBlocker.setVisibility(View.VISIBLE);
+        }
+        setShowMenu(fullScreen);
+        if (mPopup != null) {
+            ((FrameLayout) mRootView).removeView(mPopup);
+            mPopup = null;
+        }
+        mMenu.popupDismissed(topOnly);
+    }
+
+    public void onShowSwitcherPopup() {
+        if (mPieRenderer != null && mPieRenderer.showsItems()) {
+            mPieRenderer.hide();
+        }
+    }
+
+    private void setShowMenu(boolean show) {
+        if (mOnScreenIndicators != null) {
+            mOnScreenIndicators.setVisibility(show ? View.VISIBLE : View.GONE);
+        }
+        if (mMenuButton != null) {
+            mMenuButton.setVisibility(show ? View.VISIBLE : View.GONE);
+        }
+    }
+
+    public boolean collapseCameraControls() {
+        // Remove all the popups/dialog boxes
+        boolean ret = false;
+        if (mPopup != null) {
+            dismissPopup(false);
+            ret = true;
+        }
+        return ret;
+    }
+
+    protected void showPostCaptureAlert() {
+        mOnScreenIndicators.setVisibility(View.GONE);
+        mMenuButton.setVisibility(View.GONE);
+        Util.fadeIn(mReviewDoneButton);
+        mShutterButton.setVisibility(View.INVISIBLE);
+        Util.fadeIn(mReviewRetakeButton);
+    }
+
+    protected void hidePostCaptureAlert() {
+        mOnScreenIndicators.setVisibility(View.VISIBLE);
+        mMenuButton.setVisibility(View.VISIBLE);
+        Util.fadeOut(mReviewDoneButton);
+        mShutterButton.setVisibility(View.VISIBLE);
+        Util.fadeOut(mReviewRetakeButton);
+    }
+
+    public void setDisplayOrientation(int orientation) {
+        if (mFaceView != null) {
+            mFaceView.setDisplayOrientation(orientation);
+        }
+    }
+
+    // shutter button handling
+
+    public boolean isShutterPressed() {
+        return mShutterButton.isPressed();
+    }
+
+    // focus handling
+
+
+    private class ZoomChangeListener implements ZoomRenderer.OnZoomChangedListener {
+        @Override
+        public void onZoomValueChanged(int index) {
+            int newZoom = mController.onZoomChanged(index);
+            if (mZoomRenderer != null) {
+                mZoomRenderer.setZoomValue(mZoomRatios.get(newZoom));
+            }
+        }
+
+        @Override
+        public void onZoomStart() {
+            if (mPieRenderer != null) {
+                mPieRenderer.setBlockFocus(true);
+            }
+        }
+
+        @Override
+        public void onZoomEnd() {
+            if (mPieRenderer != null) {
+                mPieRenderer.setBlockFocus(false);
+            }
+        }
+    }
+
+    @Override
+    public void onPieOpened(int centerX, int centerY) {
+        mActivity.cancelActivityTouchHandling();
+        mActivity.setSwipingEnabled(false);
+        if (mFaceView != null) {
+            mFaceView.setBlockDraw(true);
+        }
+    }
+
+    @Override
+    public void onPieClosed() {
+        mActivity.setSwipingEnabled(true);
+        if (mFaceView != null) {
+            mFaceView.setBlockDraw(false);
+        }
+    }
+
+    // Surface Listener
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+        Log.v(TAG, "surfaceChanged:" + holder + " width=" + width + ". height="
+                + height);
+    }
+
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+        Log.v(TAG, "surfaceCreated: " + holder);
+        mSurfaceHolder = holder;
+        mController.onSurfaceCreated(holder);
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        Log.v(TAG, "surfaceDestroyed: " + holder);
+        mSurfaceHolder = null;
+        mController.stopPreview();
+    }
+
+    public Object getSurfaceTexture() {
+        return mSurfaceTexture;
+    }
+
+    public void setSurfaceTexture(Object st) {
+        mSurfaceTexture = st;
+    }
+
+    public SurfaceHolder getSurfaceHolder() {
+        return mSurfaceHolder;
+    }
+
+    public boolean isCountingDown() {
+        return mCountDownView.isCountingDown();
+    }
+
+    public void cancelCountDown() {
+        mCountDownView.cancelCountDown();
+    }
+
+    public void startCountDown(int sec, boolean playSound) {
+        mCountDownView.startCountDown(sec, playSound);
+    }
+
+    public void showPreferencesToast() {
+        if (mNotSelectableToast == null) {
+            String str = mActivity.getResources().getString(R.string.not_selectable_in_scene_mode);
+            mNotSelectableToast = Toast.makeText(mActivity, str, Toast.LENGTH_SHORT);
+        }
+        mNotSelectableToast.show();
+    }
+
+    public void onPause() {
+        mCountDownView.cancelCountDown();
+        // Close the camera now because other activities may need to use it.
+        mSurfaceTexture = null;
+
+        // Clear UI.
+        collapseCameraControls();
+        if (mFaceView != null) mFaceView.clear();
+
+
+        mRootView.removeOnLayoutChangeListener(mLayoutListener);
+        mPreviewWidth = 0;
+        mPreviewHeight = 0;
+    }
+
+    public void enableShutter(boolean enabled) {
+        if (mShutterButton != null) {
+            mShutterButton.setEnabled(enabled);
+        }
+    }
+
+    public void pressShutterButton() {
+        if (mShutterButton.isInTouchMode()) {
+            mShutterButton.requestFocusFromTouch();
+        } else {
+            mShutterButton.requestFocus();
+        }
+        mShutterButton.setPressed(true);
+    }
+
+    // forward from preview gestures to controller
+    @Override
+    public void onSingleTapUp(View view, int x, int y) {
+        mController.onSingleTapUp(view, x, y);
+    }
+
+    // focus UI implementation
+
+    private FocusIndicator getFocusIndicator() {
+        return (mFaceView != null && mFaceView.faceExists()) ? mFaceView : mPieRenderer;
+    }
+
+    @Override
+    public boolean hasFaces() {
+        return (mFaceView != null && mFaceView.faceExists());
+    }
+
+    public void clearFaces() {
+        if (mFaceView != null) mFaceView.clear();
+    }
+
+    @Override
+    public void clearFocus() {
+        getFocusIndicator().clear();
+    }
+
+    @Override
+    public void setFocusPosition(int x, int y) {
+        mPieRenderer.setFocus(x, y);
+    }
+
+    @Override
+    public void onFocusStarted() {
+        getFocusIndicator().showStart();
+    }
+
+    @Override
+    public void onFocusSucceeded(boolean timeout) {
+        getFocusIndicator().showSuccess(timeout);
+    }
+
+    @Override
+    public void onFocusFailed(boolean timeout) {
+        getFocusIndicator().showFail(timeout);
+    }
+
+    @Override
+    public void pauseFaceDetection() {
+        if (mFaceView != null) mFaceView.pause();
+    }
+
+    @Override
+    public void resumeFaceDetection() {
+        if (mFaceView != null) mFaceView.resume();
+    }
+
+    public void onStartFaceDetection(int orientation, boolean mirror) {
+        mFaceView.clear();
+        mFaceView.setVisibility(View.VISIBLE);
+        mFaceView.setDisplayOrientation(orientation);
+        mFaceView.setMirror(mirror);
+        mFaceView.resume();
+    }
+
+    @Override
+    public void onFaceDetection(Face[] faces, android.hardware.Camera camera) {
+        mFaceView.setFaces(faces);
+    }
+
+}
index cb7ca09..f6b6228 100644 (file)
@@ -46,7 +46,7 @@ public class PreviewGestures
     private static final int MODE_ALL = 4;
 
     private CameraActivity mActivity;
-    private CameraModule mModule;
+    private SingleTapListener mTapListener;
     private RenderOverlay mOverlay;
     private PieRenderer mPie;
     private ZoomRenderer mZoom;
@@ -72,10 +72,14 @@ public class PreviewGestures
         }
     };
 
-    public PreviewGestures(CameraActivity ctx, CameraModule module,
+    public interface SingleTapListener {
+        public void onSingleTapUp(View v, int x, int y);
+    }
+
+    public PreviewGestures(CameraActivity ctx, SingleTapListener tapListener,
             ZoomRenderer zoom, PieRenderer pie) {
         mActivity = ctx;
-        mModule = module;
+        mTapListener = tapListener;
         mPie = pie;
         mZoom = zoom;
         mMode = MODE_ALL;
@@ -199,7 +203,7 @@ public class PreviewGestures
                 cancelActivityTouchHandling(m);
                 // must have been tap
                 if (m.getEventTime() - mDown.getEventTime() < mTapTimeout) {
-                    mModule.onSingleTapUp(null,
+                    mTapListener.onSingleTapUp(null,
                             (int) mDown.getX() - mOverlay.getWindowPositionX(),
                             (int) mDown.getY() - mOverlay.getWindowPositionY());
                     return true;
similarity index 98%
rename from src/com/android/camera/VideoController.java
rename to src/com/android/camera/VideoMenu.java
index 3a22077..ab53e15 100644 (file)
@@ -28,7 +28,7 @@ import com.android.camera.ui.PieRenderer;
 import com.android.camera.ui.TimeIntervalPopup;
 import com.android.gallery3d.R;
 
-public class VideoController extends PieController
+public class VideoMenu extends PieController
         implements MoreSettingPopup.Listener,
         ListPrefSettingPopup.Listener,
         TimeIntervalPopup.Listener {
@@ -46,7 +46,7 @@ public class VideoController extends PieController
     private static final int POPUP_SECOND_LEVEL = 2;
     private int mPopupStatus;
 
-    public VideoController(CameraActivity activity, VideoModule module, PieRenderer pie) {
+    public VideoMenu(CameraActivity activity, VideoModule module, PieRenderer pie) {
         super(activity, pie);
         mModule = module;
     }
index 7bda657..9ba5bec 100644 (file)
@@ -91,7 +91,8 @@ public class VideoModule implements CameraModule,
     MediaRecorder.OnErrorListener,
     MediaRecorder.OnInfoListener,
     EffectsRecorder.EffectsListener,
-    PieRenderer.PieListener {
+    PieRenderer.PieListener,
+    PreviewGestures.SingleTapListener {
 
     private static final String TAG = "CAM_VideoModule";
 
@@ -213,7 +214,7 @@ public class VideoModule implements CameraModule,
     private RenderOverlay mRenderOverlay;
     private PieRenderer mPieRenderer;
 
-    private VideoController mVideoControl;
+    private VideoMenu mVideoControl;
     private AbstractSettingPopup mPopup;
     private int mPendingSwitchCameraId;
 
@@ -393,7 +394,7 @@ public class VideoModule implements CameraModule,
         mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay);
         if (mPieRenderer == null) {
             mPieRenderer = new PieRenderer(mActivity);
-            mVideoControl = new VideoController(mActivity, this, mPieRenderer);
+            mVideoControl = new VideoMenu(mActivity, this, mPieRenderer);
             mVideoControl.setListener(this);
             mPieRenderer.setPieListener(this);
         }
@@ -511,7 +512,6 @@ public class VideoModule implements CameraModule,
                 settings.getPreferenceGroup(R.xml.video_preferences));
     }
 
-    @Override
     public boolean collapseCameraControls() {
         boolean ret = false;
         if (mPopup != null) {