summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDoris Liu <tianliu@google.com>2013-11-20 00:24:46 -0800
committerDoris Liu <tianliu@google.com>2013-11-23 23:33:06 -0800
commitf55f3c461c5a6ae6b61fa75562ca01683aa93f9a (patch)
treef1d68caddb8a49d5e57ae138760efc9dd25e96fd
parentc4e665625b88a8363fa2bd9848bf88ec9b45637f (diff)
downloadandroid_packages_apps_Camera2-f55f3c461c5a6ae6b61fa75562ca01683aa93f9a.tar.gz
android_packages_apps_Camera2-f55f3c461c5a6ae6b61fa75562ca01683aa93f9a.tar.bz2
android_packages_apps_Camera2-f55f3c461c5a6ae6b61fa75562ca01683aa93f9a.zip
Pinhole animation, quick switch between photo and video
Also, first pass of the view hierarchy refactor. Change-Id: I6c80191f15908bd24c16b76df6ef92df3ef905fc
-rw-r--r--res/drawable-hdpi/ic_camera_normal.pngbin0 -> 1342 bytes
-rw-r--r--res/drawable-hdpi/ic_craft_normal.pngbin0 -> 1096 bytes
-rw-r--r--res/drawable-hdpi/ic_photo_sphere_normal.pngbin0 -> 1847 bytes
-rw-r--r--res/drawable-hdpi/ic_settings_normal.pngbin0 -> 1037 bytes
-rw-r--r--res/drawable-hdpi/ic_timelapse_normal.pngbin0 -> 1254 bytes
-rw-r--r--res/drawable-hdpi/ic_video_normal.pngbin0 -> 763 bytes
-rw-r--r--res/drawable-hdpi/ic_wide_angle_normal.pngbin0 -> 1040 bytes
-rw-r--r--res/drawable-mdpi/ic_camera_normal.pngbin0 -> 934 bytes
-rw-r--r--res/drawable-mdpi/ic_craft_normal.pngbin0 -> 725 bytes
-rw-r--r--res/drawable-mdpi/ic_photo_sphere_normal.pngbin0 -> 1234 bytes
-rw-r--r--res/drawable-mdpi/ic_settings_normal.pngbin0 -> 970 bytes
-rw-r--r--res/drawable-mdpi/ic_timelapse_normal.pngbin0 -> 998 bytes
-rw-r--r--res/drawable-mdpi/ic_video_normal.pngbin0 -> 650 bytes
-rw-r--r--res/drawable-mdpi/ic_wide_angle_normal.pngbin0 -> 814 bytes
-rw-r--r--res/drawable-xhdpi/ic_camera_normal.pngbin0 -> 1714 bytes
-rw-r--r--res/drawable-xhdpi/ic_craft_normal.pngbin0 -> 950 bytes
-rw-r--r--res/drawable-xhdpi/ic_photo_sphere_normal.pngbin0 -> 2520 bytes
-rw-r--r--res/drawable-xhdpi/ic_settings_normal.pngbin0 -> 1577 bytes
-rw-r--r--res/drawable-xhdpi/ic_timelapse_normal.pngbin0 -> 1780 bytes
-rw-r--r--res/drawable-xhdpi/ic_video_normal.pngbin0 -> 944 bytes
-rw-r--r--res/drawable-xhdpi/ic_wide_angle_normal.pngbin0 -> 1268 bytes
-rw-r--r--res/drawable-xxhdpi/ic_camera_normal.pngbin0 -> 2360 bytes
-rw-r--r--res/drawable-xxhdpi/ic_craft_normal.pngbin0 -> 1238 bytes
-rw-r--r--res/drawable-xxhdpi/ic_photo_sphere_normal.pngbin0 -> 3248 bytes
-rw-r--r--res/drawable-xxhdpi/ic_settings_normal.pngbin0 -> 1595 bytes
-rw-r--r--res/drawable-xxhdpi/ic_timelapse_normal.pngbin0 -> 2387 bytes
-rw-r--r--res/drawable-xxhdpi/ic_video_normal.pngbin0 -> 1536 bytes
-rw-r--r--res/drawable-xxhdpi/ic_wide_angle_normal.pngbin0 -> 1676 bytes
-rw-r--r--res/drawable/craft.pngbin1906 -> 0 bytes
-rw-r--r--res/drawable/photo.pngbin1768 -> 0 bytes
-rw-r--r--res/drawable/photosphere.pngbin2716 -> 0 bytes
-rw-r--r--res/drawable/settings.pngbin1229 -> 0 bytes
-rw-r--r--res/drawable/timelapse.pngbin485 -> 0 bytes
-rw-r--r--res/drawable/video.pngbin804 -> 0 bytes
-rw-r--r--res/drawable/wideangle.pngbin1166 -> 0 bytes
-rw-r--r--res/layout/activity_main.xml6
-rw-r--r--res/layout/generic_module.xml55
-rw-r--r--res/layout/photo_module.xml31
-rw-r--r--res/layout/video_module.xml35
-rw-r--r--res/values/dimens.xml1
-rw-r--r--src/com/android/camera/CameraActivity.java57
-rw-r--r--src/com/android/camera/PhotoModule.java4
-rw-r--r--src/com/android/camera/PhotoUI.java28
-rw-r--r--src/com/android/camera/VideoModule.java2
-rw-r--r--src/com/android/camera/VideoUI.java37
-rw-r--r--src/com/android/camera/app/AppController.java13
-rw-r--r--src/com/android/camera/app/CameraAppUI.java347
-rw-r--r--src/com/android/camera/ui/MainActivityLayout.java31
-rw-r--r--src/com/android/camera/ui/ModeListView.java63
-rw-r--r--src/com/android/camera/ui/ModeSelectorItem.java8
-rw-r--r--src/com/android/camera/ui/ModeTransitionView.java467
-rw-r--r--src/com/android/camera/ui/PieRenderer.java2
-rw-r--r--src/com/android/camera/ui/RenderOverlay.java33
53 files changed, 1060 insertions, 160 deletions
diff --git a/res/drawable-hdpi/ic_camera_normal.png b/res/drawable-hdpi/ic_camera_normal.png
new file mode 100644
index 000000000..ecafe13e1
--- /dev/null
+++ b/res/drawable-hdpi/ic_camera_normal.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_craft_normal.png b/res/drawable-hdpi/ic_craft_normal.png
new file mode 100644
index 000000000..86571e46d
--- /dev/null
+++ b/res/drawable-hdpi/ic_craft_normal.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_photo_sphere_normal.png b/res/drawable-hdpi/ic_photo_sphere_normal.png
new file mode 100644
index 000000000..64cff4233
--- /dev/null
+++ b/res/drawable-hdpi/ic_photo_sphere_normal.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_settings_normal.png b/res/drawable-hdpi/ic_settings_normal.png
new file mode 100644
index 000000000..51c559c62
--- /dev/null
+++ b/res/drawable-hdpi/ic_settings_normal.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_timelapse_normal.png b/res/drawable-hdpi/ic_timelapse_normal.png
new file mode 100644
index 000000000..6303033dd
--- /dev/null
+++ b/res/drawable-hdpi/ic_timelapse_normal.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_video_normal.png b/res/drawable-hdpi/ic_video_normal.png
new file mode 100644
index 000000000..90d9adf66
--- /dev/null
+++ b/res/drawable-hdpi/ic_video_normal.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_wide_angle_normal.png b/res/drawable-hdpi/ic_wide_angle_normal.png
new file mode 100644
index 000000000..7c66ba4c7
--- /dev/null
+++ b/res/drawable-hdpi/ic_wide_angle_normal.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_camera_normal.png b/res/drawable-mdpi/ic_camera_normal.png
new file mode 100644
index 000000000..c6034c97c
--- /dev/null
+++ b/res/drawable-mdpi/ic_camera_normal.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_craft_normal.png b/res/drawable-mdpi/ic_craft_normal.png
new file mode 100644
index 000000000..6a53af594
--- /dev/null
+++ b/res/drawable-mdpi/ic_craft_normal.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_photo_sphere_normal.png b/res/drawable-mdpi/ic_photo_sphere_normal.png
new file mode 100644
index 000000000..9278222d9
--- /dev/null
+++ b/res/drawable-mdpi/ic_photo_sphere_normal.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_settings_normal.png b/res/drawable-mdpi/ic_settings_normal.png
new file mode 100644
index 000000000..cac3ab0d4
--- /dev/null
+++ b/res/drawable-mdpi/ic_settings_normal.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_timelapse_normal.png b/res/drawable-mdpi/ic_timelapse_normal.png
new file mode 100644
index 000000000..44b02c2cf
--- /dev/null
+++ b/res/drawable-mdpi/ic_timelapse_normal.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_video_normal.png b/res/drawable-mdpi/ic_video_normal.png
new file mode 100644
index 000000000..c8a2dd155
--- /dev/null
+++ b/res/drawable-mdpi/ic_video_normal.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_wide_angle_normal.png b/res/drawable-mdpi/ic_wide_angle_normal.png
new file mode 100644
index 000000000..73f2011d3
--- /dev/null
+++ b/res/drawable-mdpi/ic_wide_angle_normal.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_camera_normal.png b/res/drawable-xhdpi/ic_camera_normal.png
new file mode 100644
index 000000000..db7ad435c
--- /dev/null
+++ b/res/drawable-xhdpi/ic_camera_normal.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_craft_normal.png b/res/drawable-xhdpi/ic_craft_normal.png
new file mode 100644
index 000000000..783581dbc
--- /dev/null
+++ b/res/drawable-xhdpi/ic_craft_normal.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_photo_sphere_normal.png b/res/drawable-xhdpi/ic_photo_sphere_normal.png
new file mode 100644
index 000000000..63f78399e
--- /dev/null
+++ b/res/drawable-xhdpi/ic_photo_sphere_normal.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_settings_normal.png b/res/drawable-xhdpi/ic_settings_normal.png
new file mode 100644
index 000000000..5c531bb85
--- /dev/null
+++ b/res/drawable-xhdpi/ic_settings_normal.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_timelapse_normal.png b/res/drawable-xhdpi/ic_timelapse_normal.png
new file mode 100644
index 000000000..f9a160420
--- /dev/null
+++ b/res/drawable-xhdpi/ic_timelapse_normal.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_video_normal.png b/res/drawable-xhdpi/ic_video_normal.png
new file mode 100644
index 000000000..c6e561cfe
--- /dev/null
+++ b/res/drawable-xhdpi/ic_video_normal.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_wide_angle_normal.png b/res/drawable-xhdpi/ic_wide_angle_normal.png
new file mode 100644
index 000000000..ffa96376d
--- /dev/null
+++ b/res/drawable-xhdpi/ic_wide_angle_normal.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_camera_normal.png b/res/drawable-xxhdpi/ic_camera_normal.png
new file mode 100644
index 000000000..125d3955d
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_camera_normal.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_craft_normal.png b/res/drawable-xxhdpi/ic_craft_normal.png
new file mode 100644
index 000000000..e7dfb21d8
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_craft_normal.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_photo_sphere_normal.png b/res/drawable-xxhdpi/ic_photo_sphere_normal.png
new file mode 100644
index 000000000..2c886415d
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_photo_sphere_normal.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_settings_normal.png b/res/drawable-xxhdpi/ic_settings_normal.png
new file mode 100644
index 000000000..1ff448531
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_settings_normal.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_timelapse_normal.png b/res/drawable-xxhdpi/ic_timelapse_normal.png
new file mode 100644
index 000000000..aaade0436
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_timelapse_normal.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_video_normal.png b/res/drawable-xxhdpi/ic_video_normal.png
new file mode 100644
index 000000000..e9fd11b9a
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_video_normal.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_wide_angle_normal.png b/res/drawable-xxhdpi/ic_wide_angle_normal.png
new file mode 100644
index 000000000..31460dda2
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_wide_angle_normal.png
Binary files differ
diff --git a/res/drawable/craft.png b/res/drawable/craft.png
deleted file mode 100644
index 89dfb44fc..000000000
--- a/res/drawable/craft.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/photo.png b/res/drawable/photo.png
deleted file mode 100644
index d5b648719..000000000
--- a/res/drawable/photo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/photosphere.png b/res/drawable/photosphere.png
deleted file mode 100644
index f60de5eba..000000000
--- a/res/drawable/photosphere.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/settings.png b/res/drawable/settings.png
deleted file mode 100644
index c50d3240f..000000000
--- a/res/drawable/settings.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/timelapse.png b/res/drawable/timelapse.png
deleted file mode 100644
index fae717653..000000000
--- a/res/drawable/timelapse.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/video.png b/res/drawable/video.png
deleted file mode 100644
index ea7b90a2d..000000000
--- a/res/drawable/video.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/wideangle.png b/res/drawable/wideangle.png
deleted file mode 100644
index 9c5486537..000000000
--- a/res/drawable/wideangle.png
+++ /dev/null
Binary files differ
diff --git a/res/layout/activity_main.xml b/res/layout/activity_main.xml
index 63c54d01f..5386794bf 100644
--- a/res/layout/activity_main.xml
+++ b/res/layout/activity_main.xml
@@ -23,6 +23,12 @@
android:background="@null">
<include layout="@layout/camera_filmstrip" />
+ <com.android.camera.ui.ModeTransitionView
+ android:id="@+id/mode_transition_view"
+ android:layerType="hardware"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
<include layout="@layout/mode_list_layout" />
</com.android.camera.ui.MainActivityLayout> \ No newline at end of file
diff --git a/res/layout/generic_module.xml b/res/layout/generic_module.xml
new file mode 100644
index 000000000..588b5d807
--- /dev/null
+++ b/res/layout/generic_module.xml
@@ -0,0 +1,55 @@
+<?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">
+ <!-- Wrap a frame layout around texture view so that when scaled, texture
+ view will not draw outside its unscaled bounds -->
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextureView
+ android:id="@+id/preview_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ <View
+ android:id="@+id/preview_cover"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/black"
+ android:visibility="gone" />
+ </FrameLayout>
+ <View
+ android:id="@+id/flash_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/white"
+ android:visibility="gone"
+ android:alpha="0" />
+ <com.android.camera.ui.RenderOverlay
+ android:id="@+id/render_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ <FrameLayout
+ android:id="@+id/module_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <include layout="@layout/camera_controls"
+ android:layout_gravity="center"
+ style="@style/CameraControls"/>
+</merge>
diff --git a/res/layout/photo_module.xml b/res/layout/photo_module.xml
index 0410f16fc..447e87afb 100644
--- a/res/layout/photo_module.xml
+++ b/res/layout/photo_module.xml
@@ -24,22 +24,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center">
- <!-- Wrap a frame layout around texture view so that when scaled, texture
- view will not draw outside its unscaled bounds -->
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <TextureView
- android:id="@+id/preview_content"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- <View
- android:id="@+id/preview_cover"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@android:color/black"
- android:visibility="gone" />
- </FrameLayout>
<ImageView
android:id="@+id/review_image"
android:layout_width="match_parent"
@@ -48,24 +32,11 @@
android:clickable="true"
android:background="@android:color/black"
android:scaleType="fitCenter"/>
- <View
- android:id="@+id/flash_overlay"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@android:color/white"
- android:visibility="gone"
- android:alpha="0" />
+
<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>
diff --git a/res/layout/video_module.xml b/res/layout/video_module.xml
index 198be8913..91288a23e 100644
--- a/res/layout/video_module.xml
+++ b/res/layout/video_module.xml
@@ -17,38 +17,6 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
- <!-- Wrap a frame layout around texture view so that when scaled, texture
- view will not draw outside its unscaled bounds -->
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <TextureView
- android:id="@+id/preview_content"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- <View
- android:id="@+id/preview_cover"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@android:color/black"
- android:visibility="gone" />
- </FrameLayout>
- <View
- android:id="@+id/flash_overlay"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@android:color/white"
- android:visibility="gone"
- android:alpha="0" />
- <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" />
@@ -67,7 +35,4 @@
android:visibility="gone"
android:onClick="onReviewPlayClicked"/>
- <include layout="@layout/camera_controls"
- android:layout_gravity="center"
- style="@style/CameraControls"/>
</merge>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1dc112a71..01d5280bd 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -158,4 +158,5 @@
<dimen name="mode_selector_icon_block_width">84dp</dimen>
<dimen name="mode_selector_icon_drawable_size">32dp</dimen>
+ <dimen name="mode_transition_view_icon_size">48dp</dimen>
</resources>
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java
index a2853065b..37a8bd676 100644
--- a/src/com/android/camera/CameraActivity.java
+++ b/src/com/android/camera/CameraActivity.java
@@ -68,6 +68,7 @@ import android.widget.ShareActionProvider;
import com.android.camera.app.AppController;
import com.android.camera.app.AppManagerFactory;
import com.android.camera.app.CameraApp;
+import com.android.camera.app.CameraAppUI;
import com.android.camera.app.CameraController;
import com.android.camera.app.CameraManager;
import com.android.camera.app.CameraManagerFactory;
@@ -100,6 +101,7 @@ 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.MainActivityLayout;
import com.android.camera.ui.ModeListView;
import com.android.camera.ui.SettingsView;
import com.android.camera.util.ApiHelper;
@@ -113,7 +115,7 @@ import com.android.camera2.R;
import java.io.File;
public class CameraActivity extends Activity
- implements AppController, ModeListView.ModeSwitchListener, CameraManager.CameraOpenCallback,
+ implements AppController, CameraManager.CameraOpenCallback,
ActionBar.OnMenuVisibilityListener, ShareActionProvider.OnShareTargetSelectedListener,
OrientationManager.OnOrientationChangeListener {
@@ -221,6 +223,7 @@ public class CameraActivity extends Activity
private CameraController mCameraController;
private boolean mPaused;
+ private CameraAppUI mCameraAppUI;
private MediaSaver mMediaSaver;
@@ -827,6 +830,11 @@ public class CameraActivity extends Activity
}
@Override
+ public int getCurrentModuleIndex() {
+ return mCurrentModeIndex;
+ }
+
+ @Override
public SurfaceTexture getPreviewBuffer() {
// TODO: implement this
return null;
@@ -1088,12 +1096,6 @@ public class CameraActivity extends Activity
ModulesInfo.setupModules(this, mModuleManager);
mModeListView = (ModeListView) findViewById(R.id.mode_list_layout);
- if (mModeListView != null) {
- mModeListView.setModeSwitchListener(this);
- } else {
- Log.e(TAG, "Cannot find mode list in the view hierarchy");
- }
-
if (ApiHelper.HAS_ROTATION_ANIMATION) {
setRotationAnimation();
}
@@ -1154,6 +1156,12 @@ public class CameraActivity extends Activity
// Set up the camera preview first so the preview shows up ASAP.
mFilmstripController.setListener(mFilmStripListener);
+ // TODO: Remove the 3rd parameter once mCameraModuleRoot is moved out of filmstrip
+ mCameraAppUI = new CameraAppUI(this,
+ (MainActivityLayout) findViewById(R.id.activity_root_view),
+ mCameraModuleRootView,
+ isSecureCamera(), isCaptureIntent());
+
int modeIndex = -1;
if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction())
|| MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) {
@@ -1198,6 +1206,12 @@ public class CameraActivity extends Activity
mSettingsManager = new SettingsManager(this);
setModuleFromModeIndex(modeIndex);
+
+ // TODO: Remove this when refactor is done.
+ if (modeIndex == ModulesInfo.MODULE_PHOTO ||
+ modeIndex == ModulesInfo.MODULE_VIDEO) {
+ mCameraAppUI.prepareModuleUI();
+ }
mCurrentModule.init(this, mCameraModuleRootView);
if (!mSecureCamera) {
@@ -1494,10 +1508,33 @@ public class CameraActivity extends Activity
return;
}
+ if (modeIndex == ModeListView.MODE_SETTING) {
+ onSettingsSelected();
+ return;
+ }
+
CameraHolder.instance().keep();
closeModule(mCurrentModule);
-
+ int oldModuleIndex = mCurrentModeIndex;
setModuleFromModeIndex(modeIndex);
+
+ // TODO: The following check is temporary for quick switch between video and photo.
+ // When the refactor is done, similar logic will be applied to all modules.
+ if (mCurrentModeIndex == ModulesInfo.MODULE_PHOTO
+ || mCurrentModeIndex == ModulesInfo.MODULE_VIDEO) {
+ if (oldModuleIndex != ModulesInfo.MODULE_PHOTO
+ && oldModuleIndex != ModulesInfo.MODULE_VIDEO) {
+ mCameraAppUI.prepareModuleUI();
+ } else {
+ mCameraAppUI.clearModuleUI();
+ }
+ } else {
+ // This is the old way of removing all views in CameraRootView. Will
+ // be deprecated soon. It is here to make sure modules that haven't
+ // been refactored can still function.
+ mCameraAppUI.clearCameraUI();
+ }
+
openModule(mCurrentModule);
mCurrentModule.onOrientationChanged(mLastRawOrientation);
if (mMediaSaver != null) {
@@ -1509,8 +1546,7 @@ public class CameraActivity extends Activity
prefs.edit().putInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, modeIndex).apply();
}
- @Override
- public void onSettingsSelected(int modeIndex) {
+ public void onSettingsSelected() {
// Temporary until we finalize the touch flow.
LayoutInflater inflater = getLayoutInflater();
SettingsView settingsView = (SettingsView) inflater.inflate(R.layout.settings_list_layout,
@@ -1590,7 +1626,6 @@ public class CameraActivity extends Activity
private void closeModule(CameraModule module) {
module.pause();
- ((ViewGroup) mCameraModuleRootView).removeAllViews();
}
private void performDeletion() {
diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java
index 468ec7b62..69706cc40 100644
--- a/src/com/android/camera/PhotoModule.java
+++ b/src/com/android/camera/PhotoModule.java
@@ -846,10 +846,10 @@ public class PhotoModule
case PhotoController.PREVIEW_STOPPED:
case PhotoController.SNAPSHOT_IN_PROGRESS:
case PhotoController.SWITCHING_CAMERA:
- mUI.enableGestures(false);
+ // TODO: Tell app UI to disable swipe
break;
case PhotoController.IDLE:
- mUI.enableGestures(true);
+ // TODO: Tell app UI to enable swipe
break;
}
}
diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java
index 0842a638a..c75e97b1f 100644
--- a/src/com/android/camera/PhotoUI.java
+++ b/src/com/android/camera/PhotoUI.java
@@ -71,7 +71,6 @@ public class PhotoUI implements PieListener,
private final AnimationManager mAnimationManager;
private CameraActivity mActivity;
private PhotoController mController;
- private PreviewGestures mGestures;
private View mRootView;
private SurfaceTexture mSurfaceTexture;
@@ -192,8 +191,9 @@ public class PhotoUI implements PieListener,
mController = controller;
mRootView = parent;
+ ViewGroup moduleRoot = (ViewGroup) mRootView.findViewById(R.id.module_layout);
mActivity.getLayoutInflater().inflate(R.layout.photo_module,
- (ViewGroup) mRootView, true);
+ (ViewGroup) moduleRoot, true);
mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay);
mFlashOverlay = mRootView.findViewById(R.id.flash_overlay);
mPreviewCover = mRootView.findViewById(R.id.preview_cover);
@@ -203,6 +203,11 @@ public class PhotoUI implements PieListener,
mTextureView.addOnLayoutChangeListener(mLayoutListener);
initIndicators();
+ mSurfaceTexture = mTextureView.getSurfaceTexture();
+ if (mSurfaceTexture != null) {
+ setTransformMatrix(mTextureView.getWidth(), mTextureView.getHeight());
+ mPreviewCover.setVisibility(View.GONE);
+ }
mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button);
mMenuButton = mRootView.findViewById(R.id.menu);
ViewStub faceViewStub = (ViewStub) mRootView
@@ -342,15 +347,7 @@ public class PhotoUI implements PieListener,
mZoomRenderer = new ZoomRenderer(mActivity);
mRenderOverlay.addRenderer(mZoomRenderer);
}
-
- if (mGestures == null) {
- // this will handle gesture disambiguation and dispatching
- mGestures = new PreviewGestures(mActivity, this, mZoomRenderer, mPieRenderer);
- mRenderOverlay.setGestures(mGestures);
- }
- mGestures.setZoomEnabled(params.isZoomSupported());
- mGestures.setRenderOverlay(mRenderOverlay);
- mRenderOverlay.requestLayout();
+ mRenderOverlay.setGestures(null);
initializeZoom(params);
updateOnScreenIndicators(params, prefGroup, prefs);
@@ -524,12 +521,6 @@ public class PhotoUI implements PieListener,
mAnimationManager.startFlashAnimation(mFlashOverlay);
}
- public void enableGestures(boolean enable) {
- if (mGestures != null) {
- mGestures.setEnabled(enable);
- }
- }
-
// forward from preview gestures to controller
@Override
public void onSingleTapUp(View view, int x, int y) {
@@ -564,9 +555,6 @@ public class PhotoUI implements PieListener,
if (mFaceView != null) {
mFaceView.setBlockDraw(!previewFocused);
}
- if (mGestures != null) {
- mGestures.setEnabled(previewFocused);
- }
if (mRenderOverlay != null) {
// this can not happen in capture mode
mRenderOverlay.setVisibility(previewFocused ? View.VISIBLE : View.GONE);
diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java
index 7d5680fb4..58de510d7 100644
--- a/src/com/android/camera/VideoModule.java
+++ b/src/com/android/camera/VideoModule.java
@@ -540,7 +540,7 @@ public class VideoModule extends CameraModule
@Override
public void onShutterButtonFocus(boolean pressed) {
- mUI.setShutterPressed(pressed);
+ // TODO: Remove this when old camera controls are removed from the UI.
}
private void readVideoPreferences() {
diff --git a/src/com/android/camera/VideoUI.java b/src/com/android/camera/VideoUI.java
index affed36cd..eff8ff732 100644
--- a/src/com/android/camera/VideoUI.java
+++ b/src/com/android/camera/VideoUI.java
@@ -79,7 +79,6 @@ public class VideoUI implements PieRenderer.PieListener,
private CameraControls mCameraControls;
private SettingsPopup mPopup;
private ZoomRenderer mZoomRenderer;
- private PreviewGestures mGestures;
private View mMenuButton;
private OnScreenIndicators mOnScreenIndicators;
private RotateLayout mRecordingTimeRect;
@@ -170,11 +169,21 @@ public class VideoUI implements PieRenderer.PieListener,
mActivity = activity;
mController = controller;
mRootView = parent;
- mActivity.getLayoutInflater().inflate(R.layout.video_module, (ViewGroup) mRootView, true);
+ ViewGroup moduleRoot = (ViewGroup) mRootView.findViewById(R.id.module_layout);
+ mActivity.getLayoutInflater().inflate(R.layout.video_module,
+ (ViewGroup) moduleRoot, true);
mPreviewCover = mRootView.findViewById(R.id.preview_cover);
mTextureView = (TextureView) mRootView.findViewById(R.id.preview_content);
mTextureView.setSurfaceTextureListener(this);
mTextureView.addOnLayoutChangeListener(mLayoutListener);
+
+ mSurfaceTexture = mTextureView.getSurfaceTexture();
+ if (mSurfaceTexture != null) {
+ mPreviewWidth = mTextureView.getWidth();
+ mPreviewHeight = mTextureView.getHeight();
+ setTransformMatrix(mPreviewWidth, mPreviewHeight);
+ mPreviewCover.setVisibility(View.GONE);
+ }
mFlashOverlay = mRootView.findViewById(R.id.flash_overlay);
mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button);
initializeMiscControls();
@@ -366,9 +375,6 @@ public class VideoUI implements PieRenderer.PieListener,
}
public void enableCameraControls(boolean enable) {
- if (mGestures != null) {
- mGestures.setZoomOnly(!enable);
- }
if (mPieRenderer != null && mPieRenderer.showsItems()) {
mPieRenderer.hide();
}
@@ -427,11 +433,7 @@ public class VideoUI implements PieRenderer.PieListener,
mZoomRenderer = new ZoomRenderer(mActivity);
}
mRenderOverlay.addRenderer(mZoomRenderer);
- if (mGestures == null) {
- mGestures = new PreviewGestures(mActivity, this, mZoomRenderer, mPieRenderer);
- mRenderOverlay.setGestures(mGestures);
- }
- mGestures.setRenderOverlay(mRenderOverlay);
+ mRenderOverlay.setGestures(null);
mPreviewThumb = mRootView.findViewById(R.id.preview_thumb);
mPreviewThumb.setOnClickListener(new OnClickListener() {
@@ -521,12 +523,6 @@ public class VideoUI implements PieRenderer.PieListener,
return false;
}
- // disable preview gestures after shutter is pressed
- public void setShutterPressed(boolean pressed) {
- if (mGestures == null) return;
- mGestures.setEnabled(!pressed);
- }
-
public void enableShutter(boolean enable) {
if (mShutterButton != null) {
mShutterButton.setEnabled(enable);
@@ -612,9 +608,7 @@ public class VideoUI implements PieRenderer.PieListener,
} else {
hideUI();
}
- if (mGestures != null) {
- mGestures.setEnabled(previewFocused);
- }
+
if (mRenderOverlay != null) {
// this can not happen in capture mode
mRenderOverlay.setVisibility(previewFocused ? View.VISIBLE : View.GONE);
@@ -627,11 +621,6 @@ public class VideoUI implements PieRenderer.PieListener,
}
public void initializeZoom(Parameters param) {
- if (param == null || !param.isZoomSupported()) {
- mGestures.setZoomEnabled(false);
- return;
- }
- mGestures.setZoomEnabled(true);
mZoomMax = param.getMaxZoom();
mZoomRatios = param.getZoomRatios();
// Currently we use immediate zoom for fast zooming to get better UX and
diff --git a/src/com/android/camera/app/AppController.java b/src/com/android/camera/app/AppController.java
index 61c1c1b4d..84398eb1f 100644
--- a/src/com/android/camera/app/AppController.java
+++ b/src/com/android/camera/app/AppController.java
@@ -24,6 +24,7 @@ import android.widget.FrameLayout;
import com.android.camera.LocationManager;
import com.android.camera.SettingsManager;
+import com.android.camera.ui.ModeListView;
/**
* The controller at app level.
@@ -65,6 +66,18 @@ public interface AppController {
*/
public boolean isPaused();
+ /**
+ * Returns current running module index.
+ */
+ public int getCurrentModuleIndex();
+
+ /**
+ * This gets called when mode is changed.
+ *
+ * @param moduleIndex index of the new module to switch to
+ */
+ public void onModeSelected(int moduleIndex);
+
/********************** UI / Camera preview **********************/
/**
diff --git a/src/com/android/camera/app/CameraAppUI.java b/src/com/android/camera/app/CameraAppUI.java
new file mode 100644
index 000000000..104c8c0be
--- /dev/null
+++ b/src/com/android/camera/app/CameraAppUI.java
@@ -0,0 +1,347 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.android.camera.AnimationManager;
+import com.android.camera.ui.CameraControls;
+import com.android.camera.ui.MainActivityLayout;
+import com.android.camera.ui.ModeListView;
+import com.android.camera.ui.ModeTransitionView;
+import com.android.camera.ui.RenderOverlay;
+import com.android.camera2.R;
+
+/**
+ * CameraAppUI centralizes control of views shared across modules. Whereas module
+ * specific views will be handled in each Module UI. For example, we can now
+ * bring the flash animation and capture animation up from each module to app
+ * level, as these animations are largely the same for all modules.
+ *
+ * This class also serves to disambiguate touch events. It recognizes all the
+ * swipe gestures that happen on the preview by attaching a touch listener to
+ * a full-screen view on top of preview TextureView. Since CameraAppUI has knowledge
+ * of how swipe from each direction should be handled, it can then redirect these
+ * events to appropriate recipient views.
+ */
+public class CameraAppUI implements ModeListView.ModeSwitchListener {
+ private final static String TAG = "CameraAppUI";
+
+ private final AppController mController;
+ private final boolean mIsCaptureIntent;
+ private final boolean mIsSecureCamera;
+ private final AnimationManager mAnimationManager;
+
+ // Swipe states:
+ private final int IDLE = 0;
+ private final int SWIPE_UP = 1;
+ private final int SWIPE_DOWN = 2;
+ private final int SWIPE_LEFT = 3;
+ private final int SWIPE_RIGHT = 4;
+
+ // Touch related measures:
+ private final int mSlop;
+ private final int SWIPE_TIME_OUT = 500;
+
+ // App level views:
+ private final ViewGroup mCameraRootView;
+ private final ModeTransitionView mModeTransitionView;
+ private final MainActivityLayout mAppRootView;
+ private final ModeListView mModeListView;
+ private final View mFilmStripView;
+ private TextureView mTextureView;
+ private CameraControls mCameraControls;
+ private RenderOverlay mRenderOverlay;
+ private View mFlashOverlay;
+ private ViewGroup mModuleUI;
+
+ private GestureDetector mGestureDetector;
+ private int mSwipeState = IDLE;
+ private ImageView mPreviewThumbView;
+
+ public interface AnimationFinishedListener {
+ public void onAnimationFinished(boolean success);
+ }
+
+ private class MyTouchListener implements View.OnTouchListener {
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ return mGestureDetector.onTouchEvent(event);
+ }
+ }
+
+ /**
+ * This gesture listener finds out the direction of the scroll gestures and
+ * sends them to CameraAppUI to do further handling.
+ */
+ private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
+ private MotionEvent mDown;
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) {
+ if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT
+ || mSwipeState != IDLE) {
+ return true;
+ }
+
+ int deltaX = (int) (ev.getX() - mDown.getX());
+ int deltaY = (int) (ev.getY() - mDown.getY());
+ if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
+ if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) {
+ // Calculate the direction of the swipe.
+ if (deltaX >= Math.abs(deltaY)) {
+ // Swipe right.
+ setSwipeState(SWIPE_RIGHT);
+ } else if (deltaX <= -Math.abs(deltaY)) {
+ // Swipe left.
+ setSwipeState(SWIPE_LEFT);
+ } else if (deltaY >= Math.abs(deltaX)) {
+ // Swipe down.
+ setSwipeState(SWIPE_DOWN);
+ } else if (deltaY <= -Math.abs(deltaX)) {
+ // Swipe up.
+ setSwipeState(SWIPE_UP);
+ }
+ }
+ }
+ return true;
+ }
+
+ private void setSwipeState(int swipeState) {
+ mSwipeState = swipeState;
+ // Notify new swipe detected.
+ onSwipeDetected(swipeState);
+ }
+
+ @Override
+ public boolean onDown(MotionEvent ev) {
+ mDown = MotionEvent.obtain(ev);
+ mSwipeState = IDLE;
+ return true;
+ }
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent ev) {
+ // This keeps pie menu functioning until the alternative is in.
+ // TODO: Remove after bottom bar is finalized.
+ mRenderOverlay.directTouchEventsToPie(mDown);
+ mRenderOverlay.directTouchEventsToPie(ev);
+ return true;
+ }
+ }
+
+ public CameraAppUI(AppController controller, MainActivityLayout appRootView,
+ ViewGroup cameraRoot,
+ boolean isSecureCamera, boolean isCaptureIntent) {
+ mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop();
+ mController = controller;
+ mIsSecureCamera = isSecureCamera;
+ mIsCaptureIntent = isCaptureIntent;
+
+ mAppRootView = appRootView;
+ mFilmStripView = appRootView.findViewById(R.id.filmstrip_view);
+ mCameraRootView = cameraRoot;
+ mModeTransitionView = (ModeTransitionView)
+ mAppRootView.findViewById(R.id.mode_transition_view);
+ mGestureDetector = new GestureDetector(controller.getAndroidContext(),
+ new MyGestureListener());
+ mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout);
+ if (mModeListView != null) {
+ mModeListView.setModeSwitchListener(this);
+ } else {
+ Log.e(TAG, "Cannot find mode list in the view hierarchy");
+ }
+ mAnimationManager = new AnimationManager();
+ }
+
+ /**
+ * Redirects touch events to appropriate recipient views based on swipe direction.
+ * More specifically, swipe up and swipe down will be handled by the view that handles
+ * mode transition; swipe left will be send to filmstrip; swipe right will be redirected
+ * to mode list in order to bring up mode list.
+ */
+ private void onSwipeDetected(int swipeState) {
+ if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) {
+ // Quick switch between photo/video.
+ if (mController.getCurrentModuleIndex() == ModeListView.MODE_PHOTO ||
+ mController.getCurrentModuleIndex() == ModeListView.MODE_VIDEO) {
+ mAppRootView.redirectTouchEventsTo(mModeTransitionView);
+
+ final int moduleToTransitionTo =
+ mController.getCurrentModuleIndex() == ModeListView.MODE_PHOTO ?
+ ModeListView.MODE_VIDEO : ModeListView.MODE_PHOTO;
+ int shadeColorId = ModeListView.getModeThemeColor(moduleToTransitionTo);
+ int iconRes = ModeListView.getModeIconResourceId(moduleToTransitionTo);
+
+ AnimationFinishedListener listener = new AnimationFinishedListener() {
+ public void onAnimationFinished(boolean success) {
+ if (success) {
+ // Go to new module when the previous operation is successful.
+ mController.onModeSelected(moduleToTransitionTo);
+ mModeTransitionView.startPeepHoleAnimation();
+ }
+ }
+ };
+ if (mSwipeState == SWIPE_UP) {
+ mModeTransitionView.prepareToPullUpShade(shadeColorId, iconRes, listener);
+ } else {
+ mModeTransitionView.prepareToPullDownShade(shadeColorId, iconRes, listener);
+ }
+ }
+ } else if (swipeState == SWIPE_LEFT) {
+ // Pass the touch sequence to filmstrip.
+ mAppRootView.redirectTouchEventsTo(mFilmStripView);
+
+ } else if (swipeState == SWIPE_RIGHT) {
+ // Pass the touch to mode switcher
+ mAppRootView.redirectTouchEventsTo(mModeListView);
+ }
+ }
+
+ /**
+ * This inflates generic_module layout, which contains all the shared views across
+ * modules. Then each module inflates their own views in the given view group. For
+ * now, this is called every time switching from a not-yet-refactored module to a
+ * refactored module. In the future, this should only need to be done once per app
+ * start.
+ */
+ public void prepareModuleUI() {
+ mCameraRootView.removeAllViews();
+ LayoutInflater inflater = (LayoutInflater) mController.getAndroidContext()
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.generic_module, mCameraRootView, true);
+
+ mModuleUI = (ViewGroup) mCameraRootView.findViewById(R.id.module_layout);
+ mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
+ mRenderOverlay = (RenderOverlay) mCameraRootView.findViewById(R.id.render_overlay);
+ mRenderOverlay.setOnTouchListener(new MyTouchListener());
+ mFlashOverlay = mCameraRootView.findViewById(R.id.flash_overlay);
+ mPreviewThumbView = (ImageView) mCameraRootView.findViewById(R.id.preview_thumb);
+
+ // TODO: Remove camera controls.
+ mCameraControls = (CameraControls) mCameraRootView.findViewById(R.id.camera_controls);
+ }
+
+ // TODO: Remove this when refactor is done.
+ // This is here to ensure refactored modules can work with not-yet-refactored ones.
+ public void clearCameraUI() {
+ mCameraRootView.removeAllViews();
+ mModuleUI = null;
+ mTextureView = null;
+ mRenderOverlay = null;
+ mFlashOverlay = null;
+ mCameraControls = null;
+ }
+
+ /**
+ * Called indirectly from each module in their initialization to get a view group
+ * to inflate the module specific views in.
+ *
+ * @return a view group for modules to attach views to
+ */
+ public ViewGroup getModuleRootView() {
+ return mModuleUI;
+ }
+
+ /**
+ * Remove all the module specific views.
+ */
+ public void clearModuleUI() {
+ if (mModuleUI != null) {
+ mModuleUI.removeAllViews();
+ }
+ mRenderOverlay.clear();
+ }
+
+ @Override
+ public void onModeSelected(int modeIndex) {
+ mController.onModeSelected(modeIndex);
+ }
+
+ /**
+ * Sets the transform matrix on the preview TextureView
+ */
+ public void setPreviewTransformMatrix(Matrix transformMatrix) {
+ if (mTextureView == null) {
+ throw new UnsupportedOperationException("Cannot set transform matrix on a null" +
+ " TextureView");
+ }
+ mTextureView.setTransform(transformMatrix);
+ }
+
+
+ /********************** Capture animation **********************/
+ /* TODO: This session is subject to UX changes. In addition to the generic
+ flash animation and post capture animation, consider designating a parameter
+ for specifying the type of animation, as well as an animation finished listener
+ so that modules can have more knowledge of the status of the animation. */
+
+ /**
+ * Starts the pre-capture animation.
+ */
+ public void startPreCaptureAnimation() {
+ mAnimationManager.startFlashAnimation(mFlashOverlay);
+ }
+
+ /**
+ * Cancels the pre-capture animation.
+ */
+ public void cancelPreCaptureAnimation() {
+ mAnimationManager.cancelAnimations();
+ }
+
+ /**
+ * Starts the post-capture animation with the current preview image.
+ */
+ public void startPostCaptureAnimation() {
+ if (mTextureView == null) {
+ Log.e(TAG, "Cannot get a frame from a null TextureView for animation");
+ return;
+ }
+ // TODO: Down sample bitmap
+ startPostCaptureAnimation(mTextureView.getBitmap());
+ }
+
+ /**
+ * Starts the post-capture animation with the given thumbnail.
+ *
+ * @param thumbnail The thumbnail for the animation.
+ */
+ public void startPostCaptureAnimation(Bitmap thumbnail) {
+ mPreviewThumbView.setImageBitmap(thumbnail);
+ mAnimationManager.startCaptureAnimation(mPreviewThumbView);
+ }
+
+ /**
+ * Cancels the post-capture animation.
+ */
+ public void cancelPostCaptureAnimation() {
+ mAnimationManager.cancelAnimations();
+ }
+}
diff --git a/src/com/android/camera/ui/MainActivityLayout.java b/src/com/android/camera/ui/MainActivityLayout.java
index 7ae293bfc..f4fb9f8a5 100644
--- a/src/com/android/camera/ui/MainActivityLayout.java
+++ b/src/com/android/camera/ui/MainActivityLayout.java
@@ -19,13 +19,15 @@ package com.android.camera.ui;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.MotionEvent;
+import android.view.View;
import android.view.ViewConfiguration;
import android.widget.FrameLayout;
import com.android.camera2.R;
-class MainActivityLayout extends FrameLayout {
+public class MainActivityLayout extends FrameLayout {
// Only check for intercepting touch events within first 500ms
private static final int SWIPE_TIME_OUT = 500;
@@ -34,6 +36,9 @@ class MainActivityLayout extends FrameLayout {
private boolean mCheckToIntercept;
private MotionEvent mDown;
private final int mSlop;
+ private final String TAG = "MainActivityLayout";
+ private boolean mRequestToInterceptTouchEvents = false;
+ private View mTouchReceiver = null;
public MainActivityLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -45,7 +50,13 @@ class MainActivityLayout extends FrameLayout {
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
mCheckToIntercept = true;
mDown = MotionEvent.obtain(ev);
+ mTouchReceiver = null;
+ mRequestToInterceptTouchEvents = false;
return false;
+ } else if (mRequestToInterceptTouchEvents) {
+ mRequestToInterceptTouchEvents = false;
+ onTouchEvent(mDown);
+ return true;
} else if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
// Do not intercept touch once child is in zoom mode
mCheckToIntercept = false;
@@ -63,6 +74,7 @@ class MainActivityLayout extends FrameLayout {
&& deltaX > mSlop) {
// Intercept right swipe
if (Math.abs(deltaX) >= Math.abs(deltaY) * 2) {
+ mTouchReceiver = mModeList;
onTouchEvent(mDown);
return true;
}
@@ -73,9 +85,11 @@ class MainActivityLayout extends FrameLayout {
@Override
public boolean onTouchEvent(MotionEvent ev) {
- // TODO: This also needs to be modified with a better touch flow.
- // Pass the right swipe to mode switcher.
- return mModeList.onTouchEvent(ev);
+ if (mTouchReceiver != null) {
+ mTouchReceiver.setVisibility(VISIBLE);
+ return mTouchReceiver.onTouchEvent(ev);
+ }
+ return false;
}
@Override
@@ -88,4 +102,13 @@ class MainActivityLayout extends FrameLayout {
public void onFinishInflate() {
mModeList = (ModeListView) findViewById(R.id.mode_list_layout);
}
+
+ public void redirectTouchEventsTo(View touchReceiver) {
+ if (touchReceiver == null) {
+ Log.e(TAG, "Cannot redirect touch to a null receiver.");
+ return;
+ }
+ mTouchReceiver = touchReceiver;
+ mRequestToInterceptTouchEvents = true;
+ }
}
diff --git a/src/com/android/camera/ui/ModeListView.java b/src/com/android/camera/ui/ModeListView.java
index e54945598..1ae70c912 100644
--- a/src/com/android/camera/ui/ModeListView.java
+++ b/src/com/android/camera/ui/ModeListView.java
@@ -23,8 +23,6 @@ import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.LayoutInflater;
@@ -75,15 +73,16 @@ public class ModeListView extends ScrollView {
private static final int ACCORDION_ANIMATION = 2;
private static final int SCROLLING = 3;
- private final int[] mIconResId = {R.drawable.photo, R.drawable.video,
- R.drawable.photosphere, R.drawable.craft, R.drawable.timelapse,
- R.drawable.wideangle, R.drawable.settings,};
+ private static final int[] mIconResId = {R.drawable.ic_camera_normal,
+ R.drawable.ic_video_normal, R.drawable.ic_photo_sphere_normal,
+ R.drawable.ic_craft_normal, R.drawable.ic_timelapse_normal,
+ R.drawable.ic_wide_angle_normal, R.drawable.ic_settings_normal,};
- private final int[] mTextResId = {R.string.mode_camera, R.string.mode_video,
+ private static final int[] mTextResId = {R.string.mode_camera, R.string.mode_video,
R.string.mode_photosphere, R.string.mode_craft, R.string.mode_timelapse,
R.string.mode_wideangle, R.string.mode_settings};
- private final int[] mIconBlockColor = {R.color.camera_mode_color,
+ private static final int[] mIconBlockColor = {R.color.camera_mode_color,
R.color.video_mode_color, R.color.photosphere_mode_color, R.color.craft_mode_color,
R.color.timelapse_mode_color, R.color.wideangle_mode_color,
R.color.settings_mode_color};
@@ -109,7 +108,6 @@ public class ModeListView extends ScrollView {
public interface ModeSwitchListener {
public void onModeSelected(int modeIndex);
- public void onSettingsSelected(int modeIndex);
}
/**
@@ -194,11 +192,7 @@ public class ModeListView extends ScrollView {
int index = getFocusItem(ev.getX(), ev.getY());
// Validate the selection
if (index != NO_ITEM_SELECTED) {
- if (index == MODE_SETTING) {
- onSettingsSelected(index);
- } else {
- onModeSelected(index);
- }
+ onModeSelected(index);
}
return true;
}
@@ -243,7 +237,6 @@ public class ModeListView extends ScrollView {
ModeSelectorItem selectorItem =
(ModeSelectorItem) inflater.inflate(R.layout.mode_selector, null);
mListView.addView(selectorItem);
-
// Set alternating background color for each mode selector in the list
if (i % 2 == 0) {
selectorItem.setBackgroundColor(getResources()
@@ -255,12 +248,7 @@ public class ModeListView extends ScrollView {
selectorItem.setIconBackgroundColor(getResources().getColor(mIconBlockColor[i]));
// Set image
- // TODO: Down-sampling here is temporary, will be removed when we get assets
- // from UX. The goal will be to fit the icon into 32dp x 32dp rect.
- BitmapFactory.Options opt = new BitmapFactory.Options();
- opt.inSampleSize = 4;
- Bitmap bitmap = BitmapFactory.decodeResource(getResources(), mIconResId[i], opt);
- selectorItem.setImageBitmap(bitmap);
+ selectorItem.setImageResource(mIconResId[i]);
// Set text
CharSequence text = getResources().getText(mTextResId[i]);
@@ -271,13 +259,6 @@ public class ModeListView extends ScrollView {
resetModeSelectors();
}
- private void onSettingsSelected(int modeIndex) {
- if (mListener != null) {
- mListener.onSettingsSelected(modeIndex);
- }
- snapBack();
- }
-
/** Notify ModeSwitchListener, if any, of the mode change. */
private void onModeSelected(int modeIndex) {
if (mListener != null) {
@@ -576,4 +557,32 @@ public class ModeListView extends ScrollView {
});
mAnimatorSet.start();
}
+
+ /**
+ * Get the theme color of a specific mode.
+ *
+ * @param modeIndex index of the mode
+ * @return theme color of the mode if input index is valid, otherwise 0
+ */
+ public static int getModeThemeColor(int modeIndex) {
+ if (modeIndex < 0 || modeIndex >= MODE_TOTAL) {
+ return 0;
+ } else {
+ return mIconBlockColor[modeIndex];
+ }
+ }
+
+ /**
+ * Get the mode icon resource id of a specific mode.
+ *
+ * @param modeIndex index of the mode
+ * @return icon resource id if the index is valid, otherwise 0
+ */
+ public static int getModeIconResourceId(int modeIndex) {
+ if (modeIndex < 0 || modeIndex >= MODE_TOTAL) {
+ return 0;
+ } else {
+ return mIconResId[modeIndex];
+ }
+ }
}
diff --git a/src/com/android/camera/ui/ModeSelectorItem.java b/src/com/android/camera/ui/ModeSelectorItem.java
index d6f25463a..5b6b1a226 100644
--- a/src/com/android/camera/ui/ModeSelectorItem.java
+++ b/src/com/android/camera/ui/ModeSelectorItem.java
@@ -113,12 +113,12 @@ class ModeSelectorItem extends FrameLayout {
}
/**
- * Sets bitmap as the icon for the mode.
+ * Sets image resource as the icon for the mode.
*
- * @param bitmap bitmap to be used as icon
+ * @param resource resource id of the asset to be used as icon
*/
- public void setImageBitmap(Bitmap bitmap) {
- mIcon.setImageBitmap(bitmap);
+ public void setImageResource(int resource) {
+ mIcon.setImageResource(resource);
}
/**
diff --git a/src/com/android/camera/ui/ModeTransitionView.java b/src/com/android/camera/ui/ModeTransitionView.java
new file mode 100644
index 000000000..c36c229d9
--- /dev/null
+++ b/src/com/android/camera/ui/ModeTransitionView.java
@@ -0,0 +1,467 @@
+/*
+ * 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.ui;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.camera.app.CameraAppUI;
+import com.android.camera.util.Gusterpolator;
+import com.android.camera2.R;
+
+/**
+ * This view is designed to handle all the animations during camera mode transition.
+ * It should only be visible during mode switch.
+ */
+public class ModeTransitionView extends View {
+ private static final String TAG = "ModeTransitionView";
+
+ private static final int PEEP_HOLE_ANIMATION_DURATION_MS = 550;
+ private static final int ICON_FADE_OUT_DURATION_MS = 850;
+
+ private static final int PULL_UP_SHADE = 0;
+ private static final int PULL_DOWN_SHADE = 1;
+ private static final int PEEP_HOLE_ANIMATION = 2;
+ private static final int IDLE = 3;
+
+ private static final float SCROLL_DISTANCE_MULTIPLY_FACTOR = 2f;
+ private static final int ALPHA_FULLY_TRANSPARENT = 0;
+ private static final int ALPHA_FULLY_OPAQUE = 255;
+ private static final int ALPHA_HALF_TRANSPARENT = 127;
+
+ private final GestureDetector mGestureDetector;
+ private final Paint mMaskPaint = new Paint();
+ private final Rect mIconRect = new Rect();
+ /** An empty drawable to fall back to when mIconDrawable set to null. */
+ private final Drawable mDefaultDrawable = new ColorDrawable();
+
+ private Drawable mIconDrawable;
+ private int mBackgroundColor;
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mPeepHoleCenterX = 0;
+ private int mPeepHoleCenterY = 0;
+ private float mRadius = 0f;
+ private int mIconSize;
+ private AnimatorSet mPeepHoleAnimator;
+ private int mAnimationType = PEEP_HOLE_ANIMATION;
+ private float mScrollDistance = 0;
+ private final Path mShadePath = new Path();
+ private final Paint mShadePaint = new Paint();
+ private CameraAppUI.AnimationFinishedListener mAnimationFinishedListener;
+ private float mScrollTrend;
+
+ public ModeTransitionView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mMaskPaint.setAlpha(0);
+ mMaskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+ mBackgroundColor = getResources().getColor(R.color.video_mode_color);
+ mGestureDetector = new GestureDetector(getContext(),
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onDown(MotionEvent ev) {
+ setScrollDistance(0f);
+ mScrollTrend = 0f;
+ return true;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2,
+ float distanceX, float distanceY) {
+ setScrollDistance(getScrollDistance()
+ + SCROLL_DISTANCE_MULTIPLY_FACTOR * distanceY);
+ mScrollTrend = 0.3f * mScrollTrend + 0.7f * distanceY;
+ return false;
+ }
+ });
+ mIconSize = getResources().getDimensionPixelSize(R.dimen.mode_transition_view_icon_size);
+ setIconDrawable(mDefaultDrawable);
+ }
+
+ /**
+ * Updates the size and shape of the shade
+ */
+ private void updateShade() {
+ if (mAnimationType == PULL_UP_SHADE || mAnimationType == PULL_DOWN_SHADE) {
+ mShadePath.reset();
+ float shadeHeight;
+ if (mAnimationType == PULL_UP_SHADE) {
+ // Scroll distance > 0.
+ mShadePath.addRect(0, mHeight - getScrollDistance(), mWidth, mHeight,
+ Path.Direction.CW);
+ shadeHeight = getScrollDistance();
+ } else {
+ // Scroll distance < 0.
+ mShadePath.addRect(0, 0, mWidth, - getScrollDistance(), Path.Direction.CW);
+ shadeHeight = getScrollDistance() * (-1);
+ }
+
+ if (mIconDrawable != null) {
+ if (shadeHeight < mHeight / 2 || mHeight == 0) {
+ mIconDrawable.setAlpha(ALPHA_FULLY_TRANSPARENT);
+ } else {
+ int alpha = ((int) shadeHeight - mHeight / 2) * ALPHA_FULLY_OPAQUE
+ / (mHeight / 2);
+ mIconDrawable.setAlpha(alpha);
+ }
+ }
+ invalidate();
+ }
+ }
+
+ /**
+ * Sets the scroll distance. Note this function gets called in every
+ * frame during animation. It should be very light weight.
+ *
+ * @param scrollDistance the scaled distance that user has scrolled
+ */
+ public void setScrollDistance(float scrollDistance) {
+ // First make sure scroll distance is clamped to the valid range.
+ if (mAnimationType == PULL_UP_SHADE) {
+ scrollDistance = Math.min(scrollDistance, mHeight);
+ scrollDistance = Math.max(scrollDistance, 0);
+ } else if (mAnimationType == PULL_DOWN_SHADE) {
+ scrollDistance = Math.min(scrollDistance, 0);
+ scrollDistance = Math.max(scrollDistance, -mHeight);
+ }
+ mScrollDistance = scrollDistance;
+ updateShade();
+ }
+
+ public float getScrollDistance() {
+ return mScrollDistance;
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ if (mAnimationType == PEEP_HOLE_ANIMATION) {
+ canvas.drawColor(mBackgroundColor);
+ if (mPeepHoleAnimator != null) {
+ // Draw a transparent circle using clear mode
+ canvas.drawCircle(mPeepHoleCenterX, mPeepHoleCenterY, mRadius, mMaskPaint);
+ }
+ } else if (mAnimationType == PULL_UP_SHADE || mAnimationType == PULL_DOWN_SHADE) {
+ canvas.drawPath(mShadePath, mShadePaint);
+ } else if (mAnimationType == IDLE) {
+ canvas.drawColor(mBackgroundColor);
+ }
+ super.onDraw(canvas);
+ mIconDrawable.draw(canvas);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ mWidth = right - left;
+ mHeight = bottom - top;
+ // Center the icon in the view.
+ mIconRect.set(mWidth / 2 - mIconSize / 2, mHeight / 2 - mIconSize / 2,
+ mWidth / 2 + mIconSize / 2, mHeight / 2 + mIconSize / 2);
+ mIconDrawable.setBounds(mIconRect);
+ }
+
+ /**
+ * This is an overloaded function. When no position is provided for the animation,
+ * the peep hole will start at the default position (i.e. center of the view).
+ */
+ public void startPeepHoleAnimation() {
+ float x = mWidth / 2;
+ float y = mHeight / 2;
+ startPeepHoleAnimation(x, y);
+ }
+
+ /**
+ * Starts the peep hole animation where the circle is centered at position (x, y).
+ */
+ private void startPeepHoleAnimation(float x, float y) {
+ if (mPeepHoleAnimator != null && mPeepHoleAnimator.isRunning()) {
+ return;
+ }
+ mAnimationType = PEEP_HOLE_ANIMATION;
+ mPeepHoleCenterX = (int) x;
+ mPeepHoleCenterY = (int) y;
+
+ int horizontalDistanceToFarEdge = Math.max(mPeepHoleCenterX, mWidth - mPeepHoleCenterX);
+ int verticalDistanceToFarEdge = Math.max(mPeepHoleCenterY, mHeight - mPeepHoleCenterY);
+ int endRadius = (int) (Math.sqrt(horizontalDistanceToFarEdge * horizontalDistanceToFarEdge
+ + verticalDistanceToFarEdge * verticalDistanceToFarEdge));
+
+ final ValueAnimator radiusAnimator = ValueAnimator.ofFloat(0, endRadius);
+ radiusAnimator.setDuration(PEEP_HOLE_ANIMATION_DURATION_MS);
+
+ final ValueAnimator iconScaleAnimator = ValueAnimator.ofFloat(1f, 0.5f);
+ iconScaleAnimator.setDuration(ICON_FADE_OUT_DURATION_MS);
+
+ final ValueAnimator iconAlphaAnimator = ValueAnimator.ofInt(ALPHA_HALF_TRANSPARENT,
+ ALPHA_FULLY_TRANSPARENT);
+ iconAlphaAnimator.setDuration(ICON_FADE_OUT_DURATION_MS);
+
+ mPeepHoleAnimator = new AnimatorSet();
+ mPeepHoleAnimator.playTogether(radiusAnimator, iconAlphaAnimator, iconScaleAnimator);
+ mPeepHoleAnimator.setInterpolator(Gusterpolator.INSTANCE);
+
+ iconAlphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ // Modify mask by enlarging the hole
+ mRadius = (Float) radiusAnimator.getAnimatedValue();
+
+ mIconDrawable.setAlpha((Integer) iconAlphaAnimator.getAnimatedValue());
+ float scale = (Float) iconScaleAnimator.getAnimatedValue();
+ int size = (int) (scale * (float) mIconSize);
+
+ mIconDrawable.setBounds(mPeepHoleCenterX - size / 2,
+ mPeepHoleCenterY - size / 2,
+ mPeepHoleCenterX + size / 2,
+ mPeepHoleCenterY + size / 2);
+
+ invalidate();
+ }
+ });
+
+ mPeepHoleAnimator.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mPeepHoleAnimator = null;
+ mRadius = 0;
+ mIconDrawable.setAlpha(ALPHA_FULLY_OPAQUE);
+ mIconDrawable.setBounds(mIconRect);
+ setVisibility(GONE);
+ mAnimationType = IDLE;
+ if (mAnimationFinishedListener != null) {
+ mAnimationFinishedListener.onAnimationFinished(true);
+ mAnimationFinishedListener = null;
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+
+ }
+ });
+ mPeepHoleAnimator.start();
+
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ boolean touchHandled = mGestureDetector.onTouchEvent(ev);
+ if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+ // TODO: Take into account fling
+ snap();
+ }
+ return touchHandled;
+ }
+
+ /**
+ * Snaps the shade to position at the end of a gesture.
+ */
+ private void snap() {
+ if (mScrollTrend >= 0 && mAnimationType == PULL_UP_SHADE) {
+ // Snap to full screen.
+ snapShadeTo(mHeight, ALPHA_FULLY_OPAQUE);
+ } else if (mScrollTrend <= 0 && mAnimationType == PULL_DOWN_SHADE) {
+ // Snap to full screen.
+ snapShadeTo(-mHeight, ALPHA_FULLY_OPAQUE);
+ } else if (mScrollTrend < 0 && mAnimationType == PULL_UP_SHADE) {
+ // Snap back.
+ snapShadeTo(0, ALPHA_FULLY_TRANSPARENT, false);
+ } else if (mScrollTrend > 0 && mAnimationType == PULL_DOWN_SHADE) {
+ // Snap back.
+ snapShadeTo(0, ALPHA_FULLY_TRANSPARENT, false);
+ }
+ }
+
+ private void snapShadeTo(int scrollDistance, int alpha) {
+ snapShadeTo(scrollDistance, alpha, true);
+ }
+
+ /**
+ * Snaps the shade to a given scroll distance and sets the icon alpha. If the shade
+ * is to snap back out, then hide the view after the animation.
+ *
+ * @param scrollDistance scaled user scroll distance
+ * @param alpha ending alpha of the icon drawable
+ * @param snapToFullScreen whether this snap animation snaps the shade to full screen
+ */
+ private void snapShadeTo(final int scrollDistance, final int alpha,
+ final boolean snapToFullScreen) {
+ if (mAnimationType == PULL_UP_SHADE || mAnimationType == PULL_DOWN_SHADE) {
+ ObjectAnimator scrollAnimator = ObjectAnimator.ofFloat(this, "scrollDistance",
+ scrollDistance);
+ scrollAnimator.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ setScrollDistance(scrollDistance);
+ mIconDrawable.setAlpha(alpha);
+ mAnimationType = IDLE;
+ if (!snapToFullScreen) {
+ setVisibility(GONE);
+ }
+ if (mAnimationFinishedListener != null) {
+ mAnimationFinishedListener.onAnimationFinished(snapToFullScreen);
+ mAnimationFinishedListener = null;
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+
+ }
+ });
+ scrollAnimator.setInterpolator(Gusterpolator.INSTANCE);
+ scrollAnimator.start();
+ }
+ }
+
+
+ /**
+ * Set the states for the animation that pulls up a shade with given shade color.
+ *
+ * @param shadeColorId color id of the shade that will be pulled up
+ * @param iconId id of the icon that will appear on top the shade
+ * @param listener a listener that will get notified when the animation
+ * is finished. Could be <code>null</code>.
+ */
+ public void prepareToPullUpShade(int shadeColorId, int iconId,
+ CameraAppUI.AnimationFinishedListener listener) {
+ prepareShadeAnimation(PULL_UP_SHADE, shadeColorId, iconId, listener);
+ }
+
+ /**
+ * Set the states for the animation that pulls down a shade with given shade color.
+ *
+ * @param shadeColorId color id of the shade that will be pulled down
+ * @param modeIconResourceId id of the icon that will appear on top the shade
+ * @param listener a listener that will get notified when the animation
+ * is finished. Could be <code>null</code>.
+ */
+ public void prepareToPullDownShade(int shadeColorId, int modeIconResourceId,
+ CameraAppUI.AnimationFinishedListener listener) {;
+ prepareShadeAnimation(PULL_DOWN_SHADE, shadeColorId, modeIconResourceId, listener);
+ }
+
+ /**
+ * Set the states for the animation that involves a shade.
+ *
+ * @param animationType type of animation that will happen to the shade
+ * @param shadeColorId color id of the shade that will be animated
+ * @param iconResId id of the icon that will appear on top the shade
+ * @param listener a listener that will get notified when the animation
+ * is finished. Could be <code>null</code>.
+ */
+ private void prepareShadeAnimation(int animationType, int shadeColorId, int iconResId,
+ CameraAppUI.AnimationFinishedListener listener) {
+ mAnimationFinishedListener = listener;
+ if (mPeepHoleAnimator != null && mPeepHoleAnimator.isRunning()) {
+ mPeepHoleAnimator.end();
+ }
+ mAnimationType = animationType;
+ resetShade(shadeColorId, iconResId);
+ }
+
+ /**
+ * Reset the shade with the given shade color and icon drawable.
+ *
+ * @param shadeColorId id of the shade color
+ * @param modeIconResourceId resource id of the icon drawable
+ */
+ private void resetShade(int shadeColorId, int modeIconResourceId) {
+ // Sets color for the shade.
+ int shadeColor = getResources().getColor(shadeColorId);
+ mBackgroundColor = shadeColor;
+ mShadePaint.setColor(shadeColor);
+ // Reset scroll distance.
+ setScrollDistance(0f);
+ // Sets new drawable.
+ updateIconDrawableByResourceId(modeIconResourceId);
+ mIconDrawable.setAlpha(0);
+ setVisibility(VISIBLE);
+ }
+
+ /**
+ * By default, all drawables instances loaded from the same resource share a
+ * common state; if you modify the state of one instance, all the other
+ * instances will receive the same modification. So here we need to make sure
+ * we mutate the drawable loaded from resource.
+ *
+ * @param modeIconResourceId resource id of the icon drawable
+ */
+ private void updateIconDrawableByResourceId(int modeIconResourceId) {
+ Drawable iconDrawable = getResources().getDrawable(modeIconResourceId);
+ if (iconDrawable == null) {
+ // Resource id not found
+ Log.e(TAG, "Invalid resource id for icon drawable. Setting icon drawable to null.");
+ setIconDrawable(null);
+ return;
+ }
+ // Mutate the drawable loaded from resource so modifying its states does
+ // not affect other drawable instances loaded from the same resource.
+ setIconDrawable(iconDrawable.mutate());
+ }
+
+ /**
+ * In order to make sure icon drawable is never set to null. Fall back to an
+ * empty drawable when icon needs to get reset.
+ *
+ * @param iconDrawable new drawable for icon. A value of <code>null</code> sets
+ * the icon drawable to the default drawable.
+ */
+ private void setIconDrawable(Drawable iconDrawable) {
+ if (iconDrawable == null) {
+ mIconDrawable = mDefaultDrawable;
+ } else {
+ mIconDrawable = iconDrawable;
+ }
+ }
+}
+
diff --git a/src/com/android/camera/ui/PieRenderer.java b/src/com/android/camera/ui/PieRenderer.java
index f1a5a9a4b..5736d7f8d 100644
--- a/src/com/android/camera/ui/PieRenderer.java
+++ b/src/com/android/camera/ui/PieRenderer.java
@@ -1064,7 +1064,7 @@ public class PieRenderer extends OverlayRenderer
@Override
public void onAnimationEnd(Animation animation) {
// Keep the focus indicator for some time.
- if (!mFocusCancelled) {
+ if (!mFocusCancelled && mOverlay != null) {
mOverlay.postDelayed(mDisappear, DISAPPEAR_TIMEOUT);
}
}
diff --git a/src/com/android/camera/ui/RenderOverlay.java b/src/com/android/camera/ui/RenderOverlay.java
index d82ce18b6..1e6b336af 100644
--- a/src/com/android/camera/ui/RenderOverlay.java
+++ b/src/com/android/camera/ui/RenderOverlay.java
@@ -87,8 +87,36 @@ public class RenderOverlay extends FrameLayout {
return mClients.size();
}
+ // TODO: Remove this when refactor is done. This is only here temporarily
+ // to keep pie working before it's replaced with bottom bar.
+ public void directTouchEventsToPie(MotionEvent ev) {
+ PieRenderer pie = null;
+ for (int i = 0; i < mClients.size(); i++) {
+ if (mClients.get(i) instanceof PieRenderer) {
+ pie = (PieRenderer) mClients.get(i);
+ break;
+ }
+ }
+ if (pie == null) {
+ return;
+ }
+ if (pie.isOpen()) {
+ pie.onTouchEvent(ev);
+ }
+ }
+
+ // TODO: Remove this when refactor is done. This is only here temporarily
+ // to keep pie working before it's replaced with bottom bar.
+ public void clear() {
+ mGestures = null;
+ while (mClients.size() > 0) {
+ remove(mClients.get(0));
+ }
+ mTouchClients.clear();
+ }
+
@Override
- public boolean dispatchTouchEvent(MotionEvent m) {
+ public boolean onTouchEvent(MotionEvent m) {
if (mGestures != null) {
if (!mGestures.isEnabled()) return false;
mGestures.dispatchTouch(m);
@@ -134,6 +162,9 @@ public class RenderOverlay extends FrameLayout {
@Override
public boolean dispatchTouchEvent(MotionEvent evt) {
+ if (mGestures == null) {
+ return false;
+ }
if (mTouchTarget != null) {
return mTouchTarget.onTouchEvent(evt);