Introduce common services and integrate Refocus end-to-end.

 Bug: 11120164
 Bug: 10073814

This adds a new common CameraServices interface and a method to every
module to get it. Through this we can expose common services and due to
the interface, we can easily mock them for tests.

Also makes the MediaSaveService not a service anymore.

Change-Id: Ifa093efd4ba53c2955ccc2a990b4f18610c29944
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java
index a78dc6b..aa6fe5e 100644
--- a/src/com/android/camera/CameraActivity.java
+++ b/src/com/android/camera/CameraActivity.java
@@ -22,12 +22,10 @@
 import android.app.Activity;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.ServiceConnection;
 import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
@@ -43,7 +41,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.preference.PreferenceManager;
@@ -70,6 +67,7 @@
 
 import com.android.camera.app.AppController;
 import com.android.camera.app.AppManagerFactory;
+import com.android.camera.app.CameraApp;
 import com.android.camera.app.CameraController;
 import com.android.camera.app.CameraManager;
 import com.android.camera.app.CameraManagerFactory;
@@ -98,6 +96,7 @@
 import com.android.camera.module.ModuleController;
 import com.android.camera.module.ModulesInfo;
 import com.android.camera.tinyplanet.TinyPlanetFragment;
+import com.android.camera.ui.CameraControls;
 import com.android.camera.ui.DetailsDialog;
 import com.android.camera.ui.FilmstripView;
 import com.android.camera.ui.ModeListView;
@@ -205,7 +204,7 @@
     private ViewGroup mUndoDeletionBar;
     private boolean mIsUndoingDeletion = false;
 
-    private Uri[] mNfcPushUris = new Uri[1];
+    private final Uri[] mNfcPushUris = new Uri[1];
 
     private ShareActionProvider mStandardShareActionProvider;
     private Intent mStandardShareIntent;
@@ -225,25 +224,10 @@
     private boolean mPaused;
 
     private MediaSaver mMediaSaver;
-    private ServiceConnection mConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName className, IBinder b) {
-            mMediaSaver = ((MediaSaveService.LocalBinder) b).getService();
-            mCurrentModule.onMediaSaverAvailable(mMediaSaver);
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName className) {
-            if (mMediaSaver != null) {
-                mMediaSaver.setQueueListener(null);
-                mMediaSaver = null;
-            }
-        }
-    };
 
 
     // close activity when screen turns off
-    private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
+    private final BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             finish();
@@ -333,7 +317,7 @@
         return localFile.getName();
     }
 
-    private FilmstripListener mFilmStripListener =
+    private final FilmstripListener mFilmStripListener =
             new FilmstripListener() {
                 @Override
                 public void onDataPromoted(int dataID) {
@@ -760,7 +744,7 @@
         }
     }
 
-    private ImageTaskManager.TaskListener mPlaceholderListener =
+    private final ImageTaskManager.TaskListener mPlaceholderListener =
             new ImageTaskManager.TaskListener() {
 
                 @Override
@@ -795,7 +779,7 @@
                 }
             };
 
-    private ImageTaskManager.TaskListener mStitchingListener =
+    private final ImageTaskManager.TaskListener mStitchingListener =
             new ImageTaskManager.TaskListener() {
                 @Override
                 public void onTaskQueued(String filePath, final Uri imageUri) {
@@ -911,11 +895,6 @@
     }
 
     @Override
-    public MediaSaver getMediaSaver() {
-        return mMediaSaver;
-    }
-
-    @Override
     public OrientationManager getOrientationManager() {
         return mOrientationManager;
     }
@@ -972,16 +951,6 @@
         }
     }
 
-    private void bindMediaSaveService() {
-        Intent intent = new Intent(this, MediaSaveService.class);
-        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
-    }
-
-    private void unbindMediaSaveService() {
-        if (mConnection != null) {
-            unbindService(mConnection);
-        }
-    }
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
@@ -1414,14 +1383,12 @@
     @Override
     public void onStart() {
         super.onStart();
-        bindMediaSaveService();
         mPanoramaViewHelper.onStart();
     }
 
     @Override
     protected void onStop() {
         mPanoramaViewHelper.onStop();
-        unbindMediaSaveService();
 
         CameraManagerFactory.recycle();
         super.onStop();
@@ -1598,7 +1565,7 @@
             mCameraController.closeCamera();
         }
         mCurrentModeIndex = agent.getModuleId();
-        mCurrentModule2 = agent.createModule();
+        mCurrentModule2 = agent.createModule((CameraApp) getApplication());
         mCurrentModule = (CameraModule) mCurrentModule2;
     }
 
diff --git a/src/com/android/camera/CameraModule.java b/src/com/android/camera/CameraModule.java
index 340fea1..9f735dd 100644
--- a/src/com/android/camera/CameraModule.java
+++ b/src/com/android/camera/CameraModule.java
@@ -21,52 +21,88 @@
 import android.view.KeyEvent;
 import android.view.View;
 
+import com.android.camera.app.CameraServices;
 import com.android.camera.app.MediaSaver;
 
-@Deprecated
-public interface CameraModule {
+public abstract class CameraModule {
 
-    public void init(CameraActivity activity, View frame);
+    /** Provides common services and functionality to the module. */
+    private final CameraServices mServices;
 
-    public void onPreviewFocusChanged(boolean previewFocused);
+    public CameraModule(CameraServices services) {
+        mServices = services;
+    }
 
-    public void onPauseBeforeSuper();
+    @Deprecated
+    public abstract void init(CameraActivity activity, View frame);
 
-    public void onPauseAfterSuper();
+    @Deprecated
+    public abstract void onPreviewFocusChanged(boolean previewFocused);
 
-    public void onResumeBeforeSuper();
+    @Deprecated
+    public abstract void onPauseBeforeSuper();
 
-    public void onResumeAfterSuper();
+    @Deprecated
+    public abstract void onPauseAfterSuper();
 
-    public void onConfigurationChanged(Configuration config);
+    @Deprecated
+    public abstract void onResumeBeforeSuper();
 
-    public void onStop();
+    @Deprecated
+    public abstract void onResumeAfterSuper();
 
-    public void installIntentFilter();
+    @Deprecated
+    public abstract void onConfigurationChanged(Configuration config);
 
-    public void onActivityResult(int requestCode, int resultCode, Intent data);
+    @Deprecated
+    public abstract void onStop();
 
-    public boolean onBackPressed();
+    @Deprecated
+    public abstract void installIntentFilter();
 
-    public boolean onKeyDown(int keyCode, KeyEvent event);
+    @Deprecated
+    public abstract void onActivityResult(int requestCode, int resultCode, Intent data);
 
-    public boolean onKeyUp(int keyCode, KeyEvent event);
+    @Deprecated
+    public abstract boolean onBackPressed();
 
-    public void onSingleTapUp(View view, int x, int y);
+    @Deprecated
+    public abstract boolean onKeyDown(int keyCode, KeyEvent event);
 
-    public void onPreviewTextureCopied();
+    @Deprecated
+    public abstract boolean onKeyUp(int keyCode, KeyEvent event);
 
-    public void onCaptureTextureCopied();
+    @Deprecated
+    public abstract void onSingleTapUp(View view, int x, int y);
 
-    public void onUserInteraction();
+    @Deprecated
+    public abstract void onPreviewTextureCopied();
 
-    public boolean updateStorageHintOnResume();
+    @Deprecated
+    public abstract void onCaptureTextureCopied();
 
-    public void onOrientationChanged(int orientation);
+    @Deprecated
+    public abstract void onUserInteraction();
 
-    public void onShowSwitcherPopup();
+    @Deprecated
+    public abstract boolean updateStorageHintOnResume();
 
-    public void onMediaSaverAvailable(MediaSaver s);
+    @Deprecated
+    public abstract void onOrientationChanged(int orientation);
 
-    public boolean arePreviewControlsVisible();
+    @Deprecated
+    public abstract void onShowSwitcherPopup();
+
+    @Deprecated
+    public abstract void onMediaSaverAvailable(MediaSaver s);
+
+    @Deprecated
+    public abstract boolean arePreviewControlsVisible();
+
+    /**
+     * @return An instance containing common services to be used by the module.
+     */
+    protected CameraServices getServices() {
+        return mServices;
+    }
 }
diff --git a/src/com/android/camera/MediaSaveService.java b/src/com/android/camera/MediaSaverImpl.java
similarity index 81%
rename from src/com/android/camera/MediaSaveService.java
rename to src/com/android/camera/MediaSaverImpl.java
index 1bcbcf2..f7e7736 100644
--- a/src/com/android/camera/MediaSaveService.java
+++ b/src/com/android/camera/MediaSaverImpl.java
@@ -16,16 +16,12 @@
 
 package com.android.camera;
 
-import android.app.Service;
 import android.content.ContentResolver;
 import android.content.ContentValues;
-import android.content.Intent;
 import android.graphics.BitmapFactory;
 import android.location.Location;
 import android.net.Uri;
 import android.os.AsyncTask;
-import android.os.Binder;
-import android.os.IBinder;
 import android.provider.MediaStore.Video;
 import android.util.Log;
 
@@ -37,40 +33,19 @@
 /**
  * A class implementing {@link com.android.camera.app.MediaSaver}.
  */
-public class MediaSaveService extends Service implements MediaSaver {
-    public static final String VIDEO_BASE_URI = "content://media/external/video/media";
+public class MediaSaverImpl implements MediaSaver {
+    private static final String TAG = "MediaSaverImpl";
+    private static final String VIDEO_BASE_URI = "content://media/external/video/media";
 
-    // The memory limit for unsaved image is 20MB.
+    /** The memory limit for unsaved image is 20MB. */
     private static final int SAVE_TASK_MEMORY_LIMIT = 20 * 1024 * 1024;
-    private static final String TAG = "CAM_" + MediaSaveService.class.getSimpleName();
 
-    private final IBinder mBinder = new LocalBinder();
     private QueueListener mQueueListener;
-    // Memory used by the total queued save request, in bytes.
+
+    /** Memory used by the total queued save request, in bytes. */
     private long mMemoryUse;
 
-    class LocalBinder extends Binder {
-        public MediaSaver getService() {
-            return MediaSaveService.this;
-        }
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mBinder;
-    }
-
-    @Override
-    public int onStartCommand(Intent intent, int flag, int startId) {
-        return START_STICKY;
-    }
-
-    @Override
-    public void onDestroy() {
-    }
-
-    @Override
-    public void onCreate() {
+    public MediaSaverImpl() {
         mMemoryUse = 0;
     }
 
@@ -123,28 +98,34 @@
     @Override
     public void setQueueListener(QueueListener l) {
         mQueueListener = l;
-        if (l == null) return;
+        if (l == null) {
+            return;
+        }
         l.onQueueStatus(isQueueFull());
     }
 
     private void onQueueFull() {
-        if (mQueueListener != null) mQueueListener.onQueueStatus(true);
+        if (mQueueListener != null) {
+            mQueueListener.onQueueStatus(true);
+        }
     }
 
     private void onQueueAvailable() {
-        if (mQueueListener != null) mQueueListener.onQueueStatus(false);
+        if (mQueueListener != null) {
+            mQueueListener.onQueueStatus(false);
+        }
     }
 
     private class ImageSaveTask extends AsyncTask <Void, Void, Uri> {
-        private byte[] data;
-        private String title;
-        private long date;
-        private Location loc;
+        private final byte[] data;
+        private final String title;
+        private final long date;
+        private final Location loc;
         private int width, height;
-        private int orientation;
-        private ExifInterface exif;
-        private ContentResolver resolver;
-        private OnMediaSavedListener listener;
+        private final int orientation;
+        private final ExifInterface exif;
+        private final ContentResolver resolver;
+        private final OnMediaSavedListener listener;
 
         public ImageSaveTask(byte[] data, String title, long date, Location loc,
                              int width, int height, int orientation, ExifInterface exif,
@@ -191,10 +172,10 @@
 
     private class VideoSaveTask extends AsyncTask <Void, Void, Uri> {
         private String path;
-        private long duration;
-        private ContentValues values;
-        private OnMediaSavedListener listener;
-        private ContentResolver resolver;
+        private final long duration;
+        private final ContentValues values;
+        private final OnMediaSavedListener listener;
+        private final ContentResolver resolver;
 
         public VideoSaveTask(String path, long duration, ContentValues values,
                 OnMediaSavedListener l, ContentResolver r) {
diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java
index e2aa32e..4e79892 100644
--- a/src/com/android/camera/PhotoModule.java
+++ b/src/com/android/camera/PhotoModule.java
@@ -58,6 +58,7 @@
 import com.android.camera.app.CameraManager.CameraShutterCallback;
 import com.android.camera.PhotoModule.NamedImages.NamedEntity;
 import com.android.camera.app.AppController;
+import com.android.camera.app.CameraServices;
 import com.android.camera.app.MediaSaver;
 import com.android.camera.exif.ExifInterface;
 import com.android.camera.exif.ExifTag;
@@ -81,9 +82,14 @@
 import java.util.Vector;
 
 public class PhotoModule
-        implements CameraModule, ModuleController, PhotoController, FocusOverlayManager.Listener,
-        CameraPreference.OnPreferenceChangedListener, ShutterButton.OnShutterButtonListener,
-        MediaSaver.QueueListener, OnCountDownFinishedListener, SensorEventListener {
+        extends CameraModule
+        implements PhotoController,
+        ModuleController,
+        FocusOverlayManager.Listener,
+        CameraPreference.OnPreferenceChangedListener,
+        ShutterButton.OnShutterButtonListener, MediaSaver.QueueListener,
+        OnCountDownFinishedListener,
+        SensorEventListener {
 
     private static final String TAG = "CAM_PhotoModule";
 
@@ -164,7 +170,7 @@
     // when the image is ready to be saved.
     private NamedImages mNamedImages;
 
-    private Runnable mDoSnapRunnable = new Runnable() {
+    private final Runnable mDoSnapRunnable = new Runnable() {
         @Override
         public void run() {
             onShutterButtonClick();
@@ -242,15 +248,15 @@
 
     private boolean mQuickCapture;
     private SensorManager mSensorManager;
-    private float[] mGData = new float[3];
-    private float[] mMData = new float[3];
-    private float[] mR = new float[16];
+    private final float[] mGData = new float[3];
+    private final float[] mMData = new float[3];
+    private final float[] mR = new float[16];
     private int mHeading = -1;
 
     // True if all the parameters needed to start preview is ready.
     private boolean mCameraPreviewParamsReady = false;
 
-    private MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
+    private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
             new MediaSaver.OnMediaSavedListener() {
                 @Override
                 public void onMediaSaved(Uri uri) {
@@ -354,6 +360,12 @@
         }
     }
 
+    /**
+     * Constructs a new photo module.
+     */
+    public PhotoModule(CameraServices services) {
+        super(services);
+    }
 
     @Override
     public void init(CameraActivity activity, View parent) {
@@ -535,7 +547,7 @@
         keepMediaProviderInstance();
 
         mUI.initializeFirstTime();
-        MediaSaver s = mActivity.getMediaSaver();
+        MediaSaver s = getServices().getMediaSaver();
         // We set the listener only when both service and shutterbutton
         // are initialized.
         if (s != null) {
@@ -557,7 +569,7 @@
         boolean recordLocation = RecordLocationPreference.get(
                 mPreferences, mContentResolver);
         mLocationManager.recordLocation(recordLocation);
-        MediaSaver s = mActivity.getMediaSaver();
+        MediaSaver s = getServices().getMediaSaver();
         if (s != null) {
             s.setQueueListener(this);
         }
@@ -613,7 +625,7 @@
     private final class ShutterCallback
             implements CameraShutterCallback {
 
-        private boolean mNeedsAnimation;
+        private final boolean mNeedsAnimation;
 
         public ShutterCallback(boolean needsAnimation) {
             mNeedsAnimation = needsAnimation;
@@ -744,7 +756,7 @@
                         exif.setTag(directionRefTag);
                         exif.setTag(directionTag);
                     }
-                    mActivity.getMediaSaver().addImage(
+                    getServices().getMediaSaver().addImage(
                             jpegData, title, date, mLocation, width, height,
                             orientation, exif, mOnMediaSavedListener, mContentResolver);
                 }
@@ -800,7 +812,7 @@
      * This class is just a thread-safe queue for name,date holder objects.
      */
     public static class NamedImages {
-        private Vector<NamedEntity> mQueue;
+        private final Vector<NamedEntity> mQueue;
 
         public NamedImages() {
             mQueue = new Vector<NamedEntity>();
@@ -857,8 +869,8 @@
         // is full then ignore.
         if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
                 || mCameraState == SWITCHING_CAMERA
-                || mActivity.getMediaSaver() == null
-                || mActivity.getMediaSaver().isQueueFull()) {
+                || getServices().getMediaSaver() == null
+                || getServices().getMediaSaver().isQueueFull()) {
             return false;
         }
         mCaptureStartTime = System.currentTimeMillis();
@@ -1270,7 +1282,7 @@
 
         mPendingSwitchCameraId = -1;
         if (mFocusManager != null) mFocusManager.removeMessages();
-        MediaSaver s = mActivity.getMediaSaver();
+        MediaSaver s = getServices().getMediaSaver();
         if (s != null) {
             s.setQueueListener(null);
         }
diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java
index 9aeb37b..adc3573 100644
--- a/src/com/android/camera/VideoModule.java
+++ b/src/com/android/camera/VideoModule.java
@@ -53,9 +53,10 @@
 import android.view.WindowManager;
 import android.widget.Toast;
 
+import com.android.camera.app.AppController;
 import com.android.camera.app.CameraManager.CameraPictureCallback;
 import com.android.camera.app.CameraManager.CameraProxy;
-import com.android.camera.app.AppController;
+import com.android.camera.app.CameraServices;
 import com.android.camera.app.MediaSaver;
 import com.android.camera.exif.ExifInterface;
 import com.android.camera.module.ModuleController;
@@ -73,9 +74,13 @@
 import java.util.Iterator;
 import java.util.List;
 
-public class VideoModule implements CameraModule, ModuleController, VideoController,
-        CameraPreference.OnPreferenceChangedListener, ShutterButton.OnShutterButtonListener,
-        MediaRecorder.OnErrorListener, MediaRecorder.OnInfoListener {
+public class VideoModule extends CameraModule
+    implements ModuleController,
+    VideoController,
+    CameraPreference.OnPreferenceChangedListener,
+    ShutterButton.OnShutterButtonListener,
+    MediaRecorder.OnErrorListener,
+    MediaRecorder.OnInfoListener {
 
     private static final String TAG = "CAM_VideoModule";
 
@@ -285,6 +290,13 @@
         }
     }
 
+    /**
+     * Construct a new video module.
+     */
+    public VideoModule(CameraServices services) {
+        super(services);
+    }
+
     private String createName(long dateTaken) {
         Date date = new Date(dateTaken);
         SimpleDateFormat dateFormat = new SimpleDateFormat(
@@ -359,7 +371,7 @@
             if (!mMediaRecorderRecording || mPaused || mSnapshotInProgress) {
                 return;
             }
-            MediaSaver s = mActivity.getMediaSaver();
+            MediaSaver s = getServices().getMediaSaver();
             if (s == null || s.isQueueFull()) {
                 return;
             }
@@ -1069,7 +1081,7 @@
             } else {
                 Log.w(TAG, "Video duration <= 0 : " + duration);
             }
-            mActivity.getMediaSaver().addVideo(mCurrentVideoFilename,
+            getServices().getMediaSaver().addVideo(mCurrentVideoFilename,
                     duration, mCurrentVideoValues,
                     mOnVideoSavedListener, mContentResolver);
         }
@@ -1722,7 +1734,7 @@
         ExifInterface exif = Exif.getExif(data);
         int orientation = Exif.getOrientation(exif);
 
-        mActivity.getMediaSaver().addImage(
+        getServices().getMediaSaver().addImage(
                 data, title, dateTaken, loc, orientation,
                 exif, mOnPhotoSavedListener, mContentResolver);
     }
diff --git a/src/com/android/camera/WideAnglePanoramaModule.java b/src/com/android/camera/WideAnglePanoramaModule.java
index 02c3726..f7dba07 100644
--- a/src/com/android/camera/WideAnglePanoramaModule.java
+++ b/src/com/android/camera/WideAnglePanoramaModule.java
@@ -46,6 +46,7 @@
 
 import com.android.camera.app.CameraManager.CameraProxy;
 import com.android.camera.app.AppController;
+import com.android.camera.app.CameraServices;
 import com.android.camera.app.MediaSaver;
 import com.android.camera.data.LocalData;
 import com.android.camera.exif.ExifInterface;
@@ -64,7 +65,9 @@
  * Activity to handle panorama capturing.
  */
 public class WideAnglePanoramaModule
-        implements CameraModule, ModuleController, WideAnglePanoramaController,
+        extends CameraModule
+        implements ModuleController,
+        WideAnglePanoramaController,
         SurfaceTexture.OnFrameAvailableListener {
 
     public static final int DEFAULT_SWEEP_ANGLE = 160;
@@ -94,8 +97,8 @@
     private WideAnglePanoramaUI mUI;
 
     private MosaicPreviewRenderer mMosaicPreviewRenderer;
-    private Object mRendererLock = new Object();
-    private Object mWaitObject = new Object();
+    private final Object mRendererLock = new Object();
+    private final Object mWaitObject = new Object();
 
     private String mPreparePreviewString;
     private String mDialogTitle;
@@ -124,7 +127,7 @@
 
     // Prefer FOCUS_MODE_INFINITY to FOCUS_MODE_CONTINUOUS_VIDEO because of
     // getting a better image quality by the former.
-    private String mTargetFocusMode = Parameters.FOCUS_MODE_INFINITY;
+    private final String mTargetFocusMode = Parameters.FOCUS_MODE_INFINITY;
 
     private PanoOrientationEventListener mOrientationEventListener;
     // The value could be 0, 90, 180, 270 for the 4 different orientations measured in clockwise
@@ -148,6 +151,13 @@
     private boolean mMosaicPreviewConfigured;
     private boolean mPreviewFocused = true;
 
+    /**
+     * Constructs a new Wide-Angle panorama module.
+     */
+    public WideAnglePanoramaModule(CameraServices services) {
+        super(services);
+    }
+
     @Override
     public void onPreviewUIReady() {
         configMosaicPreview();
@@ -651,6 +661,7 @@
     /** The orientation of the camera image. The value is the angle that the camera
      *  image needs to be rotated clockwise so it shows correctly on the display
      *  in its natural orientation. It should be 0, 90, 180, or 270.*/
+    @Override
     public int getCameraOrientation() {
         return mCameraOrientation;
     }
diff --git a/src/com/android/camera/app/AppController.java b/src/com/android/camera/app/AppController.java
index efd73d4..5adfcc5 100644
--- a/src/com/android/camera/app/AppController.java
+++ b/src/com/android/camera/app/AppController.java
@@ -152,13 +152,6 @@
     public CameraProvider getCameraProvider();
 
     /**
-     * Returns the {@link com.android.camera.app.MediaSaver}.
-     *
-     * @return {@code null} if not available yet.
-     */
-    public MediaSaver getMediaSaver();
-
-    /**
      * Returns the {@link OrientationManagerImpl}.
      *
      * @return {@code null} if not available yet.
diff --git a/src/com/android/camera/app/CameraApp.java b/src/com/android/camera/app/CameraApp.java
index 834caca..b106f74 100644
--- a/src/com/android/camera/app/CameraApp.java
+++ b/src/com/android/camera/app/CameraApp.java
@@ -18,16 +18,28 @@
 
 import android.app.Application;
 
+import com.android.camera.MediaSaverImpl;
 import com.android.camera.util.CameraUtil;
 import com.android.camera.util.UsageStatistics;
 
-public class CameraApp extends Application {
+/**
+ * The Camera application class containing important services and functionality
+ * to be used across modules.
+ */
+public class CameraApp extends Application implements CameraServices {
+    private MediaSaver mMediaSaver;
 
     @Override
     public void onCreate() {
         super.onCreate();
         UsageStatistics.initialize(this);
         CameraUtil.initialize(this);
+
+        mMediaSaver = new MediaSaverImpl();
+    }
+
+    @Override
+    public MediaSaver getMediaSaver() {
+        return mMediaSaver;
     }
 }
-
diff --git a/src/com/android/camera/app/CameraServices.java b/src/com/android/camera/app/CameraServices.java
new file mode 100644
index 0000000..ce6ef8e
--- /dev/null
+++ b/src/com/android/camera/app/CameraServices.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camera.app;
+
+public interface CameraServices {
+    /**
+     * Returns the media saver instance.
+     * <p>
+     * Deprecated. Use {@link #getSessionManager()} whenever possible. This
+     * direct access to media saver will go away.
+     */
+    @Deprecated
+    public MediaSaver getMediaSaver();
+}
diff --git a/src/com/android/camera/app/ModuleManager.java b/src/com/android/camera/app/ModuleManager.java
index 69830cc..9a72f8b 100644
--- a/src/com/android/camera/app/ModuleManager.java
+++ b/src/com/android/camera/app/ModuleManager.java
@@ -45,9 +45,12 @@
 
         /**
          * Creates the module.
+         *
+         * @param services Common services and functionality to be shared
+         *            between modules.
          * @return The module.
          */
-        public ModuleController createModule();
+        public ModuleController createModule(CameraServices services);
     }
 
     /**
diff --git a/src/com/android/camera/app/ModuleManagerImpl.java b/src/com/android/camera/app/ModuleManagerImpl.java
index 59cb72d..3521319 100644
--- a/src/com/android/camera/app/ModuleManagerImpl.java
+++ b/src/com/android/camera/app/ModuleManagerImpl.java
@@ -18,8 +18,6 @@
 
 import android.util.SparseArray;
 
-import com.android.camera.module.ModulesInfo;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -29,7 +27,7 @@
 public class ModuleManagerImpl implements ModuleManager {
     private static final String TAG = "ModuleManagerImpl";
 
-    private SparseArray<ModuleAgent> mRegisteredModuleAgents = new
+    private final SparseArray<ModuleAgent> mRegisteredModuleAgents = new
             SparseArray<ModuleAgent>(2);
     private int mDefaultModuleId = MODULE_INDEX_NONE;
 
diff --git a/src/com/android/camera/data/InProgressDataWrapper.java b/src/com/android/camera/data/InProgressDataWrapper.java
index 30314e2..e43a43f 100644
--- a/src/com/android/camera/data/InProgressDataWrapper.java
+++ b/src/com/android/camera/data/InProgressDataWrapper.java
@@ -16,7 +16,6 @@
 
 package com.android.camera.data;
 
-import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
@@ -188,13 +187,14 @@
     }
 
     @Override
-    public void isPhotoSphere(Context context, PanoramaSupportCallback callback) {
-        mLocalData.isPhotoSphere(context, callback);
+    public void requestAuxInfo(Context context, AuxInfoSupportCallback callback) {
+        mLocalData.requestAuxInfo(context, callback);
     }
 
+
     @Override
-    public void viewPhotoSphere(PhotoSphereHelper.PanoramaViewHelper helper) {
-        mLocalData.viewPhotoSphere(helper);
+    public void view(PhotoSphereHelper.PanoramaViewHelper helper) {
+        mLocalData.view(helper);
     }
 
     @Override
diff --git a/src/com/android/camera/data/LocalData.java b/src/com/android/camera/data/LocalData.java
index 0ef2282..ca59174 100644
--- a/src/com/android/camera/data/LocalData.java
+++ b/src/com/android/camera/data/LocalData.java
@@ -73,6 +73,11 @@
      */
     public static final int LOCAL_IN_PROGRESS_DATA = 7;
 
+    /**
+     * Constant for denoting an RGBZ image.
+     */
+    public static final int LOCAL_RGBZ = 8;
+
     View getView(Context ctx, int width, int height, Drawable placeHolder,
             LocalDataAdapter adapter);
 
@@ -149,7 +154,8 @@
      *
      * @return The local data type. Could be one of the following:
      * {@code LOCAL_CAMERA_PREVIEW}, {@code LOCAL_VIEW}, {@code LOCAL_IMAGE},
-     * {@code LOCAL_VIDEO}, {@code LOCAL_PHOTO_SPHERE}, and {@code LOCAL_360_PHOTO_SPHERE}
+     * {@code LOCAL_VIDEO}, {@code LOCAL_PHOTO_SPHERE},
+     * {@code LOCAL_360_PHOTO_SPHERE}, and {@code LOCAL_RGBZ}
      */
     int getLocalDataType();
 
diff --git a/src/com/android/camera/data/LocalMediaData.java b/src/com/android/camera/data/LocalMediaData.java
index 7d468af..7ab3b7a 100644
--- a/src/com/android/camera/data/LocalMediaData.java
+++ b/src/com/android/camera/data/LocalMediaData.java
@@ -38,6 +38,7 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
+import com.android.camera.data.RgbzMetadataLoader.RgbzMetadataCallback;
 import com.android.camera.filmstrip.FilmstripImageData;
 import com.android.camera.util.CameraUtil;
 import com.android.camera.util.PhotoSphereHelper;
@@ -47,6 +48,7 @@
 import java.text.DateFormat;
 import java.util.Date;
 import java.util.Locale;
+import java.util.concurrent.Semaphore;
 
 /**
  * A base class for all the local media files. The bitmap is loaded in
@@ -72,7 +74,10 @@
     protected PhotoSphereHelper.PanoramaMetadata mPanoramaMetadata;
 
     /** Used to load photo sphere metadata from image files. */
-    protected PanoramaMetadataLoader mPanoramaMetadataLoader = null;
+    protected PanoramaMetadataLoader mPanoramaMetadataLoader;
+
+    protected RgbzMetadataLoader mRgbzMetadataLoader;
+    protected Boolean mIsRgbz = null;
 
     /**
      * Used for thumbnail loading optimization. True if this data has a
@@ -80,7 +85,7 @@
      */
     protected Boolean mUsing = false;
 
-    public LocalMediaData (long contentId, String title, String mimeType,
+    public LocalMediaData(long contentId, String title, String mimeType,
             long dateTakenInSeconds, long dateModifiedInSeconds, String path,
             int width, int height, long sizeInBytes, double latitude,
             double longitude) {
@@ -159,36 +164,75 @@
     }
 
     @Override
-    public void viewPhotoSphere(PhotoSphereHelper.PanoramaViewHelper helper) {
-        helper.showPanorama(getContentUri());
+    public void view(PhotoSphereHelper.PanoramaViewHelper helper) {
+        if (mPanoramaMetadata != null && mPanoramaMetadata.mUsePanoramaViewer) {
+            helper.showPanorama(getContentUri());
+        } else if (mIsRgbz != null && mIsRgbz.booleanValue()) {
+            helper.showRgbz(getContentUri());
+        }
     }
 
     @Override
-    public void isPhotoSphere(Context context, final PanoramaSupportCallback callback) {
+    public void requestAuxInfo(Context context, final AuxInfoSupportCallback callback) {
         // If we already have metadata, use it.
-        if (mPanoramaMetadata != null) {
-            callback.panoramaInfoAvailable(mPanoramaMetadata.mUsePanoramaViewer,
-                    mPanoramaMetadata.mIsPanorama360);
+        if (mPanoramaMetadata != null && mIsRgbz != null) {
+            callback.auxInfoAvailable(mPanoramaMetadata.mUsePanoramaViewer,
+                    mPanoramaMetadata.mIsPanorama360, mIsRgbz.booleanValue());
+            return;
         }
 
-        // Otherwise prepare a loader, if we don't have one already.
-        if (mPanoramaMetadataLoader == null) {
-            mPanoramaMetadataLoader = new PanoramaMetadataLoader(getContentUri());
+        final Semaphore sem = new Semaphore(1);
+        if (mPanoramaMetadata == null) {
+            // Drain all permits so the rgbz return callback waits for the
+            // panorama data result.
+            sem.drainPermits();
+            // Otherwise prepare a loader, if we don't have one already.
+            if (mPanoramaMetadataLoader == null) {
+                mPanoramaMetadataLoader = new PanoramaMetadataLoader(getContentUri());
+            }
+            // Load the metadata asynchronously.
+            mPanoramaMetadataLoader.getPanoramaMetadata(context,
+                    new PanoramaMetadataLoader.PanoramaMetadataCallback() {
+                        @Override
+                        public void onPanoramaMetadataLoaded(
+                                PhotoSphereHelper.PanoramaMetadata metadata) {
+                            // Store the metadata and remove the loader to free
+                            // up
+                            // space.
+                            mPanoramaMetadata = metadata;
+                            mPanoramaMetadataLoader = null;
+                            sem.release();
+                        }
+                    });
         }
 
-        // Load the metadata asynchronously.
-        mPanoramaMetadataLoader.getPanoramaMetadata(context,
-                new PanoramaMetadataLoader.PanoramaMetadataCallback() {
-                    @Override
-                    public void onPanoramaMetadataLoaded(PhotoSphereHelper.PanoramaMetadata metadata) {
-                        // Store the metadata and remove the loader to free up
-                        // space.
-                        mPanoramaMetadata = metadata;
-                        mPanoramaMetadataLoader = null;
-                        callback.panoramaInfoAvailable(metadata.mUsePanoramaViewer,
-                                metadata.mIsPanorama360);
+        if (mIsRgbz == null) {
+            if (mRgbzMetadataLoader == null) {
+                mRgbzMetadataLoader = new RgbzMetadataLoader(getContentUri());
+            }
+            mRgbzMetadataLoader.getRgbzMetadata(context, new RgbzMetadataCallback() {
+                @Override
+                public void onRgbzMetadataLoaded(Boolean isRgbz) {
+                    mIsRgbz = isRgbz;
+                    mRgbzMetadataLoader = null;
+                    try {
+                        // Wait, if needed, for the result of the panorama data
+                        // update.
+                        sem.acquire();
+                    } catch (InterruptedException e) {
+                        // Do nothing
                     }
-                });
+
+                    boolean usePanoramaViewer = mPanoramaMetadata != null
+                            && mPanoramaMetadata.mUsePanoramaViewer;
+                    boolean isPanorama360 = mPanoramaMetadata != null
+                            && mPanoramaMetadata.mIsPanorama360;
+                    boolean isItRgbz = mIsRgbz != null & mIsRgbz.booleanValue();
+                    callback.auxInfoAvailable(usePanoramaViewer,
+                            isPanorama360, isItRgbz);
+                }
+            });
+        }
     }
 
     @Override
@@ -613,7 +657,7 @@
         };
 
         /** The duration in milliseconds. */
-        private long mDurationInSeconds;
+        private final long mDurationInSeconds;
 
         public VideoData(long id, String title, String mimeType,
                 long dateTakenInSeconds, long dateModifiedInSeconds,
@@ -648,7 +692,8 @@
             rotation = retriever.extractMetadata(
                     MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
 
-            // Extracts video height/width if available. If unavailable, set to 0.
+            // Extracts video height/width if available. If unavailable, set to
+            // 0.
             if (width == 0 || height == 0) {
                 String val = retriever.extractMetadata(
                         MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
@@ -761,7 +806,8 @@
             icon.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
-                    // TODO: refactor this into activities to avoid this class conversion.
+                    // TODO: refactor this into activities to avoid this class
+                    // conversion.
                     CameraUtil.playVideo((Activity) ctx, getContentUri(), mTitle);
                 }
             });
diff --git a/src/com/android/camera/data/RgbzMetadataLoader.java b/src/com/android/camera/data/RgbzMetadataLoader.java
new file mode 100644
index 0000000..c1a4a44
--- /dev/null
+++ b/src/com/android/camera/data/RgbzMetadataLoader.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camera.data;
+
+import android.content.Context;
+import android.net.Uri;
+
+import com.android.camera.exif.ExifInterface;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+/**
+ * Asynchronously loads RGBZ data.
+ */
+public class RgbzMetadataLoader {
+  public static interface RgbzMetadataCallback {
+    public void onRgbzMetadataLoaded(Boolean isRgbz);
+  }
+
+  private static final String EXIF_SOFTWARE_VALUE = "RGBZ";
+  private Boolean mIsRgbz = null;
+  private ArrayList<RgbzMetadataCallback> mCallbacksWaiting;
+  private final Uri mMediaUri;
+
+  public RgbzMetadataLoader(Uri uri) {
+    mMediaUri = uri;
+  }
+
+  /**
+   * Check whether this file is an RGBZ file.
+   *
+   * @param context The app context.
+   * @param callback Will be called with the result.
+   */
+  public synchronized void getRgbzMetadata(final Context context, RgbzMetadataCallback callback) {
+    if (mIsRgbz != null) {
+      callback.onRgbzMetadataLoaded(mIsRgbz);
+      return;
+    }
+
+    if (mCallbacksWaiting == null) {
+      mCallbacksWaiting = new ArrayList<RgbzMetadataCallback>();
+      (new Thread() {
+        @Override
+        public void run() {
+          boolean isRgbz = false;
+
+          try {
+            InputStream input;
+            input = context.getContentResolver().openInputStream(mMediaUri);
+            isRgbz = isRgbz(input);
+          } catch (FileNotFoundException e) {
+            e.printStackTrace();
+          }
+          onLoadingDone(isRgbz);
+        }
+      }).start();
+
+    }
+    mCallbacksWaiting.add(callback);
+  }
+
+  private synchronized void onLoadingDone(boolean isRgbz) {
+    mIsRgbz = isRgbz;
+    for (RgbzMetadataCallback cb : mCallbacksWaiting) {
+      cb.onRgbzMetadataLoaded(mIsRgbz);
+    }
+    mCallbacksWaiting = null;
+  }
+
+  /**
+   * @return Whether the file is an RGBZ file.
+   */
+  public static boolean isRgbz(InputStream input) {
+    ExifInterface exif = new ExifInterface();
+    try {
+      exif.readExif(input);
+      // TODO: Rather than this, check for the presence of the XMP.
+      String software = exif.getTagStringValue(ExifInterface.TAG_SOFTWARE);
+      return software != null && software.startsWith(EXIF_SOFTWARE_VALUE);
+    } catch (FileNotFoundException e) {
+      e.printStackTrace();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+    return false;
+  }
+}
diff --git a/src/com/android/camera/data/SimpleViewData.java b/src/com/android/camera/data/SimpleViewData.java
index 66d9552..55d302c 100644
--- a/src/com/android/camera/data/SimpleViewData.java
+++ b/src/com/android/camera/data/SimpleViewData.java
@@ -30,7 +30,7 @@
  * A LocalData that does nothing but only shows a view.
  */
 public class SimpleViewData implements LocalData {
-    private static final String TAG = "CAM_SimpleViewData";
+    private static final String TAG = "SimpleViewData";
 
     private final int mWidth;
     private final int mHeight;
@@ -135,13 +135,13 @@
     }
 
     @Override
-    public void isPhotoSphere(Context context, PanoramaSupportCallback callback) {
-        // Not a photo sphere panorama.
-        callback.panoramaInfoAvailable(false, false);
+    public void requestAuxInfo(Context context, AuxInfoSupportCallback callback) {
+        // Not a photo sphere panorama or rgbz image.
+        callback.auxInfoAvailable(false, false, false);
     }
 
     @Override
-    public void viewPhotoSphere(PhotoSphereHelper.PanoramaViewHelper helper) {
+    public void view(PhotoSphereHelper.PanoramaViewHelper helper) {
         // do nothing.
     }
 
diff --git a/src/com/android/camera/filmstrip/FilmstripImageData.java b/src/com/android/camera/filmstrip/FilmstripImageData.java
index c6aa179..2b6e516 100644
--- a/src/com/android/camera/filmstrip/FilmstripImageData.java
+++ b/src/com/android/camera/filmstrip/FilmstripImageData.java
@@ -29,8 +29,11 @@
     /**
      * Interface that is used to tell the caller whether an image is a photo
      * sphere.
+     *
+     * We need to deprecate this and store this data in a separate DB for additional aux data.
      */
-    public static interface PanoramaSupportCallback {
+    @Deprecated
+    public static interface AuxInfoSupportCallback {
         /**
          * Called when photo sphere info has been loaded.
          *
@@ -38,7 +41,7 @@
          * @param isPanorama360 whether the photo sphere is a full 360
          *            degree horizontal panorama
          */
-        void panoramaInfoAvailable(boolean isPanorama, boolean isPanorama360);
+        void auxInfoAvailable(boolean isPanorama, boolean isPanorama360, boolean isRgbz);
     }
 
     // View types.
@@ -131,16 +134,15 @@
     public void recycle();
 
     /**
-     * Asynchronously checks if the image is a photo sphere. Notifies the
-     * callback when the results are available.
      */
-    public void isPhotoSphere(Context context, PanoramaSupportCallback callback);
+    @Deprecated
+    public void requestAuxInfo(Context context, AuxInfoSupportCallback callback);
 
     /**
      * If the item is a valid photo sphere panorama, this method will launch
      * the viewer.
      */
-    public void viewPhotoSphere(PhotoSphereHelper.PanoramaViewHelper helper);
+    public void view(PhotoSphereHelper.PanoramaViewHelper helper);
 
     /** Whether this item is a photo. */
     public boolean isPhoto();
diff --git a/src/com/android/camera/module/ModulesInfo.java b/src/com/android/camera/module/ModulesInfo.java
index e573e5c..c42865b 100644
--- a/src/com/android/camera/module/ModulesInfo.java
+++ b/src/com/android/camera/module/ModulesInfo.java
@@ -21,6 +21,7 @@
 import com.android.camera.PhotoModule;
 import com.android.camera.VideoModule;
 import com.android.camera.WideAnglePanoramaModule;
+import com.android.camera.app.CameraServices;
 import com.android.camera.app.ModuleManager;
 import com.android.camera.ui.ModeListView;
 import com.android.camera.util.GcamHelper;
@@ -72,8 +73,8 @@
             }
 
             @Override
-            public ModuleController createModule() {
-                return new PhotoModule();
+            public ModuleController createModule(CameraServices services) {
+                return new PhotoModule(services);
             }
         });
     }
@@ -91,8 +92,8 @@
             }
 
             @Override
-            public ModuleController createModule() {
-                return new VideoModule();
+            public ModuleController createModule(CameraServices services) {
+                return new VideoModule(services);
             }
         });
     }
@@ -110,8 +111,8 @@
             }
 
             @Override
-            public ModuleController createModule() {
-                return new WideAnglePanoramaModule();
+            public ModuleController createModule(CameraServices services) {
+                return new WideAnglePanoramaModule(services);
             }
         });
     }
@@ -129,9 +130,9 @@
             }
 
             @Override
-            public ModuleController createModule() {
+            public ModuleController createModule(CameraServices services) {
                 // TODO: remove the type casting.
-                return (ModuleController) PhotoSphereHelper.createPanoramaModule();
+                return (ModuleController) PhotoSphereHelper.createPanoramaModule(services);
             }
         });
     }
@@ -149,9 +150,9 @@
             }
 
             @Override
-            public ModuleController createModule() {
+            public ModuleController createModule(CameraServices services) {
                 // TODO: remove the type casting.
-                return (ModuleController) RefocusHelper.createRefocusModule();
+                return (ModuleController) RefocusHelper.createRefocusModule(services);
             }
         });
     }
@@ -169,8 +170,8 @@
             }
 
             @Override
-            public ModuleController createModule() {
-                return (ModuleController) GcamHelper.createGcamModule();
+            public ModuleController createModule(CameraServices services) {
+                return (ModuleController) GcamHelper.createGcamModule(services);
             }
         });
     }
diff --git a/src/com/android/camera/tinyplanet/TinyPlanetFragment.java b/src/com/android/camera/tinyplanet/TinyPlanetFragment.java
index f1a1082..9b95343 100644
--- a/src/com/android/camera/tinyplanet/TinyPlanetFragment.java
+++ b/src/com/android/camera/tinyplanet/TinyPlanetFragment.java
@@ -42,6 +42,7 @@
 import com.adobe.xmp.XMPException;
 import com.adobe.xmp.XMPMeta;
 import com.android.camera.CameraActivity;
+import com.android.camera.app.CameraApp;
 import com.android.camera.app.MediaSaver.OnMediaSavedListener;
 import com.android.camera.app.MediaSaver;
 import com.android.camera.exif.ExifInterface;
@@ -304,7 +305,7 @@
             protected void onPostExecute(TinyPlanetImage image) {
                 // Once created, store the new file and add it to the filmstrip.
                 final CameraActivity activity = (CameraActivity) getActivity();
-                MediaSaver mediaSaver = activity.getMediaSaver();
+                MediaSaver mediaSaver = ((CameraApp) activity.getApplication()).getMediaSaver();
                 OnMediaSavedListener doneListener =
                         new OnMediaSavedListener() {
                             @Override
diff --git a/src/com/android/camera/ui/FilmstripBottomControls.java b/src/com/android/camera/ui/FilmstripBottomControls.java
index 451bc92..a75b657 100644
--- a/src/com/android/camera/ui/FilmstripBottomControls.java
+++ b/src/com/android/camera/ui/FilmstripBottomControls.java
@@ -30,7 +30,7 @@
  * sphere image and creating a tiny planet from a photo sphere image.
  */
 public class FilmstripBottomControls extends RelativeLayout
-    implements CameraActivity.OnActionBarVisibilityListener {
+        implements CameraActivity.OnActionBarVisibilityListener {
 
     /**
      * Classes implementing this interface can listen for events on the bottom
@@ -38,9 +38,10 @@
      */
     public static interface BottomControlsListener {
         /**
-         * Called when the user pressed the "view photosphere" button.
+         * Called when the user pressed the "view" button to e.g. view a photo
+         * sphere or RGBZ image.
          */
-        public void onViewPhotoSphere();
+        public void onView();
 
         /**
          * Called when the user pressed the "edit" button.
@@ -53,9 +54,14 @@
         public void onTinyPlanet();
     }
 
+    /** Values for the view state of the button. */
+    public static final int VIEW_NONE = 0;
+    public static final int VIEW_PHOTO_SPHERE = 1;
+    public static final int VIEW_RGBZ = 2;
+
     private BottomControlsListener mListener;
     private ImageButton mEditButton;
-    private ImageButton mViewPhotoSphereButton;
+    private ImageButton mViewButton;
     private ImageButton mTinyPlanetButton;
 
     public FilmstripBottomControls(Context context, AttributeSet attrs) {
@@ -76,13 +82,13 @@
             }
         });
 
-        mViewPhotoSphereButton = (ImageButton)
-                findViewById(R.id.filmstrip_bottom_control_panorama);
-        mViewPhotoSphereButton.setOnClickListener(new OnClickListener() {
+        mViewButton = (ImageButton)
+                findViewById(R.id.filmstrip_bottom_control_view);
+        mViewButton.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View view) {
                 if (mListener != null) {
-                    mListener.onViewPhotoSphere();
+                    mListener.onView();
                 }
             }
         });
@@ -115,9 +121,18 @@
 
     /**
      * Sets the visibility of the view-photosphere button.
+     *
+     * @param one of {@link #VIEW_NONE}, {@link #VIEW_PHOTO_SPHERE},
+     *            {@link #VIEW_RGBZ}.
      */
-    public void setViewPhotoSphereButtonVisibility(boolean visible) {
-        setVisibility(mViewPhotoSphereButton, visible);
+    public void setViewButtonVisibility(int state) {
+        if (state == VIEW_NONE) {
+            setVisibility(mViewButton, false);
+            return;
+        }
+        mViewButton.setImageResource(getViewButtonResource(state));
+        setVisibility(mViewButton, true);
+
     }
 
     /**
@@ -140,6 +155,16 @@
         });
     }
 
+    private int getViewButtonResource(int state) {
+        switch (state) {
+            case VIEW_RGBZ:
+                return R.drawable.ic_view_rgbz;
+            case VIEW_PHOTO_SPHERE:
+            default:
+                return R.drawable.ic_view_photosphere;
+        }
+    }
+
     @Override
     public void onActionBarVisibilityChanged(boolean isVisible) {
         // TODO: Fade in and out
diff --git a/src/com/android/camera/ui/FilmstripView.java b/src/com/android/camera/ui/FilmstripView.java
index 29979ae..ad8f16d 100644
--- a/src/com/android/camera/ui/FilmstripView.java
+++ b/src/com/android/camera/ui/FilmstripView.java
@@ -40,7 +40,7 @@
 import com.android.camera.filmstrip.FilmstripController;
 import com.android.camera.filmstrip.FilmstripDataAdapter;
 import com.android.camera.filmstrip.FilmstripImageData;
-import com.android.camera.filmstrip.FilmstripImageData.PanoramaSupportCallback;
+import com.android.camera.filmstrip.FilmstripImageData.AuxInfoSupportCallback;
 import com.android.camera.filmstrip.FilmstripListener;
 import com.android.camera.ui.FilmstripBottomControls.BottomControlsListener;
 import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper;
@@ -50,7 +50,7 @@
 import java.util.Arrays;
 
 public class FilmstripView extends ViewGroup implements BottomControlsListener {
-    private static final String TAG = "CAM_FilmStripView";
+    private static final String TAG = "FilmStripView";
 
     private static final int BUFFER_SIZE = 5;
     private static final int GEOMETRY_ADJUST_TIME_MS = 400;
@@ -76,7 +76,7 @@
     private float mScale;
     private MyController mController;
     private int mCenterX = -1;
-    private ViewItem[] mViewItem = new ViewItem[BUFFER_SIZE];
+    private final ViewItem[] mViewItem = new ViewItem[BUFFER_SIZE];
 
     private FilmstripListener mListener;
     private ZoomView mZoomView = null;
@@ -106,15 +106,16 @@
         private int mDataId;
         /** The position of the left of the view in the whole filmstrip. */
         private int mLeftPosition;
-        private View mView;
-        private RectF mViewArea;
+        private final View mView;
+        private final RectF mViewArea;
 
-        private ValueAnimator mTranslationXAnimator;
+        private final ValueAnimator mTranslationXAnimator;
 
         /**
          * Constructor.
          *
-         * @param id The id of the data from {@link com.android.camera.filmstrip.FilmstripDataAdapter}.
+         * @param id The id of the data from
+         *            {@link com.android.camera.filmstrip.FilmstripDataAdapter}.
          * @param v The {@code View} representing the data.
          */
         public ViewItem(
@@ -129,12 +130,18 @@
             mTranslationXAnimator.addUpdateListener(listener);
         }
 
-        /** Returns the data id from {@link com.android.camera.filmstrip.FilmstripDataAdapter}. */
+        /**
+         * Returns the data id from
+         * {@link com.android.camera.filmstrip.FilmstripDataAdapter}.
+         */
         public int getId() {
             return mDataId;
         }
 
-        /** Sets the data id from {@link com.android.camera.filmstrip.FilmstripDataAdapter}. */
+        /**
+         * Sets the data id from
+         * {@link com.android.camera.filmstrip.FilmstripDataAdapter}.
+         */
         public void setId(int id) {
             mDataId = id;
         }
@@ -164,19 +171,20 @@
          * This position is post-layout, in addition to wherever the object's
          * layout placed it.
          *
-         * @return The horizontal position of this view relative to its left position, in pixels.
+         * @return The horizontal position of this view relative to its left
+         *         position, in pixels.
          */
         public float getTranslationX() {
             return mView.getTranslationX();
         }
 
         /**
-         * The vertical location of this view relative to its top position.
-         * This position is post-layout, in addition to wherever the object's
-         * layout placed it.
+         * The vertical location of this view relative to its top position. This
+         * position is post-layout, in addition to wherever the object's layout
+         * placed it.
          *
-         * @return The vertical position of this view relative to its top position,
-         * in pixels.
+         * @return The vertical position of this view relative to its top
+         *         position, in pixels.
          */
         public float getTranslationY() {
             return mView.getTranslationY();
@@ -257,7 +265,8 @@
         public void layoutIn(Rect drawArea, int refCenter, float scale) {
             final float translationX = (mTranslationXAnimator.isRunning() ?
                     (Float) mTranslationXAnimator.getAnimatedValue() : 0f);
-            int left = (int) (drawArea.centerX() + (mLeftPosition - refCenter + translationX) * scale);
+            int left = (int) (drawArea.centerX() + (mLeftPosition - refCenter + translationX)
+                    * scale);
             int top = (int) (drawArea.centerY() - (mView.getMeasuredHeight() / 2) * scale);
             layoutAt(left, top);
             mView.setScaleX(scale);
@@ -289,13 +298,14 @@
             mView.setTranslationY(v.getTranslationY());
             mView.setTranslationX(v.getTranslationX());
         }
+
         /**
-         * Apply a scale factor (i.e. {@param postScale}) on top of current scale at
-         * pivot point ({@param focusX}, {@param focusY}). Visually it should be the
+         * Apply a scale factor (i.e. {@code postScale}) on top of current scale at
+         * pivot point ({@code focusX}, {@code focusY}). Visually it should be the
          * same as post concatenating current view's matrix with specified scale.
          */
         void postScale(float focusX, float focusY, float postScale, int viewportWidth,
-                       int viewportHeight) {
+                int viewportHeight) {
             float transX = getTranslationX();
             float transY = getTranslationY();
             // Pivot point is top left of the view, so we need to translate
@@ -309,7 +319,7 @@
         }
 
         void updateTransform(float transX, float transY, float scaleX, float scaleY,
-                                    int viewportWidth, int viewportHeight) {
+                int viewportWidth, int viewportHeight) {
             float left = transX + mView.getLeft();
             float top = transY + mView.getTop();
             RectF r = ZoomView.adjustToFitInBounds(new RectF(left, top,
@@ -476,7 +486,8 @@
             return;
         }
 
-        int[] dim = calculateChildDimension(filmstripImageData.getWidth(), filmstripImageData.getHeight(),
+        int[] dim = calculateChildDimension(filmstripImageData.getWidth(),
+                filmstripImageData.getHeight(),
                 filmstripImageData.getOrientation(), boundWidth, boundHeight);
 
         item.getView().measure(
@@ -646,9 +657,8 @@
     }
 
     /**
-     * Check the bounds of {@code mCenterX}. Always call this function after:
-     * 1. Any changes to {@code mCenterX}. 2. Any size change of the view
-     * items.
+     * Check the bounds of {@code mCenterX}. Always call this function after: 1.
+     * Any changes to {@code mCenterX}. 2. Any size change of the view items.
      *
      * @return Whether clamp happened.
      */
@@ -663,10 +673,11 @@
                 && mDataIdOnUserScrolling <= 1) {
             // Stop at the first ViewItem.
             stopScroll = true;
-        } else if(curr.getId() == 1 && mCenterX < curr.getCenterX()
+        } else if (curr.getId() == 1 && mCenterX < curr.getCenterX()
                 && mDataIdOnUserScrolling > 1 && mController.isScrolling()) {
             stopScroll = true;
-        } if (curr.getId() == mDataAdapter.getTotalNumber() - 1
+        }
+        if (curr.getId() == mDataAdapter.getTotalNumber() - 1
                 && mCenterX > curr.getCenterX()) {
             // Stop at the end.
             stopScroll = true;
@@ -697,8 +708,8 @@
     }
 
     /**
-     * Reorders the child views to be consistent with their data ID. This
-     * method should be called after adding/removing views.
+     * Reorders the child views to be consistent with their data ID. This method
+     * should be called after adding/removing views.
      */
     private void adjustChildZOrder() {
         for (int i = BUFFER_SIZE - 1; i >= 0; i--) {
@@ -715,10 +726,10 @@
      * panorama viewer.
      */
     @Override
-    public void onViewPhotoSphere() {
+    public void onView() {
         ViewItem curr = mViewItem[mCurrentItem];
         if (curr != null) {
-            mDataAdapter.getImageData(curr.getId()).viewPhotoSphere(mPanoramaViewHelper);
+            mDataAdapter.getImageData(curr.getId()).view(mPanoramaViewHelper);
         }
     }
 
@@ -754,14 +765,14 @@
     /**
      * Updates the visibility of the bottom controls.
      *
-     * @param force update the bottom controls even if the current id
-     *              has been checked for button visibilities
+     * @param force update the bottom controls even if the current id has been
+     *            checked for button visibilities
      */
     private void updateBottomControls(boolean force) {
         if (mActivity.isSecureCamera()) {
-            // We cannot show buttons in secure camera that send out of app intents,
-            // because another app with the same name can parade as the intented
-            // Activity.
+            // We cannot show buttons in secure camera that send out of app
+            // intents, because another app with the same name can parade as
+            // the intented Activity.
             return;
         }
 
@@ -800,11 +811,12 @@
             // at a weird timing.
             return;
         }
-        // TODO: Remove this from FilmstripView as it breaks the design.
-        data.isPhotoSphere(mActivity, new PanoramaSupportCallback() {
+        // TODO: Remove this from FilmstripView as it breaks the design. We need
+        // to add this to a separate DB.
+        data.requestAuxInfo(mActivity, new AuxInfoSupportCallback() {
             @Override
-            public void panoramaInfoAvailable(final boolean isPanorama,
-                    boolean isPanorama360) {
+            public void auxInfoAvailable(final boolean isPanorama,
+                    boolean isPanorama360, boolean isRgbz) {
                 // Make sure the returned data is for the current image.
                 if (requestId == getCurrentId()) {
                     if (mListener != null) {
@@ -812,16 +824,32 @@
                         // change actually.
                         mListener.onDataFocusChanged(requestId, true);
                     }
-                    mBottomControls.setViewPhotoSphereButtonVisibility(isPanorama);
                     mBottomControls.setTinyPlanetButtonVisibility(isPanorama360);
+
+                    final int viewButtonVisibility;
+                    if (isPanorama) {
+                        viewButtonVisibility = FilmstripBottomControls.VIEW_PHOTO_SPHERE;
+                    } else if (isRgbz) {
+                        viewButtonVisibility = FilmstripBottomControls.VIEW_RGBZ;
+                    } else {
+                        viewButtonVisibility = FilmstripBottomControls.VIEW_NONE;
+                    }
+
+                    mBottomControls.post(new Runnable() {
+
+                        @Override
+                        public void run() {
+                            mBottomControls.setViewButtonVisibility(viewButtonVisibility);
+                        }
+                    });
                 }
             }
         });
     }
 
     /**
-     * Keep the current item in the center. This functions does not check if
-     * the current item is null.
+     * Keep the current item in the center. This functions does not check if the
+     * current item is null.
      */
     private void snapInCenter() {
         final ViewItem currentItem = mViewItem[mCurrentItem];
@@ -833,7 +861,7 @@
 
         int snapInTime = (int) (SNAP_IN_CENTER_TIME_MS
                 * ((float) Math.abs(mCenterX - currentViewCenter))
-                /  mDrawArea.width());
+                / mDrawArea.width());
         mController.scrollToPosition(currentViewCenter,
                 snapInTime, false);
         if (getCurrentViewType() == FilmstripImageData.VIEW_TYPE_STICKY
@@ -853,8 +881,8 @@
      * @param currItem The item ID of the current one to be translated.
      * @param drawAreaWidth The width of the current draw area.
      * @param scaleFraction A {@code float} between 0 and 1. 0 if the current
-     *                      scale is {@link FILM_STRIP_SCALE}. 1 if the
-     *                      current scale is {@link FULL_SCREEN_SCALE}.
+     *            scale is {@link FILM_STRIP_SCALE}. 1 if the current scale is
+     *            {@link FULL_SCREEN_SCALE}.
      */
     private void translateLeftViewItem(
             int currItem, int drawAreaWidth, float scaleFraction) {
@@ -874,7 +902,7 @@
         final int currCenterX = curr.getCenterX();
         final int nextCenterX = next.getCenterX();
         final int translate = (int) ((nextCenterX - drawAreaWidth
-                    - currCenterX) * scaleFraction);
+                - currCenterX) * scaleFraction);
 
         curr.layoutIn(mDrawArea, mCenterX, mScale);
         curr.getView().setAlpha(1f);
@@ -956,7 +984,8 @@
                 (mScale - FILM_STRIP_SCALE) / (FULL_SCREEN_SCALE - FILM_STRIP_SCALE));
         final int fullScreenWidth = mDrawArea.width() + mViewGap;
 
-        // Decide the position for all view items on the left and the right first.
+        // Decide the position for all view items on the left and the right
+        // first.
 
         // Left items.
         for (int itemID = mCurrentItem - 1; itemID >= 0; itemID--) {
@@ -994,7 +1023,7 @@
         // Layout the current ViewItem first.
         if (immediateRight) {
             // Just do a simple layout without any special translation or
-            // fading.  The implementation in Gallery does not push the first
+            // fading. The implementation in Gallery does not push the first
             // photo to the bottom of the camera preview. Simply place the
             // photo on the right of the preview.
             final ViewItem currItem = mViewItem[mCurrentItem];
@@ -1008,7 +1037,7 @@
                 // In full-screen and mCenterX is on the left of the center,
                 // we draw the current one to "fade down".
                 fadeAndScaleRightViewItem(mCurrentItem);
-            } else if(mCenterX > currCenterX) {
+            } else if (mCenterX > currCenterX) {
                 // In full-screen and mCenterX is on the right of the center,
                 // we draw the current one translated.
                 translateLeftViewItem(mCurrentItem, fullScreenWidth, scaleFraction);
@@ -1019,8 +1048,8 @@
             }
         } else {
             final ViewItem currItem = mViewItem[mCurrentItem];
-            // The normal filmstrip has no translation for the current item. If it has
-            // translation before, gradually set it to zero.
+            // The normal filmstrip has no translation for the current item. If
+            // it has translation before, gradually set it to zero.
             currItem.setTranslationX(
                     currItem.getScaledTranslationX(mScale) * scaleFraction,
                     mScale);
@@ -1081,7 +1110,7 @@
                 }
                 curr.setTranslationX(
                         (mViewItem[mCurrentItem].getLeftPosition() - curr.getLeftPosition())
-                        * scaleFraction, mScale);
+                                * scaleFraction, mScale);
             }
         }
 
@@ -1105,7 +1134,8 @@
         mDrawArea.bottom = b;
         mZoomView.layout(mDrawArea.left, mDrawArea.top, mDrawArea.right, mDrawArea.bottom);
         // TODO: Need a more robust solution to decide when to re-layout
-        // If in the middle of zooming, only re-layout when the layout has changed.
+        // If in the middle of zooming, only re-layout when the layout has
+        // changed.
         if (!mController.isZoomStarted() || changed) {
             resetZoomView();
             layoutViewItems(changed);
@@ -1113,10 +1143,10 @@
     }
 
     /**
-     * Clears the translation and scale that has been set on the view, cancels any loading
-     * request for image partial decoding, and hides zoom view.
-     * This is needed for when there is a layout change (e.g. when users re-enter the app,
-     * or rotate the device, etc).
+     * Clears the translation and scale that has been set on the view, cancels
+     * any loading request for image partial decoding, and hides zoom view. This
+     * is needed for when there is a layout change (e.g. when users re-enter the
+     * app, or rotate the device, etc).
      */
     private void resetZoomView() {
         if (!mController.isZoomStarted()) {
@@ -1713,13 +1743,14 @@
                         snapInCenter();
                         if (mCenterX == mViewItem[mCurrentItem].getCenterX()
                                 && getCurrentViewType() == FilmstripImageData.VIEW_TYPE_STICKY) {
-                            // Special case for the scrolling end on the camera preview.
+                            // Special case for the scrolling end on the camera
+                            // preview.
                             goToFullScreen();
                         }
                     }
                 };
 
-        private ValueAnimator.AnimatorUpdateListener mScaleAnimatorUpdateListener =
+        private final ValueAnimator.AnimatorUpdateListener mScaleAnimatorUpdateListener =
                 new ValueAnimator.AnimatorUpdateListener() {
                     @Override
                     public void onAnimationUpdate(ValueAnimator animation) {
@@ -1837,7 +1868,7 @@
                     // Make sure animation ends up having the correct scale even
                     // if it is cancelled before it finishes
                     if (mScale != endScale) {
-                        current.postScale(focusX, focusY, endScale/mScale, mDrawArea.width(),
+                        current.postScale(focusX, focusY, endScale / mScale, mDrawArea.width(),
                                 mDrawArea.height());
                         mScale = endScale;
                     }
@@ -1932,8 +1963,8 @@
             final int factor = DECELERATION_FACTOR;
             // Deceleration curve for distance:
             // S(t) = s + (e - s) * (1 - (1 - t/T) ^ factor)
-            // Need to find the ending distance (e), so that the starting velocity
-            // is the velocity of fling.
+            // Need to find the ending distance (e), so that the starting
+            // velocity is the velocity of fling.
             // Velocity is the derivative of distance
             // V(t) = (e - s) * factor * (-1) * (1 - t/T) ^ (factor - 1) * (-1/T)
             //      = (e - s) * factor * (1 - t/T) ^ (factor - 1) / T
@@ -1948,7 +1979,7 @@
             final float velocity = Math.max(Math.abs(velocityX), Math.abs(velocityY));
             // Dynamically calculate duration
             final float duration = (float) (FLING_COASTING_DURATION_S
-                    * Math.pow(velocity, (1f/ (factor - 1f))));
+                    * Math.pow(velocity, (1f / (factor - 1f))));
 
             final float translationX = current.getTranslationX();
             final float translationY = current.getTranslationY();
@@ -1975,11 +2006,12 @@
             mFlingAnimator.setInterpolator(new TimeInterpolator() {
                 @Override
                 public float getInterpolation(float input) {
-                    return (float)(1.0f - Math.pow((1.0f - input), factor));
+                    return (float) (1.0f - Math.pow((1.0f - input), factor));
                 }
             });
             mFlingAnimator.addListener(new Animator.AnimatorListener() {
                 private boolean mCancelled = false;
+
                 @Override
                 public void onAnimationStart(Animator animation) {
 
@@ -2106,8 +2138,8 @@
 
         private void setSurroundingViewsVisible(boolean visible) {
             // Hide everything on the left
-            // TODO: Need to find a better way to toggle the visibility of views around
-            //       the current view.
+            // TODO: Need to find a better way to toggle the visibility of views
+            // around the current view.
             for (int i = 0; i < mCurrentItem; i++) {
                 if (i == mCurrentItem || mViewItem[i] == null) {
                     continue;
@@ -2143,7 +2175,8 @@
                 return FULL_SCREEN_SCALE;
             }
             float imageWidth = filmstripImageData.getWidth();
-            if (filmstripImageData.getOrientation() == 90 || filmstripImageData.getOrientation() == 270) {
+            if (filmstripImageData.getOrientation() == 90
+                    || filmstripImageData.getOrientation() == 270) {
                 imageWidth = filmstripImageData.getHeight();
             }
             float scale = imageWidth / curr.getWidth();
@@ -2165,7 +2198,7 @@
                 return;
             }
             FilmstripImageData filmstripImageData = mDataAdapter.getImageData(curr.getId());
-            if(!filmstripImageData.isUIActionSupported(FilmstripImageData.ACTION_ZOOM)) {
+            if (!filmstripImageData.isUIActionSupported(FilmstripImageData.ACTION_ZOOM)) {
                 return;
             }
             Uri uri = getCurrentContentUri();
@@ -2205,16 +2238,17 @@
     private static class MyScroller {
         public interface Listener {
             public void onScrollUpdate(int currX, int currY);
+
             public void onScrollEnd();
         }
 
-        private Handler mHandler;
-        private Listener mListener;
+        private final Handler mHandler;
+        private final Listener mListener;
 
         private final Scroller mScroller;
 
         private final ValueAnimator mXScrollAnimator;
-        private Runnable mScrollChecker = new Runnable() {
+        private final Runnable mScrollChecker = new Runnable() {
             @Override
             public void run() {
                 boolean newPosition = mScroller.computeScrollOffset();
@@ -2228,7 +2262,7 @@
             }
         };
 
-        private ValueAnimator.AnimatorUpdateListener mXScrollAnimatorUpdateListener =
+        private final ValueAnimator.AnimatorUpdateListener mXScrollAnimatorUpdateListener =
                 new ValueAnimator.AnimatorUpdateListener() {
                     @Override
                     public void onAnimationUpdate(ValueAnimator animation) {
@@ -2236,7 +2270,7 @@
                     }
                 };
 
-        private Animator.AnimatorListener mXScrollAnimatorListener =
+        private final Animator.AnimatorListener mXScrollAnimatorListener =
                 new Animator.AnimatorListener() {
                     @Override
                     public void onAnimationCancel(Animator animation) {
@@ -2259,7 +2293,6 @@
                     }
                 };
 
-
         public MyScroller(Context ctx, Handler handler, Listener listener,
                 TimeInterpolator interpolator) {
             mHandler = handler;
@@ -2426,11 +2459,12 @@
                     // No next photo.
                     snapInCenter();
                 }
-            } if (mCenterX == currItem.getCenterX() && currId == 0
+            }
+            if (mCenterX == currItem.getCenterX() && currId == 0
                     && getCurrentViewType() == FilmstripImageData.VIEW_TYPE_STICKY) {
                 mController.goToFullScreen();
             } else {
-                if (mDataIdOnUserScrolling  == 0 && currId != 0) {
+                if (mDataIdOnUserScrolling == 0 && currId != 0) {
                     // Special case to go to filmstrip when the user scroll away
                     // from the camera preview and the current one is not the
                     // preview anymore.
@@ -2528,14 +2562,15 @@
                 return true;
             }
 
-            // In full-screen, fling of a velocity above a threshold should go to
-            // the next/prev photos
+            // In full-screen, fling of a velocity above a threshold should go
+            // to the next/prev photos
             if (mScale == FULL_SCREEN_SCALE) {
                 int currItemCenterX = currItem.getCenterX();
 
-                if (velocityX > 0) {  // left
+                if (velocityX > 0) { // left
                     if (mCenterX > currItemCenterX) {
-                        // The visually previous item is actually the current item.
+                        // The visually previous item is actually the current
+                        // item.
                         mController.scrollToPosition(
                                 currItemCenterX, GEOMETRY_ADJUST_TIME_MS, true);
                         return true;
@@ -2546,10 +2581,11 @@
                     }
                     mController.scrollToPosition(
                             prevItem.getCenterX(), GEOMETRY_ADJUST_TIME_MS, true);
-                } else {  // right
+                } else { // right
                     if (mController.stopScrolling(false)) {
                         if (mCenterX < currItemCenterX) {
-                            // The visually next item is actually the current item.
+                            // The visually next item is actually the current
+                            // item.
                             mController.scrollToPosition(
                                     currItemCenterX, GEOMETRY_ADJUST_TIME_MS, true);
                             return true;
@@ -2596,21 +2632,23 @@
             mScaleTrend = mScaleTrend * 0.3f + scale * 0.7f;
             float newScale = mScale * scale;
             if (mScale < FULL_SCREEN_SCALE && newScale < FULL_SCREEN_SCALE) {
-                // Scaled view is smaller than or equal to screen size both before
-                // and after scaling
+                // Scaled view is smaller than or equal to screen size both
+                // before and after scaling
                 mScale = newScale;
                 if (mScale <= FILM_STRIP_SCALE) {
                     mScale = FILM_STRIP_SCALE;
                 }
                 invalidate();
             } else if (mScale < FULL_SCREEN_SCALE && newScale >= FULL_SCREEN_SCALE) {
-                // Going from smaller than screen size to bigger than or equal to screen size
+                // Going from smaller than screen size to bigger than or equal
+                // to screen size
                 mScale = FULL_SCREEN_SCALE;
                 mController.enterFullScreen();
                 invalidate();
                 mController.setSurroundingViewsVisible(false);
             } else if (mScale >= FULL_SCREEN_SCALE && newScale < FULL_SCREEN_SCALE) {
-                // Going from bigger than or equal to screen size to smaller than screen size
+                // Going from bigger than or equal to screen size to smaller
+                // than screen size
                 mScale = newScale;
                 mController.leaveFullScreen();
                 invalidate();