Merge "Make preview size smaller so that low-res images come in faster." into gb-ub-photos-carlsbad
diff --git a/res/layout/camera.xml b/res/layout/camera.xml
new file mode 100644
index 0000000..b4ec436
--- /dev/null
+++ b/res/layout/camera.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+    <!-- NewCameraRootView needs to be in a FrameLayout to set margins
+         in the layout parameters. -->
+    <com.android.camera.ui.NewCameraRootView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/camera_app_root"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" >
+    </com.android.camera.ui.NewCameraRootView>
+</FrameLayout>
diff --git a/res/layout/camera_filmstrip.xml b/res/layout/camera_filmstrip.xml
new file mode 100644
index 0000000..26fc3f0
--- /dev/null
+++ b/res/layout/camera_filmstrip.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<com.android.camera.ui.FilmStripView
+xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/filmstrip_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />
diff --git a/res/layout/new_photo_module.xml b/res/layout/new_photo_module.xml
new file mode 100644
index 0000000..70a7579
--- /dev/null
+++ b/res/layout/new_photo_module.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This layout is shared by phone and tablet in both landscape and portrait
+ orientation. The purpose of having this layout is to eventually not manually
+ recreate views when the orientation changes, by migrating the views that do not
+ need to be recreated in onConfigurationChanged from old photo_module to this
+ layout. -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_gravity="center">
+    <TextureView
+        android:id="@+id/preview_content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+    <ViewStub android:id="@+id/face_view_stub"
+        android:inflatedId="@+id/face_view"
+        android:layout="@layout/face_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone"/>
+    <com.android.camera.ui.RenderOverlay
+        android:id="@+id/render_overlay"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+    <include layout="@layout/camera_controls"
+        android:layout_gravity="center"
+        style="@style/CameraControls"/>
+</merge>
\ No newline at end of file
diff --git a/res/layout/new_video_module.xml b/res/layout/new_video_module.xml
new file mode 100644
index 0000000..9eb3e84
--- /dev/null
+++ b/res/layout/new_video_module.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<!-- This layout is shared by phone and tablet in landscape orientation. -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent">
+    <TextureView
+        android:id="@+id/preview_content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+    <FrameLayout android:id="@+id/preview_border"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="gone"
+            android:background="@drawable/ic_snapshot_border" />
+    <com.android.camera.ui.RenderOverlay
+        android:id="@+id/render_overlay"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+    <com.android.camera.ui.RotateLayout android:id="@+id/recording_time_rect"
+            style="@style/ViewfinderLabelLayout">
+        <include layout="@layout/viewfinder_labels_video" android:id="@+id/labels" />
+    </com.android.camera.ui.RotateLayout>
+    <ImageView android:id="@+id/review_image"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"
+            android:visibility="gone"
+            android:background="@android:color/black"/>
+    <ImageView
+            android:id="@+id/btn_play"
+            style="@style/ReviewControlIcon"
+            android:layout_centerInParent="true"
+            android:src="@drawable/ic_gallery_play_big"
+            android:visibility="gone"
+            android:onClick="onReviewPlayClicked"/>
+
+    <include layout="@layout/camera_controls"
+        android:layout_gravity="center"
+        style="@style/CameraControls"/>
+</merge>
diff --git a/res/values/filtershow_color.xml b/res/values/filtershow_color.xml
index f6bf7fb..927bfa2 100644
--- a/res/values/filtershow_color.xml
+++ b/res/values/filtershow_color.xml
@@ -37,4 +37,5 @@
     <color name="filtershow_stateview_selected_text">#000000</color>
     <color name="filtershow_categoryview_background">#1a1a1a</color>
     <color name="filtershow_categoryview_text">#a7a7a7</color>
+    <color name="filtershow_category_selection">#ffffffff</color>
 </resources>
\ No newline at end of file
diff --git a/src/com/android/camera/NewPreviewGestures.java b/src/com/android/camera/NewPreviewGestures.java
new file mode 100644
index 0000000..2718e55
--- /dev/null
+++ b/src/com/android/camera/NewPreviewGestures.java
@@ -0,0 +1,358 @@
+package com.android.camera;
+
+/*
+ * 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.
+ */
+
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+import com.android.camera.PreviewGestures.SwipeListener;
+import com.android.camera.ui.PieRenderer;
+import com.android.camera.ui.RenderOverlay;
+import com.android.camera.ui.ZoomRenderer;
+import com.android.gallery3d.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class NewPreviewGestures
+        implements ScaleGestureDetector.OnScaleGestureListener {
+
+    private static final String TAG = "CAM_gestures";
+
+    private static final long TIMEOUT_PIE = 200;
+    private static final int MSG_PIE = 1;
+    private static final int MODE_NONE = 0;
+    private static final int MODE_PIE = 1;
+    private static final int MODE_ZOOM = 2;
+    private static final int MODE_MODULE = 3;
+    private static final int MODE_ALL = 4;
+    private static final int MODE_SWIPE = 5;
+
+    public static final int DIR_UP = 0;
+    public static final int DIR_DOWN = 1;
+    public static final int DIR_LEFT = 2;
+    public static final int DIR_RIGHT = 3;
+
+    private NewCameraActivity mActivity;
+    private SingleTapListener mTapListener;
+    private CancelEventListener mCancelEventListener;
+    private RenderOverlay mOverlay;
+    private PieRenderer mPie;
+    private ZoomRenderer mZoom;
+    private MotionEvent mDown;
+    private MotionEvent mCurrent;
+    private ScaleGestureDetector mScale;
+    private List<View> mReceivers;
+    private int mMode;
+    private int mSlop;
+    private int mTapTimeout;
+    private boolean mEnabled;
+    private boolean mZoomOnly;
+    private int mOrientation;
+    private int[] mLocation;
+    private SwipeListener mSwipeListener;
+
+    private Handler mHandler = new Handler() {
+        public void handleMessage(Message msg) {
+            if (msg.what == MSG_PIE) {
+                mMode = MODE_PIE;
+                openPie();
+                cancelActivityTouchHandling(mDown);
+            }
+        }
+    };
+
+    public interface SingleTapListener {
+        public void onSingleTapUp(View v, int x, int y);
+    }
+
+    public interface CancelEventListener {
+        public void onTouchEventCancelled(MotionEvent cancelEvent);
+    }
+
+    interface SwipeListener {
+        public void onSwipe(int direction);
+    }
+
+    public NewPreviewGestures(NewCameraActivity ctx, SingleTapListener tapListener,
+            ZoomRenderer zoom, PieRenderer pie, SwipeListener swipe) {
+        mActivity = ctx;
+        mTapListener = tapListener;
+        mPie = pie;
+        mZoom = zoom;
+        mMode = MODE_ALL;
+        mScale = new ScaleGestureDetector(ctx, this);
+        mSlop = (int) ctx.getResources().getDimension(R.dimen.pie_touch_slop);
+        mTapTimeout = ViewConfiguration.getTapTimeout();
+        mEnabled = true;
+        mLocation = new int[2];
+        mSwipeListener = swipe;
+    }
+
+    public void setCancelEventListener(CancelEventListener listener) {
+        mCancelEventListener = listener;
+    }
+
+    public void setRenderOverlay(RenderOverlay overlay) {
+        mOverlay = overlay;
+    }
+
+    public void setOrientation(int orientation) {
+        mOrientation = orientation;
+    }
+
+    public void setEnabled(boolean enabled) {
+        mEnabled = enabled;
+        if (!enabled) {
+            cancelPie();
+        }
+    }
+
+    public void setZoomOnly(boolean zoom) {
+        mZoomOnly = zoom;
+    }
+
+    public void addTouchReceiver(View v) {
+        if (mReceivers == null) {
+            mReceivers = new ArrayList<View>();
+        }
+        mReceivers.add(v);
+    }
+
+    public void clearTouchReceivers() {
+        if (mReceivers != null) {
+            mReceivers.clear();
+        }
+    }
+
+    public boolean dispatchTouch(MotionEvent m) {
+        if (!mEnabled) {
+            return false;
+        }
+        mCurrent = m;
+        if (MotionEvent.ACTION_DOWN == m.getActionMasked()) {
+            if (checkReceivers(m)) {
+                mMode = MODE_MODULE;
+                return false;
+            } else {
+                mMode = MODE_ALL;
+                mDown = MotionEvent.obtain(m);
+                if (mPie != null && mPie.showsItems()) {
+                    mMode = MODE_PIE;
+                    return sendToPie(m);
+                }
+                if (mPie != null && !mZoomOnly) {
+                    mHandler.sendEmptyMessageDelayed(MSG_PIE, TIMEOUT_PIE);
+                }
+                if (mZoom != null) {
+                    mScale.onTouchEvent(m);
+                }
+                // make sure this is ok
+                return false;
+            }
+        } else if (mMode == MODE_NONE) {
+            return false;
+        } else if (mMode == MODE_SWIPE) {
+            if (MotionEvent.ACTION_UP == m.getActionMasked()) {
+                mSwipeListener.onSwipe(getSwipeDirection(m));
+            }
+            return true;
+        } else if (mMode == MODE_PIE) {
+            if (MotionEvent.ACTION_POINTER_DOWN == m.getActionMasked()) {
+                sendToPie(makeCancelEvent(m));
+                if (mZoom != null) {
+                    onScaleBegin(mScale);
+                }
+            } else {
+                return sendToPie(m);
+            }
+            return true;
+        } else if (mMode == MODE_ZOOM) {
+            mScale.onTouchEvent(m);
+            if (!mScale.isInProgress() && MotionEvent.ACTION_POINTER_UP == m.getActionMasked()) {
+                mMode = MODE_NONE;
+                onScaleEnd(mScale);
+            }
+            return true;
+        } else if (mMode == MODE_MODULE) {
+            return false;
+        } else {
+            // didn't receive down event previously;
+            // assume module wasn't initialzed and ignore this event.
+            if (mDown == null) {
+                return true;
+            }
+            if (MotionEvent.ACTION_POINTER_DOWN == m.getActionMasked()) {
+                if (!mZoomOnly) {
+                    cancelPie();
+                    sendToPie(makeCancelEvent(m));
+                }
+                if (mZoom != null) {
+                    mScale.onTouchEvent(m);
+                    onScaleBegin(mScale);
+                }
+            } else if ((mMode == MODE_ZOOM) && !mScale.isInProgress()
+                    && MotionEvent.ACTION_POINTER_UP == m.getActionMasked()) {
+                // user initiated and stopped zoom gesture without zooming
+                mScale.onTouchEvent(m);
+                onScaleEnd(mScale);
+            }
+            // not zoom or pie mode and no timeout yet
+            if (mZoom != null) {
+                boolean res = mScale.onTouchEvent(m);
+                if (mScale.isInProgress()) {
+                    cancelPie();
+                    cancelActivityTouchHandling(m);
+                    return res;
+                }
+            }
+            if (MotionEvent.ACTION_UP == m.getActionMasked()) {
+                cancelPie();
+                cancelActivityTouchHandling(m);
+                // must have been tap
+                if (m.getEventTime() - mDown.getEventTime() < mTapTimeout) {
+                    mTapListener.onSingleTapUp(null,
+                            (int) mDown.getX() - mOverlay.getWindowPositionX(),
+                            (int) mDown.getY() - mOverlay.getWindowPositionY());
+                    return true;
+                } else {
+                    return false;
+                }
+            } else if (MotionEvent.ACTION_MOVE == m.getActionMasked()) {
+                if ((Math.abs(m.getX() - mDown.getX()) > mSlop)
+                        || Math.abs(m.getY() - mDown.getY()) > mSlop) {
+                    // moved too far and no timeout yet, no focus or pie
+                    cancelPie();
+                    int dir = getSwipeDirection(m);
+                    if (dir == DIR_LEFT) {
+                        mMode = MODE_MODULE;
+                        return false;
+                    } else {
+                        cancelActivityTouchHandling(m);
+                        mMode = MODE_NONE;
+                    }
+                }
+            }
+            return false;
+        }
+    }
+
+    private boolean checkReceivers(MotionEvent m) {
+        if (mReceivers != null) {
+            for (View receiver : mReceivers) {
+                if (isInside(m, receiver)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    // left tests for finger moving right to left
+    private int getSwipeDirection(MotionEvent m) {
+        float dx = 0;
+        float dy = 0;
+        switch (mOrientation) {
+        case 0:
+            dx = m.getX() - mDown.getX();
+            dy = m.getY() - mDown.getY();
+            break;
+        case 90:
+            dx = - (m.getY() - mDown.getY());
+            dy = m.getX() - mDown.getX();
+            break;
+        case 180:
+            dx = -(m.getX() - mDown.getX());
+            dy = m.getY() - mDown.getY();
+            break;
+        case 270:
+            dx = m.getY() - mDown.getY();
+            dy = m.getX() - mDown.getX();
+            break;
+        }
+        if (dx < 0 && (Math.abs(dy) / -dx < 2)) return DIR_LEFT;
+        if (dx > 0 && (Math.abs(dy) / dx < 2)) return DIR_RIGHT;
+        if (dy > 0) return DIR_DOWN;
+        return DIR_UP;
+    }
+
+    private boolean isInside(MotionEvent evt, View v) {
+        v.getLocationInWindow(mLocation);
+        return (v.getVisibility() == View.VISIBLE
+                && evt.getX() >= mLocation[0] && evt.getX() < mLocation[0] + v.getWidth()
+                && evt.getY() >= mLocation[1] && evt.getY() < mLocation[1] + v.getHeight());
+    }
+
+    public void cancelActivityTouchHandling(MotionEvent m) {
+        if (mCancelEventListener != null) {
+            mCancelEventListener.onTouchEventCancelled(makeCancelEvent(m));
+        }
+    }
+
+    private MotionEvent makeCancelEvent(MotionEvent m) {
+        MotionEvent c = MotionEvent.obtain(m);
+        c.setAction(MotionEvent.ACTION_CANCEL);
+        return c;
+    }
+
+    private void openPie() {
+        mDown.offsetLocation(-mOverlay.getWindowPositionX(),
+                -mOverlay.getWindowPositionY());
+        mOverlay.directDispatchTouch(mDown, mPie);
+    }
+
+    private void cancelPie() {
+        mHandler.removeMessages(MSG_PIE);
+    }
+
+    private boolean sendToPie(MotionEvent m) {
+        m.offsetLocation(-mOverlay.getWindowPositionX(),
+                -mOverlay.getWindowPositionY());
+        return mOverlay.directDispatchTouch(m, mPie);
+    }
+
+    @Override
+    public boolean onScale(ScaleGestureDetector detector) {
+        return mZoom.onScale(detector);
+    }
+
+    @Override
+    public boolean onScaleBegin(ScaleGestureDetector detector) {
+        if (mMode != MODE_ZOOM) {
+            mMode = MODE_ZOOM;
+            cancelActivityTouchHandling(mCurrent);
+        }
+        if (mCurrent.getActionMasked() != MotionEvent.ACTION_MOVE) {
+            return mZoom.onScaleBegin(detector);
+        } else {
+            return true;
+        }
+    }
+
+    @Override
+    public void onScaleEnd(ScaleGestureDetector detector) {
+        if (mCurrent.getActionMasked() != MotionEvent.ACTION_MOVE) {
+            mZoom.onScaleEnd(detector);
+        }
+    }
+}
+