summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Hoford <hoford@google.com>2015-04-01 23:22:06 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2015-04-01 23:22:06 +0000
commit826aebc96c13d951653d14c05d9f55ec1283ec30 (patch)
treeab88a210235fa87351c6535dec943e2ec826d732
parent9270cd93e444d11d6e1b49653613409f34a0cc35 (diff)
parentfbb9dd1843197a0d2f7fcda29abbe9d170682a5d (diff)
downloadrs-826aebc96c13d951653d14c05d9f55ec1283ec30.tar.gz
Merge "inital checkin of RenderScript Camera Demo"
-rw-r--r--java/tests/RsCameraDemo/Android.mk27
-rw-r--r--java/tests/RsCameraDemo/AndroidManifest.xml31
-rw-r--r--java/tests/RsCameraDemo/_index.html6
-rw-r--r--java/tests/RsCameraDemo/res/anim/slide_in_from_left.xml26
-rw-r--r--java/tests/RsCameraDemo/res/anim/slide_out_to_right.xml25
-rw-r--r--java/tests/RsCameraDemo/res/drawable-hdpi/camera.pngbin0 -> 6440 bytes
-rw-r--r--java/tests/RsCameraDemo/res/drawable-mdpi/camera.pngbin0 -> 3329 bytes
-rw-r--r--java/tests/RsCameraDemo/res/drawable-xhdpi/camera.pngbin0 -> 9030 bytes
-rw-r--r--java/tests/RsCameraDemo/res/drawable-xxhdpi/camera.pngbin0 -> 17007 bytes
-rw-r--r--java/tests/RsCameraDemo/res/drawable/ic_back.xml27
-rw-r--r--java/tests/RsCameraDemo/res/drawable/ic_cam.xml28
-rw-r--r--java/tests/RsCameraDemo/res/layout/activity_main.xml120
-rw-r--r--java/tests/RsCameraDemo/res/values-v21/styles.xml19
-rw-r--r--java/tests/RsCameraDemo/res/values-w820dp/dimens.xml6
-rw-r--r--java/tests/RsCameraDemo/res/values/attrs.xml21
-rw-r--r--java/tests/RsCameraDemo/res/values/base-strings.xml30
-rw-r--r--java/tests/RsCameraDemo/res/values/strings.xml59
-rw-r--r--java/tests/RsCameraDemo/res/values/template-dimens.xml31
-rw-r--r--java/tests/RsCameraDemo/res/values/template-styles.xml36
-rw-r--r--java/tests/RsCameraDemo/src/com/android/example/rscamera/CameraOps.java671
-rw-r--r--java/tests/RsCameraDemo/src/com/android/example/rscamera/CameraView.java339
-rw-r--r--java/tests/RsCameraDemo/src/com/android/example/rscamera/FixedAspectSurfaceView.java157
-rw-r--r--java/tests/RsCameraDemo/src/com/android/example/rscamera/MainActivity.java176
-rw-r--r--java/tests/RsCameraDemo/src/com/android/example/rscamera/MediaStoreSaver.java155
-rw-r--r--java/tests/RsCameraDemo/src/com/android/example/rscamera/VerticalSeekBar.java78
-rw-r--r--java/tests/RsCameraDemo/src/com/android/example/rscamera/ViewfinderProcessor.java128
-rw-r--r--java/tests/RsCameraDemo/src/com/android/example/rscamera/focus_peak.rs71
27 files changed, 2267 insertions, 0 deletions
diff --git a/java/tests/RsCameraDemo/Android.mk b/java/tests/RsCameraDemo/Android.mk
new file mode 100644
index 00000000..877d2d29
--- /dev/null
+++ b/java/tests/RsCameraDemo/Android.mk
@@ -0,0 +1,27 @@
+#
+# Copyright (C) 2015 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+LOCAL_SDK_VERSION := 21
+
+LOCAL_PACKAGE_NAME := RsCameraDemo
+
+include $(BUILD_PACKAGE)
diff --git a/java/tests/RsCameraDemo/AndroidManifest.xml b/java/tests/RsCameraDemo/AndroidManifest.xml
new file mode 100644
index 00000000..2dd259d3
--- /dev/null
+++ b/java/tests/RsCameraDemo/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.example.rscamera.rscamera" >
+ <uses-feature android:name="android.hardware.camera" />
+ <uses-feature
+ android:name="android.hardware.camera.front"
+ android:required="false" />
+
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/camera"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name="com.android.example.rscamera.MainActivity"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/java/tests/RsCameraDemo/_index.html b/java/tests/RsCameraDemo/_index.html
new file mode 100644
index 00000000..5f8c5f7e
--- /dev/null
+++ b/java/tests/RsCameraDemo/_index.html
@@ -0,0 +1,6 @@
+<h1>RenderScript Camera Demo</h1>
+<h2>Am example camera with live processing in RenderScript</h2>
+<p>
+This demonstrates focus peaking implemented in RenderScript
+</p>
+
diff --git a/java/tests/RsCameraDemo/res/anim/slide_in_from_left.xml b/java/tests/RsCameraDemo/res/anim/slide_in_from_left.xml
new file mode 100644
index 00000000..75d05bb9
--- /dev/null
+++ b/java/tests/RsCameraDemo/res/anim/slide_in_from_left.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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"
+ android:shareInterpolator="false">
+
+ <translate
+ android:fromXDelta="-100%"
+ android:toXDelta="0%"
+ android:fromYDelta="0%"
+ android:toYDelta="0%"
+ android:duration="500" />
+
+</set>
diff --git a/java/tests/RsCameraDemo/res/anim/slide_out_to_right.xml b/java/tests/RsCameraDemo/res/anim/slide_out_to_right.xml
new file mode 100644
index 00000000..20ee06fc
--- /dev/null
+++ b/java/tests/RsCameraDemo/res/anim/slide_out_to_right.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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"
+ android:shareInterpolator="false">
+ <translate
+ android:fromXDelta="0%"
+ android:toXDelta="100%"
+ android:fromYDelta="0%"
+ android:toYDelta="0%"
+ android:duration="500"/>
+
+</set> \ No newline at end of file
diff --git a/java/tests/RsCameraDemo/res/drawable-hdpi/camera.png b/java/tests/RsCameraDemo/res/drawable-hdpi/camera.png
new file mode 100644
index 00000000..59908a68
--- /dev/null
+++ b/java/tests/RsCameraDemo/res/drawable-hdpi/camera.png
Binary files differ
diff --git a/java/tests/RsCameraDemo/res/drawable-mdpi/camera.png b/java/tests/RsCameraDemo/res/drawable-mdpi/camera.png
new file mode 100644
index 00000000..228cb966
--- /dev/null
+++ b/java/tests/RsCameraDemo/res/drawable-mdpi/camera.png
Binary files differ
diff --git a/java/tests/RsCameraDemo/res/drawable-xhdpi/camera.png b/java/tests/RsCameraDemo/res/drawable-xhdpi/camera.png
new file mode 100644
index 00000000..ca6c9f80
--- /dev/null
+++ b/java/tests/RsCameraDemo/res/drawable-xhdpi/camera.png
Binary files differ
diff --git a/java/tests/RsCameraDemo/res/drawable-xxhdpi/camera.png b/java/tests/RsCameraDemo/res/drawable-xxhdpi/camera.png
new file mode 100644
index 00000000..6534f06a
--- /dev/null
+++ b/java/tests/RsCameraDemo/res/drawable-xxhdpi/camera.png
Binary files differ
diff --git a/java/tests/RsCameraDemo/res/drawable/ic_back.xml b/java/tests/RsCameraDemo/res/drawable/ic_back.xml
new file mode 100644
index 00000000..1cbfdb22
--- /dev/null
+++ b/java/tests/RsCameraDemo/res/drawable/ic_back.xml
@@ -0,0 +1,27 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:autoMirrored="true"
+ >
+ <!--android:tint="?attr/colorControlNormal">-->
+ <path
+ android:pathData="M20,11L7.8,11l5.6,-5.6L12,4l-8,8l8,8l1.4,-1.4L7.8,13L20,13L20,11z"
+ android:fillColor="@android:color/black"/>
+</vector>
diff --git a/java/tests/RsCameraDemo/res/drawable/ic_cam.xml b/java/tests/RsCameraDemo/res/drawable/ic_cam.xml
new file mode 100644
index 00000000..fbeedc68
--- /dev/null
+++ b/java/tests/RsCameraDemo/res/drawable/ic_cam.xml
@@ -0,0 +1,28 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.0,12.0m-3.2,0.0a3.2,3.2 0.0,1.0 1.0,6.4 0.0a3.2,3.2 0.0,1.0 1.0,-6.4 0.0"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9.0,2.0l-1.83,2.0l-3.17,0.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,12.0c0.0,1.0 0.9,2.0 2.0,2.0l16.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0l0.0,-12.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0l-3.17,0.0l-1.83,-2.0l-6.0,0.0zm3.0,15.0c-2.76,0.0 -5.0,-2.24 -5.0,-5.0s2.24,-5.0 5.0,-5.0 5.0,2.24 5.0,5.0 -2.24,5.0 -5.0,5.0z"/>
+
+</vector> \ No newline at end of file
diff --git a/java/tests/RsCameraDemo/res/layout/activity_main.xml b/java/tests/RsCameraDemo/res/layout/activity_main.xml
new file mode 100644
index 00000000..8ad2c0ef
--- /dev/null
+++ b/java/tests/RsCameraDemo/res/layout/activity_main.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<LinearLayout
+ android:orientation="horizontal"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:custom="http://schemas.android.com/apk/res-auto"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/panels">
+
+ <com.android.example.rscamera.CameraView
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:id="@+id/preview"
+ custom:aspectRatio="1.333"
+ android:layout_weight="4"
+ android:layout_gravity="center_vertical" />
+
+
+ <ViewFlipper
+ android:layout_height="match_parent"
+ android:layout_width="0px"
+ android:id="@+id/viewFlipper"
+ android:layout_weight="1">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:id="@+id/control_bar_contents">
+
+ <Button
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:id="@+id/iso"
+ android:layout_gravity="center_horizontal"
+ android:onClick="setISO"
+ android:text="A ISO 1600" />
+
+ <Button
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:id="@+id/speed"
+ android:layout_gravity="center_horizontal"
+ android:onClick="setShutterSpeed"
+ android:text="A 1/30 s" />
+
+ <Button
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:id="@+id/focus"
+ android:layout_gravity="center_horizontal"
+ android:onClick="setFocus"
+ android:text="A 12.0m" />
+
+ <ImageButton
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_gravity="center_horizontal"
+ android:onClick="capture"
+ android:src="@drawable/ic_cam" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:id="@+id/slide">
+
+ <ImageButton
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:id="@+id/back"
+ android:layout_gravity="center_horizontal"
+ android:onClick="back"
+ android:src="@drawable/ic_back"
+ />
+
+ <ImageButton
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:id="@+id/capture"
+ android:layout_gravity="center_horizontal"
+ android:onClick="capture"
+ android:src="@drawable/ic_cam" />
+
+ <RelativeLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+
+ <com.android.example.rscamera.VerticalSeekBar
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:id="@+id/focusbar"
+ android:layout_alignParentTop="true"
+ android:layout_centerInParent="true" />
+
+ </RelativeLayout>
+
+ </LinearLayout>
+
+ </ViewFlipper>
+
+</LinearLayout> \ No newline at end of file
diff --git a/java/tests/RsCameraDemo/res/values-v21/styles.xml b/java/tests/RsCameraDemo/res/values-v21/styles.xml
new file mode 100644
index 00000000..f9166cca
--- /dev/null
+++ b/java/tests/RsCameraDemo/res/values-v21/styles.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<resources>
+ <style name="AppTheme" parent="android:Theme.Material.Light">
+ </style>
+</resources>
diff --git a/java/tests/RsCameraDemo/res/values-w820dp/dimens.xml b/java/tests/RsCameraDemo/res/values-w820dp/dimens.xml
new file mode 100644
index 00000000..62df1875
--- /dev/null
+++ b/java/tests/RsCameraDemo/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+<resources>
+ <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+ (such as screen margins) for screens with more than 820dp of available width. This
+ would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/java/tests/RsCameraDemo/res/values/attrs.xml b/java/tests/RsCameraDemo/res/values/attrs.xml
new file mode 100644
index 00000000..92216075
--- /dev/null
+++ b/java/tests/RsCameraDemo/res/values/attrs.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<resources>
+ <declare-styleable name="FixedAspectSurfaceView">
+ <attr name="aspectRatio" format="float"/>
+ </declare-styleable>
+</resources>
diff --git a/java/tests/RsCameraDemo/res/values/base-strings.xml b/java/tests/RsCameraDemo/res/values/base-strings.xml
new file mode 100644
index 00000000..d8f8952e
--- /dev/null
+++ b/java/tests/RsCameraDemo/res/values/base-strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<resources>
+ <string name="app_name">RS Camera</string>
+ <string name="intro_message">
+ <![CDATA[
+
+
+ This demo implements a real-time high-dynamic-range camera viewfinder, by alternating
+ the sensor\'s exposure time between two exposure values on even and odd frames, and then
+ compositing together the latest two frames whenever a new frame is captured.
+
+
+ ]]>
+ </string>
+</resources>
diff --git a/java/tests/RsCameraDemo/res/values/strings.xml b/java/tests/RsCameraDemo/res/values/strings.xml
new file mode 100644
index 00000000..21838d54
--- /dev/null
+++ b/java/tests/RsCameraDemo/res/values/strings.xml
@@ -0,0 +1,59 @@
+<?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.
+-->
+
+<resources>
+
+ <string name="help_button">Help</string>
+
+ <string-array name="mode_label_array">
+ <!-- must be in same order as ViewfinderProcessor.MODE_ ints -->
+ <item>Mode: Normal</item>
+ <item>Mode: Split</item>
+ <item>Mode: HDR</item>
+ </string-array>
+
+ <string name="auto_exposure_label">Auto exp. time:</string>
+ <string name="even_exposure_label">Even exp. time:</string>
+ <string name="odd_exposure_label">Odd exp. time:</string>
+
+ <string name="help_text">
+ <b>HDR Viewfinder Demo:</b>\n\n
+
+ Tap viewfinder to switch modes.\n\n
+
+ <b>Normal:</b> Standard camera preview\n
+ <b>Split:</b> Manual exposure control\n
+ <b>HDR:</b> Fused HDR viewfinder\n\n
+
+ Swipe up/down in Split/HDR modes to change manual exposure
+ values.\n\n
+
+ The left half of the viewfinder controls exposure time for
+ even-numbered frames, and the right half of the viewfinder
+ controls exposure time for odd-numbered frames
+ </string>
+
+ <string name="info">Info</string>
+
+ <string name="camera_no_good">No back-facing sufficiently capable camera available!</string>
+ <string name="camera_disabled">Camera is disabled by device policy</string>
+ <string name="camera_disconnected">Camera was disconnected before it was opened</string>
+ <string name="camera_error">Camera service reported an error</string>
+ <string name="camera_unknown">Unknown camera error: %s</string>
+
+</resources>
diff --git a/java/tests/RsCameraDemo/res/values/template-dimens.xml b/java/tests/RsCameraDemo/res/values/template-dimens.xml
new file mode 100644
index 00000000..291c4953
--- /dev/null
+++ b/java/tests/RsCameraDemo/res/values/template-dimens.xml
@@ -0,0 +1,31 @@
+<!-- Copyright (C) 2015 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.
+-->
+
+<resources>
+
+ <!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
+
+ <dimen name="margin_tiny">4dp</dimen>
+ <dimen name="margin_small">8dp</dimen>
+ <dimen name="margin_medium">16dp</dimen>
+ <dimen name="margin_large">32dp</dimen>
+ <dimen name="margin_huge">64dp</dimen>
+
+ <!-- Semantic definitions -->
+
+ <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
+ <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+
+</resources>
diff --git a/java/tests/RsCameraDemo/res/values/template-styles.xml b/java/tests/RsCameraDemo/res/values/template-styles.xml
new file mode 100644
index 00000000..c6dc4738
--- /dev/null
+++ b/java/tests/RsCameraDemo/res/values/template-styles.xml
@@ -0,0 +1,36 @@
+<!-- Copyright (C) 2015 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.
+-->
+
+<resources>
+
+ <!-- Activity themes -->
+
+ <style name="Theme.Base" parent="android:Theme.Light" />
+
+ <style name="Theme.Sample" parent="Theme.Base" />
+
+ <style name="AppTheme" parent="Theme.Sample" />
+ <!-- Widget styling -->
+
+ <style name="Widget" />
+
+ <style name="Widget.SampleMessage">
+ <item name="android:textAppearance">?android:textAppearanceMedium</item>
+ <item name="android:lineSpacingMultiplier">1.1</item>
+ </style>
+
+
+
+</resources>
diff --git a/java/tests/RsCameraDemo/src/com/android/example/rscamera/CameraOps.java b/java/tests/RsCameraDemo/src/com/android/example/rscamera/CameraOps.java
new file mode 100644
index 00000000..d9ea9e82
--- /dev/null
+++ b/java/tests/RsCameraDemo/src/com/android/example/rscamera/CameraOps.java
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2015 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.example.rscamera;
+
+import android.content.ContentResolver;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.util.Log;
+import android.util.Range;
+import android.util.Size;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.widget.Toast;
+
+import com.android.example.rscamera.rscamera.R;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Simple interface for operating the camera, with major camera operations
+ * all performed on a background handler thread.
+ */
+public class CameraOps {
+
+ private static final String TAG = "CameraOps";
+ private static final long ONE_SECOND = 1000000000;
+ public static final long CAMERA_CLOSE_TIMEOUT = 2000; // ms
+
+ private final CameraManager mCameraManager;
+ private CameraDevice mCameraDevice;
+ private CameraCaptureSession mCameraSession;
+ private List<Surface> mSurfaces;
+
+ private final ConditionVariable mCloseWaiter = new ConditionVariable();
+
+ private HandlerThread mCameraThread;
+ private Handler mCameraHandler;
+
+ private final ErrorDisplayer mErrorDisplayer;
+
+ private final CameraReadyListener mReadyListener;
+ private final Handler mReadyHandler;
+
+ private int mISOmax;
+ private int mISOmin;
+ private long mExpMax;
+ private long mExpMin;
+ private float mFocusMin;
+ private float mFocusDist = 0;
+ private int mIso;
+ boolean mAutoExposure = true;
+ boolean mAutoFocus = true;
+ private long mExposure = ONE_SECOND / 33;
+
+ private Object mAutoExposureTag = new Object();
+
+ private ImageReader mImageReader;
+ private Handler mBackgroundHandler;
+ private CameraCharacteristics mCameraInfo;
+ private HandlerThread mBackgroundThread;
+ CaptureRequest.Builder mHdrBuilder;
+ private Surface mProcessingNormalSurface;
+ CaptureRequest mPreviewRequest;
+ private String mSaveFileName;
+ private ContentResolver mContentResolver;
+
+ public String resume() {
+ String errorMessage = "Unknown error";
+ boolean foundCamera = false;
+ try {
+ // Find first back-facing camera that has necessary capability
+ String[] cameraIds = mCameraManager.getCameraIdList();
+ for (String id : cameraIds) {
+ CameraCharacteristics info = mCameraManager.getCameraCharacteristics(id);
+ int facing = info.get(CameraCharacteristics.LENS_FACING);
+
+ int level = info.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+ boolean hasFullLevel
+ = (level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
+
+ int[] capabilities = info.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+ int syncLatency = info.get(CameraCharacteristics.SYNC_MAX_LATENCY);
+ boolean hasManualControl = hasCapability(capabilities,
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR);
+ boolean hasEnoughCapability = hasManualControl &&
+ syncLatency == CameraCharacteristics.SYNC_MAX_LATENCY_PER_FRAME_CONTROL;
+ Range<Integer> irange;
+ Range<Long> lrange;
+
+ irange = info.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE);
+ mISOmax = irange.getUpper();
+ mISOmin = irange.getLower();
+ lrange = info.get(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE);
+ mExpMax = lrange.getUpper();
+ mExpMin = lrange.getLower();
+ mFocusMin = info.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE);
+
+ mFocusDist = mFocusMin;
+ StreamConfigurationMap map = info.get(
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ Size[] sizes = map.getOutputSizes(ImageFormat.JPEG);
+ Size largest = Collections.max(Arrays.asList(sizes), new Comparator<Size>() {
+ @Override
+ public int compare(Size lhs, Size rhs) {
+ int leftArea = lhs.getHeight() * lhs.getWidth();
+ int rightArea = lhs.getHeight() * lhs.getWidth();
+ return Integer.compare(leftArea, rightArea);
+ }
+ });
+ mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
+ ImageFormat.JPEG, /*maxImages*/2);
+ mImageReader.setOnImageAvailableListener(
+ mOnImageAvailableListener, mBackgroundHandler);
+
+ if (facing == CameraCharacteristics.LENS_FACING_BACK &&
+ (hasFullLevel || hasEnoughCapability)) {
+ // Found suitable camera - get info, open, and set up outputs
+ mCameraInfo = info;
+ openCamera(id);
+ foundCamera = true;
+ break;
+ }
+ }
+ if (!foundCamera) {
+ errorMessage = "no back camera";
+ }
+ } catch (CameraAccessException e) {
+ errorMessage = e.getMessage();
+ }
+ // startBackgroundThread
+ mBackgroundThread = new HandlerThread("CameraBackground");
+ mBackgroundThread.start();
+ mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
+ return (foundCamera) ? null : errorMessage;
+ }
+
+
+ private boolean hasCapability(int[] capabilities, int capability) {
+ for (int c : capabilities) {
+ if (c == capability) return true;
+ }
+ return false;
+ }
+
+ private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
+ = new ImageReader.OnImageAvailableListener() {
+
+ @Override
+ public void onImageAvailable(ImageReader reader) {
+ mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mSaveFileName, mContentResolver));
+ }
+
+ };
+
+ /**
+ * Saves a JPEG {@link android.media.Image} into the specified {@link java.io.File}.
+ */
+ private static class ImageSaver implements Runnable {
+ private final Image mImage;
+ private final String mName;
+ ContentResolver mContentResolver;
+
+ public ImageSaver(Image image, String fileName, ContentResolver contentResolver) {
+ mImage = image;
+ mName = fileName;
+ mContentResolver = contentResolver;
+ }
+
+ @Override
+ public void run() {
+ Log.v(TAG, "SAVING...");
+ MediaStoreSaver.insertImage(mContentResolver, new MediaStoreSaver.StreamWriter() {
+ @Override
+ public void write(OutputStream imageOut) throws IOException {
+ try {
+ ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
+ byte[] bytes = new byte[buffer.remaining()];
+ buffer.get(bytes);
+ imageOut.write(bytes);
+ } finally {
+ mImage.close();
+ }
+ }
+ }, mName, "Saved from Simple Camera Demo");
+ }
+ }
+
+ /**
+ * Create a new camera ops thread.
+ *
+ * @param errorDisplayer listener for displaying error messages
+ * @param readyListener listener for notifying when camera is ready for requests
+ */
+ CameraOps(CameraManager manager, ErrorDisplayer errorDisplayer,
+ CameraReadyListener readyListener) {
+ mReadyHandler = new Handler(Looper.getMainLooper());
+
+ mCameraThread = new HandlerThread("CameraOpsThread");
+ mCameraThread.start();
+
+ if (manager == null || errorDisplayer == null ||
+ readyListener == null || mReadyHandler == null) {
+ throw new IllegalArgumentException("Need valid displayer, listener, handler");
+ }
+
+ mCameraManager = manager;
+ mErrorDisplayer = errorDisplayer;
+ mReadyListener = readyListener;
+
+ }
+
+ /**
+ * Open the first backfacing camera listed by the camera manager.
+ * Displays a dialog if it cannot open a camera.
+ */
+ public void openCamera(final String cameraId) {
+ mCameraHandler = new Handler(mCameraThread.getLooper());
+
+ mCameraHandler.post(new Runnable() {
+ public void run() {
+ if (mCameraDevice != null) {
+ throw new IllegalStateException("Camera already open");
+ }
+ try {
+
+ mCameraManager.openCamera(cameraId, mCameraDeviceListener, mCameraHandler);
+ } catch (CameraAccessException e) {
+ String errorMessage = mErrorDisplayer.getErrorString(e);
+ mErrorDisplayer.showErrorDialog(errorMessage);
+ }
+ }
+ });
+ }
+
+ public void pause() {
+ closeCameraAndWait();
+ mBackgroundThread.quitSafely();
+ try {
+ mBackgroundThread.join();
+ mBackgroundThread = null;
+ mBackgroundHandler = null;
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public Size getBestSize() {
+ // Find a good size for output - largest 16:9 aspect ratio that's less than 720p
+ final int MAX_WIDTH = 1280;
+ final float TARGET_ASPECT = 16.f / 9.f;
+ final float ASPECT_TOLERANCE = 0.1f;
+
+
+ StreamConfigurationMap configs =
+ mCameraInfo.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+ Size[] outputSizes = configs.getOutputSizes(SurfaceHolder.class);
+
+ Size outputSize = outputSizes[0];
+ float outputAspect = (float) outputSize.getWidth() / outputSize.getHeight();
+ for (Size candidateSize : outputSizes) {
+ if (candidateSize.getWidth() > MAX_WIDTH) continue;
+ float candidateAspect = (float) candidateSize.getWidth() / candidateSize.getHeight();
+ boolean goodCandidateAspect =
+ Math.abs(candidateAspect - TARGET_ASPECT) < ASPECT_TOLERANCE;
+ boolean goodOutputAspect =
+ Math.abs(outputAspect - TARGET_ASPECT) < ASPECT_TOLERANCE;
+ if ((goodCandidateAspect && !goodOutputAspect) ||
+ candidateSize.getWidth() > outputSize.getWidth()) {
+ outputSize = candidateSize;
+ outputAspect = candidateAspect;
+ }
+ }
+ return outputSize;
+ }
+
+ /**
+ * Close the camera and wait for the close callback to be called in the camera thread.
+ * Times out after @{value CAMERA_CLOSE_TIMEOUT} ms.
+ */
+ public void closeCameraAndWait() {
+ mCloseWaiter.close();
+ mCameraHandler.post(mCloseCameraRunnable);
+ boolean closed = mCloseWaiter.block(CAMERA_CLOSE_TIMEOUT);
+ if (!closed) {
+ Log.e(TAG, "Timeout closing camera");
+ }
+ }
+
+ private Runnable mCloseCameraRunnable = new Runnable() {
+ public void run() {
+ if (mCameraDevice != null) {
+ mCameraDevice.close();
+ }
+ mCameraDevice = null;
+ mCameraSession = null;
+ mSurfaces = null;
+ }
+ };
+
+ /**
+ * Set the output Surfaces, and finish configuration if otherwise ready.
+ */
+ public void setSurface(Surface surface) {
+ final List<Surface> surfaceList = new ArrayList<Surface>();
+ surfaceList.add(surface);
+ surfaceList.add(mImageReader.getSurface());
+
+ mCameraHandler.post(new Runnable() {
+ public void run() {
+ mSurfaces = surfaceList;
+ startCameraSession();
+ }
+ });
+ }
+
+ /**
+ * Get a request builder for the current camera.
+ */
+ public CaptureRequest.Builder createCaptureRequest(int template) throws CameraAccessException {
+ CameraDevice device = mCameraDevice;
+ if (device == null) {
+ throw new IllegalStateException("Can't get requests when no camera is open");
+ }
+ return device.createCaptureRequest(template);
+ }
+
+ /**
+ * Set a repeating request.
+ */
+ public void setRepeatingRequest(final CaptureRequest request,
+ final CameraCaptureSession.CaptureCallback listener,
+ final Handler handler) {
+ mCameraHandler.post(new Runnable() {
+ public void run() {
+ try {
+ mCameraSession.setRepeatingRequest(request, listener, handler);
+ } catch (CameraAccessException e) {
+ String errorMessage = mErrorDisplayer.getErrorString(e);
+ mErrorDisplayer.showErrorDialog(errorMessage);
+ }
+ }
+ });
+ }
+
+ /**
+ * Set a repeating request.
+ */
+ public void setRepeatingBurst(final List<CaptureRequest> requests,
+ final CameraCaptureSession.CaptureCallback listener,
+ final Handler handler) {
+ mCameraHandler.post(new Runnable() {
+ public void run() {
+ try {
+ mCameraSession.setRepeatingBurst(requests, listener, handler);
+
+ } catch (CameraAccessException e) {
+ String errorMessage = mErrorDisplayer.getErrorString(e);
+ mErrorDisplayer.showErrorDialog(errorMessage);
+ }
+ }
+ });
+ }
+
+ /**
+ * Configure the camera session.
+ */
+ private void startCameraSession() {
+ // Wait until both the camera device is open and the SurfaceView is ready
+ if (mCameraDevice == null || mSurfaces == null) return;
+
+ try {
+
+ mCameraDevice.createCaptureSession(
+ mSurfaces, mCameraSessionListener, mCameraHandler);
+ } catch (CameraAccessException e) {
+ String errorMessage = mErrorDisplayer.getErrorString(e);
+ mErrorDisplayer.showErrorDialog(errorMessage);
+ mCameraDevice.close();
+ mCameraDevice = null;
+ }
+ }
+
+ /**
+ * Main listener for camera session events
+ * Invoked on mCameraThread
+ */
+ private CameraCaptureSession.StateCallback mCameraSessionListener =
+ new CameraCaptureSession.StateCallback() {
+
+ @Override
+ public void onConfigured(CameraCaptureSession session) {
+ mCameraSession = session;
+ mReadyHandler.post(new Runnable() {
+ public void run() {
+ // This can happen when the screen is turned off and turned back on.
+ if (null == mCameraDevice) {
+ return;
+ }
+
+ mReadyListener.onCameraReady();
+ }
+ });
+
+ }
+
+ @Override
+ public void onConfigureFailed(CameraCaptureSession session) {
+ mErrorDisplayer.showErrorDialog("Unable to configure the capture session");
+ mCameraDevice.close();
+ mCameraDevice = null;
+ }
+ };
+
+ /**
+ * Main listener for camera device events.
+ * Invoked on mCameraThread
+ */
+ private CameraDevice.StateCallback mCameraDeviceListener = new CameraDevice.StateCallback() {
+
+ @Override
+ public void onOpened(CameraDevice camera) {
+ mCameraDevice = camera;
+ startCameraSession();
+ }
+
+ @Override
+ public void onClosed(CameraDevice camera) {
+ mCloseWaiter.open();
+ }
+
+ @Override
+ public void onDisconnected(CameraDevice camera) {
+ mErrorDisplayer.showErrorDialog("The camera device has been disconnected.");
+ camera.close();
+ mCameraDevice = null;
+ }
+
+ @Override
+ public void onError(CameraDevice camera, int error) {
+ mErrorDisplayer.showErrorDialog("The camera encountered an error:" + error);
+ camera.close();
+ mCameraDevice = null;
+ }
+
+ };
+
+ public void captureStillPicture(int currentJpegRotation, String name, ContentResolver resolver) {
+ mSaveFileName = name;
+ mContentResolver = resolver;
+ try {
+ // TODO call lock focus if we are in "AF-S(One-Shot AF) mode"
+ // TODO call precapture if we are using flash
+ // This is the CaptureRequest.Builder that we use to take a picture.
+ final CaptureRequest.Builder captureBuilder =
+ createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+ Log.v(TAG, " Target " + mImageReader.getWidth() + "," + mImageReader.getHeight());
+
+ captureBuilder.addTarget(mImageReader.getSurface());
+
+ captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, currentJpegRotation);
+
+ CameraCaptureSession.CaptureCallback captureCallback
+ = new CameraCaptureSession.CaptureCallback() {
+
+ @Override
+ public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+ TotalCaptureResult result) {
+ Log.v(TAG, " onCaptureCompleted");
+ setParameters();
+ }
+ };
+
+
+ setRequest(captureBuilder.build(), captureCallback, null);
+ } catch (CameraAccessException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Set a repeating request.
+ */
+ private void setRequest(final CaptureRequest request,
+ final CameraCaptureSession.CaptureCallback listener,
+ final Handler handler) {
+ mCameraHandler.post(new Runnable() {
+ public void run() {
+ try {
+ mCameraSession.stopRepeating();
+ mCameraSession.capture(request, listener, handler);
+ } catch (CameraAccessException e) {
+ String errorMessage = mErrorDisplayer.getErrorString(e);
+ mErrorDisplayer.showErrorDialog(errorMessage);
+ }
+ }
+ });
+ }
+
+ public void setUpCamera(Surface processingNormalSurface) {
+ mProcessingNormalSurface = processingNormalSurface;
+ // Ready to send requests in, so set them up
+ try {
+ CaptureRequest.Builder previewBuilder =
+ createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ previewBuilder.addTarget(mProcessingNormalSurface);
+ previewBuilder.setTag(mAutoExposureTag);
+ mPreviewRequest = previewBuilder.build();
+ mHdrBuilder = createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ mHdrBuilder.set(CaptureRequest.CONTROL_AE_MODE,
+ CaptureRequest.CONTROL_AE_MODE_OFF);
+ mHdrBuilder.addTarget(mProcessingNormalSurface);
+ setParameters();
+
+ } catch (CameraAccessException e) {
+ String errorMessage = e.getMessage();
+ // MessageDialogFragment.newInstance(errorMessage).show(getFragmentManager(), FRAGMENT_DIALOG);
+ }
+ }
+
+ /**
+ * Start running an HDR burst on a configured camera session
+ */
+ public void setParameters() {
+ if (mHdrBuilder == null) {
+ Log.v(TAG," Camera not set up");
+ return;
+ }
+ if (mAutoExposure) {
+ mHdrBuilder.set(CaptureRequest.SENSOR_FRAME_DURATION, ONE_SECOND / 30);
+ mHdrBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, getExposure());
+ mHdrBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
+ } else {
+ mHdrBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF);
+ mHdrBuilder.set(CaptureRequest.SENSOR_FRAME_DURATION, ONE_SECOND / 30);
+ mHdrBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, getExposure());
+ mHdrBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, getIso());
+ }
+ if (mAutoFocus) {
+ mHdrBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
+ mHdrBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
+ } else {
+ mHdrBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
+ mHdrBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, getFocusDistance());
+ mHdrBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
+ }
+
+ setRepeatingRequest(mHdrBuilder.build(), mCaptureCallback, mReadyHandler);
+ }
+
+ private CameraCaptureSession.CaptureCallback mCaptureCallback
+ = new CameraCaptureSession.CaptureCallback() {
+
+ public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+ TotalCaptureResult result) {
+ }
+ };
+
+ /**
+ * Simple listener for main code to know the camera is ready for requests, or failed to
+ * start.
+ */
+ public interface CameraReadyListener {
+ public void onCameraReady();
+ }
+
+ /**
+ * Simple listener for displaying error messages
+ */
+ public interface ErrorDisplayer {
+ public void showErrorDialog(String errorMessage);
+ public String getErrorString(CameraAccessException e);
+ }
+
+ public float getFocusDistance() {
+ return mFocusDist;
+ }
+
+ public void setFocusDistance(float focusDistance) {
+ mFocusDist = focusDistance;
+ }
+
+ public void setIso(int iso) {
+ mIso = iso;
+ }
+
+ public boolean isAutoExposure() {
+ return mAutoExposure;
+ }
+
+ public void setAutoExposure(boolean autoExposure) {
+ mAutoExposure = autoExposure;
+ }
+
+ public boolean isAutoFocus() {
+ return mAutoFocus;
+ }
+
+ public void setAutoFocus(boolean autoFocus) {
+ mAutoFocus = autoFocus;
+ }
+
+ public int getIso() {
+ return mIso;
+ }
+
+ public long getExposure() {
+ return mExposure;
+ }
+
+ public void setExposure(long exposure) {
+ mExposure = exposure;
+ }
+
+ public int getIsoMax() {
+ return mISOmax;
+ }
+
+ public int getIsoMin() {
+ return mISOmin;
+ }
+
+ public long getExpMax() {
+ return mExpMax;
+ }
+
+ public long getExpMin() {
+ return mExpMin;
+ }
+
+ public float getFocusMin() {
+ return mFocusMin;
+ }
+} \ No newline at end of file
diff --git a/java/tests/RsCameraDemo/src/com/android/example/rscamera/CameraView.java b/java/tests/RsCameraDemo/src/com/android/example/rscamera/CameraView.java
new file mode 100644
index 00000000..3f79ba77
--- /dev/null
+++ b/java/tests/RsCameraDemo/src/com/android/example/rscamera/CameraView.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2015 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.example.rscamera;
+
+import android.app.Activity;
+import android.content.Context;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.TotalCaptureResult;
+import android.renderscript.RenderScript;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Size;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.View;
+
+import com.android.example.rscamera.rscamera.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by hoford on 2/27/15.
+ */
+public class CameraView extends FixedAspectSurfaceView {
+ private static final String TAG = "CameraPreView";
+
+ private static final long MICRO_SECOND = 1000;
+ private static final long MILLI_SECOND = MICRO_SECOND * 1000;
+ private static final long ONE_SECOND = MILLI_SECOND * 1000;
+
+ private Surface mPreviewSurface;
+ ViewfinderProcessor mProcessor;
+ private Surface mProcessingNormalSurface;
+ CameraOps mCameraOps;
+ CameraManager mCameraManager;
+ Activity mActivity;
+ Context mContext;
+ byte mode = 0;
+ public static final byte MODE_NONE = 0;
+ public static final byte MODE_SPEED = 1;
+ public static final byte MODE_FOCUS = 2;
+ public static final byte MODE_ISO = 3;
+ RenderScript mRS;
+ ErrorCallback mErrorCallback;
+ ParametersChangedCallback mParametersChangedCallback;
+
+ public CameraView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+
+ mRS = RenderScript.create(mContext);
+ SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ mPreviewSurface = holder.getSurface();
+ setupProcessor();
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mPreviewSurface = null;
+ }
+ };
+ getHolder().addCallback(callback);
+ mCameraManager = (CameraManager) mContext.getSystemService(mContext.CAMERA_SERVICE);
+
+ CameraOps.ErrorDisplayer errorDisplayer = new CameraOps.ErrorDisplayer() {
+
+ @Override
+ public void showErrorDialog(String errorMessage) {
+ Log.v(TAG, "ERROR");
+ if (mErrorCallback != null) {
+ mErrorCallback.showError(errorMessage);
+ }
+ //MessageDialogFragment.newInstance(errorMessage).show(getFragmentManager(), FRAGMENT_DIALOG);
+ }
+
+ @Override
+ public String getErrorString(CameraAccessException e) {
+ switch (e.getReason()) {
+ case CameraAccessException.CAMERA_DISABLED:
+ return mContext.getString(R.string.camera_disabled);
+ case CameraAccessException.CAMERA_DISCONNECTED:
+ return mContext.getString(R.string.camera_disconnected);
+ case CameraAccessException.CAMERA_ERROR:
+ return mContext.getString(R.string.camera_error);
+ default:
+ return mContext.getString(R.string.camera_unknown, e.getReason());
+
+ }
+ }
+ };
+
+ CameraOps.CameraReadyListener cameraReadyListener = new CameraOps.CameraReadyListener() {
+ @Override
+ public void onCameraReady() {
+ mCameraOps.setUpCamera(mProcessingNormalSurface);
+ }
+ };
+ setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ return touchScreen(event);
+ }
+ });
+ mCameraOps = new CameraOps(mCameraManager,
+ errorDisplayer,
+ cameraReadyListener);
+ }
+
+ public void resume(Activity activity) {
+ mActivity = activity;
+
+ String errorMessage = mCameraOps.resume();
+ if (errorMessage != null) {
+ if (mErrorCallback != null) {
+ mErrorCallback.showError(errorMessage);
+ }
+ } else {
+
+ Size outputSize = mCameraOps.getBestSize();
+ mProcessor = new ViewfinderProcessor(mRS, outputSize);
+ // Configure the output view - this will fire surfaceChanged
+ setAspectRatio((float) outputSize.getWidth() / outputSize.getHeight());
+ getHolder().setFixedSize(outputSize.getWidth(), outputSize.getHeight());
+ }
+ }
+
+ public void pause() {
+ mCameraOps.pause();
+ }
+
+ /**
+ * Once camera is open and output surfaces are ready, configure the RS processing
+ * and the camera device inputs/outputs.
+ */
+ private void setupProcessor() {
+ if (mProcessor == null || mPreviewSurface == null) return;
+ mProcessor.setOutputSurface(mPreviewSurface);
+ mProcessingNormalSurface = mProcessor.getInputSurface();
+ mCameraOps.setSurface(mProcessingNormalSurface);
+ }
+
+ public void takePicture() {
+ // Orientation
+ int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
+ int jpegRotation = Surface.ROTATION_0;
+ switch (rotation) {
+ case 90:
+ jpegRotation = Surface.ROTATION_0;
+ break;
+ case 0:
+ jpegRotation = Surface.ROTATION_90;
+ break;
+ case 180:
+ jpegRotation = Surface.ROTATION_270;
+ break;
+ case 270:
+ jpegRotation = Surface.ROTATION_180;
+ break;
+ }
+ String name = "Simple" + System.currentTimeMillis() + ".jpg";
+ mCameraOps.captureStillPicture(jpegRotation, name, mContext.getContentResolver());
+ }
+
+ private CameraCaptureSession.CaptureCallback mPhotoCallback
+ = new CameraCaptureSession.CaptureCallback() {
+
+ public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+ TotalCaptureResult result) {
+ Log.v(TAG, "onCaptureCompleted " + result.toString());
+ }
+ };
+
+ float mDownY;
+ long mExposureDown;
+ float mFocusDistDown;
+
+ public boolean touchScreen(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ mDownY = event.getY();
+ mExposureDown = mCameraOps.getExposure();
+ mFocusDistDown = mCameraOps.getFocusDistance();
+ if (mFocusDistDown == 0.0) {
+ mFocusDistDown = 0.01f;
+ }
+ }
+ float distanceY = event.getY() - mDownY;
+ float width = getWidth();
+ float height = getHeight();
+
+ float yDistNorm = distanceY / height;
+
+ float ACCELERATION_FACTOR = 8;
+ float scaleFactor = (float) Math.pow(2.f, yDistNorm * ACCELERATION_FACTOR);
+
+ switch (mode) {
+ case MODE_SPEED:
+ long exp = (long) (mExposureDown * scaleFactor);
+ exp = Math.min(mCameraOps.getExpMax(), exp);
+ mCameraOps.setExposure(Math.max(mCameraOps.getExpMin(), exp));
+ Log.v(TAG, "mExposure =" + mCameraOps.getExposure());
+ break;
+ case MODE_FOCUS:
+ float focusDist = mFocusDistDown * scaleFactor;
+ focusDist = Math.max(0.0f, Math.min(mCameraOps.getFocusMin(), focusDist));
+ if (focusDist < 0.01) focusDist = 0;
+ mCameraOps.setFocusDistance(focusDist);
+ Log.v(TAG, "mFocusDist =" + focusDist);
+ break;
+ case MODE_ISO:
+ ACCELERATION_FACTOR = 2;
+ scaleFactor = (float) Math.pow(2.f, yDistNorm * ACCELERATION_FACTOR);
+ int iso = (int) (getIso() * scaleFactor);
+ iso = Math.min(mCameraOps.getIsoMax(), iso);
+ mCameraOps.setIso(Math.max(mCameraOps.getIsoMin(), iso));
+ break;
+ }
+
+ if (mParametersChangedCallback != null) {
+ mParametersChangedCallback.parametersChanged();
+ }
+ mCameraOps.setParameters();
+
+ return true;
+ }
+
+ public void setMode(byte mode) {
+ this.mode = mode;
+ }
+
+ public byte getMode() {
+ return mode;
+ }
+
+ public int getIso() {
+ return mCameraOps.getIso();
+ }
+
+ public void setIso(int iso) {
+ mCameraOps.setIso(iso);
+ if (mParametersChangedCallback != null) {
+ mParametersChangedCallback.parametersChanged();
+ }
+ mCameraOps.setParameters();
+ }
+
+ public long getExposure() {
+ return mCameraOps.getExposure();
+ }
+
+ public void setExposure(long exposure) {
+ mCameraOps.setExposure(exposure);
+ if (mParametersChangedCallback != null) {
+ mParametersChangedCallback.parametersChanged();
+ }
+ mCameraOps.setParameters();
+ }
+
+ public float getFocusDist() {
+ return mCameraOps.getFocusDistance();
+ }
+
+ public void setFocusInMeters(float dist) {
+ float min = mCameraOps.getFocusMin();
+ float d = 10 / (dist + 10 / min);
+ setFocusDist(d);
+ }
+
+ public void setFocusDist(float dist) {
+ mCameraOps.setFocusDistance(dist);
+ mCameraOps.setParameters();
+ }
+
+ public float getMinFocusDistance() {
+ return mCameraOps.getFocusMin();
+ }
+
+ public void setAutofocus(boolean autofocus) {
+ mCameraOps.setAutoFocus(autofocus);
+ mCameraOps.setParameters();
+ }
+
+ public boolean isAutoExposure() {
+ return mCameraOps.isAutoExposure();
+ }
+
+ public boolean isAutofocus() {
+ return mCameraOps.isAutoFocus();
+ }
+
+ public void setAutoExposure(boolean autoExposure) {
+ mCameraOps.setAutoExposure(autoExposure);
+ mCameraOps.setParameters();
+ }
+
+ public static interface ErrorCallback {
+ public void showError(String errorMessage);
+ }
+
+ public void setErrorCallback(ErrorCallback errorCallback) {
+ mErrorCallback = errorCallback;
+ }
+
+ public static interface ParametersChangedCallback {
+ public void parametersChanged();
+ }
+
+ public void setParametersChangedCallback(ParametersChangedCallback parametersChangedCallback) {
+ mParametersChangedCallback = parametersChangedCallback;
+ }
+
+ float getFps() {
+ return mProcessor.getmFps();
+ }
+}
diff --git a/java/tests/RsCameraDemo/src/com/android/example/rscamera/FixedAspectSurfaceView.java b/java/tests/RsCameraDemo/src/com/android/example/rscamera/FixedAspectSurfaceView.java
new file mode 100644
index 00000000..dd72c867
--- /dev/null
+++ b/java/tests/RsCameraDemo/src/com/android/example/rscamera/FixedAspectSurfaceView.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2015 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.example.rscamera;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+
+import com.android.example.rscamera.rscamera.R;
+
+/**
+ * A SurfaceView that maintains its aspect ratio to be a desired target value.
+ * <p/>
+ * <p>Depending on the layout, the FixedAspectSurfaceView may not be able to maintain the
+ * requested aspect ratio. This can happen if both the width and the height are exactly
+ * determined by the layout. To avoid this, ensure that either the height or the width is
+ * adjustable by the view; for example, by setting the layout parameters to be WRAP_CONTENT for
+ * the dimension that is best adjusted to maintain the aspect ratio.</p>
+ */
+public class FixedAspectSurfaceView extends SurfaceView {
+
+ /**
+ * Desired width/height ratio
+ */
+ private float mAspectRatio;
+
+ private GestureDetector mGestureDetector;
+
+ public FixedAspectSurfaceView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ // Get initial aspect ratio from custom attributes
+ TypedArray a =
+ context.getTheme().obtainStyledAttributes(attrs,
+ R.styleable.FixedAspectSurfaceView, 0, 0);
+ setAspectRatio(a.getFloat(
+ R.styleable.FixedAspectSurfaceView_aspectRatio, 1.f));
+ a.recycle();
+ }
+
+ /**
+ * Set the desired aspect ratio for this view.
+ *
+ * @param aspect the desired width/height ratio in the current UI orientation. Must be a
+ * positive value.
+ */
+ public void setAspectRatio(float aspect) {
+ if (aspect <= 0) {
+ throw new IllegalArgumentException("Aspect ratio must be positive");
+ }
+ mAspectRatio = aspect;
+ requestLayout();
+ }
+
+ /**
+ * Set a gesture listener to listen for touch events
+ */
+ public void setGestureListener(Context context, GestureDetector.OnGestureListener listener) {
+ if (listener == null) {
+ mGestureDetector = null;
+ } else {
+ mGestureDetector = new GestureDetector(context, listener);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+
+ // General goal: Adjust dimensions to maintain the requested aspect ratio as much
+ // as possible. Depending on the measure specs handed down, this may not be possible
+
+ // Only set one of these to true
+ boolean scaleWidth = false;
+ boolean scaleHeight = false;
+
+ // Sort out which dimension to scale, if either can be. There are 9 combinations of
+ // possible measure specs; a few cases below handle multiple combinations
+ if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
+ // Can't adjust sizes at all, do nothing
+ } else if (widthMode == MeasureSpec.EXACTLY) {
+ // Width is fixed, heightMode either AT_MOST or UNSPECIFIED, so adjust height
+ scaleHeight = true;
+ } else if (heightMode == MeasureSpec.EXACTLY) {
+ // Height is fixed, widthMode either AT_MOST or UNSPECIFIED, so adjust width
+ scaleWidth = true;
+ } else if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
+ // Need to fit into box <= [width, height] in size.
+ // Maximize the View's area while maintaining aspect ratio
+ // This means keeping one dimension as large as possible and shrinking the other
+ float boxAspectRatio = width / (float) height;
+ if (boxAspectRatio > mAspectRatio) {
+ // Box is wider than requested aspect; pillarbox
+ scaleWidth = true;
+ } else {
+ // Box is narrower than requested aspect; letterbox
+ scaleHeight = true;
+ }
+ } else if (widthMode == MeasureSpec.AT_MOST) {
+ // Maximize width, heightSpec is UNSPECIFIED
+ scaleHeight = true;
+ } else if (heightMode == MeasureSpec.AT_MOST) {
+ // Maximize height, widthSpec is UNSPECIFIED
+ scaleWidth = true;
+ } else {
+ // Both MeasureSpecs are UNSPECIFIED. This is probably a pathological layout,
+ // with width == height == 0
+ // but arbitrarily scale height anyway
+ scaleHeight = true;
+ }
+
+ // Do the scaling
+ if (scaleWidth) {
+ width = (int) (height * mAspectRatio);
+ } else if (scaleHeight) {
+ height = (int) (width / mAspectRatio);
+ }
+
+ // Override width/height if needed for EXACTLY and AT_MOST specs
+ width = View.resolveSizeAndState(width, widthMeasureSpec, 0);
+ height = View.resolveSizeAndState(height, heightMeasureSpec, 0);
+
+ // Finally set the calculated dimensions
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (mGestureDetector != null) {
+ return mGestureDetector.onTouchEvent(event);
+ }
+ return false;
+ }
+} \ No newline at end of file
diff --git a/java/tests/RsCameraDemo/src/com/android/example/rscamera/MainActivity.java b/java/tests/RsCameraDemo/src/com/android/example/rscamera/MainActivity.java
new file mode 100644
index 00000000..777fec61
--- /dev/null
+++ b/java/tests/RsCameraDemo/src/com/android/example/rscamera/MainActivity.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2015 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.example.rscamera;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.SeekBar;
+import android.widget.ViewFlipper;
+
+import com.android.example.rscamera.rscamera.R;
+
+import java.text.DecimalFormat;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * Main Activity for this app
+ * It presents a ui for setting ISO, Shutter speed, and focus
+ */
+public class MainActivity extends Activity {
+ private static final String TAG = "MainActivity";
+ private static final long ONE_SECOND = 1000000000;
+ private CameraView mPreviewView;
+ private ViewFlipper mViewFlipper;
+ private Button mSpeedButton;
+ private Button mISOButton;
+ private Button mFocusButton;
+ private Timer mTimer;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ mSpeedButton = (Button) findViewById(R.id.speed);
+ mISOButton = (Button) findViewById(R.id.iso);
+ mFocusButton = (Button) findViewById(R.id.focus);
+ mPreviewView = (CameraView) findViewById(R.id.preview);
+ mViewFlipper = (ViewFlipper) findViewById(R.id.viewFlipper);
+ SeekBar seekBar = (SeekBar) findViewById(R.id.focusbar);
+ seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ mPreviewView.setFocusInMeters(seekBar.getProgress() / 10.f);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+ });
+ mPreviewView.setParametersChangedCallback(new CameraView.ParametersChangedCallback() {
+ @Override
+ public void parametersChanged() {
+ update_buttons();
+ }
+ });
+ mTimer = new Timer();
+
+ mTimer.scheduleAtFixedRate(new TimerTask() {
+
+ @Override
+ public void run() {
+ // TODO Auto-generated method stub
+ runOnUiThread(new Runnable() {
+ public void run() {
+ setTitle("RS Camera (" + mPreviewView.getFps() + "fps)");
+ }
+ });
+
+ }
+ }, 250, 250);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mPreviewView.resume(this);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mPreviewView.pause();
+ }
+
+ public void setShutterSpeed(View v) {
+ if (mPreviewView.isAutoExposure()) {
+ mPreviewView.setAutoExposure(false);
+ mPreviewView.setMode(CameraView.MODE_SPEED);
+ } else {
+ mPreviewView.setMode(CameraView.MODE_NONE);
+ mPreviewView.setAutoExposure(true);
+ }
+ update_buttons();
+ }
+
+ public void setISO(View v) {
+ if (mPreviewView.isAutoExposure()) {
+ mPreviewView.setAutoExposure(false);
+ mPreviewView.setMode(CameraView.MODE_ISO);
+ } else {
+ mPreviewView.setMode(CameraView.MODE_NONE);
+ mPreviewView.setAutoExposure(true);
+ }
+ update_buttons();
+ }
+
+ public void setFocus(View v) {
+ if (mPreviewView.isAutofocus()) {
+ mPreviewView.setAutofocus(false);
+ mPreviewView.setMode(CameraView.MODE_FOCUS);
+ mViewFlipper.setInAnimation(this, R.anim.slide_in_from_left);
+ mViewFlipper.setOutAnimation(this, R.anim.slide_out_to_right);
+ mViewFlipper.showNext();
+ } else {
+ mPreviewView.setMode(CameraView.MODE_NONE);
+ mPreviewView.setAutofocus(true);
+ }
+ update_buttons();
+ }
+
+ public void back(View v) {
+ mViewFlipper.setInAnimation(this, R.anim.slide_in_from_left);
+ mViewFlipper.setOutAnimation(this, R.anim.slide_out_to_right);
+ mViewFlipper.showNext();
+ }
+
+ public void capture(View v) {
+ mPreviewView.takePicture();
+ }
+
+ private void update_buttons() {
+ byte mode = mPreviewView.getMode();
+ mSpeedButton.setElevation(mode == CameraView.MODE_SPEED ? 20 : 0);
+ mFocusButton.setElevation(mode == CameraView.MODE_FOCUS ? 20 : 0);
+ mISOButton.setElevation(mode == CameraView.MODE_ISO ? 20 : 0);
+
+ String a;
+ a = (mPreviewView.isAutoExposure()) ? "A " : " ";
+ if (ONE_SECOND > mPreviewView.getExposure()) {
+ mSpeedButton.setText(a + 1 + "/" + (ONE_SECOND / mPreviewView.getExposure()) + "s");
+ } else {
+ mSpeedButton.setText(a + (mPreviewView.getExposure() / ONE_SECOND) + "\"s");
+
+ }
+ a = (mPreviewView.isAutofocus()) ? "A " : " ";
+ DecimalFormat df = new DecimalFormat("#.###");
+ float d = mPreviewView.getFocusDist();
+ if (d < 0.01) {
+ d = 0;
+ }
+ mFocusButton.setText(a + df.format(0.1 / d) + " m");
+ a = (mPreviewView.isAutoExposure()) ? "A ISO " : " ISO ";
+ mISOButton.setText(a + mPreviewView.getIso() + " M");
+ }
+}
diff --git a/java/tests/RsCameraDemo/src/com/android/example/rscamera/MediaStoreSaver.java b/java/tests/RsCameraDemo/src/com/android/example/rscamera/MediaStoreSaver.java
new file mode 100644
index 00000000..36f0f8df
--- /dev/null
+++ b/java/tests/RsCameraDemo/src/com/android/example/rscamera/MediaStoreSaver.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015 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.example.rscamera;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.provider.MediaStore.Images;
+
+/**
+ * Utility class to save images into android image database
+ */
+public class MediaStoreSaver {
+ public static interface StreamWriter {
+ void write(OutputStream imageOut) throws IOException;
+ }
+
+ public static final String insertImage(ContentResolver contentResolver,
+ Bitmap image,
+ String title,
+ String description) {
+ final Bitmap source = image;
+ StreamWriter streamWriter = new StreamWriter() {
+ public void write(OutputStream imageOut) {
+ source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);
+ }
+ };
+ return insertImage(contentResolver, streamWriter, title, description);
+ }
+
+
+ public static final String insertImage(ContentResolver cr,
+ StreamWriter source,
+ String title,
+ String description) {
+
+ ContentValues values = new ContentValues();
+ values.put(Images.Media.TITLE, title);
+ values.put(Images.Media.DISPLAY_NAME, title);
+ values.put(Images.Media.DESCRIPTION, description);
+ values.put(Images.Media.MIME_TYPE, "image/jpeg");
+ // Add the date meta data to ensure the image is added at the front of the gallery
+ values.put(Images.Media.DATE_ADDED, System.currentTimeMillis());
+ values.put(Images.Media.DATE_TAKEN, System.currentTimeMillis());
+
+ Uri url = null;
+ String stringUrl = null; /* value to be returned */
+
+ try {
+ url = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+
+ if (source != null) {
+ OutputStream imageOut = cr.openOutputStream(url);
+ try {
+ source.write(imageOut);
+ } finally {
+ imageOut.close();
+ }
+
+ long id = ContentUris.parseId(url);
+ // Wait until MINI_KIND thumbnail is generated.
+ Bitmap miniThumb = Images.Thumbnails.getThumbnail(cr, id, Images.Thumbnails.MINI_KIND, null);
+ // This is for backward compatibility.
+ storeThumbnail(cr, miniThumb, id, 50F, 50F, Images.Thumbnails.MICRO_KIND);
+ } else {
+ cr.delete(url, null, null);
+ url = null;
+ }
+ } catch (Exception e) {
+ if (url != null) {
+ cr.delete(url, null, null);
+ url = null;
+ }
+ }
+
+ if (url != null) {
+ stringUrl = url.toString();
+ }
+
+ return stringUrl;
+ }
+
+ /**
+ * A copy of the Android internals StoreThumbnail method, it used with the insertImage to
+ * populate the android.provider.MediaStore.Images.Media#insertImage with all the correct
+ * meta data. The StoreThumbnail method is private so it must be duplicated here.
+ *
+ * @see android.provider.MediaStore.Images.Media (StoreThumbnail private method)
+ */
+ private static final Bitmap storeThumbnail(
+ ContentResolver cr,
+ Bitmap source,
+ long id,
+ float width,
+ float height,
+ int kind) {
+
+ // create the matrix to scale it
+ Matrix matrix = new Matrix();
+
+ float scaleX = width / source.getWidth();
+ float scaleY = height / source.getHeight();
+
+ matrix.setScale(scaleX, scaleY);
+
+ Bitmap thumb = Bitmap.createBitmap(source, 0, 0,
+ source.getWidth(),
+ source.getHeight(), matrix,
+ true
+ );
+
+ ContentValues values = new ContentValues(4);
+ values.put(Images.Thumbnails.KIND, kind);
+ values.put(Images.Thumbnails.IMAGE_ID, (int) id);
+ values.put(Images.Thumbnails.HEIGHT, thumb.getHeight());
+ values.put(Images.Thumbnails.WIDTH, thumb.getWidth());
+
+ Uri url = cr.insert(Images.Thumbnails.EXTERNAL_CONTENT_URI, values);
+
+ try {
+ OutputStream thumbOut = cr.openOutputStream(url);
+ thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut);
+ thumbOut.close();
+ return thumb;
+ } catch (FileNotFoundException ex) {
+ return null;
+ } catch (IOException ex) {
+ return null;
+ }
+ }
+
+
+}
diff --git a/java/tests/RsCameraDemo/src/com/android/example/rscamera/VerticalSeekBar.java b/java/tests/RsCameraDemo/src/com/android/example/rscamera/VerticalSeekBar.java
new file mode 100644
index 00000000..1473e0ba
--- /dev/null
+++ b/java/tests/RsCameraDemo/src/com/android/example/rscamera/VerticalSeekBar.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 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.example.rscamera;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.SeekBar;
+
+/**
+ * Class to create a vertical slider
+ */
+public class VerticalSeekBar extends SeekBar {
+
+ public VerticalSeekBar(Context context) {
+ super(context);
+ }
+
+ public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public VerticalSeekBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(h, w, oldh, oldw);
+ }
+
+ @Override
+ protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(heightMeasureSpec, widthMeasureSpec);
+ setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
+ }
+
+ protected void onDraw(Canvas c) {
+ c.rotate(-90);
+ c.translate(-getHeight(), 0);
+
+ super.onDraw(c);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (!isEnabled()) {
+ return false;
+ }
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_MOVE:
+ case MotionEvent.ACTION_UP:
+ setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
+ onSizeChanged(getWidth(), getHeight(), 0, 0);
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ break;
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/java/tests/RsCameraDemo/src/com/android/example/rscamera/ViewfinderProcessor.java b/java/tests/RsCameraDemo/src/com/android/example/rscamera/ViewfinderProcessor.java
new file mode 100644
index 00000000..e012e920
--- /dev/null
+++ b/java/tests/RsCameraDemo/src/com/android/example/rscamera/ViewfinderProcessor.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2015 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.example.rscamera;
+
+import android.graphics.ImageFormat;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.util.Size;
+import android.view.Surface;
+
+/**
+ * Renderscript-based Focus peaking viewfinder
+ */
+public class ViewfinderProcessor {
+ int mCount;
+ long mLastTime;
+ float mFps;
+ private Allocation mInputAllocation;
+ private Allocation mOutputAllocation;
+ private HandlerThread mProcessingThread;
+ private Handler mProcessingHandler;
+ private ScriptC_focus_peak mScriptFocusPeak;
+ public ProcessingTask mProcessingTask;
+
+ public ViewfinderProcessor(RenderScript rs, Size dimensions) {
+ Type.Builder yuvTypeBuilder = new Type.Builder(rs, Element.YUV(rs));
+ yuvTypeBuilder.setX(dimensions.getWidth());
+ yuvTypeBuilder.setY(dimensions.getHeight());
+ yuvTypeBuilder.setYuvFormat(ImageFormat.YUV_420_888);
+
+ mInputAllocation = Allocation.createTyped(rs, yuvTypeBuilder.create(),
+ Allocation.USAGE_IO_INPUT | Allocation.USAGE_SCRIPT);
+
+ Type.Builder rgbTypeBuilder = new Type.Builder(rs, Element.RGBA_8888(rs));
+ rgbTypeBuilder.setX(dimensions.getWidth());
+ rgbTypeBuilder.setY(dimensions.getHeight());
+
+ mOutputAllocation = Allocation.createTyped(rs, rgbTypeBuilder.create(),
+ Allocation.USAGE_IO_OUTPUT | Allocation.USAGE_SCRIPT);
+
+ mProcessingThread = new HandlerThread("ViewfinderProcessor");
+ mProcessingThread.start();
+ mProcessingHandler = new Handler(mProcessingThread.getLooper());
+ mScriptFocusPeak = new ScriptC_focus_peak(rs);
+ mProcessingTask = new ProcessingTask(mInputAllocation);
+ }
+
+ public Surface getInputSurface() {
+ return mInputAllocation.getSurface();
+ }
+
+ public void setOutputSurface(Surface output) {
+ mOutputAllocation.setSurface(output);
+ }
+
+ public float getmFps() {
+ return mFps;
+ }
+
+ /**
+ * Class to process buffer from camera and output to buffer to screen
+ */
+ class ProcessingTask implements Runnable, Allocation.OnBufferAvailableListener {
+ private int mPendingFrames = 0;
+
+ private Allocation mInputAllocation;
+
+ public ProcessingTask(Allocation input) {
+ mInputAllocation = input;
+ mInputAllocation.setOnBufferAvailableListener(this);
+ }
+
+ @Override
+ public void onBufferAvailable(Allocation a) {
+ synchronized (this) {
+ mPendingFrames++;
+ mProcessingHandler.post(this);
+ }
+ }
+
+ @Override
+ public void run() {
+ // Find out how many frames have arrived
+ int pendingFrames;
+ synchronized (this) {
+ pendingFrames = mPendingFrames;
+ mPendingFrames = 0;
+
+ // Discard extra messages in case processing is slower than frame rate
+ mProcessingHandler.removeCallbacks(this);
+ }
+
+ // Get to newest input
+ for (int i = 0; i < pendingFrames; i++) {
+ mInputAllocation.ioReceive();
+ }
+ mCount++;
+ mScriptFocusPeak.set_gCurrentFrame(mInputAllocation);
+ long time = System.currentTimeMillis() - mLastTime;
+ if (time > 1000) {
+ mLastTime += time;
+ mFps = mCount * 1000 / (float) (time);
+ mCount = 0;
+ }
+ // Run processing pass
+ mScriptFocusPeak.forEach_peak(mOutputAllocation);
+ mOutputAllocation.ioSend();
+ }
+ }
+} \ No newline at end of file
diff --git a/java/tests/RsCameraDemo/src/com/android/example/rscamera/focus_peak.rs b/java/tests/RsCameraDemo/src/com/android/example/rscamera/focus_peak.rs
new file mode 100644
index 00000000..6513bc1b
--- /dev/null
+++ b/java/tests/RsCameraDemo/src/com/android/example/rscamera/focus_peak.rs
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.example.rscamera)
+#pragma rs_fp_relaxed
+
+rs_allocation gCurrentFrame;
+
+uchar4 __attribute__((kernel)) peak(uint32_t x, uint32_t y) {
+
+ uchar4 curPixel;
+ curPixel.r = rsGetElementAtYuv_uchar_Y(gCurrentFrame, x, y);
+ curPixel.g = rsGetElementAtYuv_uchar_U(gCurrentFrame, x, y);
+ curPixel.b = rsGetElementAtYuv_uchar_V(gCurrentFrame, x, y);
+
+ int dx = x + ((x == 0) ? 1 : -1);
+ int sum = 0;
+ int tmp;
+
+ tmp = rsGetElementAtYuv_uchar_Y(gCurrentFrame, dx, y) - curPixel.r;
+ sum += tmp * tmp;
+ tmp = rsGetElementAtYuv_uchar_U(gCurrentFrame, dx, y) - curPixel.g;
+ sum += tmp * tmp;
+ tmp = rsGetElementAtYuv_uchar_V(gCurrentFrame, dx, y) - curPixel.b;
+ sum += tmp * tmp;
+
+
+ int dy = y + ((y == 0) ? 1 : -1);
+ tmp = rsGetElementAtYuv_uchar_Y(gCurrentFrame, x, dy) - curPixel.r;
+ sum += tmp * tmp;
+ tmp = rsGetElementAtYuv_uchar_U(gCurrentFrame, x, dy) - curPixel.g;
+ sum += tmp * tmp;
+ tmp = rsGetElementAtYuv_uchar_V(gCurrentFrame, x, dy) - curPixel.b;
+ sum += tmp * tmp;
+
+ sum >>= 9;
+ sum *= sum * sum;
+
+ curPixel.a = 255;
+
+ uchar4 mergedPixel = curPixel;
+
+ int4 rgb;
+ rgb.r = mergedPixel.r +
+ mergedPixel.b * 1436 / 1024 - 179 + sum;
+ rgb.g = mergedPixel.r -
+ mergedPixel.g * 46549 / 131072 + 44 -
+ mergedPixel.b * 93604 / 131072 + 91 + sum;
+ rgb.b = mergedPixel.r +
+ mergedPixel.g * 1814 / 1024 - 227;
+ rgb.a = 255;
+
+ // Write out merged HDR result
+ uchar4 out = convert_uchar4(clamp(rgb, 0, 255));
+
+ return out;
+} \ No newline at end of file