summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--res/anim/count_down_exit.xml29
-rw-r--r--res/drawable-hdpi/ic_timer_10s_indicator.pngbin0 -> 1181 bytes
-rw-r--r--res/drawable-hdpi/ic_timer_10s_normal.pngbin0 -> 1521 bytes
-rw-r--r--res/drawable-hdpi/ic_timer_3s_indicator.pngbin0 -> 1155 bytes
-rw-r--r--res/drawable-hdpi/ic_timer_3s_normal.pngbin0 -> 1498 bytes
-rw-r--r--res/drawable-hdpi/ic_timer_off_indicator.pngbin0 -> 1384 bytes
-rw-r--r--res/drawable-hdpi/ic_timer_off_normal.pngbin0 -> 1695 bytes
-rw-r--r--res/drawable-mdpi/ic_timer_10s_indicator.pngbin0 -> 824 bytes
-rw-r--r--res/drawable-mdpi/ic_timer_10s_normal.pngbin0 -> 1045 bytes
-rw-r--r--res/drawable-mdpi/ic_timer_3s_indicator.pngbin0 -> 796 bytes
-rw-r--r--res/drawable-mdpi/ic_timer_3s_normal.pngbin0 -> 1012 bytes
-rw-r--r--res/drawable-mdpi/ic_timer_off_indicator.pngbin0 -> 907 bytes
-rw-r--r--res/drawable-mdpi/ic_timer_off_normal.pngbin0 -> 1081 bytes
-rw-r--r--res/drawable-xhdpi/ic_timer_10s_indicator.pngbin0 -> 1600 bytes
-rw-r--r--res/drawable-xhdpi/ic_timer_10s_normal.pngbin0 -> 2138 bytes
-rw-r--r--res/drawable-xhdpi/ic_timer_3s_indicator.pngbin0 -> 1553 bytes
-rw-r--r--res/drawable-xhdpi/ic_timer_3s_normal.pngbin0 -> 2061 bytes
-rw-r--r--res/drawable-xhdpi/ic_timer_off_indicator.pngbin0 -> 1798 bytes
-rw-r--r--res/drawable-xhdpi/ic_timer_off_normal.pngbin0 -> 2270 bytes
-rw-r--r--res/drawable-xxhdpi/ic_timer_10s_indicator.pngbin0 -> 2288 bytes
-rw-r--r--res/drawable-xxhdpi/ic_timer_10s_normal.pngbin0 -> 2746 bytes
-rw-r--r--res/drawable-xxhdpi/ic_timer_3s_indicator.pngbin0 -> 2221 bytes
-rw-r--r--res/drawable-xxhdpi/ic_timer_3s_normal.pngbin0 -> 2737 bytes
-rw-r--r--res/drawable-xxhdpi/ic_timer_off_indicator.pngbin0 -> 2216 bytes
-rw-r--r--res/drawable-xxhdpi/ic_timer_off_normal.pngbin0 -> 2658 bytes
-rw-r--r--res/layout-land/indicators.xml3
-rw-r--r--res/layout-land/mode_options.xml5
-rw-r--r--res/layout-port/indicators.xml3
-rw-r--r--res/layout-port/mode_options.xml5
-rw-r--r--res/layout/photo_module.xml14
-rw-r--r--res/values/arrays.xml24
-rw-r--r--src/com/android/camera/ButtonManager.java42
-rw-r--r--src/com/android/camera/PhotoModule.java107
-rw-r--r--src/com/android/camera/PhotoUI.java52
-rw-r--r--src/com/android/camera/app/CameraAppUI.java11
-rw-r--r--src/com/android/camera/settings/SettingsCache.java4
-rw-r--r--src/com/android/camera/settings/SettingsManager.java9
-rw-r--r--src/com/android/camera/ui/BottomBar.java6
-rw-r--r--src/com/android/camera/ui/CountDownView.java172
-rw-r--r--src/com/android/camera/widget/IndicatorIconController.java24
40 files changed, 458 insertions, 52 deletions
diff --git a/res/anim/count_down_exit.xml b/res/anim/count_down_exit.xml
deleted file mode 100644
index 0091c5bd7..000000000
--- a/res/anim/count_down_exit.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <alpha
- android:fromAlpha="1.0"
- android:toAlpha="0.0"
- android:duration="1000" />
- <scale
- android:fromXScale="1.0"
- android:fromYScale="1.0"
- android:toXScale="3.0"
- android:toYScale="3.0"
- android:pivotX="50%"
- android:pivotY="50%"
- android:duration="800" />
-</set> \ No newline at end of file
diff --git a/res/drawable-hdpi/ic_timer_10s_indicator.png b/res/drawable-hdpi/ic_timer_10s_indicator.png
new file mode 100644
index 000000000..8bde7dea5
--- /dev/null
+++ b/res/drawable-hdpi/ic_timer_10s_indicator.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_timer_10s_normal.png b/res/drawable-hdpi/ic_timer_10s_normal.png
new file mode 100644
index 000000000..8478ac860
--- /dev/null
+++ b/res/drawable-hdpi/ic_timer_10s_normal.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_timer_3s_indicator.png b/res/drawable-hdpi/ic_timer_3s_indicator.png
new file mode 100644
index 000000000..f367d9a00
--- /dev/null
+++ b/res/drawable-hdpi/ic_timer_3s_indicator.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_timer_3s_normal.png b/res/drawable-hdpi/ic_timer_3s_normal.png
new file mode 100644
index 000000000..e4b696d02
--- /dev/null
+++ b/res/drawable-hdpi/ic_timer_3s_normal.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_timer_off_indicator.png b/res/drawable-hdpi/ic_timer_off_indicator.png
new file mode 100644
index 000000000..e3f08f694
--- /dev/null
+++ b/res/drawable-hdpi/ic_timer_off_indicator.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_timer_off_normal.png b/res/drawable-hdpi/ic_timer_off_normal.png
new file mode 100644
index 000000000..9f1786236
--- /dev/null
+++ b/res/drawable-hdpi/ic_timer_off_normal.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_timer_10s_indicator.png b/res/drawable-mdpi/ic_timer_10s_indicator.png
new file mode 100644
index 000000000..2bbf6f3dc
--- /dev/null
+++ b/res/drawable-mdpi/ic_timer_10s_indicator.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_timer_10s_normal.png b/res/drawable-mdpi/ic_timer_10s_normal.png
new file mode 100644
index 000000000..3d06ee482
--- /dev/null
+++ b/res/drawable-mdpi/ic_timer_10s_normal.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_timer_3s_indicator.png b/res/drawable-mdpi/ic_timer_3s_indicator.png
new file mode 100644
index 000000000..db5c4ce94
--- /dev/null
+++ b/res/drawable-mdpi/ic_timer_3s_indicator.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_timer_3s_normal.png b/res/drawable-mdpi/ic_timer_3s_normal.png
new file mode 100644
index 000000000..d9e083e79
--- /dev/null
+++ b/res/drawable-mdpi/ic_timer_3s_normal.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_timer_off_indicator.png b/res/drawable-mdpi/ic_timer_off_indicator.png
new file mode 100644
index 000000000..9780cc5bb
--- /dev/null
+++ b/res/drawable-mdpi/ic_timer_off_indicator.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_timer_off_normal.png b/res/drawable-mdpi/ic_timer_off_normal.png
new file mode 100644
index 000000000..ad66d9099
--- /dev/null
+++ b/res/drawable-mdpi/ic_timer_off_normal.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_timer_10s_indicator.png b/res/drawable-xhdpi/ic_timer_10s_indicator.png
new file mode 100644
index 000000000..0040e4b05
--- /dev/null
+++ b/res/drawable-xhdpi/ic_timer_10s_indicator.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_timer_10s_normal.png b/res/drawable-xhdpi/ic_timer_10s_normal.png
new file mode 100644
index 000000000..d8104e053
--- /dev/null
+++ b/res/drawable-xhdpi/ic_timer_10s_normal.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_timer_3s_indicator.png b/res/drawable-xhdpi/ic_timer_3s_indicator.png
new file mode 100644
index 000000000..745c09f7b
--- /dev/null
+++ b/res/drawable-xhdpi/ic_timer_3s_indicator.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_timer_3s_normal.png b/res/drawable-xhdpi/ic_timer_3s_normal.png
new file mode 100644
index 000000000..c3874ac62
--- /dev/null
+++ b/res/drawable-xhdpi/ic_timer_3s_normal.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_timer_off_indicator.png b/res/drawable-xhdpi/ic_timer_off_indicator.png
new file mode 100644
index 000000000..7e6272d43
--- /dev/null
+++ b/res/drawable-xhdpi/ic_timer_off_indicator.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_timer_off_normal.png b/res/drawable-xhdpi/ic_timer_off_normal.png
new file mode 100644
index 000000000..b6554f1fd
--- /dev/null
+++ b/res/drawable-xhdpi/ic_timer_off_normal.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_timer_10s_indicator.png b/res/drawable-xxhdpi/ic_timer_10s_indicator.png
new file mode 100644
index 000000000..399456576
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_timer_10s_indicator.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_timer_10s_normal.png b/res/drawable-xxhdpi/ic_timer_10s_normal.png
new file mode 100644
index 000000000..61fffec85
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_timer_10s_normal.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_timer_3s_indicator.png b/res/drawable-xxhdpi/ic_timer_3s_indicator.png
new file mode 100644
index 000000000..7b0c5d3fd
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_timer_3s_indicator.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_timer_3s_normal.png b/res/drawable-xxhdpi/ic_timer_3s_normal.png
new file mode 100644
index 000000000..a4a7bf9c0
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_timer_3s_normal.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_timer_off_indicator.png b/res/drawable-xxhdpi/ic_timer_off_indicator.png
new file mode 100644
index 000000000..21acf8ca4
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_timer_off_indicator.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_timer_off_normal.png b/res/drawable-xxhdpi/ic_timer_off_normal.png
new file mode 100644
index 000000000..935b7cef1
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_timer_off_normal.png
Binary files differ
diff --git a/res/layout-land/indicators.xml b/res/layout-land/indicators.xml
index e2632fa5a..fa83e8f42 100644
--- a/res/layout-land/indicators.xml
+++ b/res/layout-land/indicators.xml
@@ -48,6 +48,9 @@
android:id="@+id/hdr_indicator"
style="@style/IndicatorIcon" />
<ImageView
+ android:id="@+id/countdown_timer_indicator"
+ style="@style/IndicatorIcon" />
+ <ImageView
android:id="@+id/flash_indicator"
style="@style/IndicatorIcon" />
<ImageView
diff --git a/res/layout-land/mode_options.xml b/res/layout-land/mode_options.xml
index d2ffcde9a..d8a69ceac 100644
--- a/res/layout-land/mode_options.xml
+++ b/res/layout-land/mode_options.xml
@@ -118,5 +118,10 @@
android:background="@null"
android:src="@drawable/ic_exposure"
android:contentDescription="@string/manual_exposure_compensation_desc" />
+ <com.android.camera.MultiToggleImageButton
+ android:id="@+id/countdown_toggle_button"
+ style="@style/ModeOption"
+ camera:imageIds="@array/countdown_duration_icons"
+ camera:contentDescriptionIds="@array/countdown_duration_descriptions" />
</com.android.camera.ui.TopRightWeightedLayout>
</com.android.camera.widget.ModeOptions>
diff --git a/res/layout-port/indicators.xml b/res/layout-port/indicators.xml
index 68e35fdce..d98c591e5 100644
--- a/res/layout-port/indicators.xml
+++ b/res/layout-port/indicators.xml
@@ -48,6 +48,9 @@
android:id="@+id/hdr_indicator"
style="@style/IndicatorIcon" />
<ImageView
+ android:id="@+id/countdown_timer_indicator"
+ style="@style/IndicatorIcon" />
+ <ImageView
android:id="@+id/flash_indicator"
style="@style/IndicatorIcon" />
<ImageView
diff --git a/res/layout-port/mode_options.xml b/res/layout-port/mode_options.xml
index 4d2dd99e6..566b8edbe 100644
--- a/res/layout-port/mode_options.xml
+++ b/res/layout-port/mode_options.xml
@@ -118,5 +118,10 @@
style="@style/ModeOption"
camera:imageIds="@array/camera_id_icons"
camera:contentDescriptionIds="@array/camera_id_descriptions" />
+ <com.android.camera.MultiToggleImageButton
+ android:id="@+id/countdown_toggle_button"
+ style="@style/ModeOption"
+ camera:imageIds="@array/countdown_duration_icons"
+ camera:contentDescriptionIds="@array/countdown_duration_descriptions" />
</com.android.camera.ui.TopRightWeightedLayout>
</com.android.camera.widget.ModeOptions>
diff --git a/res/layout/photo_module.xml b/res/layout/photo_module.xml
index 2cb002f24..e62d551af 100644
--- a/res/layout/photo_module.xml
+++ b/res/layout/photo_module.xml
@@ -32,4 +32,18 @@
android:clickable="true"
android:background="@android:color/black"
android:scaleType="fitCenter"/>
+ <com.android.camera.ui.CountDownView
+ android:id="@+id/count_down_view"
+ android:visibility="invisible"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ <TextView android:id="@+id/remaining_seconds"
+ android:layout_width="200dp"
+ android:layout_height="200dp"
+ android:textSize="160dp"
+ android:textColor="@android:color/white"
+ android:layout_gravity="top|left"
+ android:gravity="center"/>
+ </com.android.camera.ui.CountDownView>
+
</FrameLayout>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index ed7e19906..9b30aff56 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -457,6 +457,12 @@
<item>@string/grid_lines_on_desc</item>
</array>
+ <array name="countdown_duration_descriptions" translatable="false">
+ <item>@string/countdown_timer_off</item>
+ <item>@string/countdown_timer_duration_3s</item>
+ <item>@string/countdown_timer_duration_10s</item>
+ </array>
+
<string-array name="pref_camera_pano_orientation_entryvalues">
<item>@string/pano_orientation_horizontal</item>
<item>@string/pano_orientation_vertical</item>
@@ -474,6 +480,24 @@
<item>@string/settings_close_desc</item>
</array>
+ <array name="countdown_duration_icons" translatable="false">
+ <item>@drawable/ic_timer_off_normal</item>
+ <item>@drawable/ic_timer_3s_normal</item>
+ <item>@drawable/ic_timer_10s_normal</item>
+ </array>
+
+ <array name="pref_camera_countdown_indicators" translatable="false">
+ <item>@drawable/ic_timer_off_indicator</item>
+ <item>@drawable/ic_timer_3s_indicator</item>
+ <item>@drawable/ic_timer_10s_indicator</item>
+ </array>
+
+ <string-array name="pref_countdown_duration" translatable="false">
+ <item>0</item>
+ <item>3</item>
+ <item>10</item>
+ </string-array>
+
<!--Index of camera modes. -->
<integer name="camera_mode_photo">0</integer>
diff --git a/src/com/android/camera/ButtonManager.java b/src/com/android/camera/ButtonManager.java
index c5a6f2edc..a67e42bf3 100644
--- a/src/com/android/camera/ButtonManager.java
+++ b/src/com/android/camera/ButtonManager.java
@@ -53,6 +53,7 @@ public class ButtonManager implements SettingsManager.OnSettingChangedListener {
public static final int BUTTON_REVIEW = 9;
public static final int BUTTON_GRID_LINES = 11;
public static final int BUTTON_EXPOSURE_COMPENSATION = 12;
+ public static final int BUTTON_COUNTDOWN = 13;
/** For two state MultiToggleImageButtons, the off index. */
public static final int OFF = 0;
@@ -67,6 +68,7 @@ public class ButtonManager implements SettingsManager.OnSettingChangedListener {
private MultiToggleImageButton mButtonFlash;
private MultiToggleImageButton mButtonHdr;
private MultiToggleImageButton mButtonGridlines;
+ private MultiToggleImageButton mButtonCountdown;
/** Intent UI buttons. */
private ImageButton mButtonCancel;
@@ -172,6 +174,8 @@ public class ButtonManager implements SettingsManager.OnSettingChangedListener {
mModeOptionsPano = (RadioOptions) root.findViewById(R.id.mode_options_pano);
mModeOptionsButtons = root.findViewById(R.id.mode_options_buttons);
mModeOptions = (ModeOptions) root.findViewById(R.id.mode_options);
+
+ mButtonCountdown = (MultiToggleImageButton) root.findViewById(R.id.countdown_toggle_button);
}
@Override
@@ -218,6 +222,11 @@ public class ButtonManager implements SettingsManager.OnSettingChangedListener {
updateExposureButtons();
break;
}
+ case SettingsManager.SETTING_COUNTDOWN_DURATION: {
+ index = mSettingsManager.getStringValueIndex(id);
+ button = getButtonOrError(BUTTON_COUNTDOWN);
+ break;
+ }
default: {
// Do nothing.
}
@@ -277,6 +286,11 @@ public class ButtonManager implements SettingsManager.OnSettingChangedListener {
throw new IllegalStateException("Grid lines button could not be found.");
}
return mButtonGridlines;
+ case BUTTON_COUNTDOWN:
+ if (mButtonCountdown == null) {
+ throw new IllegalStateException("Countdown button could not be found.");
+ }
+ return mButtonCountdown;
default:
throw new IllegalArgumentException("button not known by id=" + buttonId);
}
@@ -345,6 +359,9 @@ public class ButtonManager implements SettingsManager.OnSettingChangedListener {
case BUTTON_GRID_LINES:
initializeGridLinesButton(button, cb, R.array.grid_lines_icons);
break;
+ case BUTTON_COUNTDOWN:
+ initializeCountdownButton(button, cb, R.array.countdown_duration_icons);
+ break;
default:
throw new IllegalArgumentException("button not known by id=" + buttonId);
}
@@ -690,6 +707,31 @@ public class ButtonManager implements SettingsManager.OnSettingChangedListener {
}
/**
+ * Initialize a countdown timer button.
+ */
+ private void initializeCountdownButton(MultiToggleImageButton button,
+ final ButtonCallback cb, int resIdImages) {
+ if (resIdImages > 0) {
+ button.overrideImageIds(resIdImages);
+ }
+
+ int index = mSettingsManager.getStringValueIndex(
+ SettingsManager.SETTING_COUNTDOWN_DURATION);
+ button.setState(index >= 0 ? index : 0, false);
+
+ button.setOnStateChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
+ @Override
+ public void stateChanged(View view, int state) {
+ mSettingsManager.setStringValueIndex(
+ SettingsManager.SETTING_COUNTDOWN_DURATION, state);
+ if(cb != null) {
+ cb.onStateChanged(state);
+ }
+ }
+ });
+ }
+
+ /**
* Update the visual state of the manual exposure buttons
*/
public void updateExposureButtons() {
diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java
index 33cb36e8e..9bce0ce80 100644
--- a/src/com/android/camera/PhotoModule.java
+++ b/src/com/android/camera/PhotoModule.java
@@ -32,7 +32,9 @@ import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Location;
+import android.media.AudioManager;
import android.media.CameraProfile;
+import android.media.SoundPool;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
@@ -73,6 +75,7 @@ import com.android.camera.remote.RemoteCameraModule;
import com.android.camera.settings.ResolutionUtil;
import com.android.camera.settings.SettingsManager;
import com.android.camera.settings.SettingsUtil;
+import com.android.camera.ui.CountDownView;
import com.android.camera.util.ApiHelper;
import com.android.camera.util.CameraUtil;
import com.android.camera.util.GcamHelper;
@@ -102,7 +105,8 @@ public class PhotoModule
FocusOverlayManager.Listener,
SensorEventListener,
SettingsManager.OnSettingChangedListener,
- RemoteCameraModule {
+ RemoteCameraModule,
+ CountDownView.OnCountDownStatusListener {
private static final Log.Tag TAG = new Log.Tag("PhotoModule");
@@ -243,6 +247,7 @@ public class PhotoModule
private FocusOverlayManager mFocusManager;
private final int mGcamModeIndex;
+ private final CountdownSoundPlayer mCountdownSoundPlayer = new CountdownSoundPlayer();
private String mSceneMode;
@@ -432,6 +437,25 @@ public class PhotoModule
mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
mLocationManager = mActivity.getLocationManager();
mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
+ mUI.setCountdownFinishedListener(this);
+
+ // TODO: Make this a part of app controller API.
+ View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button);
+ cancelButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ cancelCountDown();
+ }
+ });
+ }
+
+ private void cancelCountDown() {
+ if (mUI.isCountingDown()) {
+ // Cancel on-going countdown.
+ mUI.cancelCountDown();
+ }
+ mAppController.getCameraAppUI().transitionToCapture();
+ mAppController.getCameraAppUI().showModeOptions();
}
@Override
@@ -618,6 +642,7 @@ public class PhotoModule
if (mPaused) {
return;
}
+ cancelCountDown();
SettingsManager settingsManager = mActivity.getSettingsManager();
Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
@@ -751,6 +776,8 @@ public class PhotoModule
mCameraCapabilities.getExposureCompensationStep();
}
+ bottomBarSpec.enableSelfTimer = true;
+
if (isImageCaptureIntent()) {
bottomBarSpec.showCancel = true;
bottomBarSpec.cancelCallback = mCancelCallback;
@@ -1437,6 +1464,20 @@ public class PhotoModule
}
Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
+ int countDownDuration = Integer.parseInt(mActivity.getSettingsManager()
+ .get(SettingsManager.SETTING_COUNTDOWN_DURATION));
+ if (countDownDuration > 0) {
+ // Start count down.
+ mAppController.getCameraAppUI().transitionToCancel();
+ mAppController.getCameraAppUI().hideModeOptions();
+ mUI.startCountdown(countDownDuration);
+ return;
+ } else {
+ focusAndCapture();
+ }
+ }
+
+ private void focusAndCapture() {
if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
mUI.setSwipingEnabled(false);
}
@@ -1456,6 +1497,21 @@ public class PhotoModule
mFocusManager.focusAndCapture();
}
+ @Override
+ public void onRemainingSecondsChanged(int remainingSeconds) {
+ mCountdownSoundPlayer.onRemainingSecondsChanged(remainingSeconds);
+ }
+
+ @Override
+ public void onCountDownFinished() {
+ mAppController.getCameraAppUI().transitionToCapture();
+ mAppController.getCameraAppUI().showModeOptions();
+ if (mPaused) {
+ return;
+ }
+ focusAndCapture();
+ }
+
private void onResumeTasks() {
if (mPaused) {
return;
@@ -1526,6 +1582,7 @@ public class PhotoModule
@Override
public void resume() {
mPaused = false;
+ mCountdownSoundPlayer.loadSounds();
if (mFocusManager != null) {
// If camera is not open when resume is called, focus manager will
// not
@@ -1533,11 +1590,7 @@ public class PhotoModule
// preview area size change later in the initialization.
mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
}
-
- if (mUI.getPreviewAreaSizeChangedListener() != null) {
- mAppController.addPreviewAreaSizeChangedListener(
- mUI.getPreviewAreaSizeChangedListener());
- }
+ mAppController.addPreviewAreaSizeChangedListener(mUI);
// Add delay on resume from lock screen only, in order to to speed up
// the onResume --> onPause --> onResume cycle from lock screen.
@@ -1584,9 +1637,10 @@ public class PhotoModule
// and startPreview hasn't been called, then this is a no-op.
// (e.g. onResume -> onPause -> onResume).
stopPreview();
+ cancelCountDown();
+ mCountdownSoundPlayer.release();
mNamedImages = null;
-
// If we are in an image capture intent and has taken
// a picture, we just clear it in onPause.
mJpegImageData = null;
@@ -1604,10 +1658,7 @@ public class PhotoModule
}
getServices().getMemoryManager().removeListener(this);
mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
- if (mUI.getPreviewAreaSizeChangedListener() != null) {
- mAppController.removePreviewAreaSizeChangedListener(
- mUI.getPreviewAreaSizeChangedListener());
- }
+ mAppController.removePreviewAreaSizeChangedListener(mUI);
SettingsManager settingsManager = mActivity.getSettingsManager();
settingsManager.removeListener(this);
@@ -2258,4 +2309,38 @@ public class PhotoModule
public void onRemoteShutterPress() {
capture();
}
+
+ /**
+ * This class manages the loading/releasing/playing of the sounds needed for
+ * countdown timer.
+ */
+ private class CountdownSoundPlayer {
+ private SoundPool mSoundPool;
+ private int mBeepOnce;
+ private int mBeepTwice;
+
+ void loadSounds() {
+ // Load the beeps.
+ mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
+ mBeepOnce = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_once, 1);
+ mBeepTwice = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_twice, 1);
+ }
+
+ void onRemainingSecondsChanged(int newVal) {
+ if (mSoundPool == null) {
+ Log.e(TAG, "Cannot play sound - they have not been loaded.");
+ return;
+ }
+ if (newVal == 1) {
+ mSoundPool.play(mBeepTwice, 1.0f, 1.0f, 0, 0, 1.0f);
+ } else if (newVal == 2 || newVal == 3) {
+ mSoundPool.play(mBeepOnce, 1.0f, 1.0f, 0, 0, 1.0f);
+ }
+ }
+
+ void release() {
+ mSoundPool.release();
+ mSoundPool = null;
+ }
+ }
}
diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java
index f32256892..f1981a63b 100644
--- a/src/com/android/camera/PhotoUI.java
+++ b/src/com/android/camera/PhotoUI.java
@@ -20,6 +20,7 @@ import android.app.Dialog;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.Matrix;
+import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.Camera.Face;
@@ -33,6 +34,7 @@ import android.view.ViewGroup;
import com.android.camera.FocusOverlayManager.FocusUI;
import com.android.camera.cameradevice.CameraManager;
import com.android.camera.debug.Log;
+import com.android.camera.ui.CountDownView;
import com.android.camera.ui.FaceView;
import com.android.camera.ui.PreviewOverlay;
import com.android.camera.ui.PreviewStatusListener;
@@ -45,7 +47,7 @@ import com.android.camera2.R;
import java.util.List;
public class PhotoUI implements PreviewStatusListener,
- CameraManager.CameraFaceDetectionCallback {
+ CameraManager.CameraFaceDetectionCallback, PreviewStatusListener.PreviewAreaChangedListener {
private static final Log.Tag TAG = new Log.Tag("PhotoUI");
private static final int DOWN_SAMPLE_FACTOR = 4;
@@ -86,6 +88,7 @@ public class PhotoUI implements PreviewStatusListener,
}
};
private Runnable mRunnableForNextFrame = null;
+ private CountDownView mCountdownView;
@Override
public GestureDetector.OnGestureListener getGestureListener() {
@@ -130,6 +133,44 @@ public class PhotoUI implements PreviewStatusListener,
mRunnableForNextFrame = runnable;
}
+ /**
+ * Starts the countdown timer.
+ *
+ * @param sec seconds to countdown
+ */
+ public void startCountdown(int sec) {
+ mCountdownView.startCountDown(sec);
+ }
+
+ /**
+ * Sets a listener that gets notified when the countdown is finished.
+ */
+ public void setCountdownFinishedListener(CountDownView.OnCountDownStatusListener listener) {
+ mCountdownView.setCountDownStatusListener(listener);
+ }
+
+ /**
+ * Returns whether the countdown is on-going.
+ */
+ public boolean isCountingDown() {
+ return mCountdownView.isCountingDown();
+ }
+
+ /**
+ * Cancels the on-going countdown, if any.
+ */
+ public void cancelCountDown() {
+ mCountdownView.cancelCountDown();
+ }
+
+ @Override
+ public void onPreviewAreaChanged(RectF previewArea) {
+ if (mFaceView != null) {
+ mFaceView.onPreviewAreaChanged(previewArea);
+ }
+ mCountdownView.onPreviewAreaChanged(previewArea);
+ }
+
private class DecodeTask extends AsyncTask<Void, Void, Bitmap> {
private final byte [] mData;
private final int mOrientation;
@@ -184,6 +225,7 @@ public class PhotoUI implements PreviewStatusListener,
initIndicators();
mFocusUI = (FocusUI) mRootView.findViewById(R.id.focus_overlay);
mPreviewOverlay = (PreviewOverlay) mRootView.findViewById(R.id.preview_overlay);
+ mCountdownView = (CountDownView) mRootView.findViewById(R.id.count_down_view);
}
public FocusUI getFocusUI() {
@@ -471,12 +513,4 @@ public class PhotoUI implements PreviewStatusListener,
}
}
- /**
- * Returns a {@link com.android.camera.ui.PreviewStatusListener.PreviewAreaChangedListener}
- * that should be registered to listen to preview area change.
- */
- public PreviewAreaChangedListener getPreviewAreaSizeChangedListener() {
- return mFaceView;
- }
-
}
diff --git a/src/com/android/camera/app/CameraAppUI.java b/src/com/android/camera/app/CameraAppUI.java
index 19d113471..35236dbdc 100644
--- a/src/com/android/camera/app/CameraAppUI.java
+++ b/src/com/android/camera/app/CameraAppUI.java
@@ -441,6 +441,11 @@ public class CameraAppUI implements ModeListView.ModeSwitchListener,
public int minExposureCompensation;
public int maxExposureCompensation;
public float exposureCompensationStep;
+
+ /**
+ * Whether or not timer should show.
+ */
+ public boolean enableSelfTimer = false;
}
@@ -1733,6 +1738,12 @@ public class CameraAppUI implements ModeListView.ModeSwitchListener,
}
}
+ if (bottomBarSpec.enableSelfTimer) {
+ buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null);
+ } else {
+ buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN);
+ }
+
if (bottomBarSpec.enablePanoOrientation
&& PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) {
buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback);
diff --git a/src/com/android/camera/settings/SettingsCache.java b/src/com/android/camera/settings/SettingsCache.java
index e89bbd02f..bdd925340 100644
--- a/src/com/android/camera/settings/SettingsCache.java
+++ b/src/com/android/camera/settings/SettingsCache.java
@@ -137,6 +137,8 @@ public class SettingsCache {
SettingsManager.SETTING_EXPOSURE_COMPENSATION_ENABLED);
mKeyMap.put(SettingsManager.KEY_USER_SELECTED_ASPECT_RATIO,
SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO);
+ mKeyMap.put(SettingsManager.KEY_COUNTDOWN_DURATION,
+ SettingsManager.SETTING_COUNTDOWN_DURATION);
}
/**
@@ -225,6 +227,8 @@ public class SettingsCache {
return SettingsManager.getManualExposureCompensationSetting(mContext);
case SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO:
return SettingsManager.getUserSelectedAspectRatioSetting(mContext);
+ case SettingsManager.SETTING_COUNTDOWN_DURATION:
+ return SettingsManager.getCountDownDurationSetting(mContext);
default:
return mExtraSettings.settingFromId(id, mContext);
}
diff --git a/src/com/android/camera/settings/SettingsManager.java b/src/com/android/camera/settings/SettingsManager.java
index 288bc8622..7e8127446 100644
--- a/src/com/android/camera/settings/SettingsManager.java
+++ b/src/com/android/camera/settings/SettingsManager.java
@@ -394,6 +394,7 @@ public class SettingsManager {
public static final int SETTING_SHOULD_SHOW_REFOCUS_VIEWER_CLING = 31;
public static final int SETTING_EXPOSURE_COMPENSATION_ENABLED = 32;
public static final int SETTING_USER_SELECTED_ASPECT_RATIO = 33;
+ public static final int SETTING_COUNTDOWN_DURATION = 34;
// Shared preference keys.
public static final String KEY_RECORD_LOCATION = "pref_camera_recordlocation_key";
@@ -433,6 +434,7 @@ public class SettingsManager {
public static final String KEY_EXPOSURE_COMPENSATION_ENABLED =
"pref_camera_exposure_compensation_key";
public static final String KEY_USER_SELECTED_ASPECT_RATIO = "pref_user_selected_aspect_ratio";
+ public static final String KEY_COUNTDOWN_DURATION = "pref_camera_countdown_duration_key";
public static final int WHITE_BALANCE_DEFAULT_INDEX = 2;
@@ -968,6 +970,13 @@ public class SettingsManager {
KEY_EXPOSURE_COMPENSATION_ENABLED, values, FLUSH_OFF);
}
+ public static Setting getCountDownDurationSetting(Context context) {
+ String defaultValue = Integer.toString(0);
+ String[] values = context.getResources().getStringArray(R.array.pref_countdown_duration);
+ return new Setting(SOURCE_DEFAULT, TYPE_STRING, defaultValue,
+ KEY_COUNTDOWN_DURATION, values, FLUSH_OFF);
+ }
+
public static Setting getPictureSizeBackSetting(Context context) {
String defaultValue = null;
String[] values = null;
diff --git a/src/com/android/camera/ui/BottomBar.java b/src/com/android/camera/ui/BottomBar.java
index 9561cf6a5..224093cb2 100644
--- a/src/com/android/camera/ui/BottomBar.java
+++ b/src/com/android/camera/ui/BottomBar.java
@@ -88,7 +88,6 @@ public class BottomBar extends FrameLayout {
private float mCenterX;
private float mCenterY;
private final RectF mRect = new RectF();
- private final RectF mPreviewArea = new RectF();
private CaptureLayoutHelper mCaptureLayoutHelper = null;
public BottomBar(Context context, AttributeSet attrs) {
@@ -163,10 +162,10 @@ public class BottomBar extends FrameLayout {
mCancelLayout.setBackgroundColor(mBackgroundPressedColor);
} else if (MotionEvent.ACTION_UP == event.getActionMasked() ||
MotionEvent.ACTION_CANCEL == event.getActionMasked()) {
- mCancelLayout.setBackgroundColor(mBackgroundColor);
+ mCancelLayout.setBackgroundColor(mCircleColor);
} else if (MotionEvent.ACTION_MOVE == event.getActionMasked()) {
if (!mRect.contains(event.getX(), event.getY())) {
- mCancelLayout.setBackgroundColor(mBackgroundColor);
+ mCancelLayout.setBackgroundColor(mCircleColor);
}
}
return false;
@@ -205,6 +204,7 @@ public class BottomBar extends FrameLayout {
public void transitionToCancel() {
mCaptureLayout.setVisibility(View.INVISIBLE);
mIntentReviewLayout.setVisibility(View.INVISIBLE);
+ mCancelLayout.setBackgroundColor(mCircleColor);
mCancelLayout.setVisibility(View.VISIBLE);
mMode = MODE_CANCEL;
}
diff --git a/src/com/android/camera/ui/CountDownView.java b/src/com/android/camera/ui/CountDownView.java
new file mode 100644
index 000000000..8bedb6376
--- /dev/null
+++ b/src/com/android/camera/ui/CountDownView.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2014 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 java.util.Locale;
+
+import android.content.Context;
+import android.graphics.RectF;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.android.camera.debug.Log;
+import com.android.camera2.R;
+
+/**
+ * This class manages the looks of the countdown.
+ */
+public class CountDownView extends FrameLayout {
+
+ private static final Log.Tag TAG = new Log.Tag("CountDownView");
+ private static final int SET_TIMER_TEXT = 1;
+ private static final long ANIMATION_DURATION_MS = 800;
+ private TextView mRemainingSecondsView;
+ private int mRemainingSecs = 0;
+ private OnCountDownStatusListener mListener;
+ private final Handler mHandler = new MainHandler();
+ private final RectF mPreviewArea = new RectF();
+
+ /**
+ * Listener that gets notified when the countdown status has
+ * been updated (i.e. remaining seconds changed, or finished).
+ */
+ public interface OnCountDownStatusListener {
+ /**
+ * Gets notified when the remaining seconds for the countdown
+ * has changed.
+ *
+ * @param remainingSeconds seconds remained for countdown
+ */
+ public void onRemainingSecondsChanged(int remainingSeconds);
+
+ /**
+ * Gets called when countdown is finished.
+ */
+ public void onCountDownFinished();
+ }
+
+ public CountDownView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ /**
+ * Returns whether countdown is on-going.
+ */
+ public boolean isCountingDown() {
+ return mRemainingSecs > 0;
+ };
+
+ /**
+ * Responds to preview area change by centering th countdown UI in the new
+ * preview area.
+ */
+ public void onPreviewAreaChanged(RectF previewArea) {
+ mPreviewArea.set(previewArea);
+ }
+
+ private void remainingSecondsChanged(int newVal) {
+ mRemainingSecs = newVal;
+ if (mListener != null) {
+ mListener.onRemainingSecondsChanged(mRemainingSecs);
+ }
+
+ if (newVal == 0) {
+ // Countdown has finished.
+ setVisibility(View.INVISIBLE);
+ if (mListener != null) {
+ mListener.onCountDownFinished();
+ }
+ } else {
+ Locale locale = getResources().getConfiguration().locale;
+ String localizedValue = String.format(locale, "%d", newVal);
+ mRemainingSecondsView.setText(localizedValue);
+ // Fade-out animation.
+ startFadeOutAnimation();
+ // Schedule the next remainingSecondsChanged() call in 1 second
+ mHandler.sendEmptyMessageDelayed(SET_TIMER_TEXT, 1000);
+ }
+ }
+
+ private void startFadeOutAnimation() {
+ int textWidth = mRemainingSecondsView.getMeasuredWidth();
+ int textHeight = mRemainingSecondsView.getMeasuredHeight();
+ mRemainingSecondsView.setScaleX(1f);
+ mRemainingSecondsView.setScaleY(1f);
+ mRemainingSecondsView.setTranslationX(mPreviewArea.centerX() - textWidth / 2);
+ mRemainingSecondsView.setTranslationY(mPreviewArea.centerY() - textHeight / 2);
+ mRemainingSecondsView.setPivotX(textWidth / 2);
+ mRemainingSecondsView.setPivotY(textHeight / 2);
+ mRemainingSecondsView.setAlpha(1f);
+ float endScale = 2.5f;
+ mRemainingSecondsView.animate().scaleX(endScale).scaleY(endScale)
+ .alpha(0f).setDuration(ANIMATION_DURATION_MS).start();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mRemainingSecondsView = (TextView) findViewById(R.id.remaining_seconds);
+ }
+
+ /**
+ * Sets a listener that gets notified when the status of countdown has changed.
+ */
+ public void setCountDownStatusListener(OnCountDownStatusListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Starts showing countdown in the UI.
+ *
+ * @param sec duration of the countdown, in seconds
+ */
+ public void startCountDown(int sec) {
+ if (sec <= 0) {
+ Log.w(TAG, "Invalid input for countdown timer: " + sec + " seconds");
+ return;
+ }
+ if (isCountingDown()) {
+ cancelCountDown();
+ }
+ setVisibility(View.VISIBLE);
+ remainingSecondsChanged(sec);
+ }
+
+ /**
+ * Cancels the on-going countdown in the UI, if any.
+ */
+ public void cancelCountDown() {
+ if (mRemainingSecs > 0) {
+ mRemainingSecs = 0;
+ mHandler.removeMessages(SET_TIMER_TEXT);
+ setVisibility(View.INVISIBLE);
+ }
+ }
+
+ private class MainHandler extends Handler {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == SET_TIMER_TEXT) {
+ remainingSecondsChanged(mRemainingSecs -1);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/camera/widget/IndicatorIconController.java b/src/com/android/camera/widget/IndicatorIconController.java
index 610d9c71d..5392628fd 100644
--- a/src/com/android/camera/widget/IndicatorIconController.java
+++ b/src/com/android/camera/widget/IndicatorIconController.java
@@ -45,6 +45,7 @@ public class IndicatorIconController
private ImageView mFlashIndicator;
private ImageView mHdrIndicator;
private ImageView mPanoIndicator;
+ private ImageView mCountdownTimerIndicator;
private ImageView mExposureIndicatorN2;
private ImageView mExposureIndicatorN1;
@@ -56,6 +57,7 @@ public class IndicatorIconController
private TypedArray mHdrPlusIndicatorIcons;
private TypedArray mHdrIndicatorIcons;
private TypedArray mPanoIndicatorIcons;
+ private TypedArray mCountdownTimerIndicatorIcons;
private AppController mController;
@@ -82,6 +84,10 @@ public class IndicatorIconController
context.getResources().obtainTypedArray(panoIndicatorArrayId);
}
+ mCountdownTimerIndicator = (ImageView) root.findViewById(R.id.countdown_timer_indicator);
+ mCountdownTimerIndicatorIcons = context.getResources().obtainTypedArray(
+ R.array.pref_camera_countdown_indicators);
+
mExposureIndicatorN2 = (ImageView) root.findViewById(R.id.exposure_n2_indicator);
mExposureIndicatorN1 = (ImageView) root.findViewById(R.id.exposure_n1_indicator);
mExposureIndicatorP1 = (ImageView) root.findViewById(R.id.exposure_p1_indicator);
@@ -139,6 +145,7 @@ public class IndicatorIconController
syncHdrIndicator();
syncPanoIndicator();
syncExposureIndicator();
+ syncCountdownTimerIndicator();
}
/**
@@ -264,6 +271,19 @@ public class IndicatorIconController
}
}
+ private void syncCountdownTimerIndicator() {
+ ButtonManager buttonManager = mController.getButtonManager();
+
+ if (buttonManager.isEnabled(ButtonManager.BUTTON_COUNTDOWN)
+ && buttonManager.isVisible(ButtonManager.BUTTON_COUNTDOWN)) {
+ setIndicatorState(mController.getSettingsManager(),
+ SettingsManager.SETTING_COUNTDOWN_DURATION,
+ mCountdownTimerIndicator, mCountdownTimerIndicatorIcons, false);
+ } else {
+ changeVisibility(mCountdownTimerIndicator, View.GONE);
+ }
+ }
+
/**
* Sets the image resource and visibility of the indicator
* based on the indicator's corresponding setting state.
@@ -326,9 +346,13 @@ public class IndicatorIconController
syncExposureIndicator();
break;
}
+ case SettingsManager.SETTING_COUNTDOWN_DURATION:
+ syncCountdownTimerIndicator();
+ break;
default: {
// Do nothing.
}
}
}
+
}