summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOwen Lin <owenlin@google.com>2010-01-28 16:10:19 -0800
committerOwen Lin <owenlin@google.com>2010-02-26 20:26:48 +0800
commit4de149ceb47f2c251f646419907424bfb67d2b64 (patch)
tree6f48682fe065902039bad89d1f3ca90720eca819
parent02627adfa3d240d817e34af69be8d07e9c66c136 (diff)
downloadLegacyCamera-4de149ceb47f2c251f646419907424bfb67d2b64.zip
LegacyCamera-4de149ceb47f2c251f646419907424bfb67d2b64.tar.gz
LegacyCamera-4de149ceb47f2c251f646419907424bfb67d2b64.tar.bz2
The first runnable version of the new UI.
Implement the new UI with OpenGL (GLSurfaceView). Known issues: * Texture are never freed from GL * Do not consider the density of screen. Currently, the dimensions in mdpi devices are wrong. * It won't work on Sapphire, bug fired: Bug: 2473605 * The action UP event may pass a wrong target. (It should pass to the same target who recive the DOWN action. * Animation is not smooth enough. * Should not allocate objects into heap during rendering path. * The scrollbar in GLListView doesn't match the design * We should calculate our own orientation instead of using the system one. * Regression: "restore to default settings" is removed Change-Id: I93fa45831aa87787dd5ee9e43e270a9d786c5a2a
-rw-r--r--res/drawable-hdpi/ic_menuselect_flash_auto.pngbin0 -> 1559 bytes
-rw-r--r--res/drawable-hdpi/ic_menuselect_flash_off.pngbin0 -> 1887 bytes
-rw-r--r--res/drawable-hdpi/ic_menuselect_flash_on.pngbin0 -> 1083 bytes
-rw-r--r--res/drawable-hdpi/ic_menuselect_focus_auto.pngbin0 -> 1371 bytes
-rw-r--r--res/drawable-hdpi/ic_menuselect_focus_infinity.pngbin0 -> 996 bytes
-rw-r--r--res/drawable-hdpi/ic_menuselect_focus_macro.pngbin0 -> 1463 bytes
-rw-r--r--res/drawable-hdpi/ic_menuselect_gps_off.pngbin0 -> 2074 bytes
-rw-r--r--res/drawable-hdpi/ic_menuselect_gps_on.pngbin0 -> 1385 bytes
-rw-r--r--res/drawable-hdpi/ic_menuselect_off.pngbin0 -> 647 bytes
-rw-r--r--res/drawable-hdpi/ic_menuselect_on.pngbin0 -> 859 bytes
-rw-r--r--res/drawable-hdpi/ic_menuselect_settings.pngbin0 -> 1500 bytes
-rw-r--r--res/drawable-hdpi/ic_menuselect_wb_auto.pngbin0 -> 1392 bytes
-rw-r--r--res/drawable-hdpi/ic_menuselect_wb_cloudy.pngbin0 -> 913 bytes
-rw-r--r--res/drawable-hdpi/ic_menuselect_wb_daylight.pngbin0 -> 1508 bytes
-rw-r--r--res/drawable-hdpi/ic_menuselect_wb_fluorescent.pngbin0 -> 1563 bytes
-rw-r--r--res/drawable-hdpi/ic_menuselect_wb_incandescent.pngbin0 -> 1312 bytes
-rw-r--r--[-rwxr-xr-x]res/drawable-hdpi/ic_viewfinder_flash_auto.pngbin2288 -> 1877 bytes
-rw-r--r--res/drawable-hdpi/ic_viewfinder_flash_off.pngbin2985 -> 2422 bytes
-rw-r--r--[-rwxr-xr-x]res/drawable-hdpi/ic_viewfinder_flash_on.pngbin1596 -> 1258 bytes
-rw-r--r--res/drawable-hdpi/ic_viewfinder_focus_auto.pngbin0 -> 1580 bytes
-rw-r--r--res/drawable-hdpi/ic_viewfinder_focus_infinity.pngbin1177 -> 1081 bytes
-rw-r--r--res/drawable-hdpi/ic_viewfinder_focus_macro.pngbin2050 -> 1463 bytes
-rw-r--r--res/drawable-hdpi/ic_viewfinder_gps_no_signal.pngbin0 -> 1615 bytes
-rw-r--r--res/drawable-hdpi/ic_viewfinder_gps_off.pngbin0 -> 2529 bytes
-rw-r--r--res/drawable-hdpi/ic_viewfinder_gps_on.pngbin0 -> 1745 bytes
-rw-r--r--res/drawable-hdpi/ic_viewfinder_iconbar.9.pngbin0 -> 322 bytes
-rw-r--r--res/drawable-hdpi/ic_viewfinder_iconbar_highlight.9.pngbin0 -> 146 bytes
-rw-r--r--res/drawable-hdpi/ic_viewfinder_settings.pngbin0 -> 1627 bytes
-rw-r--r--res/drawable-hdpi/ic_viewfinder_wb_auto.pngbin0 -> 1710 bytes
-rw-r--r--res/drawable-hdpi/ic_viewfinder_wb_cloudy.pngbin1094 -> 1012 bytes
-rw-r--r--res/drawable-hdpi/ic_viewfinder_wb_daylight.pngbin2357 -> 1882 bytes
-rw-r--r--res/drawable-hdpi/ic_viewfinder_wb_fluorescent.pngbin2027 -> 1875 bytes
-rw-r--r--res/drawable-hdpi/ic_viewfinder_wb_incandescent.pngbin1895 -> 1504 bytes
-rw-r--r--res/drawable-hdpi/menu_popup.9.pngbin0 -> 2995 bytes
-rw-r--r--res/drawable-hdpi/menu_popup_triangle.pngbin0 -> 1277 bytes
-rw-r--r--res/drawable-hdpi/optionheader_background.9.pngbin0 -> 1180 bytes
-rw-r--r--res/drawable-hdpi/optionitem_highlight.9.pngbin0 -> 1180 bytes
-rw-r--r--res/drawable-hdpi/scrollbar_handle_vertical.9.pngbin0 -> 308 bytes
-rw-r--r--res/drawable-hdpi/zoom_shape.pngbin0 -> 407 bytes
-rw-r--r--res/drawable-hdpi/zoom_slider.pngbin0 -> 753 bytes
-rw-r--r--res/drawable-hdpi/zoom_tickmarks.pngbin0 -> 259 bytes
-rw-r--r--res/drawable-mdpi/ic_menuselect_flash_auto.pngbin0 -> 1110 bytes
-rw-r--r--res/drawable-mdpi/ic_menuselect_flash_off.pngbin0 -> 1308 bytes
-rw-r--r--res/drawable-mdpi/ic_menuselect_flash_on.pngbin0 -> 831 bytes
-rw-r--r--res/drawable-mdpi/ic_menuselect_focus_auto.pngbin0 -> 1107 bytes
-rw-r--r--res/drawable-mdpi/ic_menuselect_focus_infinity.pngbin0 -> 803 bytes
-rw-r--r--res/drawable-mdpi/ic_menuselect_focus_macro.pngbin0 -> 900 bytes
-rw-r--r--res/drawable-mdpi/ic_menuselect_gps_off.pngbin0 -> 1410 bytes
-rw-r--r--res/drawable-mdpi/ic_menuselect_gps_on.pngbin0 -> 937 bytes
-rw-r--r--res/drawable-mdpi/ic_menuselect_off.pngbin0 -> 586 bytes
-rw-r--r--res/drawable-mdpi/ic_menuselect_on.pngbin0 -> 745 bytes
-rw-r--r--res/drawable-mdpi/ic_menuselect_settings.pngbin0 -> 1074 bytes
-rw-r--r--res/drawable-mdpi/ic_menuselect_wb_auto.pngbin0 -> 1031 bytes
-rw-r--r--res/drawable-mdpi/ic_menuselect_wb_cloudy.pngbin0 -> 742 bytes
-rw-r--r--res/drawable-mdpi/ic_menuselect_wb_daylight.pngbin0 -> 1098 bytes
-rw-r--r--res/drawable-mdpi/ic_menuselect_wb_fluorescent.pngbin0 -> 1193 bytes
-rw-r--r--res/drawable-mdpi/ic_menuselect_wb_incandescent.pngbin0 -> 987 bytes
-rwxr-xr-xres/drawable-mdpi/ic_viewfinder_flash_auto.pngbin1473 -> 1358 bytes
-rw-r--r--res/drawable-mdpi/ic_viewfinder_flash_off.pngbin5416 -> 1775 bytes
-rwxr-xr-xres/drawable-mdpi/ic_viewfinder_flash_on.pngbin1038 -> 975 bytes
-rw-r--r--res/drawable-mdpi/ic_viewfinder_focus_auto.pngbin0 -> 1282 bytes
-rw-r--r--res/drawable-mdpi/ic_viewfinder_focus_infinity.pngbin3670 -> 848 bytes
-rw-r--r--res/drawable-mdpi/ic_viewfinder_focus_macro.pngbin4242 -> 900 bytes
-rw-r--r--res/drawable-mdpi/ic_viewfinder_gps_no_signal.pngbin0 -> 1142 bytes
-rw-r--r--res/drawable-mdpi/ic_viewfinder_gps_off.pngbin0 -> 1798 bytes
-rw-r--r--res/drawable-mdpi/ic_viewfinder_gps_on.pngbin0 -> 1167 bytes
-rw-r--r--res/drawable-mdpi/ic_viewfinder_iconbar.9.pngbin0 -> 322 bytes
-rw-r--r--res/drawable-mdpi/ic_viewfinder_iconbar_highlight.9.pngbin0 -> 146 bytes
-rw-r--r--res/drawable-mdpi/ic_viewfinder_settings.pngbin0 -> 1155 bytes
-rw-r--r--res/drawable-mdpi/ic_viewfinder_wb_auto.pngbin0 -> 1269 bytes
-rw-r--r--res/drawable-mdpi/ic_viewfinder_wb_cloudy.pngbin3565 -> 776 bytes
-rw-r--r--res/drawable-mdpi/ic_viewfinder_wb_daylight.pngbin4461 -> 1367 bytes
-rw-r--r--res/drawable-mdpi/ic_viewfinder_wb_fluorescent.pngbin4243 -> 1446 bytes
-rw-r--r--res/drawable-mdpi/ic_viewfinder_wb_incandescent.pngbin4090 -> 1164 bytes
-rw-r--r--res/drawable-mdpi/menu_popup.9.pngbin0 -> 2995 bytes
-rw-r--r--res/drawable-mdpi/menu_popup_triangle.pngbin0 -> 1277 bytes
-rw-r--r--res/drawable-mdpi/on_screen_hint_frame.9.pngbin4328 -> 2181 bytes
-rw-r--r--res/drawable-mdpi/optionheader_background.9.pngbin0 -> 1180 bytes
-rw-r--r--res/drawable-mdpi/optionitem_highlight.9.pngbin0 -> 1180 bytes
-rw-r--r--res/drawable-mdpi/scrollbar_handle_vertical.9.pngbin0 -> 308 bytes
-rw-r--r--res/drawable-mdpi/zoom_shape.pngbin0 -> 441 bytes
-rw-r--r--res/drawable-mdpi/zoom_slider.pngbin0 -> 819 bytes
-rw-r--r--res/drawable-mdpi/zoom_tickmarks.pngbin0 -> 407 bytes
-rw-r--r--res/layout/camera.xml45
-rw-r--r--res/values/arrays.xml45
-rw-r--r--res/values/attrs.xml1
-rw-r--r--res/values/colors.xml2
-rw-r--r--res/xml/camera_preferences.xml7
-rw-r--r--src/com/android/camera/Camera.java249
-rw-r--r--src/com/android/camera/IconListPreference.java38
-rw-r--r--src/com/android/camera/Menu3DTest.java71
-rw-r--r--src/com/android/camera/RecordLocationPreference.java8
-rw-r--r--src/com/android/camera/Util.java23
-rw-r--r--src/com/android/camera/ui/AbstractIndicator.java43
-rw-r--r--src/com/android/camera/ui/BasicIndicator.java82
-rw-r--r--src/com/android/camera/ui/CanvasTexture.java28
-rw-r--r--src/com/android/camera/ui/FrameTexture.java22
-rw-r--r--src/com/android/camera/ui/GLListView.java267
-rw-r--r--src/com/android/camera/ui/GLOptionHeader.java50
-rw-r--r--src/com/android/camera/ui/GLOptionItem.java110
-rw-r--r--src/com/android/camera/ui/GLOutOfMemoryException.java5
-rw-r--r--src/com/android/camera/ui/GLRootView.java456
-rw-r--r--src/com/android/camera/ui/GLView.java312
-rw-r--r--src/com/android/camera/ui/GLZoomIndicator.java34
-rw-r--r--src/com/android/camera/ui/GpsIndicator.java43
-rw-r--r--src/com/android/camera/ui/HeadUpDisplay.java336
-rw-r--r--src/com/android/camera/ui/IndicatorBar.java175
-rw-r--r--src/com/android/camera/ui/MeasureHelper.java42
-rw-r--r--src/com/android/camera/ui/NinePatchTexture.java93
-rw-r--r--src/com/android/camera/ui/OtherSettingsIndicator.java125
-rw-r--r--src/com/android/camera/ui/PopupWindow.java181
-rw-r--r--src/com/android/camera/ui/PopupWindowStencilImpl.java41
-rw-r--r--src/com/android/camera/ui/PreferenceAdapter.java103
-rw-r--r--src/com/android/camera/ui/RawTexture.java26
-rw-r--r--src/com/android/camera/ui/ResourceTexture.java62
-rw-r--r--src/com/android/camera/ui/RotatePane.java120
-rw-r--r--src/com/android/camera/ui/StateListTexture.java79
-rw-r--r--src/com/android/camera/ui/StringTexture.java46
-rw-r--r--src/com/android/camera/ui/Texture.java156
119 files changed, 3274 insertions, 252 deletions
diff --git a/res/drawable-hdpi/ic_menuselect_flash_auto.png b/res/drawable-hdpi/ic_menuselect_flash_auto.png
new file mode 100644
index 0000000..e229c35
--- /dev/null
+++ b/res/drawable-hdpi/ic_menuselect_flash_auto.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menuselect_flash_off.png b/res/drawable-hdpi/ic_menuselect_flash_off.png
new file mode 100644
index 0000000..cb00600
--- /dev/null
+++ b/res/drawable-hdpi/ic_menuselect_flash_off.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menuselect_flash_on.png b/res/drawable-hdpi/ic_menuselect_flash_on.png
new file mode 100644
index 0000000..710e9b7
--- /dev/null
+++ b/res/drawable-hdpi/ic_menuselect_flash_on.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menuselect_focus_auto.png b/res/drawable-hdpi/ic_menuselect_focus_auto.png
new file mode 100644
index 0000000..b99e9f5
--- /dev/null
+++ b/res/drawable-hdpi/ic_menuselect_focus_auto.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menuselect_focus_infinity.png b/res/drawable-hdpi/ic_menuselect_focus_infinity.png
new file mode 100644
index 0000000..5136816
--- /dev/null
+++ b/res/drawable-hdpi/ic_menuselect_focus_infinity.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menuselect_focus_macro.png b/res/drawable-hdpi/ic_menuselect_focus_macro.png
new file mode 100644
index 0000000..adbc25a
--- /dev/null
+++ b/res/drawable-hdpi/ic_menuselect_focus_macro.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menuselect_gps_off.png b/res/drawable-hdpi/ic_menuselect_gps_off.png
new file mode 100644
index 0000000..a7af034
--- /dev/null
+++ b/res/drawable-hdpi/ic_menuselect_gps_off.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menuselect_gps_on.png b/res/drawable-hdpi/ic_menuselect_gps_on.png
new file mode 100644
index 0000000..39d2c38
--- /dev/null
+++ b/res/drawable-hdpi/ic_menuselect_gps_on.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menuselect_off.png b/res/drawable-hdpi/ic_menuselect_off.png
new file mode 100644
index 0000000..b883c8f
--- /dev/null
+++ b/res/drawable-hdpi/ic_menuselect_off.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menuselect_on.png b/res/drawable-hdpi/ic_menuselect_on.png
new file mode 100644
index 0000000..24f5edf
--- /dev/null
+++ b/res/drawable-hdpi/ic_menuselect_on.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menuselect_settings.png b/res/drawable-hdpi/ic_menuselect_settings.png
new file mode 100644
index 0000000..8f2d768
--- /dev/null
+++ b/res/drawable-hdpi/ic_menuselect_settings.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menuselect_wb_auto.png b/res/drawable-hdpi/ic_menuselect_wb_auto.png
new file mode 100644
index 0000000..3b30e36
--- /dev/null
+++ b/res/drawable-hdpi/ic_menuselect_wb_auto.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menuselect_wb_cloudy.png b/res/drawable-hdpi/ic_menuselect_wb_cloudy.png
new file mode 100644
index 0000000..ccd7948
--- /dev/null
+++ b/res/drawable-hdpi/ic_menuselect_wb_cloudy.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menuselect_wb_daylight.png b/res/drawable-hdpi/ic_menuselect_wb_daylight.png
new file mode 100644
index 0000000..815280b
--- /dev/null
+++ b/res/drawable-hdpi/ic_menuselect_wb_daylight.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menuselect_wb_fluorescent.png b/res/drawable-hdpi/ic_menuselect_wb_fluorescent.png
new file mode 100644
index 0000000..886af77
--- /dev/null
+++ b/res/drawable-hdpi/ic_menuselect_wb_fluorescent.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menuselect_wb_incandescent.png b/res/drawable-hdpi/ic_menuselect_wb_incandescent.png
new file mode 100644
index 0000000..0b95bec
--- /dev/null
+++ b/res/drawable-hdpi/ic_menuselect_wb_incandescent.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_viewfinder_flash_auto.png b/res/drawable-hdpi/ic_viewfinder_flash_auto.png
index 27968e7..0bf7495 100755..100644
--- a/res/drawable-hdpi/ic_viewfinder_flash_auto.png
+++ b/res/drawable-hdpi/ic_viewfinder_flash_auto.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_viewfinder_flash_off.png b/res/drawable-hdpi/ic_viewfinder_flash_off.png
index 4a8bb95..afcfbad 100644
--- a/res/drawable-hdpi/ic_viewfinder_flash_off.png
+++ b/res/drawable-hdpi/ic_viewfinder_flash_off.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_viewfinder_flash_on.png b/res/drawable-hdpi/ic_viewfinder_flash_on.png
index 71ee867..827c98f 100755..100644
--- a/res/drawable-hdpi/ic_viewfinder_flash_on.png
+++ b/res/drawable-hdpi/ic_viewfinder_flash_on.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_viewfinder_focus_auto.png b/res/drawable-hdpi/ic_viewfinder_focus_auto.png
new file mode 100644
index 0000000..0a669cb
--- /dev/null
+++ b/res/drawable-hdpi/ic_viewfinder_focus_auto.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_viewfinder_focus_infinity.png b/res/drawable-hdpi/ic_viewfinder_focus_infinity.png
index 82112fc..99f8638 100644
--- a/res/drawable-hdpi/ic_viewfinder_focus_infinity.png
+++ b/res/drawable-hdpi/ic_viewfinder_focus_infinity.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_viewfinder_focus_macro.png b/res/drawable-hdpi/ic_viewfinder_focus_macro.png
index 85d4a55..adbc25a 100644
--- a/res/drawable-hdpi/ic_viewfinder_focus_macro.png
+++ b/res/drawable-hdpi/ic_viewfinder_focus_macro.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_viewfinder_gps_no_signal.png b/res/drawable-hdpi/ic_viewfinder_gps_no_signal.png
new file mode 100644
index 0000000..4a0d016
--- /dev/null
+++ b/res/drawable-hdpi/ic_viewfinder_gps_no_signal.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_viewfinder_gps_off.png b/res/drawable-hdpi/ic_viewfinder_gps_off.png
new file mode 100644
index 0000000..64636b9
--- /dev/null
+++ b/res/drawable-hdpi/ic_viewfinder_gps_off.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_viewfinder_gps_on.png b/res/drawable-hdpi/ic_viewfinder_gps_on.png
new file mode 100644
index 0000000..be84985
--- /dev/null
+++ b/res/drawable-hdpi/ic_viewfinder_gps_on.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_viewfinder_iconbar.9.png b/res/drawable-hdpi/ic_viewfinder_iconbar.9.png
new file mode 100644
index 0000000..0cd4dfe
--- /dev/null
+++ b/res/drawable-hdpi/ic_viewfinder_iconbar.9.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_viewfinder_iconbar_highlight.9.png b/res/drawable-hdpi/ic_viewfinder_iconbar_highlight.9.png
new file mode 100644
index 0000000..1b08fdf
--- /dev/null
+++ b/res/drawable-hdpi/ic_viewfinder_iconbar_highlight.9.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_viewfinder_settings.png b/res/drawable-hdpi/ic_viewfinder_settings.png
new file mode 100644
index 0000000..1a53019
--- /dev/null
+++ b/res/drawable-hdpi/ic_viewfinder_settings.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_viewfinder_wb_auto.png b/res/drawable-hdpi/ic_viewfinder_wb_auto.png
new file mode 100644
index 0000000..cf24be4
--- /dev/null
+++ b/res/drawable-hdpi/ic_viewfinder_wb_auto.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_viewfinder_wb_cloudy.png b/res/drawable-hdpi/ic_viewfinder_wb_cloudy.png
index 9766f7b..fd6f2e1 100644
--- a/res/drawable-hdpi/ic_viewfinder_wb_cloudy.png
+++ b/res/drawable-hdpi/ic_viewfinder_wb_cloudy.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_viewfinder_wb_daylight.png b/res/drawable-hdpi/ic_viewfinder_wb_daylight.png
index 3ecebe6..e9706c3 100644
--- a/res/drawable-hdpi/ic_viewfinder_wb_daylight.png
+++ b/res/drawable-hdpi/ic_viewfinder_wb_daylight.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_viewfinder_wb_fluorescent.png b/res/drawable-hdpi/ic_viewfinder_wb_fluorescent.png
index 889f607..49b9ea6 100644
--- a/res/drawable-hdpi/ic_viewfinder_wb_fluorescent.png
+++ b/res/drawable-hdpi/ic_viewfinder_wb_fluorescent.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_viewfinder_wb_incandescent.png b/res/drawable-hdpi/ic_viewfinder_wb_incandescent.png
index 0fce7ce..026bf14 100644
--- a/res/drawable-hdpi/ic_viewfinder_wb_incandescent.png
+++ b/res/drawable-hdpi/ic_viewfinder_wb_incandescent.png
Binary files differ
diff --git a/res/drawable-hdpi/menu_popup.9.png b/res/drawable-hdpi/menu_popup.9.png
new file mode 100644
index 0000000..534d3cf
--- /dev/null
+++ b/res/drawable-hdpi/menu_popup.9.png
Binary files differ
diff --git a/res/drawable-hdpi/menu_popup_triangle.png b/res/drawable-hdpi/menu_popup_triangle.png
new file mode 100644
index 0000000..59e2f99
--- /dev/null
+++ b/res/drawable-hdpi/menu_popup_triangle.png
Binary files differ
diff --git a/res/drawable-hdpi/optionheader_background.9.png b/res/drawable-hdpi/optionheader_background.9.png
new file mode 100644
index 0000000..22991e7
--- /dev/null
+++ b/res/drawable-hdpi/optionheader_background.9.png
Binary files differ
diff --git a/res/drawable-hdpi/optionitem_highlight.9.png b/res/drawable-hdpi/optionitem_highlight.9.png
new file mode 100644
index 0000000..3a17a86
--- /dev/null
+++ b/res/drawable-hdpi/optionitem_highlight.9.png
Binary files differ
diff --git a/res/drawable-hdpi/scrollbar_handle_vertical.9.png b/res/drawable-hdpi/scrollbar_handle_vertical.9.png
new file mode 100644
index 0000000..3ec0791
--- /dev/null
+++ b/res/drawable-hdpi/scrollbar_handle_vertical.9.png
Binary files differ
diff --git a/res/drawable-hdpi/zoom_shape.png b/res/drawable-hdpi/zoom_shape.png
new file mode 100644
index 0000000..c12ce6b
--- /dev/null
+++ b/res/drawable-hdpi/zoom_shape.png
Binary files differ
diff --git a/res/drawable-hdpi/zoom_slider.png b/res/drawable-hdpi/zoom_slider.png
new file mode 100644
index 0000000..6fcaecf
--- /dev/null
+++ b/res/drawable-hdpi/zoom_slider.png
Binary files differ
diff --git a/res/drawable-hdpi/zoom_tickmarks.png b/res/drawable-hdpi/zoom_tickmarks.png
new file mode 100644
index 0000000..a4191d7
--- /dev/null
+++ b/res/drawable-hdpi/zoom_tickmarks.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menuselect_flash_auto.png b/res/drawable-mdpi/ic_menuselect_flash_auto.png
new file mode 100644
index 0000000..39e1eed
--- /dev/null
+++ b/res/drawable-mdpi/ic_menuselect_flash_auto.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menuselect_flash_off.png b/res/drawable-mdpi/ic_menuselect_flash_off.png
new file mode 100644
index 0000000..44b6e95
--- /dev/null
+++ b/res/drawable-mdpi/ic_menuselect_flash_off.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menuselect_flash_on.png b/res/drawable-mdpi/ic_menuselect_flash_on.png
new file mode 100644
index 0000000..bb1343a
--- /dev/null
+++ b/res/drawable-mdpi/ic_menuselect_flash_on.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menuselect_focus_auto.png b/res/drawable-mdpi/ic_menuselect_focus_auto.png
new file mode 100644
index 0000000..2eb28da
--- /dev/null
+++ b/res/drawable-mdpi/ic_menuselect_focus_auto.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menuselect_focus_infinity.png b/res/drawable-mdpi/ic_menuselect_focus_infinity.png
new file mode 100644
index 0000000..c5f902e
--- /dev/null
+++ b/res/drawable-mdpi/ic_menuselect_focus_infinity.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menuselect_focus_macro.png b/res/drawable-mdpi/ic_menuselect_focus_macro.png
new file mode 100644
index 0000000..dd3ecb3
--- /dev/null
+++ b/res/drawable-mdpi/ic_menuselect_focus_macro.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menuselect_gps_off.png b/res/drawable-mdpi/ic_menuselect_gps_off.png
new file mode 100644
index 0000000..6f36907
--- /dev/null
+++ b/res/drawable-mdpi/ic_menuselect_gps_off.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menuselect_gps_on.png b/res/drawable-mdpi/ic_menuselect_gps_on.png
new file mode 100644
index 0000000..19d4c85
--- /dev/null
+++ b/res/drawable-mdpi/ic_menuselect_gps_on.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menuselect_off.png b/res/drawable-mdpi/ic_menuselect_off.png
new file mode 100644
index 0000000..80baf0e
--- /dev/null
+++ b/res/drawable-mdpi/ic_menuselect_off.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menuselect_on.png b/res/drawable-mdpi/ic_menuselect_on.png
new file mode 100644
index 0000000..1f027fd
--- /dev/null
+++ b/res/drawable-mdpi/ic_menuselect_on.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menuselect_settings.png b/res/drawable-mdpi/ic_menuselect_settings.png
new file mode 100644
index 0000000..495d854
--- /dev/null
+++ b/res/drawable-mdpi/ic_menuselect_settings.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menuselect_wb_auto.png b/res/drawable-mdpi/ic_menuselect_wb_auto.png
new file mode 100644
index 0000000..65f5d18
--- /dev/null
+++ b/res/drawable-mdpi/ic_menuselect_wb_auto.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menuselect_wb_cloudy.png b/res/drawable-mdpi/ic_menuselect_wb_cloudy.png
new file mode 100644
index 0000000..1867c9b
--- /dev/null
+++ b/res/drawable-mdpi/ic_menuselect_wb_cloudy.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menuselect_wb_daylight.png b/res/drawable-mdpi/ic_menuselect_wb_daylight.png
new file mode 100644
index 0000000..d16a930
--- /dev/null
+++ b/res/drawable-mdpi/ic_menuselect_wb_daylight.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menuselect_wb_fluorescent.png b/res/drawable-mdpi/ic_menuselect_wb_fluorescent.png
new file mode 100644
index 0000000..b788229
--- /dev/null
+++ b/res/drawable-mdpi/ic_menuselect_wb_fluorescent.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menuselect_wb_incandescent.png b/res/drawable-mdpi/ic_menuselect_wb_incandescent.png
new file mode 100644
index 0000000..af78583
--- /dev/null
+++ b/res/drawable-mdpi/ic_menuselect_wb_incandescent.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_viewfinder_flash_auto.png b/res/drawable-mdpi/ic_viewfinder_flash_auto.png
index 79e36b7..4a86340 100755
--- a/res/drawable-mdpi/ic_viewfinder_flash_auto.png
+++ b/res/drawable-mdpi/ic_viewfinder_flash_auto.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_viewfinder_flash_off.png b/res/drawable-mdpi/ic_viewfinder_flash_off.png
index d076636..830fb23 100644
--- a/res/drawable-mdpi/ic_viewfinder_flash_off.png
+++ b/res/drawable-mdpi/ic_viewfinder_flash_off.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_viewfinder_flash_on.png b/res/drawable-mdpi/ic_viewfinder_flash_on.png
index 91dfe24..8f600e5 100755
--- a/res/drawable-mdpi/ic_viewfinder_flash_on.png
+++ b/res/drawable-mdpi/ic_viewfinder_flash_on.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_viewfinder_focus_auto.png b/res/drawable-mdpi/ic_viewfinder_focus_auto.png
new file mode 100644
index 0000000..076b434
--- /dev/null
+++ b/res/drawable-mdpi/ic_viewfinder_focus_auto.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_viewfinder_focus_infinity.png b/res/drawable-mdpi/ic_viewfinder_focus_infinity.png
index afa0902..7eb90cb 100644
--- a/res/drawable-mdpi/ic_viewfinder_focus_infinity.png
+++ b/res/drawable-mdpi/ic_viewfinder_focus_infinity.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_viewfinder_focus_macro.png b/res/drawable-mdpi/ic_viewfinder_focus_macro.png
index e5c560f..dd3ecb3 100644
--- a/res/drawable-mdpi/ic_viewfinder_focus_macro.png
+++ b/res/drawable-mdpi/ic_viewfinder_focus_macro.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_viewfinder_gps_no_signal.png b/res/drawable-mdpi/ic_viewfinder_gps_no_signal.png
new file mode 100644
index 0000000..fbdea07
--- /dev/null
+++ b/res/drawable-mdpi/ic_viewfinder_gps_no_signal.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_viewfinder_gps_off.png b/res/drawable-mdpi/ic_viewfinder_gps_off.png
new file mode 100644
index 0000000..918c914
--- /dev/null
+++ b/res/drawable-mdpi/ic_viewfinder_gps_off.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_viewfinder_gps_on.png b/res/drawable-mdpi/ic_viewfinder_gps_on.png
new file mode 100644
index 0000000..4bbb47b
--- /dev/null
+++ b/res/drawable-mdpi/ic_viewfinder_gps_on.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_viewfinder_iconbar.9.png b/res/drawable-mdpi/ic_viewfinder_iconbar.9.png
new file mode 100644
index 0000000..0cd4dfe
--- /dev/null
+++ b/res/drawable-mdpi/ic_viewfinder_iconbar.9.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_viewfinder_iconbar_highlight.9.png b/res/drawable-mdpi/ic_viewfinder_iconbar_highlight.9.png
new file mode 100644
index 0000000..1b08fdf
--- /dev/null
+++ b/res/drawable-mdpi/ic_viewfinder_iconbar_highlight.9.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_viewfinder_settings.png b/res/drawable-mdpi/ic_viewfinder_settings.png
new file mode 100644
index 0000000..8687eec
--- /dev/null
+++ b/res/drawable-mdpi/ic_viewfinder_settings.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_viewfinder_wb_auto.png b/res/drawable-mdpi/ic_viewfinder_wb_auto.png
new file mode 100644
index 0000000..0f18ca5
--- /dev/null
+++ b/res/drawable-mdpi/ic_viewfinder_wb_auto.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_viewfinder_wb_cloudy.png b/res/drawable-mdpi/ic_viewfinder_wb_cloudy.png
index 2d02af3..8a45c53 100644
--- a/res/drawable-mdpi/ic_viewfinder_wb_cloudy.png
+++ b/res/drawable-mdpi/ic_viewfinder_wb_cloudy.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_viewfinder_wb_daylight.png b/res/drawable-mdpi/ic_viewfinder_wb_daylight.png
index 77e4a6e..35268a0 100644
--- a/res/drawable-mdpi/ic_viewfinder_wb_daylight.png
+++ b/res/drawable-mdpi/ic_viewfinder_wb_daylight.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_viewfinder_wb_fluorescent.png b/res/drawable-mdpi/ic_viewfinder_wb_fluorescent.png
index 3a5f6ef..acfd9a7 100644
--- a/res/drawable-mdpi/ic_viewfinder_wb_fluorescent.png
+++ b/res/drawable-mdpi/ic_viewfinder_wb_fluorescent.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_viewfinder_wb_incandescent.png b/res/drawable-mdpi/ic_viewfinder_wb_incandescent.png
index cddb4a1..fe5a60f 100644
--- a/res/drawable-mdpi/ic_viewfinder_wb_incandescent.png
+++ b/res/drawable-mdpi/ic_viewfinder_wb_incandescent.png
Binary files differ
diff --git a/res/drawable-mdpi/menu_popup.9.png b/res/drawable-mdpi/menu_popup.9.png
new file mode 100644
index 0000000..534d3cf
--- /dev/null
+++ b/res/drawable-mdpi/menu_popup.9.png
Binary files differ
diff --git a/res/drawable-mdpi/menu_popup_triangle.png b/res/drawable-mdpi/menu_popup_triangle.png
new file mode 100644
index 0000000..59e2f99
--- /dev/null
+++ b/res/drawable-mdpi/menu_popup_triangle.png
Binary files differ
diff --git a/res/drawable-mdpi/on_screen_hint_frame.9.png b/res/drawable-mdpi/on_screen_hint_frame.9.png
index 08c4f86..8b78d67 100644
--- a/res/drawable-mdpi/on_screen_hint_frame.9.png
+++ b/res/drawable-mdpi/on_screen_hint_frame.9.png
Binary files differ
diff --git a/res/drawable-mdpi/optionheader_background.9.png b/res/drawable-mdpi/optionheader_background.9.png
new file mode 100644
index 0000000..22991e7
--- /dev/null
+++ b/res/drawable-mdpi/optionheader_background.9.png
Binary files differ
diff --git a/res/drawable-mdpi/optionitem_highlight.9.png b/res/drawable-mdpi/optionitem_highlight.9.png
new file mode 100644
index 0000000..3a17a86
--- /dev/null
+++ b/res/drawable-mdpi/optionitem_highlight.9.png
Binary files differ
diff --git a/res/drawable-mdpi/scrollbar_handle_vertical.9.png b/res/drawable-mdpi/scrollbar_handle_vertical.9.png
new file mode 100644
index 0000000..3ec0791
--- /dev/null
+++ b/res/drawable-mdpi/scrollbar_handle_vertical.9.png
Binary files differ
diff --git a/res/drawable-mdpi/zoom_shape.png b/res/drawable-mdpi/zoom_shape.png
new file mode 100644
index 0000000..c3155f7
--- /dev/null
+++ b/res/drawable-mdpi/zoom_shape.png
Binary files differ
diff --git a/res/drawable-mdpi/zoom_slider.png b/res/drawable-mdpi/zoom_slider.png
new file mode 100644
index 0000000..c9f7c5b
--- /dev/null
+++ b/res/drawable-mdpi/zoom_slider.png
Binary files differ
diff --git a/res/drawable-mdpi/zoom_tickmarks.png b/res/drawable-mdpi/zoom_tickmarks.png
new file mode 100644
index 0000000..32f207e
--- /dev/null
+++ b/res/drawable-mdpi/zoom_tickmarks.png
Binary files differ
diff --git a/res/layout/camera.xml b/res/layout/camera.xml
index d1c1336..1a451f4 100644
--- a/res/layout/camera.xml
+++ b/res/layout/camera.xml
@@ -25,10 +25,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
- <ImageView android:id="@+id/btn_gripper"
- android:background="@drawable/btn_gripper"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
<FrameLayout android:id="@+id/frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -40,43 +36,10 @@
android:id="@+id/focus_rectangle"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- <LinearLayout android:id="@+id/indicator_bar"
- android:orientation="vertical"
- android:visibility="visible"
- android:gravity="center"
- android:layout_gravity="left|top"
- android:layout_marginTop="10dp"
- android:layout_marginLeft="10dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
- <!-- Thsee icons should be in the same order as the
- on-screen menu items -->
- <com.android.camera.IconIndicator
- style="@style/IconIndicator"
- android:id="@+id/gps_icon"
- camera:modes="@array/gps_modes"
- camera:icons="@array/gps_icons"/>
- <com.android.camera.IconIndicator
- style="@style/IconIndicator"
- android:id="@+id/flash_icon"
- camera:modes="@array/flash_modes"
- camera:icons="@array/flash_icons"/>
- <com.android.camera.IconIndicator
- style="@style/IconIndicator"
- android:id="@+id/scenemode_icon"
- camera:modes="@array/scenemode_modes"
- camera:icons="@array/scenemode_icons"/>
- <com.android.camera.IconIndicator
- style="@style/IconIndicator"
- android:id="@+id/whitebalance_icon"
- camera:modes="@array/pref_camera_whitebalance_entryvalues"
- camera:icons="@array/whitebalance_icons"/>
- <com.android.camera.IconIndicator
- style="@style/IconIndicator"
- android:id="@+id/focus_icon"
- camera:modes="@array/pref_camera_focusmode_entryvalues"
- camera:icons="@array/focusmode_icons"/>
- </LinearLayout>
+ <com.android.camera.ui.GLRootView
+ android:id="@+id/settings_ui"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
</FrameLayout>
</com.android.camera.PreviewFrameLayout>
</LinearLayout>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 33768b2..42b80f4 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -96,13 +96,13 @@
</string-array>
<array name="pref_camera_focusmode_icons" translatable="false">
- <item>@drawable/ic_viewfinder_empty</item>
- <item>@drawable/ic_viewfinder_focus_infinity</item>
- <item>@drawable/ic_viewfinder_focus_macro</item>
+ <item>@drawable/ic_menuselect_focus_auto</item>
+ <item>@drawable/ic_menuselect_focus_infinity</item>
+ <item>@drawable/ic_menuselect_focus_macro</item>
</array>
<array name="focusmode_icons">
- <item>@drawable/ic_viewfinder_empty</item>
+ <item>@drawable/ic_viewfinder_focus_auto</item>
<item>@drawable/ic_viewfinder_focus_infinity</item>
<item>@drawable/ic_viewfinder_focus_macro</item>
</array>
@@ -121,6 +121,12 @@
</string-array>
<array name="pref_camera_flashmode_icons">
+ <item>@drawable/ic_menuselect_flash_auto</item>
+ <item>@drawable/ic_menuselect_flash_on</item>
+ <item>@drawable/ic_menuselect_flash_off</item>
+ </array>
+
+ <array name="flashmode_icons">
<item>@drawable/ic_viewfinder_flash_auto</item>
<item>@drawable/ic_viewfinder_flash_on</item>
<item>@drawable/ic_viewfinder_flash_off</item>
@@ -138,8 +144,8 @@
</string-array>
<array name="pref_camera_video_flashmode_icons">
- <item>@drawable/ic_viewfinder_flash_on</item>
- <item>@drawable/ic_viewfinder_flash_off</item>
+ <item>@drawable/ic_menuselect_flash_on</item>
+ <item>@drawable/ic_menuselect_flash_off</item>
</array>
<string-array name="pref_camera_recordlocation_entryvalues" translatable="false">
@@ -152,6 +158,16 @@
<item>@string/pref_camera_recordlocation_entry_on</item>
</array>
+ <array name="pref_camera_recordlocation_icons" translatable="false">
+ <item>@drawable/ic_menuselect_gps_off</item>
+ <item>@drawable/ic_menuselect_gps_on</item>
+ </array>
+
+ <array name="recordlocation_icons">
+ <item>@drawable/ic_viewfinder_gps_off</item>
+ <item>@drawable/ic_viewfinder_gps_on</item>
+ </array>
+
<string-array name="flash_modes" translatable="false">
<item>auto</item>
<item>on</item>
@@ -173,11 +189,6 @@
<item>on</item>
</string-array>
- <array name="gps_icons">
- <item>@drawable/ic_viewfinder_empty</item>
- <item>@drawable/ic_camera_sym_gps</item>
- </array>
-
<!-- Camera Preferences White Balance dialog box entries -->
<string-array name="pref_camera_whitebalance_entries" translatable="false">
<item>@string/pref_camera_whitebalance_entry_auto</item>
@@ -196,15 +207,15 @@
</string-array>
<array name="pref_camera_whitebalance_icons">
- <item>@drawable/ic_viewfinder_empty</item>
- <item>@drawable/ic_viewfinder_wb_incandescent</item>
- <item>@drawable/ic_viewfinder_wb_daylight</item>
- <item>@drawable/ic_viewfinder_wb_fluorescent</item>
- <item>@drawable/ic_viewfinder_wb_cloudy</item>
+ <item>@drawable/ic_menuselect_wb_auto</item>
+ <item>@drawable/ic_menuselect_wb_incandescent</item>
+ <item>@drawable/ic_menuselect_wb_daylight</item>
+ <item>@drawable/ic_menuselect_wb_fluorescent</item>
+ <item>@drawable/ic_menuselect_wb_cloudy</item>
</array>
<array name="whitebalance_icons">
- <item>@drawable/ic_viewfinder_empty</item>
+ <item>@drawable/ic_viewfinder_wb_auto</item>
<item>@drawable/ic_viewfinder_wb_incandescent</item>
<item>@drawable/ic_viewfinder_wb_daylight</item>
<item>@drawable/ic_viewfinder_wb_fluorescent</item>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 7a4f19d..74a6091 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -29,6 +29,7 @@
</declare-styleable>
<declare-styleable name="IconListPreference">
<attr name="icons" />
+ <attr name="largeIcons" format="reference" />
</declare-styleable>
<declare-styleable name="EvenlySpacedLayout">
<attr name="orientation">
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 7193b7c..a70d319 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -20,4 +20,6 @@
<resources>
<color name="recording_time_elapsed_text">#FFFFFFFF</color>
<color name="recording_time_remaining_text">#FFFF0033</color>
+ <color name="indicator_pressed">#992B2B2B</color>
+ <color name="indicator_default">#00FFFFFF</color>
</resources>
diff --git a/res/xml/camera_preferences.xml b/res/xml/camera_preferences.xml
index 0eec3ab..7bbab46 100644
--- a/res/xml/camera_preferences.xml
+++ b/res/xml/camera_preferences.xml
@@ -23,6 +23,7 @@
camera:defaultValue="@string/pref_camera_flashmode_default"
camera:title="@string/pref_camera_flashmode_title"
camera:icons="@array/pref_camera_flashmode_icons"
+ camera:largeIcons="@array/flashmode_icons"
camera:entries="@array/pref_camera_flashmode_entries"
camera:entryValues="@array/pref_camera_flashmode_entryvalues" />
<ListPreference
@@ -36,6 +37,7 @@
camera:defaultValue="@string/pref_camera_whitebalance_default"
camera:title="@string/pref_camera_whitebalance_title"
camera:icons="@array/pref_camera_whitebalance_icons"
+ camera:largeIcons="@array/whitebalance_icons"
camera:entries="@array/pref_camera_whitebalance_entries"
camera:entryValues="@array/pref_camera_whitebalance_entryvalues" />
<ListPreference
@@ -48,6 +50,8 @@
camera:key="pref_camera_recordlocation_key"
camera:defaultValue="@string/pref_camera_recordlocation_default"
camera:title="@string/pref_camera_recordlocation_title"
+ camera:icons="@array/pref_camera_recordlocation_icons"
+ camera:largeIcons="@array/recordlocation_icons"
camera:entries="@array/pref_camera_recordlocation_entries"
camera:entryValues="@array/pref_camera_recordlocation_entryvalues" />
<ListPreference
@@ -61,11 +65,10 @@
camera:title="@string/pref_camera_jpegquality_title"
camera:entries="@array/pref_camera_jpegquality_entries"
camera:entryValues="@array/pref_camera_jpegquality_entryvalues" />
- <IconListPreference
+ <ListPreference
camera:key="pref_camera_focusmode_key"
camera:defaultValue="@string/pref_camera_focusmode_default"
camera:title="@string/pref_camera_focusmode_title"
- camera:icons="@array/pref_camera_focusmode_icons"
camera:entries="@array/pref_camera_focusmode_entries"
camera:entryValues="@array/pref_camera_focusmode_entryvalues" />
<ListPreference
diff --git a/src/com/android/camera/Camera.java b/src/com/android/camera/Camera.java
index b3dd504..9989bac 100644
--- a/src/com/android/camera/Camera.java
+++ b/src/com/android/camera/Camera.java
@@ -72,6 +72,8 @@ import android.widget.ZoomButtonsController;
import com.android.camera.gallery.IImage;
import com.android.camera.gallery.IImageList;
+import com.android.camera.ui.GLRootView;
+import com.android.camera.ui.HeadUpDisplay;
import java.io.File;
import java.io.FileNotFoundException;
@@ -92,9 +94,6 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
private static final String TAG = "camera";
- // This value must be as same as the item value of the string array
- // "flash_mode" in file "res/values/arrays.xml".
- private static final String NO_FLASH_MODE = "no_flash";
private static final int CROP_MSG = 1;
private static final int FIRST_TIME_INIT = 2;
private static final int RESTART_PREVIEW = 3;
@@ -104,15 +103,9 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
// The reason why it is set to 0.7 is just because 1.0 is too bright.
private static final float DEFAULT_CAMERA_BRIGHTNESS = 0.7f;
- private static final String GPS_MODE_ON = "on";
- private static final String GPS_MODE_OFF = "off";
-
private static final int SCREEN_DELAY = 2 * 60 * 1000;
private static final int FOCUS_BEEP_VOLUME = 100;
- private static final String SCENE_MODE_ON = "on";
- private static final String SCENE_MODE_OFF = "off";
-
private boolean mZooming = false;
private boolean mSmoothZoomSupported = false;
private int mZoomValue; // The current zoom value.
@@ -140,17 +133,14 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
private SurfaceHolder mSurfaceHolder = null;
private ShutterButton mShutterButton;
private FocusRectangle mFocusRectangle;
- private IconIndicator mGpsIndicator;
- private IconIndicator mFlashIndicator;
- private IconIndicator mFocusIndicator;
- private IconIndicator mWhitebalanceIndicator;
- private IconIndicator mSceneModeIndicator;
private ToneGenerator mFocusToneGenerator;
private ZoomButtonsController mZoomButtons;
private GestureDetector mGestureDetector;
private Switcher mSwitcher;
private boolean mStartPreviewFail = false;
+ private GLRootView mRootView;
+
// mPostCaptureAlert, mLastPictureButton, mThumbController
// are non-null only if isImageCaptureIntent() is true.
private ImageView mLastPictureButton;
@@ -223,6 +213,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
private final Handler mHandler = new MainHandler();
private OnScreenSettings mSettings;
private boolean mQuickCapture;
+ private HeadUpDisplay mHeadUpDisplay;
/**
* This Handler is used to post message back onto the main thread of the
@@ -283,6 +274,11 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
if (!mIsImageCaptureIntent) {
setOrientationIndicator(mLastOrientation);
}
+ mRootView.queueEvent(new Runnable() {
+ public void run() {
+ mHeadUpDisplay.setOrientation(mLastOrientation);
+ }
+ });
}
}
};
@@ -320,9 +316,6 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
mFocusRectangle = (FocusRectangle) findViewById(R.id.focus_rectangle);
updateFocusIndicator();
- // Initialize GPS indicator.
- mGpsIndicator = (IconIndicator) findViewById(R.id.gps_icon);
-
ImageManager.ensureOSXCompatibleFolder();
initializeScreenBrightness();
@@ -334,6 +327,13 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
initializeZoom();
mFirstTimeInitialized = true;
+
+ mRootView = (GLRootView) findViewById(R.id.settings_ui);
+ mHeadUpDisplay = new HeadUpDisplay();
+ CameraSettings settings = new CameraSettings(this, mInitialParams);
+ mHeadUpDisplay.initialize(this,
+ settings.getPreferenceGroup(R.xml.camera_preferences));
+ mRootView.setContentPane(mHeadUpDisplay);
}
private void updateThumbnailButton() {
@@ -548,7 +548,9 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
// update so update GPS indicator when we receive data.
if (mRecordLocation
&& LocationManager.GPS_PROVIDER.equals(mProvider)) {
- mGpsIndicator.setMode(GPS_MODE_ON);
+ if (mHeadUpDisplay != null) {
+ mHeadUpDisplay.setGpsHasSignal(true);
+ }
}
mLastLocation.set(newLocation);
mValid = true;
@@ -569,7 +571,9 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
mValid = false;
if (mRecordLocation &&
LocationManager.GPS_PROVIDER.equals(provider)) {
- mGpsIndicator.setMode(GPS_MODE_OFF);
+ if (mHeadUpDisplay != null) {
+ mHeadUpDisplay.setGpsHasSignal(false);
+ }
}
break;
}
@@ -972,14 +976,6 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
mSwitcher.setOnSwitchListener(this);
mSwitcher.addTouchView(findViewById(R.id.camera_switch_set));
}
- findViewById(R.id.btn_gripper)
- .setOnTouchListener(new GripperTouchListener());
-
- mFlashIndicator = (IconIndicator) findViewById(R.id.flash_icon);
- mFocusIndicator = (IconIndicator) findViewById(R.id.focus_icon);
- mSceneModeIndicator = (IconIndicator) findViewById(R.id.scenemode_icon);
- mWhitebalanceIndicator =
- (IconIndicator) findViewById(R.id.whitebalance_icon);
// Make sure preview is started.
try {
@@ -991,7 +987,6 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
} catch (InterruptedException ex) {
// ignore
}
- removeUnsupportedIndicators();
}
private void setOrientationIndicator(int degree) {
@@ -1003,37 +998,6 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
R.id.video_switch_icon)).setDegree(degree);
}
- private void removeUnsupportedIndicators() {
- if (mParameters.getSupportedFocusModes() == null) {
- mFocusIndicator.setVisibility(View.GONE);
- }
-
- if (mParameters.getSupportedWhiteBalance() == null) {
- mWhitebalanceIndicator.setVisibility(View.GONE);
- }
-
- if (mParameters.getSupportedFlashModes() == null) {
- mFlashIndicator.setVisibility(View.GONE);
- }
-
- if (mParameters.getSupportedSceneModes() == null) {
- mSceneModeIndicator.setVisibility(View.GONE);
- }
- }
-
- private class GripperTouchListener implements View.OnTouchListener {
- public boolean onTouch(View view, MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- return true;
- case MotionEvent.ACTION_UP:
- showOnScreenSettings();
- return true;
- }
- return false;
- }
- }
-
@Override
public void onStart() {
super.onStart();
@@ -1071,46 +1035,6 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
mPreferences.registerOnSharedPreferenceChangeListener(this);
}
- private void showOnScreenSettings() {
- if (mSettings == null) {
- mSettings = new OnScreenSettings(
- findViewById(R.id.camera_preview));
-
- Runnable resetPreferences = new Runnable() {
- public void run() {
- restorePreferences();
- }
- };
-
- mSettings.setRestoreRunner(resetPreferences);
-
- CameraSettings helper =
- new CameraSettings(this, mInitialParams);
- mSettings.setPreferenceGroup(helper
- .getPreferenceGroup(R.xml.camera_preferences));
- mSettings.setOnVisibilityChangedListener(this);
-
- String sceneMode = mParameters.getSceneMode();
- if (sceneMode == null
- || Parameters.SCENE_MODE_AUTO.equals(sceneMode)) {
- // If scene mode is auto, cancel override in settings
- mSettings.overrideSettings(CameraSettings.KEY_FLASH_MODE, null);
- mSettings.overrideSettings(CameraSettings.KEY_FOCUS_MODE, null);
- mSettings.overrideSettings(
- CameraSettings.KEY_WHITE_BALANCE, null);
- } else {
- // If scene mode is not auto, override the value in settings
- mSettings.overrideSettings(CameraSettings.KEY_FLASH_MODE,
- mParameters.getFlashMode());
- mSettings.overrideSettings(CameraSettings.KEY_FOCUS_MODE,
- mParameters.getFocusMode());
- mSettings.overrideSettings(CameraSettings.KEY_WHITE_BALANCE,
- mParameters.getWhiteBalance());
- }
- }
- mSettings.setVisible(true);
- }
-
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_retake:
@@ -1316,8 +1240,9 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
}
@Override
- public void onResume() {
+ protected void onResume() {
super.onResume();
+ mPreferences.registerOnSharedPreferenceChangeListener(this);
mPausing = false;
mJpegPictureCallbackTime = 0;
@@ -1357,6 +1282,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
// Close the camera now because other activities may need to use it.
closeCamera();
resetScreenOn();
+ mPreferences.unregisterOnSharedPreferenceChangeListener(this);
if (mSettings != null && mSettings.isVisible()) {
mSettings.setVisible(false);
@@ -1364,7 +1290,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
if (mFirstTimeInitialized) {
mOrientationListener.disable();
- mGpsIndicator.setMode(GPS_MODE_OFF);
+ mHeadUpDisplay.setGpsHasSignal(false);
if (!mIsImageCaptureIntent) {
mThumbController.storeData(
ImageManager.getLastImageThumbPath());
@@ -1528,12 +1454,6 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
doFocus(false);
}
return true;
- case KeyEvent.KEYCODE_MENU:
- if (mIsImageCaptureIntent) {
- showOnScreenSettings();
- return true;
- }
- break;
}
return super.onKeyUp(keyCode, event);
}
@@ -1863,30 +1783,38 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
// If scene mode is set, we cannot set flash mode, white balance, and
// focus mode, instead, we read it from driver
- String flashMode;
- String whiteBalance;
if (!Parameters.SCENE_MODE_AUTO.equals(sceneMode)) {
- flashMode = mParameters.getFlashMode();
- whiteBalance = mParameters.getWhiteBalance();
+ final String flashMode = mParameters.getFlashMode();
+ final String whiteBalance = mParameters.getWhiteBalance();
mFocusMode = mParameters.getFocusMode();
- if (mSettings != null) {
- mSettings.overrideSettings(
- CameraSettings.KEY_FLASH_MODE, flashMode);
- mSettings.overrideSettings(
- CameraSettings.KEY_WHITE_BALANCE, whiteBalance);
- mSettings.overrideSettings(
- CameraSettings.KEY_FOCUS_MODE, mFocusMode);
+
+ if (mRootView != null) {
+ final String finalFlashMode = flashMode;
+ mRootView.queueEvent(new Runnable() {
+ public void run() {
+ mHeadUpDisplay.overrideSettings(
+ CameraSettings.KEY_FLASH_MODE, flashMode);
+ mHeadUpDisplay.overrideSettings(
+ CameraSettings.KEY_WHITE_BALANCE, whiteBalance);
+ mHeadUpDisplay.overrideSettings(
+ CameraSettings.KEY_FOCUS_MODE, mFocusMode);
+ }});
}
} else {
- if (mSettings != null) {
- mSettings.overrideSettings(CameraSettings.KEY_FLASH_MODE, null);
- mSettings.overrideSettings(CameraSettings.KEY_FOCUS_MODE, null);
- mSettings.overrideSettings(
- CameraSettings.KEY_WHITE_BALANCE, null);
+ if (mRootView != null) {
+ mRootView.queueEvent(new Runnable() {
+ public void run() {
+ mHeadUpDisplay.overrideSettings(
+ CameraSettings.KEY_FLASH_MODE, null);
+ mHeadUpDisplay.overrideSettings(
+ CameraSettings.KEY_FOCUS_MODE, null);
+ mHeadUpDisplay.overrideSettings(
+ CameraSettings.KEY_WHITE_BALANCE, null);
+ }});
}
// Set flash mode.
- flashMode = mPreferences.getString(
+ String flashMode = mPreferences.getString(
CameraSettings.KEY_FLASH_MODE,
getString(R.string.pref_camera_flashmode_default));
List<String> supportedFlash = mParameters.getSupportedFlashModes();
@@ -1901,7 +1829,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
}
// Set white balance parameter.
- whiteBalance = mPreferences.getString(
+ String whiteBalance = mPreferences.getString(
CameraSettings.KEY_WHITE_BALANCE,
getString(R.string.pref_camera_whitebalance_default));
if (isSupported(whiteBalance,
@@ -1929,24 +1857,6 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
mCameraDevice.setParameters(mParameters);
}
-
- // We post the runner because this function can be called from
- // non-UI thread (i.e., startPreviewThread).
- final String finalWhiteBalance = whiteBalance;
- final String finalFlashMode = flashMode;
- final String finalSceneMode =
- Parameters.SCENE_MODE_AUTO.equals(sceneMode)
- ? SCENE_MODE_OFF
- : SCENE_MODE_ON;
-
- mHandler.post(new Runnable() {
- public void run() {
- mFocusIndicator.setMode(mFocusMode);
- mWhitebalanceIndicator.setMode(finalWhiteBalance);
- mSceneModeIndicator.setMode(finalSceneMode);
- mFlashIndicator.setMode(finalFlashMode);
- }
- });
}
private void gotoGallery() {
@@ -2094,16 +2004,6 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
});
gallery.setIcon(android.R.drawable.ic_menu_gallery);
mGalleryItems.add(gallery);
-
- MenuItem item = menu.add(Menu.NONE, Menu.NONE,
- MenuHelper.POSITION_CAMERA_SETTING, R.string.settings)
- .setOnMenuItemClickListener(new OnMenuItemClickListener() {
- public boolean onMenuItemClick(MenuItem item) {
- showOnScreenSettings();
- return true;
- }
- });
- item.setIcon(android.R.drawable.ic_menu_preferences);
}
public boolean onSwitchChanged(Switcher source, boolean onOff) {
@@ -2116,25 +2016,32 @@ public class Camera extends NoSearchActivity implements View.OnClickListener,
}
public void onSharedPreferenceChanged(
- SharedPreferences preferences, String key) {
- // ignore the events after "onPause()"
- if (mPausing) return;
-
- if (CameraSettings.KEY_RECORD_LOCATION.equals(key)) {
- mRecordLocation = RecordLocationPreference.get(
- preferences, getContentResolver());
- if (mRecordLocation) {
- startReceivingLocationUpdates();
- } else {
- stopReceivingLocationUpdates();
+ final SharedPreferences preferences, final String key) {
+ Runnable runnable = new Runnable() {
+ public void run() {
+ // ignore the events after "onPause()"
+ if (mPausing) return;
+ Log.v(TAG, "call()");
+
+ if (CameraSettings.KEY_RECORD_LOCATION.equals(key)) {
+ mRecordLocation = RecordLocationPreference.get(
+ preferences, getContentResolver());
+ if (mRecordLocation) {
+ startReceivingLocationUpdates();
+ } else {
+ stopReceivingLocationUpdates();
+ }
+ } else if (CameraSettings.KEY_QUICK_CAPTURE.equals(key)) {
+ mQuickCapture = getQuickCaptureSettings();
+ } else {
+ // All preferences except RECORD_LOCATION are camera
+ // parameters. Call setCameraParameters to take effect now.
+ setCameraParameters();
+ }
+ Log.v(TAG, "call() end");
}
- } else if (CameraSettings.KEY_QUICK_CAPTURE.equals(key)) {
- mQuickCapture = getQuickCaptureSettings();
- } else {
- // All preferences except RECORD_LOCATION are camera parameters.
- // Call setCameraParameters to take effect now.
- setCameraParameters();
- }
+ };
+ runOnUiThread(runnable);
}
private boolean getQuickCaptureSettings() {
diff --git a/src/com/android/camera/IconListPreference.java b/src/com/android/camera/IconListPreference.java
index 25c641c..54a101f 100644
--- a/src/com/android/camera/IconListPreference.java
+++ b/src/com/android/camera/IconListPreference.java
@@ -24,32 +24,56 @@ import android.util.AttributeSet;
/** A {@code ListPreference} where each entry has a corresponding icon. */
public class IconListPreference extends ListPreference {
+
+ private final int mIconIds[];
+ private final int mLargeIconIds[];
+
private Drawable mIcons[];
- private Resources mResources;
+ private final Resources mResources;
public IconListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.IconListPreference, 0, 0);
mResources = context.getResources();
- setIcons(a.getResourceId(R.styleable.IconListPreference_icons, 0));
+ mIconIds = getIconIds(a.getResourceId(
+ R.styleable.IconListPreference_icons, 0));
+ mLargeIconIds = getIconIds(a.getResourceId(
+ R.styleable.IconListPreference_largeIcons, 0));
a.recycle();
}
public Drawable[] getIcons() {
+ if (mIcons == null) {
+ int n = mIconIds.length;
+ Drawable[] drawable = new Drawable[n];
+ int[] id = mIconIds;
+ for (int i = 0; i < n; ++i) {
+ drawable[i] = id[i] == 0 ? null : mResources.getDrawable(id[i]);
+ }
+ mIcons = drawable;
+ }
return mIcons;
}
- private void setIcons(int iconsRes) {
+ public int[] getLargeIconIds() {
+ return mLargeIconIds;
+ }
+
+ public int[] getIconIds() {
+ return mIconIds;
+ }
+
+ private int[] getIconIds(int iconsRes) {
+ if (iconsRes == 0) return null;
TypedArray array = mResources.obtainTypedArray(iconsRes);
int n = array.length();
- Drawable drawable[] = new Drawable[n];
+ int ids[] = new int[n];
for (int i = 0; i < n; ++i) {
- int id = array.getResourceId(i, 0);
- drawable[i] = id == 0 ? null : mResources.getDrawable(id);
+ ids[i] = array.getResourceId(i, 0);
}
array.recycle();
- mIcons = drawable;
+ return ids;
}
public void setIcons(Drawable[] icons) {
diff --git a/src/com/android/camera/Menu3DTest.java b/src/com/android/camera/Menu3DTest.java
new file mode 100644
index 0000000..5e599ad
--- /dev/null
+++ b/src/com/android/camera/Menu3DTest.java
@@ -0,0 +1,71 @@
+package com.android.camera;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.OrientationEventListener;
+
+import com.android.camera.ui.GLRootView;
+import com.android.camera.ui.HeadUpDisplay;
+
+public class Menu3DTest extends Activity {
+
+ private static String TAG = "Menu3DTest";
+
+ private GLRootView mRootView;
+ private OrientationEventListener mOrientationListener;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ mRootView = new GLRootView(this);
+
+ // set background as 18% gray :D
+ mRootView.setBackgroundColor(0xffb7b7b7);
+ final HeadUpDisplay hud = new HeadUpDisplay();
+ mRootView.setContentPane(hud);
+
+ PreferenceInflater inflater = new PreferenceInflater(this);
+ PreferenceGroup preferenceGroup =
+ (PreferenceGroup) inflater.inflate(R.xml.camera_preferences);
+ hud.initialize(this, preferenceGroup);
+ hud.overrideSettings(CameraSettings.KEY_FOCUS_MODE, "macro");
+
+ setContentView(mRootView);
+
+ mOrientationListener = new OrientationEventListener(this) {
+ private int mLastOrientation = -1;
+ @Override
+ public void onOrientationChanged(int orientation) {
+ // We keep the last known orientation. So if the user
+ // first orient the camera then point the camera to
+ if (orientation != ORIENTATION_UNKNOWN) {
+ orientation += 90;
+ }
+ final int finalOrientation =
+ ImageManager.roundOrientation(orientation);
+ if (mLastOrientation != finalOrientation) {
+ mLastOrientation = finalOrientation;
+ mRootView.queueEvent(new Runnable() {
+ public void run() {
+ hud.setOrientation(finalOrientation);
+ }
+ });
+ }
+ }
+ };
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mRootView.onResume();
+ mOrientationListener.enable();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mRootView.onPause();
+ mOrientationListener.disable();
+ }
+}
diff --git a/src/com/android/camera/RecordLocationPreference.java b/src/com/android/camera/RecordLocationPreference.java
index 95c1fbf..7a98c25 100644
--- a/src/com/android/camera/RecordLocationPreference.java
+++ b/src/com/android/camera/RecordLocationPreference.java
@@ -16,18 +16,16 @@
package com.android.camera;
-import android.content.Context;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.SharedPreferences;
-import android.provider.Settings;
import android.util.AttributeSet;
-import android.util.Log;
/**
* {@code RecordLocationPreference} is used to keep the "store locaiton"
* option in {@code SharedPreference}.
*/
-public class RecordLocationPreference extends ListPreference {
+public class RecordLocationPreference extends IconListPreference {
public static final String KEY = "pref_camera_recordlocation_key";
@@ -35,7 +33,7 @@ public class RecordLocationPreference extends ListPreference {
public static final String VALUE_ON = "on";
public static final String VALUE_OFF = "off";
- private ContentResolver mResolver;
+ private final ContentResolver mResolver;
public RecordLocationPreference(Context context, AttributeSet attrs) {
super(context, attrs);
diff --git a/src/com/android/camera/Util.java b/src/com/android/camera/Util.java
index df014cd..5cbfd13 100644
--- a/src/com/android/camera/Util.java
+++ b/src/com/android/camera/Util.java
@@ -18,17 +18,13 @@ package com.android.camera;
import android.app.Activity;
import android.app.AlertDialog;
-import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
import android.graphics.Matrix;
-import android.graphics.Rect;
import android.net.Uri;
-import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.view.View;
@@ -259,11 +255,6 @@ public class Util {
}
}
- public static boolean equals(String a, String b) {
- // return true if both string are null or the content equals
- return a == b || a.equals(b);
- }
-
// Returns an intent which is used for "set as" menu items.
public static Intent createSetAsIntent(IImage image) {
Uri u = image.fullSizeImageUri();
@@ -346,4 +337,18 @@ public class Util {
public static boolean equals(Object a, Object b) {
return (a == b) || (a == null ? false : a.equals(b));
}
+
+ public static boolean isPowerOf2(int n) {
+ return (n & -n) == n;
+ }
+
+ public static int nextPowerOf2(int n) {
+ n -= 1;
+ n |= n >>> 16;
+ n |= n >>> 8;
+ n |= n >>> 4;
+ n |= n >>> 2;
+ n |= n >>> 1;
+ return n + 1;
+ }
}
diff --git a/src/com/android/camera/ui/AbstractIndicator.java b/src/com/android/camera/ui/AbstractIndicator.java
new file mode 100644
index 0000000..531640d
--- /dev/null
+++ b/src/com/android/camera/ui/AbstractIndicator.java
@@ -0,0 +1,43 @@
+
+package com.android.camera.ui;
+
+import android.graphics.Rect;
+
+import javax.microedition.khronos.opengles.GL11;
+
+public abstract class AbstractIndicator extends GLView {
+ private static final int DEFAULT_PADDING = 5;
+
+ abstract protected ResourceTexture getIcon();
+
+ public AbstractIndicator() {
+ setPaddings(DEFAULT_PADDING, 0, DEFAULT_PADDING, 0);
+ }
+
+ @Override
+ protected void onMeasure(int widthSpec, int heightSpec) {
+ ResourceTexture icon = getIcon();
+ new MeasureHelper(this)
+ .setPreferedContentSize(icon.getWidth(), icon.getHeight())
+ .measure(widthSpec, heightSpec);
+ }
+
+ @Override
+ protected void render(GLRootView root, GL11 gl) {
+ ResourceTexture icon = getIcon();
+
+ Rect p = mPaddings;
+ int width = getWidth() - p.left - p.right;
+ int height = getHeight() - p.top - p.bottom;
+
+ if (icon != null && icon.bind(root, gl)) {
+ icon.draw(root,
+ p.left + (width - icon.getWidth()) / 2,
+ p.top + (height - icon.getHeight()) / 2);
+ }
+ }
+
+ abstract public GLView getPopupContent();
+
+ abstract public void overrideSettings(String key, String settings);
+}
diff --git a/src/com/android/camera/ui/BasicIndicator.java b/src/com/android/camera/ui/BasicIndicator.java
new file mode 100644
index 0000000..0d97ee7
--- /dev/null
+++ b/src/com/android/camera/ui/BasicIndicator.java
@@ -0,0 +1,82 @@
+package com.android.camera.ui;
+
+import android.content.Context;
+
+import com.android.camera.IconListPreference;
+import com.android.camera.R;
+import com.android.camera.ui.GLListView.OnItemSelectedListener;
+
+public class BasicIndicator extends AbstractIndicator {
+
+ private final ResourceTexture mIcon[];
+ private final IconListPreference mPreference;
+ protected int mIndex;
+ private GLListView mPopupContent;
+ private PreferenceAdapter mModel;
+ private String mOverride;
+
+ public BasicIndicator(IconListPreference preference) {
+ mPreference = preference;
+ mIcon = new ResourceTexture[preference.getIconIds().length];
+ mIndex = preference.findIndexOfValue(preference.getValue());
+ }
+
+ @Override
+ public void overrideSettings(String key, String settings) {
+ IconListPreference pref = mPreference;
+
+ if (!pref.getKey().equals(key)) return;
+ mOverride = settings;
+ mIndex = pref.findIndexOfValue(
+ settings == null ? pref.getValue() : settings);
+ invalidate();
+ }
+
+ @Override
+ public GLView getPopupContent() {
+ if (mPopupContent == null) {
+ Context context = getGLRootView().getContext();
+ mPopupContent = new GLListView();
+ mPopupContent.setHighLight(new NinePatchTexture(
+ context, R.drawable.optionitem_highlight));
+ mPopupContent.setScroller(new NinePatchTexture(
+ context, R.drawable.scrollbar_handle_vertical));
+ mModel = new PreferenceAdapter(context, mPreference);
+ mPopupContent.setOnItemSelectedListener(new MyListener(mModel));
+ mPopupContent.setDataModel(mModel);
+ }
+ mModel.overrideSettings(mOverride);
+ return mPopupContent;
+ }
+
+ protected void onPreferenceChanged(int newIndex) {
+ mIndex = newIndex;
+ invalidate();
+ }
+
+ private class MyListener implements OnItemSelectedListener {
+
+ private final PreferenceAdapter mAdapter;
+
+ public MyListener(PreferenceAdapter adapter) {
+ mAdapter = adapter;
+ }
+
+ public void onItemSelected(GLView view, int position) {
+ mAdapter.onItemSelected(view, position);
+ onPreferenceChanged(position - 1);
+ }
+ }
+
+ @Override
+ protected ResourceTexture getIcon() {
+ int index = mIndex;
+ if (mIcon[index] == null) {
+ IconListPreference pref = mPreference;
+ Context context = getGLRootView().getContext();
+ mIcon[index] = new ResourceTexture(
+ context, mPreference.getLargeIconIds()[index]);
+ }
+ return mIcon[index];
+ }
+}
diff --git a/src/com/android/camera/ui/CanvasTexture.java b/src/com/android/camera/ui/CanvasTexture.java
new file mode 100644
index 0000000..f1ac85a
--- /dev/null
+++ b/src/com/android/camera/ui/CanvasTexture.java
@@ -0,0 +1,28 @@
+package com.android.camera.ui;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+
+/** Using a canvas to draw the texture */
+public abstract class CanvasTexture extends Texture {
+ protected Canvas mCanvas;
+
+ public CanvasTexture(int width, int height) {
+ setSize(width, height);
+ }
+
+ @Override
+ protected Bitmap getBitmap() {
+ Bitmap bitmap = generateGLCompatibleBitmap(mWidth, mHeight);
+ mCanvas = new Canvas(bitmap);
+ onDraw(mCanvas, bitmap);
+ return bitmap;
+ }
+
+ @Override
+ protected void freeBitmap(Bitmap bitmap) {
+ bitmap.recycle();
+ }
+
+ abstract protected void onDraw(Canvas canvas, Bitmap backing);
+}
diff --git a/src/com/android/camera/ui/FrameTexture.java b/src/com/android/camera/ui/FrameTexture.java
new file mode 100644
index 0000000..cc56f8c
--- /dev/null
+++ b/src/com/android/camera/ui/FrameTexture.java
@@ -0,0 +1,22 @@
+package com.android.camera.ui;
+
+import android.graphics.Rect;
+
+import javax.microedition.khronos.opengles.GL11;
+
+public abstract class FrameTexture extends Texture {
+
+ public FrameTexture() {
+ }
+
+ public FrameTexture(GL11 gl, int id, int state) {
+ super(gl, id, state);
+ }
+
+ @Override
+ public void setSize(int width, int height) {
+ super.setSize(width, height);
+ }
+
+ abstract public Rect getPaddings();
+}
diff --git a/src/com/android/camera/ui/GLListView.java b/src/com/android/camera/ui/GLListView.java
new file mode 100644
index 0000000..c5161ea
--- /dev/null
+++ b/src/com/android/camera/ui/GLListView.java
@@ -0,0 +1,267 @@
+package com.android.camera.ui;
+
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+import android.graphics.Rect;
+import android.view.MotionEvent;
+import android.view.View.MeasureSpec;
+
+import javax.microedition.khronos.opengles.GL11;
+
+public class GLListView extends GLView {
+ private static final String TAG = "GLListView";
+
+ private static final int MOTION_THRESHOLD = 15;
+ private static final int INDEX_NONE = -1;
+ private static final int MOTION_NONE = -1;
+
+ private Model mModel;
+
+ private int mHighlightIndex = INDEX_NONE;
+ private GLView mHighlightView;
+
+ private NinePatchTexture mHighLight;
+ private NinePatchTexture mScrollbar;
+
+ private int mVisibleStart = 0; // inclusive
+ private int mVisibleEnd = 0; // exclusive
+
+ private int mMotionStartY = MOTION_NONE;
+ private boolean mHasMeasured = false;
+ private boolean mHasMoved = false;
+
+ private OnItemSelectedListener mOnItemSelectedListener;
+
+ static public interface Model {
+ public int size();
+ public GLView getView(int index);
+ public boolean isSelectable(int index);
+ }
+
+ static public interface OnItemSelectedListener {
+ public void onItemSelected(GLView view, int position);
+ }
+
+ public void setHighLight(NinePatchTexture highLight) {
+ mHighLight = highLight;
+ }
+
+ public void setDataModel(Model model) {
+ mModel = model;
+ mScrollY = 0;
+ requestLayout();
+ }
+
+ public void setOnItemSelectedListener(OnItemSelectedListener l) {
+ mOnItemSelectedListener = l;
+ }
+
+ @Override
+ protected void render(GLRootView root, GL11 gl) {
+ root.clipRect(0, 0, getWidth(), getHeight());
+ if (mHighlightIndex != INDEX_NONE) {
+ GLView view = mModel.getView(mHighlightIndex);
+ Rect bounds = view.bounds();
+ if (mHighLight != null) {
+ int width = bounds.width();
+ int height = bounds.height();
+ mHighLight.setSize(width, height);
+ if (mHighLight.bind(root, gl)) {
+ root.draw2D(bounds.left - mScrollX,
+ bounds.top - mScrollY, width, height);
+ }
+ }
+ }
+ super.render(root, gl);
+ root.clearClip();
+
+ if (mScrollbar != null && mScrollHeight > getHeight()) {
+ int width = this.mScrollbar.getIntrinsicWidth();
+ int height = getHeight() * getHeight() / mScrollHeight;
+ mScrollbar.setSize(width, height);
+ if (mScrollbar.bind(root, gl)) {
+ int yoffset = mScrollY * getHeight() / mScrollHeight;
+ root.draw2D(getWidth() - width, yoffset, width, height);
+ }
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthSpec, int heightSpec) {
+
+ int heightMode = MeasureSpec.getMode(heightSpec);
+ int widthMode = MeasureSpec.getMode(widthSpec);
+
+ // first get the total height
+ int height = 0;
+ int maxWidth = 0;
+ for (int i = 0, n = mModel.size(); i < n; ++i) {
+ GLView view = mModel.getView(i);
+ view.measure(widthSpec, MeasureSpec.UNSPECIFIED);
+ height += view.getMeasuredHeight();
+ maxWidth = Math.max(maxWidth, view.getMeasuredWidth());
+ }
+
+ // if we need to show the scroll bar ...
+ if ((mScrollbar != null && (heightMode == MeasureSpec.AT_MOST
+ || heightMode == MeasureSpec.EXACTLY))
+ && height > MeasureSpec.getSize(heightSpec)) {
+ if (widthMode != MeasureSpec.UNSPECIFIED) {
+ int cWidthSpec = widthSpec - mScrollbar.getIntrinsicWidth();
+ height = 0;
+ for (int i = 0, n = mModel.size(); i < n; ++i) {
+ GLView view = mModel.getView(i);
+ view.measure(cWidthSpec, MeasureSpec.UNSPECIFIED);
+ height += view.getMeasuredHeight();
+ maxWidth = Math.max(maxWidth, view.getMeasuredWidth());
+ }
+ }
+ maxWidth += mScrollbar.getIntrinsicWidth();
+ }
+
+ mScrollHeight = height;
+ mHasMeasured = true;
+
+ new MeasureHelper(this)
+ .setPreferedContentSize(maxWidth, height)
+ .measure(widthSpec, heightSpec);
+ }
+
+ @Override
+ public int getComponentCount() {
+ return mVisibleEnd - mVisibleStart;
+ }
+
+ @Override
+ public GLView getComponent(int index) {
+ if (index < 0 || index >= mVisibleEnd - mVisibleStart) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ return mModel.getView(mVisibleStart + index);
+ }
+
+ @Override
+ public void requestLayout() {
+ mHasMeasured = false;
+ super.requestLayout();
+ }
+
+ @Override
+ protected void onLayout(
+ boolean change, int left, int top, int right, int bottom) {
+
+ if (!mHasMeasured || mMeasuredWidth != (right - left)) {
+ measure(makeMeasureSpec(right - left, MeasureSpec.EXACTLY),
+ makeMeasureSpec(bottom - top, MeasureSpec.EXACTLY));
+ }
+
+ int width = mScrollHeight > (bottom - top) && mScrollbar != null
+ ? right - left - mScrollbar.getIntrinsicWidth()
+ : right - left;
+ int yoffset = 0;
+
+ for (int i = 0, n = mModel.size(); i < n; ++i) {
+ GLView item = mModel.getView(i);
+ int nextOffset = yoffset + item.getMeasuredHeight();
+ item.layout(0, yoffset, width, nextOffset);
+ yoffset = nextOffset;
+ }
+ setScrollPosition(mScrollY, true);
+ }
+
+ private void setScrollPosition(int position, boolean force) {
+ int height = getHeight();
+
+ position = Math.max(0, position);
+ position = Math.min(mScrollHeight - height, position);
+
+ if (!force && position == mScrollY) return;
+ mScrollY = position;
+
+ int n = mModel.size();
+
+ int start = 0;
+ int end = 0;
+ for (start = 0; start < n; ++start) {
+ if (position < mModel.getView(start).mBounds.bottom) break;
+ }
+
+ position += height;
+ for (end = start; end < n; ++ end) {
+ if (position <= mModel.getView(end).mBounds.top) break;
+ }
+ setVisibleRange(start , end);
+ invalidate();
+ }
+
+ private void setVisibleRange(int start, int end) {
+ if (start == mVisibleStart && end == mVisibleEnd) return;
+ mVisibleStart = start;
+ mVisibleEnd = end;
+ }
+
+ @Override
+ protected boolean dispatchTouchEvent(MotionEvent event) {
+ return onTouch(event);
+ }
+
+ @Override @SuppressWarnings("fallthrough")
+ protected boolean onTouch(MotionEvent event) {
+ int y = (int) event.getY();
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_MOVE:
+ if ((mMotionStartY != MOTION_NONE) && (mHasMoved
+ || Math.abs(y - mMotionStartY) > MOTION_THRESHOLD)) {
+ setHighlightItem(null, INDEX_NONE);
+ setScrollPosition(mScrollY + mMotionStartY - y, false);
+ mMotionStartY = y;
+ mHasMoved = true;
+ } else {
+ findAndSetHighlightItem(y);
+ }
+ break;
+ case MotionEvent.ACTION_DOWN:
+ mHasMoved = false;
+ mMotionStartY = mScrollHeight > getHeight() ? y : MOTION_NONE;
+ break;
+ case MotionEvent.ACTION_UP:
+ if (!mHasMoved) {
+ if (mOnItemSelectedListener != null && mHighlightView != null) {
+ mOnItemSelectedListener
+ .onItemSelected(mHighlightView, mHighlightIndex);
+ }
+ mHasMoved = false;
+ }
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_OUTSIDE:
+ setHighlightItem(null, INDEX_NONE);
+ }
+ return true;
+ }
+
+ private void findAndSetHighlightItem(int y) {
+ int position = y + mScrollY;
+ for (int i = mVisibleStart, n = mVisibleEnd; i < n; ++i) {
+ GLView child = mModel.getView(i);
+ if (child.mBounds.bottom > position) {
+ if (mModel.isSelectable(i)) {
+ setHighlightItem(child, i);
+ return;
+ }
+ break;
+ }
+ }
+ setHighlightItem(null, INDEX_NONE);
+ }
+
+ private void setHighlightItem(GLView view, int index) {
+ if (index == mHighlightIndex) return;
+ mHighlightIndex = index;
+ mHighlightView = view;
+ if (mHighLight != null) invalidate();
+ }
+
+ public void setScroller(NinePatchTexture scrollbar) {
+ this.mScrollbar = scrollbar;
+ requestLayout();
+ }
+}
diff --git a/src/com/android/camera/ui/GLOptionHeader.java b/src/com/android/camera/ui/GLOptionHeader.java
new file mode 100644
index 0000000..d49fbc4
--- /dev/null
+++ b/src/com/android/camera/ui/GLOptionHeader.java
@@ -0,0 +1,50 @@
+package com.android.camera.ui;
+
+import android.graphics.Rect;
+
+import com.android.camera.ListPreference;
+
+import javax.microedition.khronos.opengles.GL11;
+
+public class GLOptionHeader extends GLView {
+ private static final int FONT_COLOR = 0xFF979797;
+
+ private final ListPreference mPreference;
+ private final StringTexture mTitle;
+ private NinePatchTexture mBackground;
+
+ public GLOptionHeader(ListPreference preference) {
+ mPreference = preference;
+ mTitle = StringTexture.newInstance(
+ preference.getTitle(), 16, FONT_COLOR);
+ }
+
+ public ListPreference getPreference() {
+ return mPreference;
+ }
+
+ public void setBackground(NinePatchTexture background) {
+ if (mBackground == background) return;
+ mBackground = background;
+ invalidate();
+ }
+
+ @Override
+ protected void onMeasure(int widthSpec, int heightSpec) {
+ new MeasureHelper(this)
+ .setPreferedContentSize(mTitle.getWidth(), mTitle.getHeight())
+ .measure(widthSpec, heightSpec);
+ }
+
+ @Override
+ protected void render(GLRootView root, GL11 gl) {
+ if (mBackground != null) {
+ mBackground.setSize(getWidth(), getHeight());
+ if (mBackground.bind(root, gl)) mBackground.draw(root, 0, 0);
+ }
+ if (mTitle.bind(root, gl)) {
+ Rect p = mPaddings;
+ mTitle.draw(root, p.left, p.top);
+ }
+ }
+}
diff --git a/src/com/android/camera/ui/GLOptionItem.java b/src/com/android/camera/ui/GLOptionItem.java
new file mode 100644
index 0000000..00d7a55
--- /dev/null
+++ b/src/com/android/camera/ui/GLOptionItem.java
@@ -0,0 +1,110 @@
+package com.android.camera.ui;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.view.animation.Transformation;
+
+import com.android.camera.R;
+
+import javax.microedition.khronos.opengles.GL11;
+
+public class GLOptionItem extends GLView {
+ private static final int FONT_COLOR = Color.WHITE;
+ private static final int MINIMAL_WIDTH = 180;
+ private static final int MINIMAL_HEIGHT = 48;
+ private static final int NO_ICON_LEADING_SPACE = 15;
+ private static final int TEXT_LEFT_PADDING = 9;
+ private static final int TEXT_RIGHT_PADDING = 15;
+ private static final float ENABLED_ALPHA = 1f;
+ private static final float DISABLED_ALPHA = 0.3f;
+
+ private static ResourceTexture sCheckOn;
+ private static ResourceTexture sCheckOff;
+
+ private final ResourceTexture mIcon;
+ private final StringTexture mText;
+ private boolean mEnabled = true;
+
+ private ResourceTexture mCheckBox;
+
+ private static void initCheckIcons(Context context) {
+ if (sCheckOn != null) return;
+ sCheckOn = new ResourceTexture(context, R.drawable.ic_menuselect_on);
+ sCheckOff = new ResourceTexture(context, R.drawable.ic_menuselect_off);
+ }
+
+ public GLOptionItem(Context context, int iconId, String title) {
+ initCheckIcons(context);
+ mIcon = iconId == 0 ? null : new ResourceTexture(context, iconId);
+ mText = StringTexture.newInstance(title, 26, FONT_COLOR);
+ mCheckBox = sCheckOff;
+ }
+
+ @Override
+ protected void onMeasure(int widthSpec, int heightSpec) {
+ int width = (mIcon == null ? NO_ICON_LEADING_SPACE : mIcon.getWidth())
+ + mText.getWidth() + mCheckBox.getWidth()
+ + TEXT_RIGHT_PADDING + TEXT_LEFT_PADDING;
+ int height = Math.max(Math.max(mIcon == null ? 0 : mIcon.getHeight(),
+ mText.getHeight()), mCheckBox.getHeight());
+
+ width = Math.max(MINIMAL_WIDTH, width);
+ height = Math.max(MINIMAL_HEIGHT, height);
+
+ new MeasureHelper(this)
+ .setPreferedContentSize(width, height)
+ .measure(widthSpec, heightSpec);
+ }
+
+ @Override
+ protected void render(GLRootView root, GL11 gl) {
+ Rect p = mPaddings;
+
+ int width = getWidth() - p.left - p.right;
+ int height = getHeight() - p.top - p.bottom;
+
+ int xoffset = p.left;
+
+ Transformation trans = root.getTransformation();
+ float oldAlpha = trans.getAlpha();
+ trans.setAlpha(oldAlpha * (mEnabled ? ENABLED_ALPHA : DISABLED_ALPHA));
+
+ ResourceTexture icon = mIcon;
+ if (icon != null) {
+ if (icon.bind(root, gl)) {
+ icon.draw(root, xoffset,
+ p.top + (height - icon.getHeight()) / 2);
+ }
+ xoffset += icon.getWidth();
+ } else {
+ xoffset += NO_ICON_LEADING_SPACE;
+ }
+
+ StringTexture title = mText;
+ xoffset += TEXT_LEFT_PADDING;
+ if (title.bind(root, gl)) {
+ int yoffset = p.top + (height - title.getHeight()) / 2;
+ //TODO: cut the text if it is too long
+ title.draw(root, xoffset, yoffset);
+ }
+
+ ResourceTexture checkbox = mCheckBox;
+ if (checkbox.bind(root, gl)) {
+ int yoffset = p.top + (height - checkbox.getHeight()) / 2;
+ checkbox.draw(root, width - checkbox.getWidth(), yoffset);
+ }
+ trans.setAlpha(oldAlpha);
+ }
+
+ public void setChecked(boolean checked) {
+ mCheckBox = checked ? sCheckOn : sCheckOff;
+ invalidate();
+ }
+
+ public void setEnabled(boolean enabled) {
+ if (mEnabled == enabled) return;
+ mEnabled = enabled;
+ invalidate();
+ }
+}
diff --git a/src/com/android/camera/ui/GLOutOfMemoryException.java b/src/com/android/camera/ui/GLOutOfMemoryException.java
new file mode 100644
index 0000000..2e2bac7
--- /dev/null
+++ b/src/com/android/camera/ui/GLOutOfMemoryException.java
@@ -0,0 +1,5 @@
+package com.android.camera.ui;
+
+public class GLOutOfMemoryException extends Exception {
+
+}
diff --git a/src/com/android/camera/ui/GLRootView.java b/src/com/android/camera/ui/GLRootView.java
new file mode 100644
index 0000000..23db5f8
--- /dev/null
+++ b/src/com/android/camera/ui/GLRootView.java
@@ -0,0 +1,456 @@
+package com.android.camera.ui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.PixelFormat;
+import android.opengl.GLSurfaceView;
+import android.opengl.GLU;
+import android.os.Process;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.animation.Transformation;
+
+import com.android.camera.Util;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Stack;
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+import javax.microedition.khronos.opengles.GL11;
+import javax.microedition.khronos.opengles.GL11Ext;
+
+public class GLRootView extends GLSurfaceView
+ implements GLSurfaceView.Renderer {
+ private static final String TAG = "GLRootView";
+
+ private final boolean ENABLE_FPS_TEST = false;
+ private int mFrameCount = 0;
+ private long mFrameCountingStart = 0;
+
+ // The event processing timeout for GL events, 1.5 seconds
+ private static final long EVENT_TIMEOUT = 1500;
+ private static final int VERTEX_BUFFER_SIZE = 8;
+
+ private static final int FLAG_INITIALIZED = 1;
+ private static final int FLAG_NEED_LAYOUT = 2;
+ private static final int FLAG_TEXTURE_MODE = 4;
+
+ private GL11 mGL;
+ private GLView mContentView;
+ private DisplayMetrics mDisplayMetrics;
+
+ private final Stack<Transformation> mFreeTransform =
+ new Stack<Transformation>();
+
+ private final Transformation mTransformation = new Transformation();
+ private final Stack<Transformation> mTransformStack =
+ new Stack<Transformation>();
+
+ private float mLastAlpha = mTransformation.getAlpha();
+
+ private final float mMatrixValues[] = new float[16];
+
+ private ByteBuffer mVertexBuffer;
+ private ByteBuffer mTexCoordBuffer;
+
+ private int mFlags = FLAG_NEED_LAYOUT;
+
+ public GLRootView(Context context) {
+ super(context);
+ initialize();
+ }
+
+ public Transformation obtainTransformation() {
+ return mFreeTransform.isEmpty() ? new Transformation() : mFreeTransform.pop();
+ }
+
+ public void freeTransformation(Transformation freeTransformation) {
+ mFreeTransform.push(freeTransformation);
+ }
+
+ public GLRootView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initialize();
+ }
+
+ public Transformation getTransformation() {
+ return mTransformation;
+ }
+
+ public void pushTransform() {
+ Transformation trans = obtainTransformation();
+ trans.set(mTransformation);
+ mTransformStack.push(trans);
+ }
+
+ public void popTransform() {
+ Transformation trans = mTransformStack.pop();
+ mTransformation.set(trans);
+ freeTransformation(trans);
+ }
+
+ private void initialize() {
+ mFlags |= FLAG_INITIALIZED;
+ setEGLConfigChooser(8, 8, 8, 8, 0, 0);
+ getHolder().setFormat(PixelFormat.TRANSLUCENT);
+ setZOrderOnTop(true);
+ super.setDebugFlags(DEBUG_CHECK_GL_ERROR);
+
+ setRenderer(this);
+
+ mVertexBuffer = ByteBuffer
+ .allocateDirect(VERTEX_BUFFER_SIZE * Float.SIZE / Byte.SIZE)
+ .order(ByteOrder.nativeOrder());
+ mVertexBuffer.asFloatBuffer()
+ .put(new float[] {0, 0, 1, 0, 0, 1, 1, 1})
+ .position(0);
+ mTexCoordBuffer = ByteBuffer
+ .allocateDirect(VERTEX_BUFFER_SIZE * Float.SIZE / Byte.SIZE)
+ .order(ByteOrder.nativeOrder());
+ }
+
+ public void setContentPane(GLView content) {
+ mContentView = content;
+ content.mRootView = this;
+
+ // no parent for the content pane
+ content.onAddToParent(null);
+ requestLayoutContentPane();
+ }
+
+ public GLView getContentPane() {
+ return mContentView;
+ }
+
+ public GLRootView getGLRootView() {
+ return this;
+ }
+
+ void handleLowMemory() {
+ //TODO: delete texture from GL
+ }
+
+ public synchronized void requestLayoutContentPane() {
+ if (mContentView == null || (mFlags & FLAG_NEED_LAYOUT) != 0) return;
+
+ // "View" system will invoke onLayout() for initialization(bug ?), we
+ // have to ignore it since the GLThread is not ready yet.
+ if ((mFlags & FLAG_INITIALIZED) == 0) return;
+
+ mFlags |= FLAG_NEED_LAYOUT;
+ requestRender();
+ }
+
+ private synchronized void layoutContentPane() {
+ mFlags &= ~FLAG_NEED_LAYOUT;
+ int width = getWidth();
+ int height = getHeight();
+ Log.v(TAG, "layout content pane " + width + "x" + height);
+ if (mContentView != null && width != 0 && height != 0) {
+ mContentView.layout(0, 0, width, height);
+ }
+ }
+
+ @Override
+ protected void onLayout(
+ boolean changed, int left, int top, int right, int bottom) {
+ if (changed) requestLayoutContentPane();
+ }
+
+ /**
+ * Called when the context is created, possibly after automatic destruction.
+ */
+ // This is a GLSurfaceView.Renderer callback
+ public void onSurfaceCreated(GL10 gl1, EGLConfig config) {
+ GL11 gl = (GL11) gl1;
+ if (mGL != null) {
+ // The GL Object has changed
+ Log.i(TAG, "GLObject has changed from " + mGL + " to " + gl);
+ }
+ mGL = gl;
+
+ if (!ENABLE_FPS_TEST) {
+ setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
+ } else {
+ setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
+ }
+
+ // Increase the priority of the render thread
+ Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
+
+ // Disable unused state
+ gl.glDisable(GL11.GL_LIGHTING);
+
+ // Enable used features
+ gl.glEnable(GL11.GL_BLEND);
+ gl.glEnable(GL11.GL_SCISSOR_TEST);
+ gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+ gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
+ gl.glEnable(GL11.GL_TEXTURE_2D);
+ gl.glEnable(GL11.GL_STENCIL_TEST);
+
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV,
+ GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
+
+ // Set the blend function for premultiplied alpha.
+ gl.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
+
+ // Set the background color
+ gl.glClearColor(0f, 0f, 0f, 0f);
+ gl.glVertexPointer(2, GL11.GL_FLOAT, 0, mVertexBuffer);
+ gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, mTexCoordBuffer);
+ }
+
+ protected void setTexCoords(float ... point) {
+ mTexCoordBuffer.asFloatBuffer().put(point).position(0);
+ }
+
+ /**
+ * Called when the OpenGL surface is recreated without destroying the
+ * context.
+ */
+ // This is a GLSurfaceView.Renderer callback
+ public void onSurfaceChanged(GL10 gl1, int width, int height) {
+ Log.v(TAG, "onSurfaceChanged: " + width + "x" + height
+ + ", gl10: " + gl1.toString());
+ GL11 gl = (GL11) gl1;
+
+ gl.glMatrixMode(GL11.GL_PROJECTION);
+ gl.glLoadIdentity();
+
+ GLU.gluOrtho2D(gl, 0, width, 0, height);
+ Matrix matrix = mTransformation.getMatrix();
+ matrix.reset();
+ matrix.preTranslate(0, getHeight());
+ matrix.preScale(1, -1);
+ }
+
+ private void setAlphaValue(float alpha) {
+ if (mLastAlpha == alpha) return;
+
+ GL11 gl = mGL;
+ mLastAlpha = alpha;
+ if (alpha >= 0.95f) {
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV,
+ GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
+ } else {
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV,
+ GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
+ gl.glColor4f(alpha, alpha, alpha, alpha);
+ }
+ }
+
+ public void drawRect(int x, int y, int width, int height) {
+ drawRect(x, y, width, height, mTransformation.getAlpha());
+ }
+
+ public void drawRect(int x, int y, int width, int height, float alpha) {
+ GL11 gl = mGL;
+ gl.glPushMatrix();
+ mTransformation.getMatrix().getValues(mMatrixValues);
+
+ setAlphaValue(alpha);
+ gl.glMultMatrixf(toGLMatrix(mMatrixValues), 0);
+ gl.glTranslatef(x, y, 0);
+ gl.glScalef(width, height, 1);
+ gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, 0, 4);
+ gl.glPopMatrix();
+ }
+
+ public void clipRect(int x, int y, int width, int height) {
+ float point[] = new float[]{x, y + height, x + width, y};
+
+ mTransformation.getMatrix().mapPoints(point);
+
+ // mMatrix could be a rotation matrix. In this case, we need to find
+ // the boundaries after rotation. (only handle 90 * n degrees)
+ if (point[0] > point[2]) {
+ x = (int) point[2];
+ width = (int) point[0] - x;
+ } else {
+ x = (int) point[0];
+ width = (int) point[2] - x;
+ }
+ if (point[1] > point[3]) {
+ y = (int) point[3];
+ height = (int) point[1] - y;
+ } else {
+ y = (int) point[1];
+ height = (int) point[3] - y;
+ }
+ mGL.glScissor(x, y, width, height);
+ }
+
+ public void clearClip() {
+ mGL.glScissor(0, 0, getWidth(), getHeight());
+ }
+
+ private static float[] toGLMatrix(float v[]) {
+ v[15] = v[8]; v[13] = v[5]; v[5] = v[4]; v[4] = v[1];
+ v[12] = v[2]; v[1] = v[3]; v[3] = v[6];
+ v[2] = v[6] = v[8] = v[9] = 0;
+ v[10] = 1;
+ return v;
+ }
+
+ public void draw2D(int x, int y, int width, int height, float alpha) {
+ if (width <= 0 || height <= 0) return ;
+
+ Matrix matrix = mTransformation.getMatrix();
+ matrix.getValues(mMatrixValues);
+
+ // Test if it has been rotated, if it is, glDrawTexiOES won't work
+ if (mMatrixValues[1] != 0
+ || mMatrixValues[3] != 0 || mMatrixValues[0] < 0) {
+ drawRect(x, y, width, height, alpha);
+ } else {
+ // draw the rect from bottom-left to top-right
+ float points[] = new float[]{x, y + height, x + width, y};
+ matrix.mapPoints(points);
+ x = (int) points[0];
+ y = (int) points[1];
+ width = (int) points[2] - x;
+ height = (int) points[3] - y;
+ if (width > 0 && height > 0) {
+ setAlphaValue(alpha);
+ ((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height);
+ }
+ }
+ }
+
+ public void draw2D(int x, int y, int width, int height) {
+ draw2D(x, y, width, height, mTransformation.getAlpha());
+ }
+
+ // This is a GLSurfaceView.Renderer callback
+ public void onDrawFrame(GL10 gl) {
+ if (ENABLE_FPS_TEST) {
+ long now = System.nanoTime();
+ if (mFrameCountingStart == 0) {
+ mFrameCountingStart = now;
+ } else if ((now - mFrameCountingStart) > 1000000000) {
+ Log.v(TAG, "fps: " + (double) mFrameCount
+ * 1000000000 / (now - mFrameCountingStart));
+ mFrameCountingStart = now;
+ mFrameCount = 0;
+ }
+ ++mFrameCount;
+ }
+ if ((mFlags & FLAG_NEED_LAYOUT) != 0) layoutContentPane();
+
+ // gl.glScissor(0, 0, getWidth(), getHeight());
+ gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_STENCIL_BUFFER_BIT);
+ gl.glEnable(GL11.GL_BLEND);
+ gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
+
+ /*gl.glBindTexture(GL11.GL_TEXTURE_2D, 0);
+ gl.glColor4f(0, 0, 0.5f, 0.4f);
+ drawRect(30, 30, 30, 30);*/
+
+ if (mContentView != null) {
+ mContentView.render(GLRootView.this, (GL11) gl);
+ }
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+
+ // Allocate a new event to prevent concurrency access
+ FutureTask<Boolean> task = new FutureTask<Boolean>(
+ new TouchEventHandler(MotionEvent.obtain(event)));
+ queueEvent(task);
+ try {
+ return task.get(EVENT_TIMEOUT, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ Log.w(TAG, "event timeout, assume the event is handled");
+ return true;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private class TouchEventHandler implements Callable<Boolean> {
+
+ private final MotionEvent mEvent;
+
+ public TouchEventHandler(MotionEvent event) {
+ mEvent = event;
+ }
+
+ public Boolean call() throws Exception {
+ return mContentView.dispatchTouchEvent(mEvent);
+ }
+ }
+
+ public DisplayMetrics getDisplayMetrics() {
+ if (mDisplayMetrics == null) {
+ mDisplayMetrics = new DisplayMetrics();
+ ((Activity) getContext()).getWindowManager()
+ .getDefaultDisplay().getMetrics(mDisplayMetrics);
+ }
+ return mDisplayMetrics;
+ }
+
+ public Texture copyTexture2D(int x, int y, int width, int height)
+ throws GLOutOfMemoryException {
+
+ Matrix matrix = mTransformation.getMatrix();
+ matrix.getValues(mMatrixValues);
+
+ // Test if it has been rotated, if it is, glDrawTexiOES won't work
+ if (mMatrixValues[1] != 0 || mMatrixValues[3] != 0) {
+ throw new IllegalArgumentException("cannot support rotated matrix");
+ }
+
+ float points[] = new float[]{x, y + height, x + width, y};
+ matrix.mapPoints(points);
+ x = (int) points[0];
+ y = (int) points[1];
+ width = (int) points[2] - x;
+ height = (int) points[3] - y;
+
+ GL11 gl = mGL;
+ int newWidth = Util.nextPowerOf2(width);
+ int newHeight = Util.nextPowerOf2(height);
+ int glError = GL11.GL_NO_ERROR;
+
+ int[] textureId = new int[1];
+ gl.glGenTextures(1, textureId, 0);
+ gl.glBindTexture(GL11.GL_TEXTURE_2D, textureId[0]);
+ int[] cropRect = {0, 0, width, height};
+ gl.glTexParameteriv(GL11.GL_TEXTURE_2D,
+ GL11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect, 0);
+ gl.glTexParameteri(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
+ gl.glTexParameteri(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
+ gl.glTexParameterf(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
+ gl.glTexParameterf(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
+ gl.glCopyTexImage2D(GL11.GL_TEXTURE_2D, 0,
+ GL11.GL_RGBA, x, y, newWidth, newHeight, 0);
+ glError = gl.glGetError();
+
+ if (glError == GL11.GL_OUT_OF_MEMORY) {
+ throw new GLOutOfMemoryException();
+ }
+
+ if (glError == GL11.GL_NO_ERROR) {
+ return new RawTexture(gl, textureId[0], width, height,
+ (float) width / newWidth, (float) height / newHeight);
+ }
+ throw new RuntimeException(
+ "Texture copy fail, glError " + glError);
+ }
+
+}
diff --git a/src/com/android/camera/ui/GLView.java b/src/com/android/camera/ui/GLView.java
new file mode 100644
index 0000000..1df1f9e
--- /dev/null
+++ b/src/com/android/camera/ui/GLView.java
@@ -0,0 +1,312 @@
+package com.android.camera.ui;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.view.MotionEvent;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Transformation;
+
+import java.util.ArrayList;
+
+import javax.microedition.khronos.opengles.GL11;
+
+public class GLView {
+ GLRootView mRootView;
+
+ public static final int STATE_EMPTY = 0;
+ public static final int STATE_PRESSED = 1;
+
+ public static final int VISIBLE = 0;
+ public static final int INVISIBLE = 1;
+
+ public static final int FLAG_INVISIBLE = 1;
+ public static final int FLAG_SET_MEASURED_SIZE = 2;
+ public static final int FLAG_LAYOUT_REQUESTED = 4;
+
+ private static final String TAG = "GLView";
+
+ protected final Rect mBounds = new Rect();
+ protected final Rect mPaddings = new Rect();
+
+ private GLView mParent;
+ private ArrayList<GLView> mComponents;
+
+ private OnTouchListener mOnTouchListener;
+ private Animation mAnimation;
+
+ protected int mViewState = STATE_EMPTY;
+ protected int mViewFlags = 0;
+
+ protected int mMeasuredWidth = 0;
+ protected int mMeasuredHeight = 0;
+
+ private int mLastWidthSpec = -1;
+ private int mLastHeightSpec = -1;
+
+ protected int mScrollY = 0;
+ protected int mScrollX = 0;
+ protected int mScrollHeight = 0;
+ protected int mScrollWidth = 0;
+
+ protected void onStateChanged(int oldState, int newState) {
+ }
+
+ protected void addStates(int states) {
+ int newState = (mViewState | states);
+ if (newState != mViewState) {
+ onStateChanged(mViewState, newState);
+ mViewState = newState;
+ }
+ }
+
+ protected void removeStates(int states) {
+ int newState = (mViewState & ~states);
+ if (newState != mViewState) {
+ onStateChanged(mViewState, newState);
+ mViewState = newState;
+ }
+ }
+
+ public void startAnimation(Animation animation) {
+ mAnimation = animation;
+ animation.initialize(getWidth(),
+ getHeight(), mParent.getWidth(), mParent.getHeight());
+ mAnimation.start();
+ invalidate();
+ }
+
+ public void setVisibility(int visibility) {
+ if (visibility == getVisibility()) return;
+ if (visibility == VISIBLE) {
+ mViewFlags &= ~FLAG_INVISIBLE;
+ } else {
+ mViewFlags |= FLAG_INVISIBLE;
+ }
+ invalidate();
+ }
+
+ public int getVisibility() {
+ return (mViewFlags & FLAG_INVISIBLE) == 0 ? VISIBLE : INVISIBLE;
+ }
+
+ public static interface OnTouchListener {
+ public boolean onTouch(GLView view, MotionEvent event);
+ }
+
+ public boolean setBounds(int left, int top, int right, int bottom) {
+ boolean sizeChanged = (right - left) != (mBounds.right - mBounds.left)
+ || (bottom - top) != (mBounds.bottom - mBounds.top);
+ mBounds.set(left, top, right, bottom);
+ return sizeChanged;
+ }
+
+ protected void onAddToParent(GLView parent) {
+ mParent = parent;
+ }
+
+ public void clearComponents() {
+ if (mComponents != null) mComponents.clear();
+ }
+
+ public int getComponentCount() {
+ return mComponents == null ? 0 : mComponents.size();
+ }
+
+ public GLView getComponent(int index) {
+ if (mComponents == null) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ return mComponents.get(index);
+ }
+
+ public void addComponent(GLView component) {
+ if (mComponents == null) {
+ mComponents = new ArrayList<GLView>();
+ }
+ mComponents.add(component);
+ component.onAddToParent(this);
+ }
+
+ public Rect bounds() {
+ return mBounds;
+ }
+
+ public int getWidth() {
+ return mBounds.right - mBounds.left;
+ }
+
+ public int getHeight() {
+ return mBounds.bottom - mBounds.top;
+ }
+
+ public GLRootView getGLRootView() {
+ if (mRootView == null && mParent != null) {
+ mRootView = mParent.getGLRootView();
+ }
+ return mRootView;
+ }
+
+ public void setOnTouchListener(OnTouchListener listener) {
+ mOnTouchListener = listener;
+ }
+
+ public void invalidate() {
+ GLRootView root = getGLRootView();
+ if (root != null) root.requestRender();
+ }
+
+ public void requestLayout() {
+ mViewFlags |= FLAG_LAYOUT_REQUESTED;
+ if (mParent != null) {
+ mParent.requestLayout();
+ } else {
+ // Is this a content pane ?
+ GLRootView root = getGLRootView();
+ if (root != null) root.requestLayoutContentPane();
+ }
+ }
+
+ protected void render(GLRootView view, GL11 gl) {
+ renderBackground(view, gl);
+ int n = getComponentCount();
+ if (n == 0) return;
+ for (int i = 0; i < n; ++i) {
+ GLView component = getComponent(i);
+ if (component.getVisibility() != GLView.VISIBLE
+ && component.mAnimation == null) continue;
+ renderChild(view, gl, component);
+ }
+ }
+
+ protected void renderBackground(GLRootView view, GL11 gl) {
+ }
+
+ protected void renderChild(GLRootView root, GL11 gl, GLView component) {
+ Animation anim = component.mAnimation;
+ int xoffset = component.mBounds.left - mScrollX;
+ int yoffset = component.mBounds.top - mScrollY;
+
+ Transformation transform = root.getTransformation();
+ Matrix matrix = transform.getMatrix();
+ matrix.preTranslate(xoffset, yoffset);
+ if (anim != null) {
+ long now = AnimationUtils.currentAnimationTimeMillis();
+ Transformation temp = root.obtainTransformation();
+ temp.clear();
+ if (!anim.getTransformation(now, temp)) {
+ component.mAnimation = null;
+ }
+ invalidate();
+ root.pushTransform();
+ transform.compose(temp);
+ root.freeTransformation(temp);
+ }
+ component.render(root, gl);
+ if (anim != null) root.popTransform();
+ matrix.preTranslate(-xoffset, -yoffset);
+ }
+
+ protected boolean onTouch(MotionEvent event) {
+ if (mOnTouchListener != null) {
+ return mOnTouchListener.onTouch(this, event);
+ }
+ return false;
+ }
+
+ protected boolean dispatchTouchEvent(MotionEvent event) {
+ if (mComponents != null) {
+ int eventX = (int) event.getX();
+ int eventY = (int) event.getY();
+ for (int i = 0, n = getComponentCount(); i < n; ++i) {
+ GLView component = getComponent(i);
+ if (component.getVisibility() != GLView.VISIBLE) continue;
+ Rect rect = new Rect(component.mBounds);
+ if (rect.contains(eventX, eventY)) {
+ event.offsetLocation(-rect.left, -rect.top);
+ if (component.dispatchTouchEvent(event)) return true;
+ event.offsetLocation(rect.left, rect.top);
+ }
+ }
+ }
+ return onTouch(event);
+ }
+
+ public Rect getPaddings() {
+ return mPaddings;
+ }
+
+ public void setPaddings(Rect paddings) {
+ mPaddings.set(paddings);
+ }
+
+ public void setPaddings(int left, int top, int right, int bottom) {
+ mPaddings.set(left, top, right, bottom);
+ }
+
+ public void layout(int left, int top, int right, int bottom) {
+ boolean sizeChanged = setBounds(left, top, right, bottom);
+ if (sizeChanged) {
+ mViewFlags &= ~FLAG_LAYOUT_REQUESTED;
+ onLayout(true, left, top, right, bottom);
+ } else if ((mViewFlags & FLAG_LAYOUT_REQUESTED)!= 0) {
+ mViewFlags &= ~FLAG_LAYOUT_REQUESTED;
+ onLayout(false, left, top, right, bottom);
+ }
+ }
+
+ public void measure(int widthSpec, int heightSpec) {
+ if (widthSpec == mLastWidthSpec && heightSpec == mLastHeightSpec
+ && (mViewFlags & FLAG_LAYOUT_REQUESTED) == 0) {
+ return;
+ }
+
+ mLastWidthSpec = widthSpec;
+ mLastHeightSpec = heightSpec;
+
+ mViewFlags &= ~FLAG_SET_MEASURED_SIZE;
+ onMeasure(widthSpec, heightSpec);
+ if ((mViewFlags & FLAG_SET_MEASURED_SIZE) == 0) {
+ throw new IllegalStateException(getClass().getName()
+ + " should call setMeasuredSize() in onMeasure()");
+ }
+ }
+
+ protected void onMeasure(int widthSpec, int heightSpec) {
+ }
+
+ protected void setMeasuredSize(int width, int height) {
+ mViewFlags |= FLAG_SET_MEASURED_SIZE;
+ mMeasuredWidth = width;
+ mMeasuredHeight = height;
+ }
+
+ public int getMeasuredWidth() {
+ return mMeasuredWidth;
+ }
+
+ public int getMeasuredHeight() {
+ return mMeasuredHeight;
+ }
+
+ protected void onLayout(
+ boolean changeSize, int left, int top, int right, int bottom) {
+ }
+
+ public boolean getBoundsOf(GLView child, Rect out) {
+ int xoffset = 0;
+ int yoffset = 0;
+ GLView view = child;
+ while (view != this) {
+ if (view == null) return false;
+ Rect bounds = view.mBounds;
+ xoffset += bounds.left;
+ yoffset += bounds.top;
+ view = view.mParent;
+ }
+ out.set(xoffset, yoffset,
+ xoffset + child.getWidth(), yoffset + child.getHeight());
+ return true;
+ }
+
+}
diff --git a/src/com/android/camera/ui/GLZoomIndicator.java b/src/com/android/camera/ui/GLZoomIndicator.java
new file mode 100644
index 0000000..c6612bf
--- /dev/null
+++ b/src/com/android/camera/ui/GLZoomIndicator.java
@@ -0,0 +1,34 @@
+package com.android.camera.ui;
+
+import android.graphics.Color;
+import android.graphics.Rect;
+
+import javax.microedition.khronos.opengles.GL11;
+
+public class GLZoomIndicator extends GLView {
+
+ private final StringTexture mTitle;
+
+ public GLZoomIndicator(String title) {
+ mTitle = StringTexture.newInstance(title, 24, Color.WHITE);
+ }
+
+ @Override
+ protected void onMeasure(int widthSpec, int heightSpec) {
+ new MeasureHelper(this)
+ .setPreferedContentSize(mTitle.getWidth(), mTitle.getHeight())
+ .measure(widthSpec, heightSpec);
+ }
+
+ @Override
+ protected void render(GLRootView root, GL11 gl) {
+ if (mTitle.bind(root, gl)) {
+ Rect p = mPaddings;
+ int width = getWidth() - p.left - p.right;
+ int height = getHeight() - p.top - p.bottom;
+ mTitle.draw(root,
+ p.left + (width - mTitle.getWidth()) / 2,
+ p.top + (height - mTitle.getHeight()) / 2);
+ }
+ }
+}
diff --git a/src/com/android/camera/ui/GpsIndicator.java b/src/com/android/camera/ui/GpsIndicator.java
new file mode 100644
index 0000000..27fe0a1
--- /dev/null
+++ b/src/com/android/camera/ui/GpsIndicator.java
@@ -0,0 +1,43 @@
+package com.android.camera.ui;
+
+import android.content.Context;
+
+import com.android.camera.IconListPreference;
+import com.android.camera.R;
+
+public class GpsIndicator extends BasicIndicator {
+
+ private static final int GPS_ON_INDEX = 1;
+
+ private ResourceTexture mNoSignalIcon;
+ private boolean mHasSignal = false;
+
+ public GpsIndicator(IconListPreference preference) {
+ super(preference);
+ }
+
+ @Override
+ protected ResourceTexture getIcon() {
+ if (mIndex == GPS_ON_INDEX && !mHasSignal) {
+ if (mNoSignalIcon == null) {
+ Context context = getGLRootView().getContext();
+ mNoSignalIcon = new ResourceTexture(
+ context, R.drawable.ic_viewfinder_gps_no_signal);
+ }
+ return mNoSignalIcon;
+ }
+ return super.getIcon();
+ }
+
+ public void setHasSignal(boolean hasSignal) {
+ if (mHasSignal == hasSignal) return;
+ mHasSignal = hasSignal;
+ invalidate();
+ }
+
+ @Override
+ protected void onPreferenceChanged(int newIndex) {
+ mHasSignal = false;
+ super.onPreferenceChanged(newIndex);
+ }
+}
diff --git a/src/com/android/camera/ui/HeadUpDisplay.java b/src/com/android/camera/ui/HeadUpDisplay.java
new file mode 100644
index 0000000..c2e444c
--- /dev/null
+++ b/src/com/android/camera/ui/HeadUpDisplay.java
@@ -0,0 +1,336 @@
+package com.android.camera.ui;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View.MeasureSpec;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+
+import com.android.camera.CameraSettings;
+import com.android.camera.IconListPreference;
+import com.android.camera.ListPreference;
+import com.android.camera.PreferenceGroup;
+import com.android.camera.R;
+
+import java.util.ArrayList;
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
+
+public class HeadUpDisplay extends GLView {
+ private static final int INDICATOR_BAR_RIGHT_MARGIN = 15;
+ private static final int POPUP_WINDOW_OVERLAP = 30;
+ private static final float MAX_HEIGHT_RATIO = 0.8f;
+ private static final float MAX_WIDTH_RATIO = 0.8f;
+
+ private static final int HIDE_POPUP_WINDOW = 0;
+ private static final int DEACTIVATE_INDICATOR_BAR = 1;
+ protected static final String TAG = "HeadUpDisplay";
+
+ private IndicatorBar mIndicatorBar;
+ private GpsIndicator mGpsIndicator;
+
+ private PopupWindow mPopupWindow;
+
+ private int mAnchorX;
+ private int mAnchorY;
+ private int mOrientation = 0;
+
+ private Listener mListener;
+
+ // TODO: move this part (handler) into GLSurfaceView
+ private final HandlerThread mTimerThread = new HandlerThread("UI Timer");
+
+ private final Handler mHandler;
+
+ public HeadUpDisplay() {
+ mTimerThread.start();
+ mHandler = new Handler(mTimerThread.getLooper()) {
+
+ @Override
+ public void handleMessage(Message msg) {
+ GLRootView root = getGLRootView();
+ FutureTask<Void> task = null;
+ switch(msg.what) {
+ case HIDE_POPUP_WINDOW:
+ task = new FutureTask<Void>(mHidePopupWindow);
+ break;
+ case DEACTIVATE_INDICATOR_BAR:
+ task = new FutureTask<Void>(mDeactivateIndicatorBar);
+ break;
+ }
+
+ if (task == null) return;
+ try {
+ root.queueEvent(task);
+ task.get();
+ } catch (Exception e) {
+ Log.e(TAG, "error in concurrent code", e);
+ }
+ }
+ };
+ }
+
+ private final Callable<Void> mHidePopupWindow = new Callable<Void> () {
+ public Void call() throws Exception {
+ hidePopupWindow();
+ return null;
+ }
+ };
+
+ private final Callable<Void> mDeactivateIndicatorBar = new Callable<Void> () {
+ public Void call() throws Exception {
+ if (mIndicatorBar != null) mIndicatorBar.setActivated(false);
+ return null;
+ }
+ };
+
+ static public interface Listener {
+ public void onPopupWindowVisibilityChanged(int visibility);
+ }
+
+ public void overrideSettings(final String ... keyvalues) {
+ if (keyvalues.length % 2 != 0) {
+ throw new IllegalArgumentException();
+ }
+ GLRootView root = getGLRootView();
+ if (root != null) {
+ root.queueEvent(new Runnable() {
+ public void run() {
+ for (int i = 0, n = keyvalues.length; i < n; i += 2) {
+ mIndicatorBar.overrideSettings(
+ keyvalues[i], keyvalues[i + 1]);
+ }
+ }
+ });
+ } else {
+ for (int i = 0, n = keyvalues.length; i < n; i += 2) {
+ mIndicatorBar.overrideSettings(keyvalues[i], keyvalues[i + 1]);
+ }
+ }
+ }
+
+ @Override
+ protected void onLayout(
+ boolean changed, int left, int top, int right, int bottom) {
+ int width = right - left;
+ int height = bottom - top;
+ mIndicatorBar.measure(
+ MeasureSpec.makeMeasureSpec(width / 3, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ DisplayMetrics metrics = getGLRootView().getDisplayMetrics();
+ int rightMargin = (int) (metrics.density * INDICATOR_BAR_RIGHT_MARGIN);
+
+ mIndicatorBar.layout(
+ width - mIndicatorBar.getMeasuredWidth() - rightMargin, 0,
+ width - rightMargin, height);
+
+ if(mPopupWindow != null
+ && mPopupWindow.getVisibility() == GLView.VISIBLE) {
+ layoutPopupWindow(mAnchorX, mAnchorY);
+ }
+ }
+
+ public void initialize(Context context, PreferenceGroup preferenceGroup) {
+ initializeIndicatorBar(context, preferenceGroup);
+ }
+
+ private void layoutPopupWindow(int anchorX, int anchorY) {
+ mAnchorX = anchorX;
+ mAnchorY = anchorY;
+
+ int width = (int) (getWidth() * MAX_WIDTH_RATIO + .5);
+ int height = (int) (getHeight() * MAX_HEIGHT_RATIO + .5);
+
+ mPopupWindow.measure(
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
+
+ width = mPopupWindow.getMeasuredWidth();
+ height = mPopupWindow.getMeasuredHeight();
+
+ int xoffset = Math.max(anchorX - width, 0);
+ int yoffset = Math.max(0, anchorY - height / 2);
+
+ if (yoffset + height > getHeight()) {
+ yoffset = getHeight() - height;
+ }
+ mPopupWindow.setAnchorPosition(anchorY - yoffset);
+ mPopupWindow.layout(
+ xoffset, yoffset, xoffset + width, yoffset + height);
+ }
+
+ public void showPopupWindow(int anchorX, int anchorY) {
+ layoutPopupWindow(anchorX, anchorY);
+ mPopupWindow.popup();
+ if (mListener != null) {
+ mListener.onPopupWindowVisibilityChanged(GLView.VISIBLE);
+ }
+ }
+
+ private void scheduleDeactiviateIndicatorBar() {
+ mHandler.removeMessages(HIDE_POPUP_WINDOW);
+ mHandler.sendEmptyMessageDelayed(HIDE_POPUP_WINDOW, 2000);
+ mHandler.removeMessages(DEACTIVATE_INDICATOR_BAR);
+ mHandler.sendEmptyMessageDelayed(DEACTIVATE_INDICATOR_BAR, 3000);
+ }
+
+ public void hidePopupWindow() {
+ mIndicatorBar.setSelectedIndex(IndicatorBar.INDEX_NONE);
+ }
+
+ public void deactivateIndicatorBar() {
+ if (mIndicatorBar == null) return;
+ mIndicatorBar.setActivated(false);
+ }
+
+ public void setOrientation(int orientation) {
+ mOrientation = orientation;
+ if (mPopupWindow == null) return;
+ if (mPopupWindow.getVisibility() == GLView.VISIBLE) {
+ Animation alpha = new AlphaAnimation(0, 1);
+ alpha.setDuration(250);
+ mPopupWindow.startAnimation(alpha);
+ scheduleDeactiviateIndicatorBar();
+ }
+ mPopupWindow.setOrientation(orientation);
+ }
+
+ private void initializePopupWindow(Context context) {
+ mPopupWindow = new PopupWindow();
+ mPopupWindow.setBackground(
+ new NinePatchTexture(context, R.drawable.menu_popup));
+ mPopupWindow.setAnchor(new ResourceTexture(
+ context, R.drawable.menu_popup_triangle), 23);
+ mPopupWindow.setVisibility(GLView.INVISIBLE);
+ mPopupWindow.setOrientation(mOrientation);
+ addComponent(mPopupWindow);
+ }
+
+ @Override
+ protected boolean dispatchTouchEvent(MotionEvent event) {
+ if (super.dispatchTouchEvent(event)) {
+ scheduleDeactiviateIndicatorBar();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean onTouch(MotionEvent event) {
+ if (mPopupWindow == null
+ || mPopupWindow.getVisibility() == GLView.INVISIBLE) {
+ return false;
+ }
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_UP:
+ mPopupWindow.popoff();
+ if (mListener != null) {
+ mListener.onPopupWindowVisibilityChanged(GLView.INVISIBLE);
+ }
+ mIndicatorBar.setSelectedIndex(IndicatorBar.INDEX_NONE);
+ mIndicatorBar.setActivated(false);
+ break;
+ }
+ return true;
+ }
+
+ static private ListPreference[] getListPreferences(
+ PreferenceGroup group, String ... prefKeys) {
+ ArrayList<ListPreference> list = new ArrayList<ListPreference>();
+ for (String key : prefKeys) {
+ ListPreference pref = group.findPreference(key);
+ if (pref != null && pref.getEntries().length > 0) {
+ list.add(pref);
+ }
+ }
+ return list.toArray(new ListPreference[list.size()]);
+ }
+
+ private static BasicIndicator addIndicator(
+ IndicatorBar indicatorBar, PreferenceGroup group, String key) {
+ IconListPreference iconPref =
+ (IconListPreference) group.findPreference(key);
+ if (iconPref == null) return null;
+ BasicIndicator indicator = new BasicIndicator(iconPref);
+ indicatorBar.addComponent(indicator);
+ return indicator;
+ }
+
+ private void initializeIndicatorBar(
+ Context context, PreferenceGroup group) {
+
+ mIndicatorBar = new IndicatorBar();
+
+ mIndicatorBar.setBackground(new NinePatchTexture(
+ context, R.drawable.ic_viewfinder_iconbar));
+ mIndicatorBar.setHighlight(new NinePatchTexture(
+ context, R.drawable.ic_viewfinder_iconbar_highlight));
+
+ OtherSettingsIndicator otherSettings = new OtherSettingsIndicator(
+ getListPreferences(group,
+ CameraSettings.KEY_FOCUS_MODE,
+ CameraSettings.KEY_SCENE_MODE,
+ CameraSettings.KEY_PICTURE_SIZE,
+ CameraSettings.KEY_JPEG_QUALITY,
+ CameraSettings.KEY_COLOR_EFFECT));
+ mIndicatorBar.addComponent(otherSettings);
+
+ GpsIndicator gpsIndicator = new GpsIndicator((IconListPreference)
+ group.findPreference(CameraSettings.KEY_RECORD_LOCATION));
+ mGpsIndicator = gpsIndicator;
+ mIndicatorBar.addComponent(gpsIndicator);
+
+ addIndicator(mIndicatorBar, group, CameraSettings.KEY_WHITE_BALANCE);
+ addIndicator(mIndicatorBar, group, CameraSettings.KEY_FLASH_MODE);
+
+ addComponent(mIndicatorBar);
+ mIndicatorBar.setOnItemSelectedListener(new IndicatorBarListener());
+ }
+
+ public void setGpsHasSignal(final boolean hasSignal) {
+ GLRootView root = getGLRootView();
+ if (root != null) {
+ root.queueEvent(new Runnable() {
+ public void run() {
+ mGpsIndicator.setHasSignal(hasSignal);
+ }
+ });
+ } else {
+ mGpsIndicator.setHasSignal(hasSignal);
+ }
+ }
+
+ private class IndicatorBarListener
+ implements IndicatorBar.OnItemSelectedListener {
+
+ public void onItemSelected(GLView view, int position) {
+ Rect rect = new Rect();
+ getBoundsOf(view, rect);
+ int anchorX = rect.left + POPUP_WINDOW_OVERLAP;
+ int anchorY = (rect.top + rect.bottom) / 2;
+
+ AbstractIndicator indicator = (AbstractIndicator) view;
+ if (mPopupWindow == null) {
+ initializePopupWindow(getGLRootView().getContext());
+ }
+ mPopupWindow.setContent(indicator.getPopupContent());
+
+ if (mPopupWindow.getVisibility() == GLView.VISIBLE) {
+ layoutPopupWindow(anchorX, anchorY);
+ } else {
+ showPopupWindow(anchorX, anchorY);
+ }
+ }
+
+ public void onNothingSelected() {
+ mPopupWindow.popoff();
+ }
+ }
+}
diff --git a/src/com/android/camera/ui/IndicatorBar.java b/src/com/android/camera/ui/IndicatorBar.java
new file mode 100644
index 0000000..1c60a37
--- /dev/null
+++ b/src/com/android/camera/ui/IndicatorBar.java
@@ -0,0 +1,175 @@
+package com.android.camera.ui;
+
+import android.graphics.Rect;
+import android.view.MotionEvent;
+import android.view.View.MeasureSpec;
+import android.view.animation.AlphaAnimation;
+
+import javax.microedition.khronos.opengles.GL11;
+
+public class IndicatorBar extends GLView {
+
+ public static final int INDEX_NONE = -1;
+
+ private NinePatchTexture mBackground;
+ private NinePatchTexture mHighlight;
+ private int mSelectedIndex = INDEX_NONE;
+
+ private OnItemSelectedListener mSelectedListener;
+ private boolean mActivated = false;
+
+ private class Background extends GLView {
+ @Override
+ protected void render(GLRootView root, GL11 gl) {
+ mBackground.setSize(getWidth(), getHeight());
+ if (mBackground.bind(root, gl)) {
+ mBackground.draw(root, 0, 0);
+ }
+ if (mActivated && mSelectedIndex != INDEX_NONE
+ && mHighlight != null) {
+ Rect bounds = IndicatorBar.this.getComponent(
+ mSelectedIndex + 1).mBounds;
+ mHighlight.setSize(bounds.width(), bounds.height());
+ if (mHighlight.bind(root, gl)) {
+ mHighlight.draw(root, bounds.left, bounds.top);
+ }
+ }
+ }
+ }
+
+ public interface OnItemSelectedListener {
+ public void onItemSelected(GLView view, int position);
+ public void onNothingSelected();
+ }
+
+ public IndicatorBar() {
+ GLView background = new Background();
+ background.setVisibility(GLView.INVISIBLE);
+ addComponent(background);
+ }
+
+ public void overrideSettings(String key, String value) {
+ for (int i = 1, n = getComponentCount(); i < n; ++i) {
+ AbstractIndicator indicator = (AbstractIndicator) getComponent(i);
+ indicator.overrideSettings(key, value);
+ }
+ }
+
+ public void setOnItemSelectedListener(OnItemSelectedListener l) {
+ mSelectedListener = l;
+ }
+
+ public void setBackground(NinePatchTexture background) {
+ if (mBackground == background) return;
+ mBackground = background;
+ if (background != null) {
+ setPaddings(background.getPaddings());
+ } else {
+ setPaddings(0, 0, 0, 0);
+ }
+ invalidate();
+ }
+
+ public void setHighlight(NinePatchTexture highlight) {
+ if (mHighlight == highlight) return;
+ mHighlight = highlight;
+ invalidate();
+ }
+
+ @Override
+ protected void onMeasure(int widthSpec, int heightSpec) {
+ int width = 0;
+ int height = 0;
+ for (int i = 1, n = getComponentCount(); i < n; ++i) {
+ GLView component = getComponent(i);
+ component.measure(
+ MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ width = Math.max(width, component.getMeasuredWidth());
+ height += component.getMeasuredHeight();
+ }
+ new MeasureHelper(this)
+ .setPreferedContentSize(width, height)
+ .measure(widthSpec, heightSpec);
+ }
+
+ @Override
+ protected void onLayout(
+ boolean changed, int left, int top, int right, int bottom) {
+ // Background
+ getComponent(0).layout(0, 0, right - left, bottom - top);
+
+ int count = getComponentCount();
+ Rect p = mPaddings;
+ int cBottom = bottom - top - p.bottom;
+ int cRight = right - left - p.right;
+ int yoffset = mPaddings.top;
+ int xoffset = mPaddings.left;
+ for (int i = 1; i < count; ++i) {
+ int cHeight = (cBottom - yoffset) / (count - i);
+ int nextYoffset = yoffset + cHeight;
+ getComponent(i).layout(xoffset, yoffset, cRight, nextYoffset);
+ yoffset = nextYoffset;
+ }
+ }
+
+ private void setSelectedItem(GLView view, int index) {
+ if (index == mSelectedIndex) return;
+ mSelectedIndex = index;
+ if (mSelectedListener != null) {
+ if (index == INDEX_NONE) {
+ mSelectedListener.onNothingSelected();
+ } else {
+ mSelectedListener.onItemSelected(view, index);
+ }
+ }
+ invalidate();
+ }
+
+ public void setSelectedIndex(int index) {
+ if (index == mSelectedIndex) return;
+ setSelectedItem(index == INDEX_NONE ? null :getComponent(index), index);
+ }
+
+ public void setActivated(boolean activated) {
+ if (activated == mActivated) return;
+ mActivated = activated;
+ if (activated) {
+ GLView background = getComponent(0);
+ background.setVisibility(GLView.VISIBLE);
+ AlphaAnimation anim = new AlphaAnimation(0, 1);
+ anim.setDuration(200);
+ background.startAnimation(anim);
+ } else {
+ GLView background = getComponent(0);
+ background.setVisibility(GLView.INVISIBLE);
+ AlphaAnimation anim = new AlphaAnimation(1, 0);
+ anim.setDuration(200);
+ background.startAnimation(anim);
+ }
+ }
+
+ @Override
+ protected boolean dispatchTouchEvent(MotionEvent event) {
+ // Do not pass motion events to children
+ return onTouch(event);
+ }
+
+ @Override @SuppressWarnings("fallthrough")
+ protected boolean onTouch(MotionEvent event) {
+ int y = (int) event.getY();
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ setActivated(true);
+ case MotionEvent.ACTION_MOVE:
+ for (int i = 1, n = getComponentCount(); i < n; ++i) {
+ GLView component = getComponent(i);
+ if (y <= component.mBounds.bottom) {
+ setSelectedItem(component, i - 1);
+ return true;
+ }
+ }
+ setSelectedItem(null, INDEX_NONE);
+ }
+ return true;
+ }
+}
diff --git a/src/com/android/camera/ui/MeasureHelper.java b/src/com/android/camera/ui/MeasureHelper.java
new file mode 100644
index 0000000..943b243
--- /dev/null
+++ b/src/com/android/camera/ui/MeasureHelper.java
@@ -0,0 +1,42 @@
+package com.android.camera.ui;
+
+import android.graphics.Rect;
+import android.view.View.MeasureSpec;
+
+public class MeasureHelper {
+
+ private final GLView mComponent;
+ private int mPreferedWidth;
+ private int mPreferedHeight;
+
+ public MeasureHelper(GLView component) {
+ mComponent = component;
+ }
+
+ public MeasureHelper setPreferedContentSize(int width, int height) {
+ mPreferedWidth = width;
+ mPreferedHeight = height;
+ return this;
+ }
+
+ public void measure(int widthSpec, int heightSpec) {
+ Rect p = mComponent.getPaddings();
+ setMeasuredSize(
+ getLength(widthSpec, mPreferedWidth + p.left + p.right),
+ getLength(heightSpec, mPreferedHeight + p.top + p.bottom));
+ }
+
+ private static int getLength(int measureSpec, int prefered) {
+ int specLength = MeasureSpec.getSize(measureSpec);
+ switch(MeasureSpec.getMode(measureSpec)) {
+ case MeasureSpec.EXACTLY: return specLength;
+ case MeasureSpec.AT_MOST: return Math.min(prefered, specLength);
+ default: return prefered;
+ }
+ }
+
+ protected void setMeasuredSize(int width, int height) {
+ mComponent.setMeasuredSize(width, height);
+ }
+
+}
diff --git a/src/com/android/camera/ui/NinePatchTexture.java b/src/com/android/camera/ui/NinePatchTexture.java
new file mode 100644
index 0000000..4dd772a
--- /dev/null
+++ b/src/com/android/camera/ui/NinePatchTexture.java
@@ -0,0 +1,93 @@
+package com.android.camera.ui;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.NinePatchDrawable;
+
+import javax.microedition.khronos.opengles.GL11;
+
+public class NinePatchTexture extends FrameTexture {
+
+ private MyTexture mDelegate;
+
+ private NinePatchDrawable mNinePatch;
+
+ private final Context mContext;
+ private final int mResId;
+
+ private int mLastWidth = -1;
+ private int mLastHeight = -1;
+
+ private final Rect mPaddings = new Rect();
+
+ public NinePatchTexture(Context context, int resId) {
+ this.mContext = context;
+ this.mResId = resId;
+ }
+
+ @Override
+ public void setSize(int width, int height) {
+ super.setSize(width, height);
+ }
+
+ @Override
+ public boolean bind(GLRootView root, GL11 gl) {
+ if (mLastWidth != mWidth || mLastHeight != mHeight) {
+ if (mDelegate != null) mDelegate.deleteFromGL(gl);
+ mDelegate = new MyTexture(mWidth, mHeight);
+ mLastWidth = mWidth;
+ mLastHeight = mHeight;
+ }
+ return mDelegate.bind(root, gl);
+ }
+
+ protected NinePatchDrawable getNinePatch() {
+ if (mNinePatch == null) {
+ mNinePatch = (NinePatchDrawable)
+ mContext.getResources().getDrawable(mResId);
+ mNinePatch.getPadding(mPaddings);
+ }
+ return mNinePatch;
+ }
+
+ private class MyTexture extends CanvasTexture {
+
+ public MyTexture(int width, int height) {
+ super(width, height);
+ }
+
+ @Override
+ protected void onDraw (Canvas canvas, Bitmap backing) {
+ NinePatchDrawable npd = getNinePatch();
+ npd.setBounds(0, 0, mWidth, mHeight);
+ npd.draw(canvas);
+ }
+ }
+
+ @Override
+ protected void freeBitmap(Bitmap bitmap) {
+ mDelegate.freeBitmap(bitmap);
+ }
+
+ @Override
+ protected Bitmap getBitmap() {
+ return mDelegate.getBitmap();
+ }
+
+ public int getIntrinsicWidth() {
+ return getNinePatch().getIntrinsicWidth();
+ }
+
+ public int getIntrinsicHeight() {
+ return getNinePatch().getIntrinsicHeight();
+ }
+
+ @Override
+ public Rect getPaddings() {
+ // get the paddings from nine patch
+ if (mNinePatch == null) getNinePatch();
+ return mPaddings;
+ }
+}
diff --git a/src/com/android/camera/ui/OtherSettingsIndicator.java b/src/com/android/camera/ui/OtherSettingsIndicator.java
new file mode 100644
index 0000000..27d0d80
--- /dev/null
+++ b/src/com/android/camera/ui/OtherSettingsIndicator.java
@@ -0,0 +1,125 @@
+package com.android.camera.ui;
+
+import android.content.Context;
+
+import com.android.camera.ListPreference;
+import com.android.camera.R;
+
+import java.util.HashMap;
+
+public class OtherSettingsIndicator extends AbstractIndicator {
+
+ private final ListPreference mPreference[];
+ private final PreferenceAdapter mAdapters[];
+ private ResourceTexture mIcon;
+ private GLListView mPopupContent;
+ private final HashMap<String, String> mOverrides = new HashMap<String, String>();
+
+ public OtherSettingsIndicator(ListPreference preference[]) {
+ mPreference = preference.clone();
+ mAdapters = new PreferenceAdapter[preference.length];
+ }
+
+ @Override
+ protected ResourceTexture getIcon() {
+ if (mIcon == null) {
+ Context context = getGLRootView().getContext();
+ mIcon = new ResourceTexture(
+ context, R.drawable.ic_viewfinder_settings);
+ }
+ return mIcon;
+ }
+
+ @Override
+ public void overrideSettings(String key, String value) {
+ if (value == null) {
+ mOverrides.remove(key);
+ } else {
+ mOverrides.put(key, value);
+ }
+ if (mPopupContent != null) {
+ ListPreference prefs[] = mPreference;
+ for (int i = 0, n = prefs.length; i < n; ++i) {
+ if (!prefs[i].getKey().equals(key)) continue;
+ mAdapters[i].overrideSettings(value);
+ break;
+ }
+ }
+ }
+
+ private UberAdapter buildUberAdapter() {
+ ListPreference prefs[] = mPreference;
+ PreferenceAdapter adapters[] = mAdapters;
+ Context context = getGLRootView().getContext();
+ for (int i = 0, n = prefs.length; i < n; ++i) {
+ adapters[i] = new PreferenceAdapter(context, prefs[i]);
+ String override = mOverrides.get(prefs[i].getKey());
+ if (override != null) adapters[i].overrideSettings(override);
+ }
+ return new UberAdapter(adapters);
+ }
+
+ @Override
+ public GLView getPopupContent() {
+ if (mPopupContent == null) {
+ Context context = getGLRootView().getContext();
+ mPopupContent = new GLListView();
+ mPopupContent.setHighLight(new NinePatchTexture(
+ context, R.drawable.optionitem_highlight));
+ mPopupContent.setScroller(new NinePatchTexture(
+ context, R.drawable.scrollbar_handle_vertical));
+ UberAdapter adapter = buildUberAdapter();
+ mPopupContent.setOnItemSelectedListener(adapter);
+ mPopupContent.setDataModel(adapter);
+ }
+ return mPopupContent;
+ }
+
+ private class UberAdapter implements
+ GLListView.Model, GLListView.OnItemSelectedListener {
+
+ private final PreferenceAdapter mAdapters[];
+
+ public UberAdapter(PreferenceAdapter[] adapters) {
+ mAdapters = adapters;
+ }
+
+ public GLView getView(int index) {
+ for (PreferenceAdapter adapter : mAdapters) {
+ if (index < adapter.size()) {
+ return adapter.getView(index);
+ }
+ index -= adapter.size();
+ }
+ return null;
+ }
+
+ public boolean isSelectable(int index) {
+ for (PreferenceAdapter adapter : mAdapters) {
+ if (index < adapter.size()) {
+ return adapter.isSelectable(index);
+ }
+ index -= adapter.size();
+ }
+ return true;
+ }
+
+ public int size() {
+ int size = 0;
+ for (PreferenceAdapter adapter : mAdapters) {
+ size += adapter.size();
+ }
+ return size;
+ }
+
+ public void onItemSelected(GLView view, int position) {
+ for (PreferenceAdapter adapter : mAdapters) {
+ if (position < adapter.size()) {
+ adapter.onItemSelected(view, position);
+ return;
+ }
+ position -= adapter.size();
+ }
+ }
+ }
+}
diff --git a/src/com/android/camera/ui/PopupWindow.java b/src/com/android/camera/ui/PopupWindow.java
new file mode 100644
index 0000000..616b3de
--- /dev/null
+++ b/src/com/android/camera/ui/PopupWindow.java
@@ -0,0 +1,181 @@
+package com.android.camera.ui;
+
+import android.graphics.Rect;
+import android.view.View.MeasureSpec;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.OvershootInterpolator;
+import android.view.animation.ScaleAnimation;
+
+import javax.microedition.khronos.opengles.GL11;
+
+public class PopupWindow extends GLView {
+
+ protected Texture mAnchor;
+ protected int mAnchorOffset;
+
+ protected int mAnchorPosition;
+ private final RotatePane mRotatePane = new RotatePane();
+
+ protected NinePatchTexture mBackground;
+
+ public PopupWindow() {
+ super.addComponent(mRotatePane);
+ }
+
+ public void setBackground(NinePatchTexture background) {
+ if (background == mBackground) return;
+ mBackground = background;
+ if (background != null) {
+ setPaddings(mBackground.getPaddings());
+ } else {
+ setPaddings(0, 0, 0, 0);
+ }
+ invalidate();
+ }
+
+ public void setAnchor(Texture anchor, int offset) {
+ mAnchor = anchor;
+ mAnchorOffset = offset;
+ }
+
+ @Override
+ public void addComponent(GLView component) {
+ throw new UnsupportedOperationException("use setContent(GLView)");
+ }
+
+ @Override
+ protected void onMeasure(int widthSpec, int heightSpec) {
+ int widthMode = MeasureSpec.getMode(widthSpec);
+ if (widthMode != MeasureSpec.UNSPECIFIED) {
+ Rect p = mPaddings;
+ int width = MeasureSpec.getSize(widthSpec);
+ widthSpec = MeasureSpec.makeMeasureSpec(
+ Math.max(0, width - p.left - p.right
+ - mAnchor.getWidth() + mAnchorOffset), widthMode);
+ }
+
+ int heightMode = MeasureSpec.getMode(heightSpec);
+ if (heightMode != MeasureSpec.UNSPECIFIED) {
+ int height = MeasureSpec.getSize(widthSpec);
+ widthSpec = MeasureSpec.makeMeasureSpec(Math.max(
+ 0, height - mPaddings.top - mPaddings.bottom), heightMode);
+ }
+
+ int cWidth = 0;
+ int cHeight = 0;
+
+ Rect p = mPaddings;
+ GLView child = mRotatePane;
+ child.measure(widthSpec, heightSpec);
+ setMeasuredSize(child.getMeasuredWidth()
+ + p.left + p.right + mAnchor.getWidth() - mAnchorOffset,
+ child.getMeasuredHeight() + p.top + p.bottom);
+ }
+
+ @Override
+ protected void onLayout(
+ boolean change, int left, int top, int right, int bottom) {
+ Rect p = getPaddings();
+ GLView view = mRotatePane;
+ view.layout(p.left, p.top,
+ getWidth() - p.right - mAnchor.getWidth() + mAnchorOffset,
+ getHeight() - p.bottom);
+ }
+
+ public void setAnchorPosition(int yoffset) {
+ mAnchorPosition = yoffset;
+ }
+
+ @Override
+ protected void renderBackground(GLRootView rootView, GL11 gl) {
+ int width = getWidth();
+ int height = getHeight();
+ int aWidth = mAnchor.getWidth();
+ int aHeight = mAnchor.getHeight();
+
+ Rect p = mPaddings;
+ int aXoffset = width - aWidth;
+ int aYoffset = Math.max(p.top, mAnchorPosition - aHeight / 2);
+ aYoffset = Math.min(aYoffset, height - p.bottom - aHeight);
+
+ if (mAnchor != null) {
+ if (mAnchor.bind(rootView, gl)) {
+ rootView.draw2D(aXoffset, aYoffset, aWidth, aHeight);
+ }
+ }
+
+ Texture backup = null;
+ try {
+ backup = rootView.copyTexture2D(
+ aXoffset, aYoffset, aWidth, aHeight);
+ } catch (GLOutOfMemoryException e) {
+ e.printStackTrace();
+ }
+
+ if (mBackground != null) {
+ mBackground.setSize(width - aWidth + mAnchorOffset, height);
+ if (mBackground.bind(rootView, gl)) {
+ rootView.draw2D(
+ 0, 0, mBackground.getWidth(), mBackground.getHeight());
+ }
+ }
+
+ if (backup.bind(rootView, gl)) {
+ gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO);
+ rootView.draw2D(aXoffset, aYoffset, aWidth, aHeight, 1);
+ gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
+ }
+ }
+
+ public void setContent(GLView content) {
+ mRotatePane.setContent(content);
+ }
+
+ @Override
+ public void clearComponents() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void popup() {
+ setVisibility(GLView.VISIBLE);
+
+ AnimationSet set = new AnimationSet(false);
+ Animation scale = new ScaleAnimation(
+ 0.7f, 1f, 0.7f, 1f, getWidth(), getHeight() / 2);
+ Animation alpha = new AlphaAnimation(0.5f, 1.0f);
+
+ set.addAnimation(scale);
+ set.addAnimation(alpha);
+ scale.setDuration(200);
+ alpha.setDuration(200);
+ scale.setInterpolator(new OvershootInterpolator());
+ startAnimation(set);
+ }
+
+ public void popoff() {
+ setVisibility(GLView.INVISIBLE);
+ Animation alpha = new AlphaAnimation(0.7f, 0.0f);
+ alpha.setDuration(100);
+ startAnimation(alpha);
+ }
+
+ public void setOrientation(int orientation) {
+ switch (orientation) {
+ case 90:
+ mRotatePane.setOrientation(RotatePane.LEFT);
+ break;
+ case 180:
+ mRotatePane.setOrientation(RotatePane.DOWN);
+ break;
+ case 270:
+ mRotatePane.setOrientation(RotatePane.RIGHT);
+ break;
+ default:
+ mRotatePane.setOrientation(RotatePane.UP);
+ break;
+ }
+ }
+
+}
diff --git a/src/com/android/camera/ui/PopupWindowStencilImpl.java b/src/com/android/camera/ui/PopupWindowStencilImpl.java
new file mode 100644
index 0000000..eaf9134
--- /dev/null
+++ b/src/com/android/camera/ui/PopupWindowStencilImpl.java
@@ -0,0 +1,41 @@
+package com.android.camera.ui;
+
+import android.graphics.Rect;
+
+import javax.microedition.khronos.opengles.GL11;
+
+public class PopupWindowStencilImpl extends PopupWindow {
+
+
+ @Override
+ protected void renderBackground(GLRootView rootView, GL11 gl) {
+ int width = getWidth();
+ int height = getHeight();
+ int aWidth = mAnchor.getWidth();
+ int aHeight = mAnchor.getHeight();
+
+ Rect p = mPaddings;
+ int aXoffset = width - aWidth;
+ int aYoffset = Math.max(p.top, mAnchorPosition - aHeight / 2);
+ aYoffset = Math.min(aYoffset, height - p.bottom - aHeight);
+
+ if (mAnchor != null) {
+ gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
+ gl.glStencilFunc(GL11.GL_ALWAYS, 1, 1);
+ if (mAnchor.bind(rootView, gl)) {
+ rootView.draw2D(aXoffset, aYoffset, aWidth, aHeight);
+ }
+ gl.glStencilFunc(GL11.GL_NOTEQUAL, 1, 1);
+ gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
+ }
+
+ if (mBackground != null) {
+ mBackground.setSize(width - aWidth + mAnchorOffset, height);
+ if (mBackground.bind(rootView, gl)) {
+ rootView.draw2D(
+ 0, 0, mBackground.getWidth(), mBackground.getHeight());
+ }
+ }
+ }
+
+}
diff --git a/src/com/android/camera/ui/PreferenceAdapter.java b/src/com/android/camera/ui/PreferenceAdapter.java
new file mode 100644
index 0000000..233f154
--- /dev/null
+++ b/src/com/android/camera/ui/PreferenceAdapter.java
@@ -0,0 +1,103 @@
+package com.android.camera.ui;
+
+import android.content.Context;
+
+import com.android.camera.IconListPreference;
+import com.android.camera.ListPreference;
+import com.android.camera.R;
+import com.android.camera.Util;
+
+import java.util.ArrayList;
+
+public class PreferenceAdapter
+ implements GLListView.Model, GLListView.OnItemSelectedListener {
+
+ private static final int ICON_NONE = 0;
+
+ private final Context mContext;
+ private final ArrayList<GLView> mContent = new ArrayList<GLView>();
+ private final ListPreference mPreference;
+ private String mOverride;
+
+ public PreferenceAdapter(Context context, ListPreference preference) {
+ mContext = context;
+ mPreference = preference;
+ generateContent(preference);
+ }
+
+ public void overrideSettings(String settings) {
+ if (Util.equals(settings, mOverride)) return;
+ mOverride = settings;
+
+ CharSequence[] values = mPreference.getEntryValues();
+ String value = mPreference.getValue();
+ if (settings == null) {
+ for (int i = 1, n = mContent.size(); i < n; ++i) {
+ GLOptionItem item = (GLOptionItem) mContent.get(i);
+ item.setChecked(values[i - 1].equals(value));
+ item.setEnabled(true);
+ }
+ } else {
+ for (int i = 1, n = mContent.size(); i < n; ++i) {
+ GLOptionItem item = (GLOptionItem) mContent.get(i);
+ boolean checked = values[i - 1].equals(settings);
+ item.setChecked(checked);
+ item.setEnabled(checked);
+ }
+ }
+ }
+
+ private void generateContent(ListPreference preference) {
+ Context context = mContext;
+
+ GLOptionHeader header = new GLOptionHeader(preference);
+ header.setBackground(new NinePatchTexture(
+ context, R.drawable.optionheader_background));
+ header.setPaddings(5, 2, 5, 2);
+ mContent.add(header);
+ CharSequence[] entries = preference.getEntries();
+ CharSequence[] values = preference.getEntryValues();
+ String value = preference.getValue();
+ int [] icons = null;
+ if (preference instanceof IconListPreference) {
+ IconListPreference iPref = (IconListPreference) preference;
+ icons = iPref.getIconIds();
+ }
+ for (int i = 0, n = entries.length; i < n; ++i) {
+ GLOptionItem item = new GLOptionItem(
+ context, icons == null ? ICON_NONE : icons[i],
+ entries[i].toString());
+ item.setPaddings(5, 2, 5, 2);
+ item.setChecked(values[i].equals(value));
+ mContent.add(item);
+ }
+ }
+
+ public void onItemSelected(GLView view, int position) {
+ if (mOverride != null) return;
+ ListPreference pref = mPreference;
+ CharSequence[] values = pref.getEntryValues();
+ if (position < values.length + 1) {
+ int index = position - 1;
+ int oldIndex = pref.findIndexOfValue(pref.getValue());
+ if (oldIndex != index) {
+ pref.setValueIndex(index);
+ ((GLOptionItem) mContent.get(1 + oldIndex)).setChecked(false);
+ ((GLOptionItem) view).setChecked(true);
+ }
+ return;
+ }
+ }
+
+ public GLView getView(int index) {
+ return mContent.get(index);
+ }
+
+ public boolean isSelectable(int index) {
+ return mContent.get(index) instanceof GLOptionItem;
+ }
+
+ public int size() {
+ return mContent.size();
+ }
+}
diff --git a/src/com/android/camera/ui/RawTexture.java b/src/com/android/camera/ui/RawTexture.java
new file mode 100644
index 0000000..d8c9cda
--- /dev/null
+++ b/src/com/android/camera/ui/RawTexture.java
@@ -0,0 +1,26 @@
+package com.android.camera.ui;
+
+import android.graphics.Bitmap;
+
+import javax.microedition.khronos.opengles.GL11;
+
+public class RawTexture extends Texture {
+
+ protected RawTexture(GL11 gl, int id,
+ int width, int height, float widthf, float heightf) {
+ super(gl, id, STATE_LOADED);
+ super.setSize(width, height);
+ super.setTexCoordSize(widthf, heightf);
+ }
+
+ @Override
+ protected void freeBitmap(Bitmap bitmap) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected Bitmap getBitmap() {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/src/com/android/camera/ui/ResourceTexture.java b/src/com/android/camera/ui/ResourceTexture.java
new file mode 100644
index 0000000..0d19d2a
--- /dev/null
+++ b/src/com/android/camera/ui/ResourceTexture.java
@@ -0,0 +1,62 @@
+package com.android.camera.ui;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+
+import com.android.camera.Util;
+
+public class ResourceTexture extends Texture {
+
+ private final Context mContext;
+ private final int mResId;
+ private Bitmap mBitmap;
+
+ public ResourceTexture(Context context, int resId) {
+ mContext = Util.checkNotNull(context);
+ mResId = resId;
+ }
+
+ @Override
+ protected Bitmap getBitmap() {
+ if (mBitmap != null) return mBitmap;
+ mBitmap = BitmapFactory.decodeResource(mContext.getResources(), mResId);
+ setSize(mBitmap.getWidth(), mBitmap.getHeight());
+
+ if (Util.isPowerOf2(mWidth) && Util.isPowerOf2(mHeight)) return mBitmap;
+
+ Bitmap oldBitmap = mBitmap;
+ mBitmap = generateGLCompatibleBitmap(mWidth, mHeight);
+ Canvas canvas = new Canvas(mBitmap);
+ canvas.drawBitmap(oldBitmap, new Matrix(), null);
+ oldBitmap.recycle();
+
+ return mBitmap;
+ }
+
+ @Override
+ public int getHeight() {
+ if (mHeight == UNSPECIFIED) {
+ getBitmap();
+ }
+ return mHeight;
+ }
+
+ @Override
+ public int getWidth() {
+ if (mHeight == UNSPECIFIED) {
+ getBitmap();
+ }
+ return mWidth;
+ }
+
+ @Override
+ protected void freeBitmap(Bitmap bitmap) {
+ Util.Assert(bitmap == mBitmap);
+ bitmap.recycle();
+ mBitmap = null;
+ }
+
+}
diff --git a/src/com/android/camera/ui/RotatePane.java b/src/com/android/camera/ui/RotatePane.java
new file mode 100644
index 0000000..0a7eefd
--- /dev/null
+++ b/src/com/android/camera/ui/RotatePane.java
@@ -0,0 +1,120 @@
+package com.android.camera.ui;
+
+import android.graphics.Matrix;
+import android.view.MotionEvent;
+
+import javax.microedition.khronos.opengles.GL11;
+
+
+public class RotatePane extends GLView {
+
+ public static final int UP = 0;
+ public static final int RIGHT = 1;
+ public static final int DOWN = 2;
+ public static final int LEFT = 3;
+
+ private int mOrientation = 0;
+
+ private GLView mChild;
+
+ @Override
+ protected void onLayout(
+ boolean change, int left, int top, int right, int bottom) {
+ int width = right - left;
+ int height = bottom - top;
+ switch (mOrientation) {
+ case UP:
+ case DOWN:
+ mChild.layout(0, 0, width, height);
+ break;
+ case LEFT:
+ case RIGHT:
+ mChild.layout(0, 0, height, width);
+ break;
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthSpec, int heightSpec) {
+ GLView c = mChild;
+ switch(mOrientation) {
+ case UP:
+ case DOWN:
+ c.measure(widthSpec, heightSpec);
+ setMeasuredSize(c.getMeasuredWidth(), c.getMeasuredHeight());
+ break;
+ case LEFT:
+ case RIGHT:
+ mChild.measure(heightSpec, widthSpec);
+ setMeasuredSize(c.getMeasuredHeight(), c.getMeasuredWidth());
+ }
+ }
+
+ @Override
+ protected void render(GLRootView view, GL11 gl) {
+
+ if (mOrientation == UP) {
+ mChild.render(view, gl);
+ return;
+ }
+
+ view.pushTransform();
+ Matrix matrix = view.getTransformation().getMatrix();
+ float width = getWidth();
+ float height = getHeight();
+ switch (mOrientation) {
+ case DOWN:
+ matrix.preRotate(180, width / 2, height / 2);
+ break;
+ case LEFT:
+ matrix.preRotate(270, height / 2, height / 2);
+ break;
+ case RIGHT:
+ matrix.preRotate(90, width / 2, width / 2);
+ break;
+ }
+ mChild.render(view, gl);
+ view.popTransform();
+ }
+
+ @Override
+ protected boolean dispatchTouchEvent(MotionEvent event) {
+ float x = event.getX();
+ float y = event.getY();
+ float width = getWidth();
+ float height = getHeight();
+ switch (mOrientation) {
+ case DOWN: event.setLocation(width - x, height - y); break;
+ case LEFT: event.setLocation(height - y, x); break;
+ case RIGHT: event.setLocation(y, width - x); break;
+ }
+ boolean result = mChild.dispatchTouchEvent(event);
+ event.setLocation(x, y);
+ return result;
+ }
+
+ public void setOrientation(int orientation) {
+ if (mOrientation == orientation) return;
+ mOrientation = orientation;
+ requestLayout();
+ }
+
+ public void setContent(GLView view) {
+ if (mChild == view) return;
+
+ if (mChild != null) super.clearComponents();
+ mChild = view;
+ if (view != null) super.addComponent(view);
+ requestLayout();
+ }
+
+ @Override
+ public void addComponent(GLView view) {
+ throw new UnsupportedOperationException("use setContent(GLView)");
+ }
+
+ @Override
+ public void clearComponents() {
+ throw new UnsupportedOperationException("use setContent(null)");
+ }
+}
diff --git a/src/com/android/camera/ui/StateListTexture.java b/src/com/android/camera/ui/StateListTexture.java
new file mode 100644
index 0000000..432450c
--- /dev/null
+++ b/src/com/android/camera/ui/StateListTexture.java
@@ -0,0 +1,79 @@
+package com.android.camera.ui;
+
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+
+import java.util.ArrayList;
+
+import javax.microedition.khronos.opengles.GL11;
+
+public class StateListTexture extends FrameTexture {
+
+ private int mIndex;
+ private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
+
+ private static class Entry {
+ public int mMustHave;
+ public int mMustNotHave;
+ public FrameTexture mTexture;
+
+ public Entry(int mustHave, int mustNotHave, FrameTexture texture) {
+ mMustHave = mustHave;
+ mMustNotHave = mustNotHave;
+ mTexture = texture;
+ }
+ }
+
+ @Override
+ protected void freeBitmap(Bitmap bitmap) {
+ mEntries.get(mIndex).mTexture.freeBitmap(bitmap);
+ }
+
+ @Override
+ protected Bitmap getBitmap() {
+ return mEntries.get(mIndex).mTexture.getBitmap();
+ }
+
+ private int getStateIndex(int state) {
+ for (int i = 0, n = mEntries.size(); i < n; ++i) {
+ Entry entry = mEntries.get(i);
+ if ((entry.mMustHave & state) == entry.mMustHave
+ && (state & entry.mMustNotHave) == 0) return i;
+ }
+ return -1;
+ }
+
+ @Override
+ public void setSize(int width, int height) {
+ super.setSize(width, height);
+ for (Entry entry : mEntries) {
+ entry.mTexture.setSize(width, height);
+ }
+ }
+
+ public boolean isDifferent(int stateA, int stateB) {
+ return getStateIndex(stateA) != getStateIndex(stateB);
+ }
+
+ public boolean setState(int state) {
+ int oldIndex = mIndex;
+ mIndex = getStateIndex(state);
+ return mIndex != oldIndex;
+ }
+
+ @Override
+ public boolean bind(GLRootView root, GL11 gl) {
+ if (mIndex < 0) return false;
+ return mEntries.get(mIndex).mTexture.bind(root, gl);
+ }
+
+ public void addState(
+ int mustHave, int mustNotHave, FrameTexture texture) {
+ mEntries.add(new Entry(mustHave, mustNotHave, texture));
+ }
+
+ @Override
+ public Rect getPaddings() {
+ return mEntries.get(mIndex).mTexture.getPaddings();
+ }
+}
diff --git a/src/com/android/camera/ui/StringTexture.java b/src/com/android/camera/ui/StringTexture.java
new file mode 100644
index 0000000..c518e81
--- /dev/null
+++ b/src/com/android/camera/ui/StringTexture.java
@@ -0,0 +1,46 @@
+package com.android.camera.ui;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.FontMetricsInt;
+
+public class StringTexture extends CanvasTexture {
+ private static int DEFAULT_PADDING = 1;
+
+ private final String mTitle;
+ private final Paint mPaint;
+ private final FontMetricsInt mMetrics;
+
+ public StringTexture(String text, Paint paint,
+ FontMetricsInt metrics, int width, int height) {
+ super(width, height);
+ mTitle = text;
+ mPaint = paint;
+ mMetrics = metrics;
+ }
+
+
+ public static StringTexture newInstance(String text, Paint paint) {
+ FontMetricsInt metrics = paint.getFontMetricsInt();
+ int width = (int) (.5f + paint.measureText(text)) + DEFAULT_PADDING * 2;
+ int height = metrics.bottom - metrics.top + DEFAULT_PADDING * 2;
+ return new StringTexture(text, paint, metrics, width, height);
+ }
+
+ public static StringTexture newInstance(
+ String text, float textSize, int color) {
+ Paint paint = new Paint();
+ paint.setTextSize(textSize);
+ paint.setAntiAlias(true);
+ paint.setColor(color);
+
+ return newInstance(text, paint);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas, Bitmap backing) {
+ canvas.translate(DEFAULT_PADDING, DEFAULT_PADDING - mMetrics.ascent);
+ canvas.drawText(mTitle, 0, 0, mPaint);
+ }
+}
diff --git a/src/com/android/camera/ui/Texture.java b/src/com/android/camera/ui/Texture.java
new file mode 100644
index 0000000..6d5f8d8
--- /dev/null
+++ b/src/com/android/camera/ui/Texture.java
@@ -0,0 +1,156 @@
+package com.android.camera.ui;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.opengl.GLUtils;
+
+import com.android.camera.Util;
+
+import javax.microedition.khronos.opengles.GL11;
+import javax.microedition.khronos.opengles.GL11Ext;
+
+public abstract class Texture {
+
+ @SuppressWarnings("unused")
+ private static final String TAG = "Texture";
+ protected static final int UNSPECIFIED = -1;
+
+ public static final int STATE_UNLOADED = 0;
+ public static final int STATE_LOADED = 1;
+ public static final int STATE_ERROR = -1;
+
+ private GL11 mGL;
+
+ private int mId;
+ private int mState;
+
+ protected int mWidth = UNSPECIFIED;
+ protected int mHeight = UNSPECIFIED;
+
+ private float mTexCoordWidth = 1.0f;
+ private float mTexCoordHeight = 1.0f;
+
+ protected Texture(GL11 gl, int id, int state) {
+ mGL = gl;
+ mId = id;
+ mState = state;
+ }
+
+ protected Texture() {
+ this(null, 0, STATE_UNLOADED);
+ }
+
+ protected void setSize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ }
+
+ protected void setTexCoordSize(float width, float height) {
+ mTexCoordWidth = width;
+ mTexCoordHeight = height;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public int getWidth() {
+ return mWidth;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
+
+ protected abstract Bitmap getBitmap();
+
+ protected abstract void freeBitmap(Bitmap bitmap);
+
+ public void deleteFromGL(GL11 gl) {
+ gl.glDeleteTextures(1, new int[]{mId}, 0);
+ mState = STATE_UNLOADED;
+ }
+
+ private void uploadToGL(GL11 gl) throws GLOutOfMemoryException {
+ Bitmap bitmap = getBitmap();
+ int glError = GL11.GL_NO_ERROR;
+ if (bitmap != null) {
+ int[] textureId = new int[1];
+ try {
+ // Define a vertically flipped crop rectangle for
+ // OES_draw_texture.
+ int[] cropRect = {0, mHeight, mWidth, - mHeight};
+
+ // Upload the bitmap to a new texture.
+ gl.glGenTextures(1, textureId, 0);
+ gl.glBindTexture(GL11.GL_TEXTURE_2D, textureId[0]);
+ gl.glTexParameteriv(GL11.GL_TEXTURE_2D,
+ GL11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect, 0);
+ gl.glTexParameteri(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
+ gl.glTexParameteri(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
+ gl.glTexParameterf(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
+ gl.glTexParameterf(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
+ GLUtils.texImage2D(GL11.GL_TEXTURE_2D, 0, bitmap, 0);
+ } finally {
+ freeBitmap(bitmap);
+ }
+ if (glError == GL11.GL_OUT_OF_MEMORY) {
+ throw new GLOutOfMemoryException();
+ }
+ if (glError != GL11.GL_NO_ERROR) {
+ mId = 0;
+ mState = STATE_UNLOADED;
+ throw new RuntimeException(
+ "Texture upload fail, glError " + glError);
+ } else {
+ // Update texture state.
+ mGL = gl;
+ mId = textureId[0];
+ mState = Texture.STATE_LOADED;
+ }
+ } else {
+ mState = STATE_ERROR;
+ throw new RuntimeException("Texture load fail, no bitmap");
+ }
+ }
+
+ public void draw(GLRootView root, int x, int y) {
+ draw(root, x, y, getWidth(), getHeight());
+ }
+
+ public void draw(GLRootView root,
+ int x, int y, int width, int height) {
+ root.draw2D(x, y, width, height);
+ }
+
+ protected boolean bind(GLRootView glRootView, GL11 gl) {
+ if (mState == Texture.STATE_UNLOADED || mGL != gl) {
+ mState = Texture.STATE_UNLOADED;
+ try {
+ uploadToGL(gl);
+ } catch (GLOutOfMemoryException e) {
+ glRootView.handleLowMemory();
+ return false;
+ }
+ } else {
+ gl.glBindTexture(GL11.GL_TEXTURE_2D, getId());
+ }
+
+ float w = mTexCoordWidth;
+ float h = mTexCoordHeight;
+ glRootView.setTexCoords(0, 0, w, 0, 0, h, w, h);
+ return true;
+ }
+
+ protected Bitmap generateGLCompatibleBitmap(int width, int height) {
+ int newWidth = Util.nextPowerOf2(width);
+ int newHeight = Util.nextPowerOf2(height);
+ mTexCoordWidth = (float) width / newWidth;
+ mTexCoordHeight = (float) height / newHeight;
+ return Bitmap.createBitmap(newWidth, newHeight, Config.ARGB_8888);
+ }
+}