Merge "SnapdragonCamera: Fix auto HDR indication issue" into camera.lnx.1.0-dev.1.0
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 13e4f5d..ac727e2 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -4,7 +4,7 @@
     package="org.codeaurora.snapcam">
 
     <uses-sdk
-        android:minSdkVersion="21"
+        android:minSdkVersion="23"
         android:targetSdkVersion="23" />
 
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
diff --git a/res/layout/capture_module.xml b/res/layout/capture_module.xml
index 2715dcb..02c32ac 100644
--- a/res/layout/capture_module.xml
+++ b/res/layout/capture_module.xml
@@ -40,8 +40,8 @@
 
         <SurfaceView
             android:id="@+id/mdp_preview_content2"
-            android:layout_width="100dp"
-            android:layout_height="100dp" />
+            android:layout_width="300dp"
+            android:layout_height="300dp" />
     </FrameLayout>
 
     <View
diff --git a/res/layout/video_module.xml b/res/layout/video_module.xml
index 1c1db44..c106351 100644
--- a/res/layout/video_module.xml
+++ b/res/layout/video_module.xml
@@ -26,8 +26,7 @@
         <SurfaceView
             android:id="@+id/mdp_preview_content"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:visibility="gone" />
+            android:layout_height="match_parent" />
     </FrameLayout>
     <FrameLayout
         android:layout_width="match_parent"
diff --git a/res/values/qcomarrays.xml b/res/values/qcomarrays.xml
index a4c4637..40c8b8a 100644
--- a/res/values/qcomarrays.xml
+++ b/res/values/qcomarrays.xml
@@ -863,5 +863,15 @@
         <item>@string/pref_camera_dual_camera_value_bayer</item>
         <item>@string/pref_camera_dual_camera_value_mono</item>
     </string-array>
+
+    <string-array name="pref_camera_mono_preview_entries" translatable="true">
+        <item>@string/pref_camera_mono_preview_entry_on</item>
+        <item>@string/pref_camera_mono_preview_entry_off</item>
+    </string-array>
+
+    <string-array name="pref_camera_mono_preview_entryvalues" translatable="false">
+        <item>@string/pref_camera_mono_preview_value_on</item>
+        <item>@string/pref_camera_mono_preview_value_off</item>
+    </string-array>
 </resources>
 
diff --git a/res/values/qcomstrings.xml b/res/values/qcomstrings.xml
index 0e21bb5..888000c 100644
--- a/res/values/qcomstrings.xml
+++ b/res/values/qcomstrings.xml
@@ -953,9 +953,8 @@
     <string name="pref_camera_camera2_value_enable">enable</string>
     <string name="pref_camera_camera2_value_disable">disable</string>
 
-
     <string name="pref_camera_dual_camera_title">Dual Camera Mode</string>
-    <string name="pref_camera_dual_camera_default">bayer</string>
+    <string name="pref_camera_dual_camera_default">dual</string>
     <string name="pref_camera_dual_camera_entry_dual">Dual-camera Linked</string>
     <string name="pref_camera_dual_camera_entry_bayer">Single Bayer Camera</string>
     <string name="pref_camera_dual_camera_entry_mono">Single Mono Camera</string>
@@ -963,5 +962,13 @@
     <string name="pref_camera_dual_camera_value_dual">dual</string>
     <string name="pref_camera_dual_camera_value_bayer">bayer</string>
     <string name="pref_camera_dual_camera_value_mono">mono</string>
+
+    <string name="pref_camera_mono_preview_title">Mono Preview</string>
+    <string name="pref_camera_mono_preview_default">off</string>
+    <string name="pref_camera_mono_preview_entry_on">On</string>
+    <string name="pref_camera_mono_preview_entry_off">Off</string>
+
+    <string name="pref_camera_mono_preview_value_on">on</string>
+    <string name="pref_camera_mono_preview_value_off">off</string>
 </resources>
 
diff --git a/res/xml/camera_preferences.xml b/res/xml/camera_preferences.xml
index 028ff4d..86eb36f 100644
--- a/res/xml/camera_preferences.xml
+++ b/res/xml/camera_preferences.xml
@@ -379,4 +379,11 @@
             camera:title="@string/pref_camera_dual_camera_title"
             camera:entries="@array/pref_camera_dual_camera_entries"
             camera:entryValues="@array/pref_camera_dual_camera_entryvalues" />
+
+    <ListPreference
+        camera:key="pref_camera_mono_preview_key"
+        camera:defaultValue="@string/pref_camera_mono_preview_default"
+        camera:title="@string/pref_camera_mono_preview_title"
+        camera:entries="@array/pref_camera_mono_preview_entries"
+        camera:entryValues="@array/pref_camera_mono_preview_entryvalues" />
 </PreferenceGroup>
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java
index 80387a9..fafb408 100644
--- a/src/com/android/camera/CameraActivity.java
+++ b/src/com/android/camera/CameraActivity.java
@@ -1813,7 +1813,9 @@
     public void onModuleSelected(int moduleIndex) {
         if (moduleIndex == 0 && CAMERA_2_ON) moduleIndex = ModuleSwitcher.CAPTURE_MODULE_INDEX;
         if (mCurrentModuleIndex == moduleIndex) {
-            return;
+            if (mCurrentModuleIndex != ModuleSwitcher.CAPTURE_MODULE_INDEX) {
+                return;
+            }
         }
         CameraHolder.instance().keep();
         closeModule(mCurrentModule);
@@ -1840,7 +1842,6 @@
         mCameraVideoModuleRootView.setVisibility(View.GONE);
         mCameraPanoModuleRootView.setVisibility(View.GONE);
         mCameraCaptureModuleRootView.setVisibility(View.GONE);
-        mCameraRootFrame.removeAllViews();
         mCurrentModuleIndex = moduleIndex;
         switch (moduleIndex) {
             case ModuleSwitcher.VIDEO_MODULE_INDEX:
@@ -1851,7 +1852,6 @@
                     mVideoModule.reinit();
                 }
                 mCurrentModule = mVideoModule;
-                mCameraRootFrame.addView(mCameraVideoModuleRootView);
                 mCameraVideoModuleRootView.setVisibility(View.VISIBLE);
                 break;
 
@@ -1863,7 +1863,6 @@
                     mPhotoModule.reinit();
                 }
                 mCurrentModule = mPhotoModule;
-                mCameraRootFrame.addView(mCameraPhotoModuleRootView);
                 mCameraPhotoModuleRootView.setVisibility(View.VISIBLE);
                 break;
 
@@ -1873,7 +1872,6 @@
                     mPanoModule.init(this, mCameraPanoModuleRootView);
                 }
                 mCurrentModule = mPanoModule;
-                mCameraRootFrame.addView(mCameraPanoModuleRootView);
                 mCameraPanoModuleRootView.setVisibility(View.VISIBLE);
                 break;
 
@@ -1883,7 +1881,6 @@
                     mCaptureModule.init(this, mCameraCaptureModuleRootView);
                 }
                 mCurrentModule = mCaptureModule;
-                mCameraRootFrame.addView(mCameraCaptureModuleRootView);
                 mCameraCaptureModuleRootView.setVisibility(View.VISIBLE);
                 break;
             case ModuleSwitcher.LIGHTCYCLE_MODULE_INDEX: //Unused module for now
@@ -1897,7 +1894,6 @@
                     mPhotoModule.reinit();
                 }
                 mCurrentModule = mPhotoModule;
-                mCameraRootFrame.addView(mCameraPhotoModuleRootView);
                 mCameraPhotoModuleRootView.setVisibility(View.VISIBLE);
                 break;
         }
diff --git a/src/com/android/camera/CameraSettings.java b/src/com/android/camera/CameraSettings.java
index 48d161d..b0bc9c8 100644
--- a/src/com/android/camera/CameraSettings.java
+++ b/src/com/android/camera/CameraSettings.java
@@ -248,6 +248,7 @@
 
     public static final String KEY_CAMERA2 = "pref_camera_camera2_key";
     public static final String KEY_DUAL_CAMERA = "pref_camera_dual_camera_key";
+    public static final String KEY_MONO_PREVIEW = "pref_camera_mono_preview_key";
 
     public static final String KEY_REFOCUS_PROMPT = "refocus-prompt";
 
diff --git a/src/com/android/camera/CaptureMenu.java b/src/com/android/camera/CaptureMenu.java
index dbc8132..99f919a 100644
--- a/src/com/android/camera/CaptureMenu.java
+++ b/src/com/android/camera/CaptureMenu.java
@@ -26,6 +26,7 @@
 import android.graphics.Rect;
 import android.preference.PreferenceManager;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -91,14 +92,24 @@
 
         mOtherKeys1 = new String[]{
                 CameraSettings.KEY_FLASH_MODE,
+                CameraSettings.KEY_RECORD_LOCATION,
+                CameraSettings.KEY_JPEG_QUALITY,
+                CameraSettings.KEY_CAMERA_SAVEPATH,
+                CameraSettings.KEY_WHITE_BALANCE,
                 CameraSettings.KEY_CAMERA2,
                 CameraSettings.KEY_DUAL_CAMERA
         };
 
+        //Todo: 2nd string to contain only developer settings
         mOtherKeys2 = new String[]{
                 CameraSettings.KEY_FLASH_MODE,
+                CameraSettings.KEY_RECORD_LOCATION,
+                CameraSettings.KEY_JPEG_QUALITY,
+                CameraSettings.KEY_CAMERA_SAVEPATH,
+                CameraSettings.KEY_WHITE_BALANCE,
                 CameraSettings.KEY_CAMERA2,
-                CameraSettings.KEY_DUAL_CAMERA
+                CameraSettings.KEY_DUAL_CAMERA,
+                CameraSettings.KEY_MONO_PREVIEW
         };
 
     }
@@ -106,9 +117,8 @@
     @Override
     // Hit when an item in a popup gets selected
     public void onListPrefChanged(ListPreference pref) {
-        animateFadeOut(mListSubMenu, 2);
         onSettingChanged(pref);
-        ((ListMenu) mListMenu).resetHighlight();
+        closeView();
     }
 
     public boolean handleBackKey() {
@@ -384,6 +394,12 @@
         listMenu.initialize(mPreferenceGroup, keys);
         mListMenu = listMenu;
 
+        ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_DUAL_CAMERA);
+        if (!pref.getValue().equals("dual")) {
+            setPreference(CameraSettings.KEY_MONO_PREVIEW, "off");
+            mListMenu.setPreferenceEnabled(CameraSettings.KEY_MONO_PREVIEW, false);
+        }
+
         if (mListener != null) {
             mListener.onSharedPreferenceChanged();
         }
@@ -516,18 +532,29 @@
     @Override
     public void onSettingChanged(ListPreference pref) {
         super.onSettingChanged(pref);
-        if (same(pref, CameraSettings.KEY_CAMERA2, "enable")) {
+        String key = pref.getKey();
+        String value = pref.getValue();
+        Log.d(TAG, "" + key + " " + value);
+        //Todo: restructure by using switch and create function for each case
+        if (key.equals(CameraSettings.KEY_CAMERA2)) {
             SharedPreferences prefs = PreferenceManager
                     .getDefaultSharedPreferences(mActivity);
-            prefs.edit().putBoolean(CameraSettings.KEY_CAMERA2, true).apply();
-            CameraActivity.CAMERA_2_ON = true;
-            mActivity.onModuleSelected(ModuleSwitcher.CAPTURE_MODULE_INDEX);
-        } else if (notSame(pref, CameraSettings.KEY_CAMERA2, "enable")) {
-            SharedPreferences prefs = PreferenceManager
-                    .getDefaultSharedPreferences(mActivity);
-            prefs.edit().putBoolean(CameraSettings.KEY_CAMERA2, false).apply();
-            CameraActivity.CAMERA_2_ON = false;
-            mActivity.onModuleSelected(ModuleSwitcher.PHOTO_MODULE_INDEX);
+            if (value.equals("enable")) {
+                prefs.edit().putBoolean(CameraSettings.KEY_CAMERA2, true).apply();
+                CameraActivity.CAMERA_2_ON = true;
+                mActivity.onModuleSelected(ModuleSwitcher.CAPTURE_MODULE_INDEX);
+            } else if (value.equals("disable")) {
+                prefs.edit().putBoolean(CameraSettings.KEY_CAMERA2, false).apply();
+                CameraActivity.CAMERA_2_ON = false;
+                mActivity.onModuleSelected(ModuleSwitcher.PHOTO_MODULE_INDEX);
+            }
+        } else if (key.equals(CameraSettings.KEY_DUAL_CAMERA)) {
+            boolean changeMode = CaptureModule.setMode(value);
+            if (changeMode) mActivity.onModuleSelected(ModuleSwitcher.CAPTURE_MODULE_INDEX);
+        } else if (key.equals(CameraSettings.KEY_MONO_PREVIEW)) {
+            if (value.equals("on")) {
+            } else if (value.equals("off")) {
+            }
         }
     }
 
diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java
index 604a705..52f9327 100644
--- a/src/com/android/camera/CaptureModule.java
+++ b/src/com/android/camera/CaptureModule.java
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.graphics.ImageFormat;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCaptureSession;
@@ -36,22 +37,30 @@
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.MeteringRectangle;
 import android.hardware.camera2.params.StreamConfigurationMap;
+import android.media.CameraProfile;
 import android.media.Image;
 import android.media.ImageReader;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
 import android.util.Log;
 import android.util.Size;
 import android.util.SparseIntArray;
 import android.view.KeyEvent;
+import android.view.OrientationEventListener;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.View;
+import android.widget.Toast;
 
 import com.android.camera.PhotoModule.NamedImages;
 import com.android.camera.PhotoModule.NamedImages.NamedEntity;
+import com.android.camera.ui.RotateTextToast;
+import com.android.camera.util.CameraUtil;
 
 import org.codeaurora.snapcam.R;
 
@@ -60,13 +69,20 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
-public class CaptureModule implements CameraModule, PhotoController {
-    private static final int NUMCAM = 1;
+public class CaptureModule implements CameraModule, PhotoController,
+        MediaSaveService.Listener {
+    public static final int DUAL_MODE = 0;
+    public static final int BAYER_MODE = 1;
+    public static final int MONO_MODE = 2;
+    private static final int OPEN_CAMERA = 0;
+    private static final int MAX_NUM_CAM = 3;
     /**
      * Conversion from screen rotation to JPEG orientation.
      */
@@ -91,7 +107,11 @@
      * Camera state: Picture was taken.
      */
     private static final int STATE_PICTURE_TAKEN = 4;
+    //Todo: Read ids from the device dynamically
+    private static final int BAYER_ID = 0;
+    private static final int MONO_ID = 1;
     private static final String TAG = "SnapCam_CaptureModule";
+    private static int MODE = DUAL_MODE;
 
     static {
         ORIENTATIONS.append(Surface.ROTATION_0, 90);
@@ -100,44 +120,45 @@
         ORIENTATIONS.append(Surface.ROTATION_270, 180);
     }
 
+    MeteringRectangle[][] mAFRegions = new MeteringRectangle[MAX_NUM_CAM][];
+    CaptureRequest.Key<Byte> BayerMonoLinkEnableKey =
+            new CaptureRequest.Key<>("org.codeaurora.qcamera3.dualcam_link_meta_data.enable",
+                    Byte.class);
+    CaptureRequest.Key<Byte> BayerMonoLinkMainKey =
+            new CaptureRequest.Key<>("org.codeaurora.qcamera3.dualcam_link_meta_data.is_main",
+                    Byte.class);
+    CaptureRequest.Key<Integer> BayerMonoLinkSessionIdKey =
+            new CaptureRequest.Key<>("org.codeaurora.qcamera3.dualcam_link_meta_data" +
+                    ".related_camera_id", Integer.class);
+    private int mLastResultAFState = -1;
+    private Rect[] mCropRegion = new Rect[MAX_NUM_CAM];
+    private boolean mAutoFocusSupported;
+    // The degrees of the device rotated clockwise from its natural orientation.
+    private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
+    private int mJpegQuality;
+    private Map<String, String> mSettings = new HashMap<String, String>();
+    private boolean mFirstTimeInitialized;
+    private boolean mInitialized = false;
+    private boolean mIsLinked = false;
     private long mCaptureStartTime;
+    private boolean mPaused = true;
     private boolean mSurfaceReady = false;
-    private boolean mCameraOpened = false;
-    private CameraDevice[] mCameraDevice = new CameraDevice[NUMCAM];
-    private String[] mCameraId = new String[NUMCAM];
+    private boolean[] mCameraOpened = new boolean[MAX_NUM_CAM];
+    private CameraDevice[] mCameraDevice = new CameraDevice[MAX_NUM_CAM];
+    private String[] mCameraId = new String[MAX_NUM_CAM];
     private CaptureUI mUI;
     private CameraActivity mActivity;
     private PreferenceGroup mPreferenceGroup;
     private ComboPreferences mPreferences;
-    private CaptureRequest.Builder[] mCaptureBuilder = new CaptureRequest.Builder[NUMCAM];
-    private final CameraPreference.OnPreferenceChangedListener prefListener = new
-            CameraPreference.OnPreferenceChangedListener() {
-                @Override
-                public void onSharedPreferenceChanged(ListPreference pref) {
-                    applyPreference(0, pref);
-                    mUI.overrideSettings(pref.getKey(), null);
-                }
-
-                @Override
-                public void onSharedPreferenceChanged() {
-                }
-
-                @Override
-                public void onRestorePreferencesClicked() {
-                }
-
-                @Override
-                public void onOverriddenPreferencesClicked() {
-                }
-
-                @Override
-                public void onCameraPickerClicked(int cameraId) {
-                }
-            };
+    private CameraCharacteristics[] mCharacteristics = new CameraCharacteristics[MAX_NUM_CAM];
+    private List<Integer> mCharacteristicsIndex;
+    private float mZoomValue = 1f;
+    private FocusStateListener mFocusStateListener;
+    private LocationManager mLocationManager;
     /**
      * A {@link CameraCaptureSession } for camera preview.
      */
-    private CameraCaptureSession[] mCaptureSession = new CameraCaptureSession[NUMCAM];
+    private CameraCaptureSession[] mCaptureSession = new CameraCaptureSession[MAX_NUM_CAM];
     /**
      * An additional thread for running tasks that shouldn't block the UI.
      */
@@ -153,7 +174,7 @@
     /**
      * An {@link ImageReader} that handles still image capture.
      */
-    private ImageReader[] mImageReader = new ImageReader[NUMCAM];
+    private ImageReader[] mImageReader = new ImageReader[MAX_NUM_CAM];
     private NamedImages mNamedImages;
     private ContentResolver mContentResolver;
     private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
@@ -174,6 +195,7 @@
 
         @Override
         public void onImageAvailable(ImageReader reader) {
+            Log.d(TAG, "image available");
             mCaptureStartTime = System.currentTimeMillis();
             mNamedImages.nameNewImage(mCaptureStartTime);
             NamedEntity name = mNamedImages.getNextNameEntity();
@@ -182,30 +204,27 @@
 
             Image mImage = reader.acquireNextImage();
             ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
+            //Todo: dont create new buffer and use the one from ImageReader
             byte[] bytes = new byte[buffer.remaining()];
             buffer.get(bytes);
 
             mActivity.getMediaSaveService().addImage(
                     bytes, title, date, null, reader.getWidth(), reader.getHeight(),
                     0, null, mOnMediaSavedListener, mContentResolver, "jpeg");
+            mImage.close();
         }
 
     };
-
     /**
      * {@link CaptureRequest.Builder} for the camera preview
      */
-    private CaptureRequest.Builder[] mPreviewRequestBuilder = new CaptureRequest.Builder[NUMCAM];
-    /**
-     * {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder}
-     */
-    private CaptureRequest[] mPreviewRequest = new CaptureRequest[NUMCAM];
+    private CaptureRequest.Builder[] mPreviewRequestBuilder = new CaptureRequest.Builder[MAX_NUM_CAM];
     /**
      * The current state of camera state for taking pictures.
      *
      * @see #mCaptureCallback
      */
-    private int mState = STATE_PREVIEW;
+    private int[] mState = new int[MAX_NUM_CAM];
     /**
      * A {@link Semaphore} make sure the camera open callback happens first before closing the
      * camera.
@@ -218,23 +237,28 @@
             = new CameraCaptureSession.CaptureCallback() {
 
         private void process(CaptureResult result) {
-            switch (mState) {
+            int id = (int) result.getRequest().getTag();
+
+            switch (mState[id]) {
                 case STATE_PREVIEW: {
-                    // We have nothing to do when the camera preview is working normally.
                     break;
                 }
                 case STATE_WAITING_LOCK: {
                     Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
+                    Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
+                    Log.d(TAG, "STATE_WAITING_LOCK afState:" + afState + " aeState:" + aeState);
+                    // AF_PASSIVE is added for continous auto focus mode
                     if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
-                            CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
+                            CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState ||
+                            CaptureRequest.CONTROL_AF_STATE_PASSIVE_FOCUSED == afState ||
+                            CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED == afState) {
                         // CONTROL_AE_STATE can be null on some devices
-                        Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
-                        if (aeState == null ||
-                                aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
-                            mState = STATE_PICTURE_TAKEN;
-                            captureStillPicture(0);
+                        if (aeState == null || (aeState == CaptureResult
+                                .CONTROL_AE_STATE_CONVERGED) && isFlashOff()) {
+                            mState[id] = STATE_PICTURE_TAKEN;
+                            captureStillPicture(id);
                         } else {
-                            runPrecaptureSequence(0);
+                            runPrecaptureSequence(id);
                         }
                     }
                     break;
@@ -245,7 +269,7 @@
                     if (aeState == null ||
                             aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
                             aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
-                        mState = STATE_WAITING_NON_PRECAPTURE;
+                        mState[id] = STATE_WAITING_NON_PRECAPTURE;
                     }
                     break;
                 }
@@ -253,8 +277,8 @@
                     // CONTROL_AE_STATE can be null on some devices
                     Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
                     if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
-                        mState = STATE_PICTURE_TAKEN;
-                        captureStillPicture(0);
+                        mState[id] = STATE_PICTURE_TAKEN;
+                        captureStillPicture(id);
                     }
                     break;
                 }
@@ -265,6 +289,8 @@
         public void onCaptureProgressed(CameraCaptureSession session,
                                         CaptureRequest request,
                                         CaptureResult partialResult) {
+            int id = (int) partialResult.getRequest().getTag();
+            if (id == getMainCameraId()) updateFocusStateChange(partialResult);
             process(partialResult);
         }
 
@@ -272,63 +298,208 @@
         public void onCaptureCompleted(CameraCaptureSession session,
                                        CaptureRequest request,
                                        TotalCaptureResult result) {
+            int id = (int) result.getRequest().getTag();
+            if (id == getMainCameraId()) updateFocusStateChange(result);
             process(result);
         }
-
     };
+    private final CameraPreference.OnPreferenceChangedListener prefListener = new
+            CameraPreference.OnPreferenceChangedListener() {
+                @Override
+                public void onSharedPreferenceChanged(ListPreference pref) {
+                    if (mPaused) return;
+                    if (CameraSettings.KEY_CAMERA_SAVEPATH.equals(pref.getKey())) {
+                        Storage.setSaveSDCard(
+                                mPreferences.getString(CameraSettings.KEY_CAMERA_SAVEPATH, "0")
+                                        .equals("1"));
+                        mActivity.updateStorageSpaceAndHint();
+                    }
+
+                    switch (MODE) {
+                        case BAYER_MODE:
+                            applyPreference(0, pref);
+                            break;
+                        case MONO_MODE:
+                            applyPreference(1, pref);
+                            break;
+                        case DUAL_MODE:
+                            applyPreference(0, pref);
+                            applyPreference(1, pref);
+                    }
+                    mUI.overrideSettings(pref.getKey(), null);
+                }
+
+                @Override
+                public void onSharedPreferenceChanged() {
+                    if (mPaused) return;
+                    boolean recordLocation = RecordLocationPreference.get(
+                            mPreferences, mContentResolver);
+                    mLocationManager.recordLocation(recordLocation);
+                }
+
+                @Override
+                public void onRestorePreferencesClicked() {
+                }
+
+                @Override
+                public void onOverriddenPreferencesClicked() {
+                }
+
+                @Override
+                public void onCameraPickerClicked(int cameraId) {
+                }
+            };
     private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
 
         @Override
         public void onOpened(CameraDevice cameraDevice) {
             int id = Integer.parseInt(cameraDevice.getId());
-
+            Log.d(TAG, "onOpened " + id);
             mCameraOpenCloseLock.release();
-
-            PreferenceInflater inflater = new PreferenceInflater(mActivity);
-            PreferenceGroup group =
-                    (PreferenceGroup) inflater.inflate(R.xml.camera_preferences);
-            mPreferenceGroup = group;
-            mActivity.runOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    mUI.onCameraOpened(mPreferenceGroup, prefListener);
-                }
-
-
-            });
+            if (mPaused) {
+                return;
+            }
+            if (MODE == DUAL_MODE && id == BAYER_ID) {
+                Message msg = Message.obtain();
+                msg.what = OPEN_CAMERA;
+                msg.arg1 = MONO_ID;
+                mCameraHandler.sendMessage(msg);
+            }
+            if (!mInitialized) {
+                mInitialized = true;
+                mActivity.runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        mUI.onCameraOpened(mCharacteristics, mCharacteristicsIndex,
+                                mPreferenceGroup, prefListener);
+                    }
+                });
+            }
 
             mCameraDevice[id] = cameraDevice;
-            mCameraOpened = true;
+            mCameraOpened[id] = true;
             createSession(id);
         }
 
         @Override
         public void onDisconnected(CameraDevice cameraDevice) {
-            mCameraOpenCloseLock.release();
+            int id = Integer.parseInt(cameraDevice.getId());
+            Log.d(TAG, "onDisconnected " + id);
             cameraDevice.close();
             mCameraDevice = null;
+            mCameraOpenCloseLock.release();
         }
 
         @Override
         public void onError(CameraDevice cameraDevice, int error) {
             int id = Integer.parseInt(cameraDevice.getId());
-            mCameraOpenCloseLock.release();
+            Log.d(TAG, "onError " + id + error);
             cameraDevice.close();
-            mCameraDevice = null;
+            mCameraDevice[id] = null;
+            mCameraOpenCloseLock.release();
             if (null != mActivity) {
                 mActivity.finish();
             }
         }
 
+        @Override
+        public void onClosed(CameraDevice cameraDevice) {
+            int id = Integer.parseInt(cameraDevice.getId());
+            Log.d(TAG, "onClosed " + id);
+            mCameraDevice[id] = null;
+            mCameraOpenCloseLock.release();
+        }
+
     };
 
+    public static boolean setMode(String value) {
+        int mode = DUAL_MODE;
+        switch (value) {
+            case "dual":
+                mode = DUAL_MODE;
+                break;
+            case "bayer":
+                mode = BAYER_MODE;
+                break;
+            case "mono":
+                mode = MONO_MODE;
+                break;
+        }
+        if (MODE == mode) return false;
+        MODE = mode;
+        return true;
+    }
+
+    public static int getQualityNumber(String jpegQuality) {
+        try {
+            int qualityPercentile = Integer.parseInt(jpegQuality);
+            if (qualityPercentile >= 0 && qualityPercentile <= 100)
+                return qualityPercentile;
+            else
+                return 85;
+        } catch (NumberFormatException nfe) {
+            //chosen quality is not a number, continue
+        }
+        int value = 0;
+        switch (jpegQuality) {
+            case "superfine":
+                value = CameraProfile.QUALITY_HIGH;
+                break;
+            case "fine":
+                value = CameraProfile.QUALITY_MEDIUM;
+                break;
+            case "normal":
+                value = CameraProfile.QUALITY_LOW;
+                break;
+            default:
+                return 85;
+        }
+        return CameraProfile.getJpegEncodingQualityParameter(value);
+    }
+
+    private void initializeFirstTime() {
+        if (mFirstTimeInitialized || mPaused) {
+            return;
+        }
+
+        //Todo: test record location. Jack to provide instructions
+        // Initialize location service.
+        boolean recordLocation = RecordLocationPreference.get(
+                mPreferences, mContentResolver);
+        mLocationManager.recordLocation(recordLocation);
+
+        mUI.initializeFirstTime();
+        MediaSaveService s = mActivity.getMediaSaveService();
+        // We set the listener only when both service and shutterbutton
+        // are initialized.
+        if (s != null) {
+            s.setListener(this);
+        }
+
+        mNamedImages = new NamedImages();
+        mFirstTimeInitialized = true;
+    }
+
+    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();
+        mUI.initializeSecondTime();
+    }
+
     private void createSession(final int id) {
-        if (!mCameraOpened || !mSurfaceReady) return;
+        if (mPaused || !mCameraOpened[id] || !mSurfaceReady) return;
         List<Surface> list = new LinkedList<Surface>();
         mUI.hidePreviewCover();
         try {
             Surface surface;
-            if (id == 0) {
+            if (id == BAYER_ID || (id == MONO_ID && MODE == MONO_MODE)) {
                 SurfaceHolder sh = mUI.getSurfaceHolder();
                 if (sh == null) {
                     return;
@@ -344,19 +515,9 @@
             // We set up a CaptureRequest.Builder with the output Surface.
             mPreviewRequestBuilder[id] = mCameraDevice[id].createCaptureRequest(CameraDevice
                     .TEMPLATE_PREVIEW);
+            mPreviewRequestBuilder[id].setTag(id);
             mPreviewRequestBuilder[id].addTarget(surface);
 
-            // This is the CaptureRequest.Builder that we use to take a picture.
-            mCaptureBuilder[id] =
-                    mCameraDevice[id].createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
-            mCaptureBuilder[id].addTarget(mImageReader[id].getSurface());
-
-            // Use the same AE and AF modes as the preview.
-            mCaptureBuilder[id].set(CaptureRequest.CONTROL_AF_MODE,
-                    CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
-            mCaptureBuilder[id].set(CaptureRequest.CONTROL_AE_MODE,
-                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
-
             list.add(surface);
             list.add(mImageReader[id].getSurface());
             // Here, we create a CameraCaptureSession for camera preview.
@@ -366,24 +527,20 @@
                         @Override
                         public void onConfigured(CameraCaptureSession cameraCaptureSession) {
                             // The camera is already closed
-                            if (null == mCameraDevice[id]) {
+                            if (mPaused || null == mCameraDevice[id]) {
                                 return;
                             }
                             // When the session is ready, we start displaying the preview.
                             mCaptureSession[id] = cameraCaptureSession;
+                            initializePreviewConfiguration(id);
                             try {
-                                // Auto focus should be continuous for camera preview.
-                                mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_AF_MODE,
-                                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
-                                // Flash is automatically enabled when necessary.
-                                mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_AE_MODE,
-                                        CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
-
+                                if (MODE == DUAL_MODE) {
+                                    linkBayerMono(id);
+                                    mIsLinked = true;
+                                }
                                 // Finally, we start displaying the camera preview.
-                                mPreviewRequest[id] = mPreviewRequestBuilder[id].build();
-                                mCaptureSession[id].setRepeatingRequest(mPreviewRequest[id],
-                                        mCaptureCallback, mCameraHandler);
-                                applyAllSettings(0);
+                                mCaptureSession[id].setRepeatingRequest(mPreviewRequestBuilder[id]
+                                        .build(), mCaptureCallback, mCameraHandler);
                             } catch (CameraAccessException e) {
                                 e.printStackTrace();
                             }
@@ -401,64 +558,149 @@
 
     @Override
     public void init(CameraActivity activity, View parent) {
-        mCameraOpened = false;
+        Log.d(TAG, "init");
+        for (int i = 0; i < MAX_NUM_CAM; i++) {
+            mCameraOpened[i] = false;
+        }
         mSurfaceReady = false;
+
         mActivity = activity;
+        for (int i = 0; i < MAX_NUM_CAM; i++) {
+            mState[i] = STATE_PREVIEW;
+        }
 
         mPreferences = new ComboPreferences(mActivity);
         CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal(), activity);
 
-        mPreferences.setLocalId(mActivity, 0);
+        mPreferences.setLocalId(mActivity, BAYER_ID);
         CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
 
+        PreferenceInflater inflater = new PreferenceInflater(mActivity);
+        PreferenceGroup group =
+                (PreferenceGroup) inflater.inflate(R.xml.camera_preferences);
+        mPreferenceGroup = group;
+
+        ListPreference pref = group.findPreference(CameraSettings.KEY_DUAL_CAMERA);
+        setMode(pref.getValue());
+
         mContentResolver = mActivity.getContentResolver();
         mUI = new CaptureUI(activity, this, parent);
         mUI.initializeControlByIntent();
-        mNamedImages = new NamedImages();
+        mFocusStateListener = new FocusStateListener(mUI);
+        mLocationManager = new LocationManager(mActivity, mUI);
+        Storage.setSaveSDCard(
+                mPreferences.getString(CameraSettings.KEY_CAMERA_SAVEPATH, "0").equals("1"));
     }
 
     /**
      * Initiate a still image capture.
      */
     private void takePicture() {
-        lockFocus(0);
+        Log.d(TAG, "takePicture");
+        switch (MODE) {
+            case DUAL_MODE:
+                lockFocus(BAYER_ID);
+                lockFocus(MONO_ID);
+                break;
+            case BAYER_MODE:
+                lockFocus(BAYER_ID);
+                break;
+            case MONO_MODE:
+                lockFocus(MONO_ID);
+                break;
+        }
     }
 
     /**
      * Lock the focus as the first step for a still image capture.
      */
     private void lockFocus(int id) {
+        Log.d(TAG, "lockFocus " + id);
         try {
-            // This is how to tell the camera to lock focus.
-            mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_AF_TRIGGER,
-                    CameraMetadata.CONTROL_AF_TRIGGER_START);
-            // Tell #mCaptureCallback to wait for the lock.
-            mState = STATE_WAITING_LOCK;
-            mCaptureSession[id].capture(mPreviewRequestBuilder[id].build(), mCaptureCallback,
-                    mCameraHandler);
+            CaptureRequest.Builder builder = mCameraDevice[id].createCaptureRequest(CameraDevice
+                    .TEMPLATE_PREVIEW);
+            builder.setTag(id);
+            builder.addTarget(getPreviewSurface(id));
+
+            builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
+            builder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_AUTO);
+            builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
+            applyWhiteBalance(builder);
+            applyZoom(builder, id);
+            mState[id] = STATE_WAITING_LOCK;
+            mCaptureSession[id].capture(builder.build(), mCaptureCallback, mCameraHandler);
         } catch (CameraAccessException e) {
             e.printStackTrace();
         }
     }
 
+    private void autoFocusTrigger(int id) {
+        Log.d(TAG, "autoFocusTrigger " + id);
+        try {
+            CaptureRequest.Builder builder = mCameraDevice[id].createCaptureRequest(CameraDevice
+                    .TEMPLATE_PREVIEW);
+            builder.setTag(id);
+            builder.addTarget(getPreviewSurface(id));
+
+            builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
+            builder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_AUTO);
+            builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
+            applyWhiteBalance(builder);
+            applyZoom(builder, id);
+            applyAFRegions(builder, id);
+            mCaptureSession[id].capture(builder.build(), mCaptureCallback, mCameraHandler);
+        } catch (CameraAccessException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void linkBayerMono(int id) {
+        Log.d(TAG, "linkBayerMono " + id);
+        if (id == BAYER_ID) {
+            mPreviewRequestBuilder[id].set(BayerMonoLinkEnableKey, (byte) 1);
+            mPreviewRequestBuilder[id].set(BayerMonoLinkMainKey, (byte) 1);
+            mPreviewRequestBuilder[id].set(BayerMonoLinkSessionIdKey, MONO_ID);
+        } else if (id == MONO_ID) {
+            mPreviewRequestBuilder[id].set(BayerMonoLinkEnableKey, (byte) 1);
+            mPreviewRequestBuilder[id].set(BayerMonoLinkMainKey, (byte) 0);
+            mPreviewRequestBuilder[id].set(BayerMonoLinkSessionIdKey, BAYER_ID);
+        }
+    }
+
+    public void unLinkBayerMono(int id) {
+        Log.d(TAG, "unlinkBayerMono " + id);
+        if (id == BAYER_ID) {
+            mPreviewRequestBuilder[id].set(BayerMonoLinkEnableKey, (byte) 0);
+        } else if (id == MONO_ID) {
+            mPreviewRequestBuilder[id].set(BayerMonoLinkEnableKey, (byte) 0);
+        }
+    }
+
+
     /**
      * Capture a still picture. This method should be called when we get a response in
      * {@link #mCaptureCallback} from both {@link #lockFocus()}.
      */
     private void captureStillPicture(final int id) {
+        Log.d(TAG, "captureStillPicture " + id);
         try {
-            if (null == mActivity || null == mCameraDevice) {
+            if (null == mActivity || null == mCameraDevice[id]) {
                 return;
             }
+            CaptureRequest.Builder captureBuilder =
+                    mCameraDevice[id].createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+
+            captureBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
+            captureBuilder.addTarget(getPreviewSurface(id));
+            captureBuilder.addTarget(mImageReader[id].getSurface());
+            captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
+            captureBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest
+                    .CONTROL_AF_TRIGGER_IDLE);
+            applyCaptureSettings(captureBuilder, id);
 
             // Orientation
             int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
-            mCaptureBuilder[id].set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
-            List<CaptureRequest> burstList = new ArrayList<CaptureRequest>();
-
-            for (int i = 0; i < 1; i++) {
-                burstList.add(mCaptureBuilder[id].build());
-            }
+            captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
 
             CameraCaptureSession.CaptureCallback CaptureCallback
                     = new CameraCaptureSession.CaptureCallback() {
@@ -467,16 +709,18 @@
                 public void onCaptureCompleted(CameraCaptureSession session,
                                                CaptureRequest request,
                                                TotalCaptureResult result) {
+                    Log.d(TAG, "captureStillPicture onCaptureCompleted");
                 }
 
                 @Override
                 public void onCaptureSequenceCompleted(CameraCaptureSession session, int
                         sequenceId, long frameNumber) {
+                    Log.d(TAG, "captureStillPicture onCaptureSequenceCompleted");
                     unlockFocus(id);
                 }
-
             };
-            mCaptureSession[id].captureBurst(burstList, CaptureCallback, mCallbackHandler);
+            mCaptureSession[id].stopRepeating();
+            mCaptureSession[id].capture(captureBuilder.build(), CaptureCallback, mCallbackHandler);
         } catch (CameraAccessException e) {
             Log.d(TAG, "Capture still picture has failed");
             e.printStackTrace();
@@ -488,14 +732,21 @@
      * we get a response in {@link #mCaptureCallback} from {@link #lockFocus()}.
      */
     private void runPrecaptureSequence(int id) {
+        Log.d(TAG, "runPrecaptureSequence");
         try {
-            // This is how to tell the camera to trigger.
-            mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
+            CaptureRequest.Builder builder = mCameraDevice[id].createCaptureRequest(CameraDevice
+                    .TEMPLATE_PREVIEW);
+            builder.setTag(id);
+            builder.addTarget(getPreviewSurface(id));
+            builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
+            builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
                     CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
-            // Tell #mCaptureCallback to wait for the precapture sequence to be set.
-            mState = STATE_WAITING_PRECAPTURE;
-            mCaptureSession[id].capture(mPreviewRequestBuilder[id].build(), mCaptureCallback,
-                    mCameraHandler);
+            // Applying flash only to capture does not work. Need to apply flash here.
+            applyFlash(builder);
+            applyWhiteBalance(builder);
+            applyZoom(builder, id);
+            mState[id] = STATE_WAITING_PRECAPTURE;
+            mCaptureSession[id].capture(builder.build(), mCaptureCallback, mCameraHandler);
         } catch (CameraAccessException e) {
             e.printStackTrace();
         }
@@ -508,33 +759,36 @@
      * @param height The height of available size for camera preview
      */
     private void setUpCameraOutputs() {
-        Activity activity = mActivity;
-        CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
+        CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
         try {
             String[] cameraIdList = manager.getCameraIdList();
-            for (int i = 0; i < NUMCAM; i++) {
+
+            for (int i = 0; i < cameraIdList.length; i++) {
                 String cameraId = cameraIdList[i];
                 CameraCharacteristics characteristics
                         = manager.getCameraCharacteristics(cameraId);
-
+                mCharacteristics[i] = characteristics;
+                mCharacteristicsIndex.add(i);
                 StreamConfigurationMap map = characteristics.get(
                         CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                 if (map == null) {
                     continue;
                 }
-
+                Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
+                Log.d(TAG, "flash : " + (available == null ? false : available));
                 // For still image captures, we use the largest available size.
                 Size largest = Collections.max(
                         Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
                         new CompareSizesByArea());
 
                 mImageReader[i] = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
-                        ImageFormat.JPEG, 10);
+                        ImageFormat.JPEG, 3);
                 mImageReader[i].setOnImageAvailableListener(
                         mOnImageAvailableListener, mImageAvailableHandler);
                 mCameraId[i] = cameraId;
-                //return;
             }
+            mAutoFocusSupported = CameraUtil.isAutoFocusSupported(mCharacteristics,
+                    mCharacteristicsIndex);
         } catch (CameraAccessException e) {
             e.printStackTrace();
         } catch (NullPointerException e) {
@@ -546,45 +800,73 @@
      * finished.
      */
     private void unlockFocus(int id) {
+        Log.d(TAG, "unlockFocus " + id);
         try {
-            // Reset the auto-focus trigger
-            mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_AF_TRIGGER,
-                    CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
-            mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_AE_MODE,
-                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
-            mCaptureSession[id].capture(mPreviewRequestBuilder[id].build(), mCaptureCallback,
-                    mCameraHandler);
-            // After this, the camera will go back to the normal state of preview.
-            mState = STATE_PREVIEW;
-            mCaptureSession[id].setRepeatingRequest(mPreviewRequest[id], mCaptureCallback,
-                    mCameraHandler);
+            CaptureRequest.Builder builder = mCameraDevice[id].createCaptureRequest(CameraDevice
+                    .TEMPLATE_PREVIEW);
+            builder.setTag(id);
+            builder.addTarget(getPreviewSurface(id));
+            builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest
+                    .CONTROL_MODE_AUTO);
+            builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest
+                    .CONTROL_AF_MODE_AUTO);
+            builder.set(CaptureRequest.CONTROL_AF_TRIGGER,
+                    CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
+            //Todo: Create applyCommonSettings function for settings applied everytime
+            applyWhiteBalance(builder);
+            applyZoom(builder, id);
+            mCaptureSession[id].capture(builder.build(), mCaptureCallback, mCameraHandler);
+            mState[id] = STATE_PREVIEW;
+            mCaptureSession[id].setRepeatingRequest(mPreviewRequestBuilder[id].build(),
+                    mCaptureCallback, mCameraHandler);
         } catch (CameraAccessException e) {
             e.printStackTrace();
         }
     }
 
+
     /**
      * Closes the current {@link CameraDevice}.
      */
     private void closeCamera() {
+        Log.d(TAG, "closeCamera");
         try {
             mCameraOpenCloseLock.acquire();
-            for (int i = 0; i < NUMCAM; i++) {
+            for (int i = 0; i < MAX_NUM_CAM; i++) {
                 if (null != mCaptureSession[i]) {
+                    if (mIsLinked) {
+                        unLinkBayerMono(i);
+                        try {
+                            mCaptureSession[i].capture(mPreviewRequestBuilder[i].build(), null,
+                                    mCameraHandler);
+                        } catch (CameraAccessException e) {
+                            e.printStackTrace();
+                        }
+                    }
+
                     mCaptureSession[i].close();
                     mCaptureSession[i] = null;
                 }
-                if (null != mCameraDevice[i]) {
-                    mCameraDevice[i].close();
-                    mCameraDevice[i] = null;
-                    mCameraOpened = false;
-                }
                 if (null != mImageReader[i]) {
                     mImageReader[i].close();
                     mImageReader[i] = null;
                 }
             }
+            for (int i = 0; i < MAX_NUM_CAM; i++) {
+                if (null != mCameraDevice[i]) {
+                    mCameraDevice[i].close();
+                    mCameraDevice[i] = null;
+                    mCameraOpened[i] = false;
+                }
+            }
+            /* no need to set this in the callback and handle asynchronously. This is the same
+               reason as why we release the semaphore here, not in camera close callback function
+               as we don't have to protect the case where camera open() gets called during camera
+               close(). The low level framework/HAL handles the synchronization for open()
+               happens after close() */
+            mIsLinked = false;
         } catch (InterruptedException e) {
+            mCameraOpenCloseLock.release();
             throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
         } finally {
             mCameraOpenCloseLock.release();
@@ -602,7 +884,7 @@
         mCallbackThread = new HandlerThread("CameraCallback");
         mCallbackThread.start();
 
-        mCameraHandler = new Handler(mCameraThread.getLooper());
+        mCameraHandler = new MyCameraHandler(mCameraThread.getLooper());
         mImageAvailableHandler = new Handler(mImageAvailableThread.getLooper());
         mCallbackHandler = new Handler(mCallbackThread.getLooper());
     }
@@ -638,12 +920,17 @@
     }
 
     private void openCamera(int id) {
+        if (mPaused) {
+            return;
+        }
+        Log.d(TAG, "openCamera " + id);
         CameraManager manager;
         try {
             manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
             mCameraId[id] = manager.getCameraIdList()[id];
-            if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
+            if (!mCameraOpenCloseLock.tryAcquire(5000, TimeUnit.MILLISECONDS)) {
                 Log.d(TAG, "Time out waiting to lock camera opening.");
+                throw new RuntimeException("Time out waiting to lock camera opening");
             }
             manager.openCamera(mCameraId[id], mStateCallback, mCameraHandler);
         } catch (CameraAccessException e) {
@@ -655,17 +942,19 @@
 
     @Override
     public void onPreviewFocusChanged(boolean previewFocused) {
-
+        mUI.onPreviewFocusChanged(previewFocused);
     }
 
     @Override
     public void onPauseBeforeSuper() {
-
+        mPaused = true;
     }
 
     @Override
     public void onPauseAfterSuper() {
+        Log.d(TAG, "onPause");
         mUI.showPreviewCover();
+        if (mLocationManager != null) mLocationManager.recordLocation(false);
         stopBackgroundThread();
         closeCamera();
         mUI.onPause();
@@ -673,17 +962,37 @@
 
     @Override
     public void onResumeBeforeSuper() {
-
+        mPaused = false;
     }
 
     @Override
     public void onResumeAfterSuper() {
+        Log.d(TAG, "onResume " + MODE);
+        mCharacteristicsIndex = new ArrayList<>();
         setUpCameraOutputs();
-        openCamera(0);
+        readInitialValues();
         startBackgroundThread();
+        Message msg = Message.obtain();
+        msg.what = OPEN_CAMERA;
+        switch (MODE) {
+            case DUAL_MODE:
+            case BAYER_MODE:
+                msg.arg1 = BAYER_ID;
+                mCameraHandler.sendMessage(msg);
+                break;
+            case MONO_MODE:
+                msg.what = OPEN_CAMERA;
+                msg.arg1 = MONO_ID;
+                mCameraHandler.sendMessage(msg);
+                break;
+        }
+        if (!mFirstTimeInitialized) {
+            initializeFirstTime();
+        } else {
+            initializeSecondTime();
+        }
         mUI.hidePreviewCover();
         mUI.enableShutter(true);
-        mUI.initializeFirstTime();
     }
 
     @Override
@@ -708,7 +1017,7 @@
 
     @Override
     public boolean onBackPressed() {
-        return false;
+        return mUI.onBackPressed();
     }
 
     @Override
@@ -727,13 +1036,31 @@
     }
 
     @Override
+    public void onZoomChanged(float requestedZoom) {
+        mZoomValue = requestedZoom;
+
+        switch (MODE) {
+            case DUAL_MODE:
+                applyZoomAndUpdate(BAYER_ID);
+                applyZoomAndUpdate(MONO_ID);
+                break;
+            case BAYER_MODE:
+                applyZoomAndUpdate(BAYER_ID);
+                break;
+            case MONO_MODE:
+                applyZoomAndUpdate(MONO_ID);
+                break;
+        }
+    }
+
+    @Override
     public boolean isImageCaptureIntent() {
         return false;
     }
 
     @Override
     public boolean isCameraIdle() {
-        return false;
+        return true;
     }
 
     @Override
@@ -768,7 +1095,42 @@
 
     @Override
     public void onSingleTapUp(View view, int x, int y) {
+        if (mPaused || mCameraDevice == null || !mFirstTimeInitialized || !mAutoFocusSupported ||
+                !isStatePreview()) {
+            return;
+        }
+        mUI.setFocusPosition(x, y);
+        mUI.onFocusStarted();
+        switch (MODE) {
+            case DUAL_MODE:
+                triggerFocusAtPoint(x, y, BAYER_ID);
+                triggerFocusAtPoint(x, y, MONO_ID);
+                break;
+            case BAYER_MODE:
+                triggerFocusAtPoint(x, y, BAYER_ID);
+                break;
+            case MONO_MODE:
+                triggerFocusAtPoint(x, y, MONO_ID);
+                break;
+        }
+    }
 
+    private int getMainCameraId() {
+        switch (MODE) {
+            case DUAL_MODE:
+            case BAYER_MODE:
+                return BAYER_ID;
+            case MONO_MODE:
+                return MONO_ID;
+        }
+        return 0;
+    }
+
+    private boolean isStatePreview() {
+        for (int i = 0; i < mState.length; i++) {
+            if (mState[i] != STATE_PREVIEW) return false;
+        }
+        return true;
     }
 
     @Override
@@ -793,13 +1155,37 @@
 
     @Override
     public void enableRecordingLocation(boolean enable) {
+        Log.d(TAG, "CaptureModule enableRecordingLocation " + enable);
+        setLocationPreference(enable ? RecordLocationPreference.VALUE_ON
+                : RecordLocationPreference.VALUE_OFF);
+    }
 
+    private void setLocationPreference(String value) {
+        mPreferences.edit()
+                .putString(CameraSettings.KEY_RECORD_LOCATION, value)
+                .apply();
+        prefListener.onSharedPreferenceChanged();
     }
 
     @Override
     public void onPreviewUIReady() {
+        if (mPaused) {
+            return;
+        }
+        Log.d(TAG, "onPreviewUIReady");
         mSurfaceReady = true;
-        createSession(0);
+        switch (MODE) {
+            case DUAL_MODE:
+                createSession(BAYER_ID);
+                createSession(MONO_ID);
+                break;
+            case BAYER_MODE:
+                createSession(BAYER_ID);
+                break;
+            case MONO_MODE:
+                createSession(MONO_ID);
+                break;
+        }
     }
 
     @Override
@@ -829,7 +1215,15 @@
 
     @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
+        // the correct orientation.
+        if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
+        int oldOrientation = mOrientation;
+        mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
+        if (oldOrientation != mOrientation) {
+            mUI.setOrientation(mOrientation, true);
+        }
     }
 
     @Override
@@ -839,7 +1233,9 @@
 
     @Override
     public void onMediaSaveServiceConnected(MediaSaveService s) {
-
+        if (mFirstTimeInitialized) {
+            s.setListener(this);
+        }
     }
 
     @Override
@@ -854,7 +1250,15 @@
 
     @Override
     public void onSwitchSavePath() {
-
+        if (mUI.mMenuInitialized) {
+            mUI.setPreference(CameraSettings.KEY_CAMERA_SAVEPATH, "1");
+        } else {
+            mPreferences.edit()
+                    .putString(CameraSettings.KEY_CAMERA_SAVEPATH, "1")
+                    .apply();
+        }
+        RotateTextToast.makeText(mActivity, R.string.on_switch_save_path_to_sdcard,
+                Toast.LENGTH_SHORT).show();
     }
 
     @Override
@@ -872,23 +1276,225 @@
 
     }
 
-    private void applyPreference(int cameraId, ListPreference pref) {
-        if (pref.getKey().equals(CameraSettings.KEY_FLASH_MODE)) {
-            if (pref.getValue().equals("on")) {
-                mCaptureBuilder[cameraId].set(CaptureRequest.CONTROL_AE_MODE,
-                        CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
-            } else if (pref.getValue().equals("auto")) {
-                mCaptureBuilder[cameraId].set(CaptureRequest.CONTROL_AE_MODE,
-                        CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
-            } else if (pref.getValue().equals("off")) {
-                mCaptureBuilder[cameraId].set(CaptureRequest.CONTROL_AE_MODE,
-                        CaptureRequest.CONTROL_AE_MODE_ON);
+    private boolean isFlashOff() {
+        return readSetting(CameraSettings.KEY_FLASH_MODE).equals("off");
+    }
+
+    private void initializePreviewConfiguration(int id) {
+        mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_MODE, CaptureRequest
+                .CONTROL_MODE_AUTO);
+        mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest
+                .CONTROL_AF_MODE_CONTINUOUS_PICTURE);
+        mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest
+                .CONTROL_AF_TRIGGER_IDLE);
+        mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest
+                .CONTROL_AE_MODE_ON);
+        mPreviewRequestBuilder[id].set(CaptureRequest.FLASH_MODE, CaptureRequest
+                .FLASH_MODE_OFF);
+        applyWhiteBalance(mPreviewRequestBuilder[id]);
+        applyZoom(mPreviewRequestBuilder[id], id);
+    }
+
+    private void readInitialValues() {
+        for (int i = 0; i < mPreferenceGroup.size(); i++) {
+            CameraPreference pref = mPreferenceGroup.get(i);
+            if (pref instanceof ListPreference) {
+                ListPreference listPref = (ListPreference) pref;
+                storeSetting(listPref.getKey(), listPref.getValue());
             }
         }
     }
 
-    private void applyAllSettings(int cameraId) {
+    public Rect cropRegionForZoom(int id) {
+        Log.d(TAG, "cropRegionForZoom " + id);
+        Rect activeRegion = mCharacteristics[id].get(CameraCharacteristics
+                .SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+        Rect cropRegion = new Rect();
 
+        int xCenter = activeRegion.width() / 2;
+        int yCenter = activeRegion.height() / 2;
+        int xDelta = (int) (activeRegion.width() / (2 * mZoomValue));
+        int yDelta = (int) (activeRegion.height() / (2 * mZoomValue));
+        cropRegion.set(xCenter - xDelta, yCenter - yDelta, xCenter + xDelta, yCenter + yDelta);
+        mCropRegion[id] = cropRegion;
+        return mCropRegion[id];
+    }
+
+    private void applyZoom(CaptureRequest.Builder request, int id) {
+        request.set(CaptureRequest.SCALER_CROP_REGION, cropRegionForZoom(id));
+    }
+
+    private void applyCaptureSettings(CaptureRequest.Builder request, int id) {
+        applyFlash(request);
+        applyWhiteBalance(request);
+        applyJpegQuality(request);
+        applyZoom(request, id);
+    }
+
+    private void applyPreference(int cameraId, ListPreference pref) {
+        boolean updatePreview = false;
+        String key = pref.getKey();
+        String value = pref.getValue();
+        storeSetting(key, value);
+        switch (key) {
+            //Todo: CreateUISettings file and add UI preference settings to there
+            case CameraSettings.KEY_JPEG_QUALITY:
+                mJpegQuality = getQualityNumber(value);
+                break;
+            case CameraSettings.KEY_WHITE_BALANCE:
+                updatePreview = true;
+                applyWhiteBalance(mPreviewRequestBuilder[cameraId]);
+                break;
+        }
+        if (updatePreview) {
+            try {
+                mCaptureSession[cameraId].setRepeatingRequest(mPreviewRequestBuilder[cameraId]
+                        .build(), mCaptureCallback, mCameraHandler);
+            } catch (CameraAccessException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private void applyZoomAndUpdate(int id) {
+        applyZoom(mPreviewRequestBuilder[id], id);
+        try {
+            mCaptureSession[id].setRepeatingRequest(mPreviewRequestBuilder[id]
+                    .build(), mCaptureCallback, mCameraHandler);
+        } catch (CameraAccessException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void storeSetting(String key, String value) {
+        mSettings.put(key, value);
+    }
+
+    private String readSetting(String key) {
+        return mSettings.get(key);
+    }
+
+    private void applyJpegQuality(CaptureRequest.Builder request) {
+        request.set(CaptureRequest.JPEG_QUALITY, (byte) mJpegQuality);
+    }
+
+    private void applyAFRegions(CaptureRequest.Builder request, int id) {
+        request.set(CaptureRequest.CONTROL_AF_REGIONS, mAFRegions[id]);
+    }
+
+    private void applyWhiteBalance(CaptureRequest.Builder request) {
+        String value = readSetting(CameraSettings.KEY_WHITE_BALANCE);
+        switch (value) {
+            case "incandescent":
+                request.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest
+                        .CONTROL_AWB_MODE_INCANDESCENT);
+                break;
+            case "fluorescent":
+                request.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest
+                        .CONTROL_AWB_MODE_FLUORESCENT);
+                break;
+            case "auto":
+                request.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest
+                        .CONTROL_AWB_MODE_AUTO);
+                break;
+            case "daylight":
+                request.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest
+                        .CONTROL_AWB_MODE_DAYLIGHT);
+                break;
+            case "cloudy-daylight":
+                request.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest
+                        .CONTROL_AWB_MODE_CLOUDY_DAYLIGHT);
+                break;
+        }
+    }
+
+    private Surface getPreviewSurface(int id) {
+        if (MODE == DUAL_MODE && id == MONO_ID) {
+            return mUI.getSurfaceHolder2().getSurface();
+        } else {
+            return mUI.getSurfaceHolder().getSurface();
+        }
+    }
+
+    private void applyFlash(CaptureRequest.Builder request, String value) {
+        switch (value) {
+            case "on":
+                request.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest
+                        .CONTROL_AE_MODE_ON_ALWAYS_FLASH);
+                request.set(CaptureRequest.FLASH_MODE, CaptureRequest
+                        .FLASH_MODE_SINGLE);
+                break;
+            case "auto":
+                request.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest
+                        .CONTROL_AE_MODE_ON_AUTO_FLASH);
+                break;
+            case "off":
+                request.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest
+                        .CONTROL_AE_MODE_ON);
+                request.set(CaptureRequest.FLASH_MODE, CaptureRequest
+                        .FLASH_MODE_OFF);
+                break;
+        }
+    }
+
+    private void applyFlash(CaptureRequest.Builder request) {
+        String value = readSetting(CameraSettings.KEY_FLASH_MODE);
+        applyFlash(request, value);
+    }
+
+    @Override
+    public void onQueueStatus(boolean full) {
+        mUI.enableShutter(!full);
+    }
+
+    public void triggerFocusAtPoint(float x, float y, int id) {
+        Point p;
+        if (id == getMainCameraId()) {
+            p = mUI.getSurfaceViewSize();
+        } else {
+            p = mUI.getSurfaceView2Size();
+        }
+        int width = p.x;
+        int height = p.y;
+        x = x / width;
+        y = y / height;
+        mAFRegions[id] = afRectangle(x, y, mCropRegion[id]);
+        autoFocusTrigger(id);
+    }
+
+    private MeteringRectangle[] afRectangle(float x, float y, Rect cropRegion) {
+        int side = Math.max(cropRegion.width(), cropRegion.height()) / 8;
+        int xCenter = (int) (cropRegion.left + x * cropRegion.width());
+        int yCenter = (int) (cropRegion.top + y * cropRegion.height());
+        Rect meteringRegion = new Rect(xCenter - side / 2, yCenter - side / 2, xCenter +
+                side / 2, yCenter + side / 2);
+
+        meteringRegion.left = CameraUtil.clamp(meteringRegion.left, cropRegion.left, cropRegion
+                .right);
+        meteringRegion.top = CameraUtil.clamp(meteringRegion.top, cropRegion.top, cropRegion
+                .bottom);
+        meteringRegion.right = CameraUtil.clamp(meteringRegion.right, cropRegion.left, cropRegion
+                .right);
+        meteringRegion.bottom = CameraUtil.clamp(meteringRegion.bottom, cropRegion.top,
+                cropRegion.bottom);
+        MeteringRectangle[] meteringRectangle = new MeteringRectangle[1];
+        meteringRectangle[0] = new MeteringRectangle(meteringRegion, 1);
+        return meteringRectangle;
+    }
+
+    private void updateFocusStateChange(CaptureResult result) {
+        final int resultAFState = result.get(CaptureResult.CONTROL_AF_STATE);
+
+        // Report state change when AF state has changed.
+        if (resultAFState != mLastResultAFState && mFocusStateListener != null) {
+            mActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mFocusStateListener.onFocusStatusUpdate(resultAFState);
+                }
+            });
+        }
+        mLastResultAFState = resultAFState;
     }
 
     /**
@@ -904,4 +1510,23 @@
         }
 
     }
+
+    private class MyCameraHandler extends Handler {
+
+        public MyCameraHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case OPEN_CAMERA: {
+                    int id = msg.arg1;
+                    openCamera(id);
+                    break;
+                }
+            }
+        }
+    }
+
 }
diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java
index 9095cd1..a6accae 100644
--- a/src/com/android/camera/CaptureUI.java
+++ b/src/com/android/camera/CaptureUI.java
@@ -24,8 +24,8 @@
 import android.graphics.Point;
 import android.graphics.RectF;
 import android.graphics.drawable.AnimationDrawable;
-import android.hardware.Camera;
 import android.hardware.Camera.Face;
+import android.hardware.camera2.CameraCharacteristics;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.MotionEvent;
@@ -70,7 +70,7 @@
         CameraManager.CameraFaceDetectionCallback {
 
     private static final String TAG = "SnapCam_CaptureUI";
-    private boolean mMenuInitialized = false;
+    public boolean mMenuInitialized = false;
     private boolean surface1created = false;
     private boolean surface2created = false;
     private CameraActivity mActivity;
@@ -111,8 +111,6 @@
     private OnScreenIndicators mOnScreenIndicators;
     private PieRenderer mPieRenderer;
     private ZoomRenderer mZoomRenderer;
-    private int mZoomMax;
-    private List<Integer> mZoomRatios;
     private int mPreviewWidth = 0;
     private int mPreviewHeight = 0;
     private int mOriginalPreviewWidth = 0;
@@ -189,16 +187,12 @@
         mPreviewCover = mRootView.findViewById(R.id.preview_cover);
         // display the view
         mSurfaceView = (SurfaceView) mRootView.findViewById(R.id.mdp_preview_content);
-        mSurfaceView.setVisibility(View.VISIBLE);
         mSurfaceView2 = (SurfaceView) mRootView.findViewById(R.id.mdp_preview_content2);
-        //mSurfaceView2.setVisibility(View.VISIBLE);
+        mSurfaceView2.setZOrderMediaOverlay(true);
         mSurfaceHolder = mSurfaceView.getHolder();
         mSurfaceHolder.addCallback(this);
-        //mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
         mSurfaceHolder2 = mSurfaceView2.getHolder();
         mSurfaceHolder2.addCallback(callback);
-        //mSurfaceHolder2.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
-        mSurfaceView.addOnLayoutChangeListener(mLayoutListener);
         Log.v(TAG, "Using mdp_preview_content (MDP path)");
 
         mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay);
@@ -284,7 +278,15 @@
                 mRootView.findViewById(R.id.on_screen_indicators));
     }
 
-    public void onCameraOpened(PreferenceGroup prefGroup, OnPreferenceChangedListener listener) {
+    public void onCameraOpened(CameraCharacteristics[] characteristics,
+                               List<Integer> characteristicsIndex, PreferenceGroup prefGroup,
+                               OnPreferenceChangedListener listener) {
+        if (mPieRenderer == null) {
+            mPieRenderer = new PieRenderer(mActivity);
+            mPieRenderer.setPieListener(this);
+            mRenderOverlay.addRenderer(mPieRenderer);
+        }
+
         if (mMenu == null) {
             mMenu = new CaptureMenu(mActivity, this);
             mMenu.setListener(listener);
@@ -304,8 +306,11 @@
         }
         mGestures.setCaptureMenu(mMenu);
 
+        mGestures.setZoomEnabled(CameraUtil.isZoomSupported(characteristics, characteristicsIndex));
         mGestures.setRenderOverlay(mRenderOverlay);
         mRenderOverlay.requestLayout();
+
+        initializeZoom(characteristics, characteristicsIndex);
         mActivity.setPreviewGestures(mGestures);
     }
 
@@ -355,31 +360,32 @@
         mShutterButton.setVisibility(View.VISIBLE);
     }
 
+    // called from onResume every other time
+    public void initializeSecondTime() {
+        if (mMenu != null) {
+            mMenu.reloadPreferences();
+        }
+    }
+
     public void doShutterAnimation() {
         AnimationDrawable frameAnimation = (AnimationDrawable) mShutterButton.getDrawable();
         frameAnimation.stop();
         frameAnimation.start();
     }
 
-    // called from onResume every other time
-    public void initializeSecondTime(Camera.Parameters params) {
-        initializeZoom(params);
-        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.
+    public void initializeZoom(CameraCharacteristics[] characteristics,
+                               List<Integer> characteristicsIndex) {
+        if ((characteristics == null) || !CameraUtil.isZoomSupported(characteristics,
+                characteristicsIndex) || (mZoomRenderer == null))
+            return;
         if (mZoomRenderer != null) {
-            mZoomRenderer.setZoomMax(mZoomMax);
-            mZoomRenderer.setZoom(params.getZoom());
-            mZoomRenderer.setZoomValue(mZoomRatios.get(params.getZoom()));
+            float zoomMax = Float.MAX_VALUE;
+            for (int i = 0; i < characteristicsIndex.size(); i++) {
+                zoomMax = Math.min(characteristics[characteristicsIndex.get(i)].get
+                        (CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM), zoomMax);
+            }
+            mZoomRenderer.setZoomMax(zoomMax);
+            mZoomRenderer.setZoom(1f);
             mZoomRenderer.setOnZoomChangeListener(new ZoomChangeListener());
         }
     }
@@ -671,6 +677,14 @@
         }
     }
 
+    public void hideSurfaceView() {
+        mSurfaceView.setVisibility(View.INVISIBLE);
+    }
+
+    public void showSurfaceView() {
+        mSurfaceView.setVisibility(View.VISIBLE);
+    }
+
     public void onPause() {
         // Clear UI.
         collapseCameraControls();
@@ -678,7 +692,7 @@
 
     // focus UI implementation
     private FocusIndicator getFocusIndicator() {
-        return null;
+        return mPieRenderer;
     }
 
     @Override
@@ -689,6 +703,10 @@
     public void clearFaces() {
     }
 
+    public void setPreference(String key, String value) {
+        mMenu.setPreference(key, value);
+    }
+
     @Override
     public void clearFocus() {
         FocusIndicator indicator = getFocusIndicator();
@@ -768,16 +786,28 @@
         }
     }
 
+    public Point getSurfaceViewSize() {
+        Point point = new Point();
+        if (mSurfaceView != null) point.set(mSurfaceView.getWidth(), mSurfaceView.getHeight());
+        return point;
+    }
+
+    public Point getSurfaceView2Size() {
+        Point point = new Point();
+        if (mSurfaceView2 != null) point.set(mSurfaceView2.getWidth(), mSurfaceView2.getHeight());
+        return point;
+    }
+
     public int getOrientation() {
         return mOrientation;
     }
 
     private class ZoomChangeListener implements ZoomRenderer.OnZoomChangedListener {
         @Override
-        public void onZoomValueChanged(int index) {
-            int newZoom = mController.onZoomChanged(index);
+        public void onZoomValueChanged(float mZoomValue) {
+            mController.onZoomChanged(mZoomValue);
             if (mZoomRenderer != null) {
-                mZoomRenderer.setZoomValue(mZoomRatios.get(newZoom));
+                mZoomRenderer.setZoom(mZoomValue);
             }
         }
 
@@ -795,6 +825,11 @@
                 mPieRenderer.setBlockFocus(false);
             }
         }
+
+        @Override
+        public void onZoomValueChanged(int index) {
+
+        }
     }
 
 }
diff --git a/src/com/android/camera/FocusStateListener.java b/src/com/android/camera/FocusStateListener.java
new file mode 100644
index 0000000..42edd5c
--- /dev/null
+++ b/src/com/android/camera/FocusStateListener.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ *     Redistribution and use in source and binary forms, with or without
+ *     modification, are permitted provided that the following conditions are
+ *     met:
+ *         * Redistributions of source code must retain the above copyright
+ *           notice, this list of conditions and the following disclaimer.
+ *         * Redistributions in binary form must reproduce the above
+ *           copyright notice, this list of conditions and the following
+ *           disclaimer in the documentation and/or other materials provided
+ *           with the distribution.
+ *         * Neither the name of The Linux Foundation nor the names of its
+ *           contributors may be used to endorse or promote products derived
+ *           from this software without specific prior written permission.
+ *
+ *     THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ *     WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ *     ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ *     BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *     CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *     SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ *     WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ *     OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ *     IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.camera;
+
+import android.hardware.camera2.CaptureResult;
+
+public class FocusStateListener {
+    private CaptureUI mUI;
+
+    public FocusStateListener(CaptureUI ui) {
+        mUI = ui;
+    }
+
+    public void onFocusStatusUpdate(int focusState) {
+        switch (focusState) {
+            case CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN:
+                mUI.onFocusStarted();
+                break;
+            case CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED:
+                mUI.onFocusSucceeded(true);
+                break;
+            case CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
+                mUI.onFocusFailed(true);
+                break;
+            case CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED:
+                mUI.onFocusSucceeded(true);
+                break;
+            case CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN:
+                mUI.onFocusStarted();
+                break;
+            case CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED:
+                mUI.onFocusFailed(true);
+                break;
+            case CaptureResult.CONTROL_AF_STATE_INACTIVE:
+                mUI.clearFocus();
+                break;
+        }
+    }
+}
diff --git a/src/com/android/camera/PhotoController.java b/src/com/android/camera/PhotoController.java
index b08f8c0..887f0d3 100644
--- a/src/com/android/camera/PhotoController.java
+++ b/src/com/android/camera/PhotoController.java
@@ -37,6 +37,8 @@
     // returns the actual set zoom value
     public int onZoomChanged(int requestedZoom);
 
+    public void onZoomChanged(float requestedZoom);
+
     public boolean isImageCaptureIntent();
 
     public boolean isCameraIdle();
diff --git a/src/com/android/camera/PhotoMenu.java b/src/com/android/camera/PhotoMenu.java
index b3c6117..b9ecdcc 100644
--- a/src/com/android/camera/PhotoMenu.java
+++ b/src/com/android/camera/PhotoMenu.java
@@ -16,6 +16,7 @@
 
 package com.android.camera;
 
+import java.util.HashSet;
 import java.util.Locale;
 
 import android.animation.Animator;
@@ -117,6 +118,7 @@
     private MakeupLevelListener mMakeupListener;
     private MakeupHandler mHandler = new MakeupHandler();
     private static final int MAKEUP_MESSAGE_ID = 0;
+    private HashSet<View> mWasVisibleSet = new HashSet<View>();
 
     public PhotoMenu(CameraActivity activity, PhotoUI ui, MakeupLevelListener makeupListener) {
         super(activity);
@@ -1509,7 +1511,17 @@
         }
         mSceneModeSwitcher.setVisibility(status);
         mFilterModeSwitcher.setVisibility(status);
-        mCameraSwitcher.setVisibility(status);
+        if(status == View.INVISIBLE) {
+            if(mCameraSwitcher.getVisibility() == View.VISIBLE) {
+                mWasVisibleSet.add(mCameraSwitcher);
+            }
+            mCameraSwitcher.setVisibility(status);
+        } else {
+            if(mWasVisibleSet.contains(mCameraSwitcher)) {
+                mCameraSwitcher.setVisibility(status);
+                mWasVisibleSet.remove(mCameraSwitcher);
+            }
+        }
         mPreviewThumbnail.setVisibility(status);
     }
 }
diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java
index d7353ba..4d2ec6a 100644
--- a/src/com/android/camera/PhotoModule.java
+++ b/src/com/android/camera/PhotoModule.java
@@ -696,6 +696,7 @@
     private void switchCamera() {
         if (mPaused) return;
 
+        mUI.applySurfaceChange(PhotoUI.SURFACE_STATUS.HIDE);
         Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
         mCameraId = mPendingSwitchCameraId;
         mPendingSwitchCameraId = -1;
@@ -735,6 +736,7 @@
         mFocusManager.setParameters(mInitialParams);
         setupPreview();
 
+        mUI.applySurfaceChange(PhotoUI.SURFACE_STATUS.SURFACE_VIEW);
         // reset zoom value index
         mZoomValue = 0;
         resizeForPreviewAspectRatio();
@@ -2367,6 +2369,7 @@
     @Override
     public void onResumeAfterSuper() {
         mLastPhotoTakenWithRefocus = false;
+        mUI.showSurfaceView();
         // Add delay on resume from lock screen only, in order to to speed up
         // the onResume --> onPause --> onResume cycle from lock screen.
         // Don't do always because letting go of thread can cause delay.
@@ -2375,17 +2378,17 @@
                 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) {
             Log.v(TAG, "On resume, from lock screen.");
 
+            // Check if there is a need to take a snapshot without
+            // waiting for the shutter click
+            if (isInstantCaptureEnabled()) {
+                mInstantCaptureSnapShot = true;
+            }
+
             // Note: onPauseAfterSuper() will delete this runnable, so we will
             // at most have 1 copy queued up.
             mHandler.postDelayed(new Runnable() {
                 public void run() {
                     onResumeTasks();
-
-                    // Check if there is a need to take a snapshot without
-                    // waiting for the shutter click
-                    if (isInstantCaptureEnabled()) {
-                        mInstantCaptureSnapShot = true;
-                    }
                 }
             }, ON_RESUME_TASKS_DELAY_MSEC);
         } else {
@@ -2428,6 +2431,8 @@
             mOpenCameraThread.start();
         }
 
+        mUI.applySurfaceChange(PhotoUI.SURFACE_STATUS.SURFACE_VIEW);
+
         mJpegPictureCallbackTime = 0;
         mZoomValue = 0;
 
@@ -2466,6 +2471,8 @@
     @Override
     public void onPauseBeforeSuper() {
         mPaused = true;
+        mUI.applySurfaceChange(PhotoUI.SURFACE_STATUS.HIDE);
+
         Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
         if (gsensor != null) {
             mSensorManager.unregisterListener(this, gsensor);
@@ -2494,6 +2501,7 @@
     public void onPauseAfterSuper() {
         Log.v(TAG, "On pause.");
         mUI.showPreviewCover();
+        mUI.hideSurfaceView();
 
         try {
             if (mOpenCameraThread != null) {
@@ -2567,12 +2575,6 @@
         // we will update focus manager with proper UI.
         if (mFocusManager != null && mUI != null) {
             mFocusManager.setPhotoUI(mUI);
-
-            View root = mUI.getRootView();
-            // These depend on camera parameters.
-            int width = root.getWidth();
-            int height = root.getHeight();
-            mFocusManager.setPreviewSize(width, height);
         }
     }
 
@@ -4660,6 +4662,11 @@
     }
 
     @Override
+    public void onZoomChanged(float requestedZoom) {
+
+    }
+
+    @Override
     public int getCameraState() {
         return mCameraState;
     }
diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java
index 7e3659f..d4503af 100644
--- a/src/com/android/camera/PhotoUI.java
+++ b/src/com/android/camera/PhotoUI.java
@@ -160,26 +160,15 @@
     private int mOrientation;
     private float mScreenBrightness = 0.0f;
 
+    public enum SURFACE_STATUS {
+        HIDE,
+        SURFACE_VIEW;
+    }
+
     public interface SurfaceTextureSizeChangedListener {
         public void onSurfaceTextureSizeChanged(int uncroppedWidth, int uncroppedHeight);
     }
 
-    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) {
-            tryToCloseSubList();
-
-            Camera.Parameters parameters = ((PhotoModule)mController).getParameters();
-            if(parameters != null) {
-                Camera.Size size = parameters.getPreviewSize();
-                if (size != null) {
-                    setAspectRatio((float) size.width / size.height);
-                }
-            }
-        }
-    };
-
     public CameraControls getCameraControls() {
         return mCameraControls;
     }
@@ -233,6 +222,14 @@
         }
     }
 
+    public synchronized void applySurfaceChange(SURFACE_STATUS status) {
+        if(status == SURFACE_STATUS.HIDE) {
+            mSurfaceView.setVisibility(View.GONE);
+            return;
+        }
+        mSurfaceView.setVisibility(View.VISIBLE);
+    }
+
     public PhotoUI(CameraActivity activity, PhotoController controller, View parent) {
         mActivity = activity;
         mController = controller;
@@ -246,11 +243,8 @@
         mSurfaceHolder = mSurfaceView.getHolder();
         mSurfaceHolder.addCallback(this);
         mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
-        mSurfaceView.addOnLayoutChangeListener(mLayoutListener);
         Log.v(TAG, "Using mdp_preview_content (MDP path)");
-
-        View surfaceContainer = mRootView.findViewById(R.id.preview_container);
-        surfaceContainer.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+        mSurfaceView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
             @Override
             public void onLayoutChange(View v, int left, int top, int right,
                     int bottom, int oldLeft, int oldTop, int oldRight,
@@ -258,6 +252,8 @@
                 int width = right - left;
                 int height = bottom - top;
 
+                tryToCloseSubList();
+
                 if (mMaxPreviewWidth == 0 && mMaxPreviewHeight == 0) {
                     mMaxPreviewWidth = width;
                     mMaxPreviewHeight = height;
@@ -478,9 +474,6 @@
         if (mFaceView != null) {
             mFaceView.setLayoutParams(lp);
         }
-
-        mController.onScreenSizeChanged((int) mSurfaceTextureUncroppedWidth,
-                (int) mSurfaceTextureUncroppedHeight);
     }
 
     public void setSurfaceTextureSizeChangedListener(SurfaceTextureSizeChangedListener listener) {
@@ -597,6 +590,7 @@
         });
         if (mController.isImageCaptureIntent()) {
             hideSwitcher();
+            mCameraControls.hideRemainingPhotoCnt();
             mSwitcher.setSwitcherVisibility(false);
             ViewGroup cameraControls = (ViewGroup) mRootView.findViewById(R.id.camera_controls);
             mActivity.getLayoutInflater().inflate(R.layout.review_module_control, cameraControls);
@@ -1148,6 +1142,11 @@
                 mPieRenderer.setBlockFocus(false);
             }
         }
+
+        @Override
+        public void onZoomValueChanged(float value) {
+
+        }
     }
 
     @Override
@@ -1176,6 +1175,13 @@
         return mSurfaceHolder;
     }
 
+    public void hideSurfaceView() {
+        mSurfaceView.setVisibility(View.INVISIBLE);
+    }
+
+    public void showSurfaceView() {
+        mSurfaceView.setVisibility(View.VISIBLE);
+    }
     // Countdown timer
 
     private void initializeCountDown() {
diff --git a/src/com/android/camera/PreviewGestures.java b/src/com/android/camera/PreviewGestures.java
index 00f7d02..4f26240 100644
--- a/src/com/android/camera/PreviewGestures.java
+++ b/src/com/android/camera/PreviewGestures.java
@@ -107,8 +107,9 @@
                 else if (mCaptureMenu != null && !mCaptureMenu.isMenuBeingShown())
                     mCaptureMenu.openFirstLevel();
                 return true;
+            } else {
+                return onSingleTapUp(e2);
             }
-            return false;
         }
 
         private boolean isLeftSwipe(int orientation, int deltaX, int deltaY) {
diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java
index 4724bd2..8e2dc90 100644
--- a/src/com/android/camera/VideoModule.java
+++ b/src/com/android/camera/VideoModule.java
@@ -1094,6 +1094,8 @@
             mUI.enableShutter(true);
         }
 
+        mUI.applySurfaceChange(VideoUI.SURFACE_STATUS.SURFACE_VIEW);
+
         mUI.initDisplayChangeListener();
         // Initializing it here after the preview is started.
         mUI.initializeZoom(mParameters);
@@ -1279,6 +1281,7 @@
         if(mWasMute != mIsMute) {
             setMute(mWasMute, false);
         }
+        mUI.applySurfaceChange(VideoUI.SURFACE_STATUS.HIDE);
     }
 
     @Override
@@ -1657,12 +1660,13 @@
 
             long duration = 0L;
             MediaMetadataRetriever retriever = new MediaMetadataRetriever();
-            retriever.setDataSource(mCurrentVideoFilename);
+
             try {
+                retriever.setDataSource(mCurrentVideoFilename);
                 duration = Long.valueOf(retriever.extractMetadata(
                             MediaMetadataRetriever.METADATA_KEY_DURATION));
-            } catch (NumberFormatException e) {
-                Log.e(TAG, "cannot retrieve duration metadata");
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "cannot access the file");
             }
             retriever.release();
 
@@ -2716,6 +2720,7 @@
         }
 
         Log.d(TAG, "Start to switch camera.");
+        mUI.applySurfaceChange(VideoUI.SURFACE_STATUS.HIDE);
         mCameraId = mPendingSwitchCameraId;
         mPendingSwitchCameraId = -1;
         setCameraId(mCameraId);
@@ -2727,6 +2732,7 @@
         CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
         openCamera();
         readVideoPreferences();
+        mUI.applySurfaceChange(VideoUI.SURFACE_STATUS.SURFACE_VIEW);
         startPreview();
         initializeVideoSnapshot();
         resizeForPreviewAspectRatio();
diff --git a/src/com/android/camera/VideoUI.java b/src/com/android/camera/VideoUI.java
index 9a882e9..8447db2 100644
--- a/src/com/android/camera/VideoUI.java
+++ b/src/com/android/camera/VideoUI.java
@@ -130,13 +130,10 @@
     private float mSurfaceTextureUncroppedWidth;
     private float mSurfaceTextureUncroppedHeight;
 
-    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) {
-            tryToCloseSubList();
-        }
-    };
+    public enum SURFACE_STATUS {
+        HIDE,
+        SURFACE_VIEW;
+    }
 
     public void showPreviewCover() {
         mPreviewCover.setVisibility(View.VISIBLE);
@@ -177,6 +174,14 @@
         }
     }
 
+    public synchronized void applySurfaceChange(SURFACE_STATUS status) {
+        if(status == SURFACE_STATUS.HIDE) {
+            mSurfaceView.setVisibility(View.GONE);
+            return;
+        }
+        mSurfaceView.setVisibility(View.VISIBLE);
+    }
+
     public VideoUI(CameraActivity activity, VideoController controller, View parent) {
         mActivity = activity;
         mController = controller;
@@ -190,9 +195,7 @@
         mSurfaceHolder = mSurfaceView.getHolder();
         mSurfaceHolder.addCallback(this);
         mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
-        mSurfaceView.addOnLayoutChangeListener(mLayoutListener);
         Log.v(TAG, "Using mdp_preview_content (MDP path)");
-
         View surfaceContainer = mRootView.findViewById(R.id.preview_container);
         surfaceContainer.addOnLayoutChangeListener(new OnLayoutChangeListener() {
             @Override
@@ -202,6 +205,7 @@
                 int width = right - left;
                 int height = bottom - top;
 
+                tryToCloseSubList();
                 if (mMaxPreviewWidth == 0 && mMaxPreviewHeight == 0) {
                     mMaxPreviewWidth = width;
                     mMaxPreviewHeight = height;
@@ -1108,6 +1112,11 @@
                 mPieRenderer.setBlockFocus(false);
             }
         }
+
+        @Override
+        public void onZoomValueChanged(float value) {
+
+        }
     }
 
     // SurfaceHolder callbacks
diff --git a/src/com/android/camera/ui/CameraControls.java b/src/com/android/camera/ui/CameraControls.java
index 587cfca..9895689 100644
--- a/src/com/android/camera/ui/CameraControls.java
+++ b/src/com/android/camera/ui/CameraControls.java
@@ -84,7 +84,7 @@
     private float[][] mLocY = new float[4][10];
     private boolean[] mTempEnabled = new boolean[10];
     private boolean mLocSet = false;
-
+    private boolean mHideRemainingPhoto = false;
     private LinearLayout mRemainingPhotos;
     private TextView mRemainingPhotosText;
     private int mCurrentRemaining = -1;
@@ -706,7 +706,8 @@
                 mPreview.animate().translationXBy(mSize).setDuration(ANIME_DURATION);
                 break;
         }
-        if (mRemainingPhotos.getVisibility() == View.INVISIBLE) {
+        if ((mRemainingPhotos.getVisibility() == View.INVISIBLE) &&
+                !mHideRemainingPhoto){
             mRemainingPhotos.setVisibility(View.VISIBLE);
         }
         mRefocusToast.setVisibility(View.GONE);
@@ -922,7 +923,7 @@
 
     public void updateRemainingPhotos(int remaining) {
         long remainingStorage = Storage.getAvailableSpace() - Storage.LOW_STORAGE_THRESHOLD_BYTES;
-        if (remaining < 0 && remainingStorage <= 0) {
+        if ((remaining < 0 && remainingStorage <= 0) || mHideRemainingPhoto) {
             mRemainingPhotos.setVisibility(View.GONE);
         } else {
             for (int i = mRemainingPhotos.getChildCount() - 1; i >= 0; --i) {
@@ -961,7 +962,7 @@
 
     public void showRefocusToast(boolean show) {
         mRefocusToast.setVisibility(show ? View.VISIBLE : View.GONE);
-        if (mCurrentRemaining > 0 ) {
+        if ((mCurrentRemaining > 0 ) && !mHideRemainingPhoto) {
             mRemainingPhotos.setVisibility(show ? View.GONE : View.VISIBLE);
         }
     }
@@ -1003,6 +1004,12 @@
         mMenu.setVisibility(View.VISIBLE);
     }
 
+    public void hideRemainingPhotoCnt() {
+        mHideRemainingPhoto = true;
+        mRemainingPhotos.setVisibility(View.GONE);
+        mRemainingPhotosText.setVisibility(View.GONE);
+    }
+
     private class ArrowTextView extends TextView {
         private static final int TEXT_SIZE = 14;
         private static final int PADDING_SIZE = 18;
diff --git a/src/com/android/camera/ui/ZoomRenderer.java b/src/com/android/camera/ui/ZoomRenderer.java
index 7a2e2e8..0358bda 100644
--- a/src/com/android/camera/ui/ZoomRenderer.java
+++ b/src/com/android/camera/ui/ZoomRenderer.java
@@ -49,11 +49,16 @@
     private int mZoomFraction;
     private Rect mTextBounds;
     private int mOrientation;
+    private boolean mCamera2 = false;
+    private float mZoomValue;
+    private float mZoomMinValue;
+    private float mZoomMaxValue;
 
     public interface OnZoomChangedListener {
         void onZoomStart();
         void onZoomEnd();
         void onZoomValueChanged(int index);  // only for immediate zoom
+        void onZoomValueChanged(float value);
     }
 
     public ZoomRenderer(Context ctx) {
@@ -81,10 +86,23 @@
         mMinZoom = 0;
     }
 
+    public void setZoomMax(float zoomMax) {
+        mCamera2 = true;
+        mZoomMaxValue = zoomMax;
+        mZoomMinValue = 1f;
+    }
+
     public void setZoom(int index) {
         mCircleSize = mMinCircle + index * (mMaxCircle - mMinCircle) / (mMaxZoom - mMinZoom);
     }
 
+    public void setZoom(float zoomValue) {
+        mCamera2 = true;
+        mZoomValue = zoomValue;
+        mCircleSize = (int) (mMinCircle + (mMaxCircle - mMinCircle) * (mZoomValue - mZoomMinValue) /
+                (mZoomMaxValue - mZoomMinValue));
+    }
+
     public void setZoomValue(int value) {
         value = value / 10;
         mZoomSig = value / 10;
@@ -120,6 +138,7 @@
         canvas.drawCircle((float) mCenterX, (float) mCenterY,
                 mCircleSize, mPaint);
         String txt = mZoomSig+"."+mZoomFraction+"x";
+        if (mCamera2) txt = "" + mZoomValue;
         mTextPaint.getTextBounds(txt, 0, txt.length(), mTextBounds);
         canvas.drawText(txt, mCenterX - mTextBounds.centerX(), mCenterY - mTextBounds.centerY(),
                 mTextPaint);
@@ -133,8 +152,16 @@
         circle = Math.min(mMaxCircle, circle);
         if (mListener != null && circle != mCircleSize) {
             mCircleSize = circle;
-            int zoom = mMinZoom + (int) ((mCircleSize - mMinCircle) * (mMaxZoom - mMinZoom) / (mMaxCircle - mMinCircle));
-            mListener.onZoomValueChanged(zoom);
+            if (mCamera2) {
+                float zoom = mZoomMinValue + (mZoomMaxValue - mZoomMinValue) / (mMaxCircle -
+                        mMinCircle) * (mCircleSize - mMinCircle);
+                zoom = ((int) (zoom * 10)) / 10.0f;
+                mListener.onZoomValueChanged(zoom);
+            } else {
+                int zoom = mMinZoom + (int) ((mCircleSize - mMinCircle) * (mMaxZoom - mMinZoom) /
+                        (mMaxCircle - mMinCircle));
+                mListener.onZoomValueChanged(zoom);
+            }
             update();
         }
         return true;
diff --git a/src/com/android/camera/util/CameraUtil.java b/src/com/android/camera/util/CameraUtil.java
index 812220c..b1deca6 100644
--- a/src/com/android/camera/util/CameraUtil.java
+++ b/src/com/android/camera/util/CameraUtil.java
@@ -35,6 +35,7 @@
 import android.hardware.Camera.CameraInfo;
 import android.hardware.Camera.Parameters;
 import android.hardware.Camera.Size;
+import android.hardware.camera2.CameraCharacteristics;
 import android.location.Location;
 import android.net.Uri;
 import android.os.Handler;
@@ -1168,4 +1169,32 @@
         return retRatio;
     }
 
+    public static boolean isZoomSupported(CameraCharacteristics[] characteristics, List<Integer>
+            characteristicsIndex) {
+        for (int i = 0; i < characteristicsIndex.size(); i++) {
+            if (!isZoomSupported(characteristics[characteristicsIndex.get(i)]))
+                return false;
+        }
+        return true;
+    }
+
+    public static boolean isZoomSupported(CameraCharacteristics characteristic) {
+        return characteristic.get(CameraCharacteristics
+                .SCALER_AVAILABLE_MAX_DIGITAL_ZOOM) > 1f;
+    }
+
+    public static boolean isAutoFocusSupported(CameraCharacteristics[] characteristics, List<Integer>
+            characteristicsIndex) {
+        for (int i = 0; i < characteristicsIndex.size(); i++) {
+            if (!isAutoFocusSupported(characteristics[characteristicsIndex.get(i)]))
+                return false;
+        }
+        return true;
+    }
+
+    public static boolean isAutoFocusSupported(CameraCharacteristics characteristic) {
+        Integer maxAfRegions = characteristic.get(
+                CameraCharacteristics.CONTROL_MAX_REGIONS_AF);
+        return maxAfRegions != null && maxAfRegions > 0;
+    }
 }