Merge "Initial interface for the burst module." into ub-camera-haleakala
diff --git a/project.properties b/project.properties
index 2f095e1..6e18427 100644
--- a/project.properties
+++ b/project.properties
@@ -11,4 +11,4 @@
 #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
 
 # Project target.
-target=android-L
+target=android-21
diff --git a/res/layout-land/aspect_ratio_dialog_content.xml b/res/layout-land/aspect_ratio_dialog_content.xml
index c183f1d..7aca76f 100644
--- a/res/layout-land/aspect_ratio_dialog_content.xml
+++ b/res/layout-land/aspect_ratio_dialog_content.xml
@@ -38,8 +38,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="@string/photo_size_selection_title"
-        android:textSize="22sp"
-        android:textColor="@color/dialog_text_color"
+        style="@style/fullscreen_dialog_title"
         android:layout_margin="28dp" />
     <TextView
         android:layout_width="match_parent"
diff --git a/res/layout-land/location_dialog_content.xml b/res/layout-land/location_dialog_content.xml
index 2577f69..e3dab88 100644
--- a/res/layout-land/location_dialog_content.xml
+++ b/res/layout-land/location_dialog_content.xml
@@ -30,8 +30,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="@string/remember_location_title"
-            android:textSize="22sp"
-            android:textColor="@color/dialog_text_color"
+            style="@style/fullscreen_dialog_title"
             android:layout_marginLeft="28dp"
             android:layout_marginRight="28dp"
             android:layout_marginBottom="28dp" />
diff --git a/res/layout-port/aspect_ratio_dialog_content.xml b/res/layout-port/aspect_ratio_dialog_content.xml
index 087909e..4086889 100644
--- a/res/layout-port/aspect_ratio_dialog_content.xml
+++ b/res/layout-port/aspect_ratio_dialog_content.xml
@@ -27,8 +27,7 @@
         android:layout_margin="28dp"
         android:gravity="center"
         android:text="@string/photo_size_selection_title"
-        android:textSize="22sp"
-        android:textColor="@color/dialog_text_color" />
+        style="@style/fullscreen_dialog_title" />
     <include
         layout="@layout/aspect_ratio_selector"
         android:layout_width="match_parent"
diff --git a/res/layout-port/location_dialog_content.xml b/res/layout-port/location_dialog_content.xml
index 0e1e453..fee5d64 100644
--- a/res/layout-port/location_dialog_content.xml
+++ b/res/layout-port/location_dialog_content.xml
@@ -22,8 +22,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="@string/remember_location_title"
-        android:textSize="22sp"
-        android:textColor="@color/dialog_text_color"
+        style="@style/fullscreen_dialog_title"
         android:layout_margin="28dp" />
     <ImageView
         android:layout_width="wrap_content"
diff --git a/res/values/styles.xml b/res/values/styles.xml
index ad38594..7c0f7ca 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -216,13 +216,18 @@
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">match_parent</item>
     </style>
+    <style name="fullscreen_dialog_title">
+        <item name="android:textSize">24sp</item>
+        <item name="android:fontFamily">sans-serif-regular</item>
+        <item name="android:textColor">@color/dialog_text_color</item>
+    </style>
     <style name="BlueButton">
         <item name="android:layout_width">144dp</item>
         <item name="android:layout_height">48dp</item>
         <item name="android:background">@drawable/button_cling</item>
-        <item name="android:fontFamily">sans-serif-regular</item>
-        <item name="android:textColor">@color/blue_button_text_color</item>
         <item name="android:textSize">14sp</item>
+        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:textColor">@color/blue_button_text_color</item>
         <item name="android:padding">12dp</item>
     </style>
 </resources>
diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java
index 9b20ee9..a6b7ec6 100644
--- a/src/com/android/camera/CaptureModule.java
+++ b/src/com/android/camera/CaptureModule.java
@@ -19,7 +19,6 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
-import android.graphics.Bitmap;
 import android.graphics.Matrix;
 import android.graphics.RectF;
 import android.graphics.SurfaceTexture;
@@ -460,8 +459,9 @@
 
     @Override
     public void onRemoteShutterPress() {
+        Log.d(TAG, "onRemoteShutterPress");
         // TODO: Check whether shutter is enabled.
-        onShutterButtonClick();
+        takePictureNow();
     }
 
     @Override
@@ -546,6 +546,7 @@
     @Override
     public void pause() {
         mPaused = true;
+        getServices().getRemoteShutterListener().onModuleExit();
         cancelCountDown();
         closeCamera();
         resetTextureBufferSize();
@@ -848,8 +849,8 @@
     }
 
     @Override
-    public void onThumbnailResult(Bitmap bitmap) {
-        // TODO
+    public void onThumbnailResult(byte[] jpegData) {
+        getServices().getRemoteShutterListener().onPictureTaken(jpegData);
     }
 
     @Override
diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java
index f5d697a..b4b5ee7 100644
--- a/src/com/android/camera/PhotoModule.java
+++ b/src/com/android/camera/PhotoModule.java
@@ -1953,10 +1953,15 @@
             }
             mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
         }
-        setCameraParameters(UPDATE_PARAM_ALL);
 
+        // Nexus 4 must have picture size set to > 640x480 before other
+        // parameters are set in setCameraParameters, b/18227551. This call to
+        // updateParametersPictureSize should occur before setCameraParameters
+        // to address the issue.
         updateParametersPictureSize();
 
+        setCameraParameters(UPDATE_PARAM_ALL);
+
         mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
 
         Log.i(TAG, "startPreview");
diff --git a/src/com/android/camera/data/CameraDataAdapter.java b/src/com/android/camera/data/CameraDataAdapter.java
index 07100ef..7fd2d09 100644
--- a/src/com/android/camera/data/CameraDataAdapter.java
+++ b/src/com/android/camera/data/CameraDataAdapter.java
@@ -180,7 +180,7 @@
         int pos = findDataByContentUri(uri);
         if (pos != -1) {
             // a duplicate one, just do a substitute.
-            Log.v(TAG, "found duplicate data");
+            Log.v(TAG, "found duplicate data: " + uri);
             updateData(pos, newData);
             return false;
         } else {
@@ -339,6 +339,7 @@
         @Override
         protected List<LocalData> doInBackground(ContentResolver... contentResolvers) {
             if (mMinPhotoId != LocalMediaData.QUERY_ALL_MEDIA_ID) {
+                Log.v(TAG, "updating media metadata with photos newer than id: " + mMinPhotoId);
                 final ContentResolver cr = contentResolvers[0];
                 return LocalMediaData.PhotoData.query(cr, LocalMediaData.PhotoData.CONTENT_URI,
                         mMinPhotoId);
@@ -348,11 +349,19 @@
 
         @Override
         protected void onPostExecute(List<LocalData> newPhotoData) {
+            if (newPhotoData == null) {
+                Log.w(TAG, "null data returned from new photos query");
+                return;
+            }
+            Log.v(TAG, "new photos query return num items: " + newPhotoData.size());
             if (!newPhotoData.isEmpty()) {
                 LocalData newestPhoto = newPhotoData.get(0);
                 // We may overlap with another load task or a query task, in which case we want
                 // to be sure we never decrement the oldest seen id.
-                mLastPhotoId = Math.max(mLastPhotoId, newestPhoto.getContentId());
+                long newLastPhotoId = newestPhoto.getContentId();
+                Log.v(TAG, "updating last photo id (old:new) " +
+                        mLastPhotoId + ":" + newLastPhotoId);
+                mLastPhotoId = Math.max(mLastPhotoId, newLastPhotoId);
             }
             // We may add data that is already present, but if we do, it will be deduped in addData.
             // addData does not dedupe session items, so we ignore them here
@@ -406,12 +415,27 @@
 
             long lastPhotoId = LocalMediaData.QUERY_ALL_MEDIA_ID;
             if (!photoData.isEmpty()) {
+                // This relies on {@link LocalMediaData.QUERY_ORDER} returning
+                // items sorted descending by ID, as such we can just pull the
+                // ID from the first item in the result to establish the last
+                // (max) photo ID.
                 lastPhotoId = photoData.get(0).getContentId();
             }
 
-            l.addAll(photoData);
-            l.addAll(videoData);
+            if (photoData != null) {
+                Log.v(TAG, "retrieved photo metadata, number of items: " + photoData.size());
+                l.addAll(photoData);
+            }
+            if (videoData != null) {
+                Log.v(TAG, "retrieved video metadata, number of items: " + videoData.size());
+                l.addAll(videoData);
+            }
+            Log.v(TAG, "sorting video/photo metadata");
+            // Photos should be sorted within photo/video by ID, which in most
+            // cases should correlate well to the date taken/modified. This sort
+            // operation makes all photos/videos sorted by date in one list.
             l.sort(new LocalData.NewestFirstComparator());
+            Log.v(TAG, "sorted video/photo metadata");
 
             // Load enough metadata so it's already loaded when we open the filmstrip.
             for (int i = 0; i < MAX_METADATA && i < l.size(); i++) {
diff --git a/src/com/android/camera/data/LocalMediaData.java b/src/com/android/camera/data/LocalMediaData.java
index 950695f..9956108 100644
--- a/src/com/android/camera/data/LocalMediaData.java
+++ b/src/com/android/camera/data/LocalMediaData.java
@@ -328,8 +328,10 @@
 
         static final Uri CONTENT_URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
 
-        private static final String QUERY_ORDER = MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC, "
-                + MediaStore.Images.ImageColumns._ID + " DESC";
+        // Sort all data by ID. This must be aligned with
+        // {@link CameraDataAdapter.QueryTask} which relies on the highest ID
+        // being first in any data returned.
+        private static final String QUERY_ORDER = MediaStore.Images.ImageColumns._ID + " DESC";
         /**
          * These values should be kept in sync with column IDs (COL_*) above.
          */
diff --git a/src/com/android/camera/one/OneCamera.java b/src/com/android/camera/one/OneCamera.java
index fcf0631..743be37 100644
--- a/src/com/android/camera/one/OneCamera.java
+++ b/src/com/android/camera/one/OneCamera.java
@@ -17,7 +17,6 @@
 package com.android.camera.one;
 
 import android.content.Context;
-import android.graphics.Bitmap;
 import android.location.Location;
 import android.net.Uri;
 import android.view.Surface;
@@ -159,7 +158,7 @@
          * Called when a thumbnail image is provided before the final image is
          * finished.
          */
-        public void onThumbnailResult(Bitmap bitmap);
+        public void onThumbnailResult(byte[] jpegData);
 
         /**
          * Called when the final picture is done taking
diff --git a/src/com/android/camera/settings/SettingsUtil.java b/src/com/android/camera/settings/SettingsUtil.java
index d0f9b09..acf8921 100644
--- a/src/com/android/camera/settings/SettingsUtil.java
+++ b/src/com/android/camera/settings/SettingsUtil.java
@@ -24,6 +24,7 @@
 import android.util.SparseArray;
 
 import com.android.camera.debug.Log;
+import com.android.camera.util.ApiHelper;
 import com.android.camera.util.Callback;
 import com.android.camera2.R;
 import com.android.ex.camera2.portability.CameraDeviceInfo;
@@ -353,7 +354,8 @@
      */
     private static int getNextSupportedVideoQualityIndex(int cameraId, int start) {
         for (int i = start + 1; i < sVideoQualities.length; ++i) {
-            if (CamcorderProfile.hasProfile(cameraId, sVideoQualities[i])) {
+            if (isVideoQualitySupported(sVideoQualities[i])
+                    && CamcorderProfile.hasProfile(cameraId, sVideoQualities[i])) {
                 // We found a new supported quality.
                 return i;
             }
@@ -371,6 +373,19 @@
     }
 
     /**
+     * @return Whether the given {@link CamcorderProfile} is supported on the
+     *         current device/OS version.
+     */
+    private static boolean isVideoQualitySupported(int videoQuality) {
+        // 4k is only supported on L or higher but some devices falsely report
+        // to have support for it on K, see b/18172081.
+        if (!ApiHelper.isLOrHigher() && videoQuality == CamcorderProfile.QUALITY_2160P) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
      * Returns the index of the size within the given list that is closest to
      * the given target pixel count.
      */
diff --git a/src/com/android/camera/ui/PreviewOverlay.java b/src/com/android/camera/ui/PreviewOverlay.java
index 3ccc758..6ff8549 100644
--- a/src/com/android/camera/ui/PreviewOverlay.java
+++ b/src/com/android/camera/ui/PreviewOverlay.java
@@ -259,9 +259,8 @@
         }
 
         public void layout(int l, int t, int r, int b) {
-            // TODO: Needs to be centered in preview TextureView
-            mCenterX = (r - l) / 2;
-            mCenterY = (b - t) / 2;
+            mCenterX = (r + l) / 2;
+            mCenterY = (b + t) / 2;
             // UI will extend from 20% to 80% of maximum inset circle.
             float insetCircleDiameter = Math.min(getWidth(), getHeight());
             mOuterRadius = insetCircleDiameter * 0.5f * ZOOM_UI_SIZE;
diff --git a/src/com/android/camera/util/ApiHelper.java b/src/com/android/camera/util/ApiHelper.java
index 8e25464..7f6f59c 100644
--- a/src/com/android/camera/util/ApiHelper.java
+++ b/src/com/android/camera/util/ApiHelper.java
@@ -60,6 +60,10 @@
             && "hammerhead".equalsIgnoreCase(Build.DEVICE);
     public static final boolean IS_NEXUS_6 = "motorola".equalsIgnoreCase(Build.MANUFACTURER)
             && "shamu".equalsIgnoreCase(Build.DEVICE);
+    public static final boolean IS_NEXUS_9 = "htc".equalsIgnoreCase(Build.MANUFACTURER)
+            && ("flounder".equalsIgnoreCase(Build.DEVICE)
+                 || "flounder_lte".equalsIgnoreCase(Build.DEVICE));
+    public static final boolean IS_HTC = "htc".equalsIgnoreCase(Build.MANUFACTURER);
 
     public static final boolean HAS_CAMERA_2_API = isLOrHigher();
 
diff --git a/src/com/android/camera/util/CameraUtil.java b/src/com/android/camera/util/CameraUtil.java
index 658e73b..d6237a9 100644
--- a/src/com/android/camera/util/CameraUtil.java
+++ b/src/com/android/camera/util/CameraUtil.java
@@ -40,7 +40,6 @@
 import android.os.ParcelFileDescriptor;
 import android.telephony.TelephonyManager;
 import android.util.DisplayMetrics;
-import android.util.FloatMath;
 import android.util.TypedValue;
 import android.view.Display;
 import android.view.OrientationEventListener;
@@ -514,7 +513,15 @@
     public static int getOptimalPreviewSizeIndex(Context context,
             List<Size> sizes, double targetRatio) {
         // Use a very small tolerance because we want an exact match.
-        final double ASPECT_TOLERANCE = 0.01;
+        final double ASPECT_TOLERANCE;
+        // HTC 4:3 ratios is over .01 from true 4:3, targeted fix for those
+        // devices here, see b/18241645
+        if (ApiHelper.IS_HTC && targetRatio > 1.3433 && targetRatio < 1.35) {
+            Log.w(TAG, "4:3 ratio out of normal tolerance, increasing tolerance to 0.02");
+            ASPECT_TOLERANCE = 0.02;
+        } else {
+            ASPECT_TOLERANCE = 0.01;
+        }
         if (sizes == null) {
             return -1;
         }
@@ -553,7 +560,7 @@
         // Cannot find the one match the aspect ratio. This should not happen.
         // Ignore the requirement.
         if (optimalSizeIndex == -1) {
-            Log.w(TAG, "No preview size match the aspect ratio");
+            Log.w(TAG, "No preview size match the aspect ratio. available sizes: " + sizes);
             minDiff = Double.MAX_VALUE;
             for (int i = 0; i < sizes.size(); i++) {
                 Size size = sizes.get(i);
@@ -926,7 +933,7 @@
         float sigma = len;
         float sum = 0;
         for (int i = 0; i <= mid; i++) {
-            float ex = FloatMath.exp(-(i - mid) * (i - mid) / (mid * mid))
+            float ex = (float) Math.exp(-(i - mid) * (i - mid) / (mid * mid))
                     / (2 * sigma * sigma);
             int symmetricIndex = len - 1 - i;
             mask[i] = ex;
diff --git a/src/com/android/camera/widget/FilmstripView.java b/src/com/android/camera/widget/FilmstripView.java
index 4760fb7..95888fe 100644
--- a/src/com/android/camera/widget/FilmstripView.java
+++ b/src/com/android/camera/widget/FilmstripView.java
@@ -1256,18 +1256,22 @@
                 // It's in full-screen mode.
                 fadeAndScaleRightViewItem(itemID);
             } else {
-                if (curr.getVisibility() == INVISIBLE) {
-                    curr.setVisibility(VISIBLE);
-                }
+                boolean setToVisible = (curr.getVisibility() == INVISIBLE);
+
                 if (itemID == mCurrentItem + 1) {
                     curr.setAlpha(1f - scaleFraction);
                 } else {
                     if (scaleFraction == 0f) {
                         curr.setAlpha(1f);
                     } else {
-                        curr.setVisibility(INVISIBLE);
+                        setToVisible = false;
                     }
                 }
+
+                if (setToVisible) {
+                    curr.setVisibility(VISIBLE);
+                }
+
                 curr.setTranslationX(
                         (mViewItem[mCurrentItem].getLeftPosition() - curr.getLeftPosition()) *
                                 scaleFraction);