diff options
author | Owen Lin <owenlin@google.com> | 2010-01-28 16:10:19 -0800 |
---|---|---|
committer | Owen Lin <owenlin@google.com> | 2010-02-26 20:26:48 +0800 |
commit | 4de149ceb47f2c251f646419907424bfb67d2b64 (patch) | |
tree | 6f48682fe065902039bad89d1f3ca90720eca819 | |
parent | 02627adfa3d240d817e34af69be8d07e9c66c136 (diff) | |
download | LegacyCamera-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
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 Binary files differnew file mode 100644 index 0000000..e229c35 --- /dev/null +++ b/res/drawable-hdpi/ic_menuselect_flash_auto.png diff --git a/res/drawable-hdpi/ic_menuselect_flash_off.png b/res/drawable-hdpi/ic_menuselect_flash_off.png Binary files differnew file mode 100644 index 0000000..cb00600 --- /dev/null +++ b/res/drawable-hdpi/ic_menuselect_flash_off.png diff --git a/res/drawable-hdpi/ic_menuselect_flash_on.png b/res/drawable-hdpi/ic_menuselect_flash_on.png Binary files differnew file mode 100644 index 0000000..710e9b7 --- /dev/null +++ b/res/drawable-hdpi/ic_menuselect_flash_on.png diff --git a/res/drawable-hdpi/ic_menuselect_focus_auto.png b/res/drawable-hdpi/ic_menuselect_focus_auto.png Binary files differnew file mode 100644 index 0000000..b99e9f5 --- /dev/null +++ b/res/drawable-hdpi/ic_menuselect_focus_auto.png diff --git a/res/drawable-hdpi/ic_menuselect_focus_infinity.png b/res/drawable-hdpi/ic_menuselect_focus_infinity.png Binary files differnew file mode 100644 index 0000000..5136816 --- /dev/null +++ b/res/drawable-hdpi/ic_menuselect_focus_infinity.png diff --git a/res/drawable-hdpi/ic_menuselect_focus_macro.png b/res/drawable-hdpi/ic_menuselect_focus_macro.png Binary files differnew file mode 100644 index 0000000..adbc25a --- /dev/null +++ b/res/drawable-hdpi/ic_menuselect_focus_macro.png diff --git a/res/drawable-hdpi/ic_menuselect_gps_off.png b/res/drawable-hdpi/ic_menuselect_gps_off.png Binary files differnew file mode 100644 index 0000000..a7af034 --- /dev/null +++ b/res/drawable-hdpi/ic_menuselect_gps_off.png diff --git a/res/drawable-hdpi/ic_menuselect_gps_on.png b/res/drawable-hdpi/ic_menuselect_gps_on.png Binary files differnew file mode 100644 index 0000000..39d2c38 --- /dev/null +++ b/res/drawable-hdpi/ic_menuselect_gps_on.png diff --git a/res/drawable-hdpi/ic_menuselect_off.png b/res/drawable-hdpi/ic_menuselect_off.png Binary files differnew file mode 100644 index 0000000..b883c8f --- /dev/null +++ b/res/drawable-hdpi/ic_menuselect_off.png diff --git a/res/drawable-hdpi/ic_menuselect_on.png b/res/drawable-hdpi/ic_menuselect_on.png Binary files differnew file mode 100644 index 0000000..24f5edf --- /dev/null +++ b/res/drawable-hdpi/ic_menuselect_on.png diff --git a/res/drawable-hdpi/ic_menuselect_settings.png b/res/drawable-hdpi/ic_menuselect_settings.png Binary files differnew file mode 100644 index 0000000..8f2d768 --- /dev/null +++ b/res/drawable-hdpi/ic_menuselect_settings.png diff --git a/res/drawable-hdpi/ic_menuselect_wb_auto.png b/res/drawable-hdpi/ic_menuselect_wb_auto.png Binary files differnew file mode 100644 index 0000000..3b30e36 --- /dev/null +++ b/res/drawable-hdpi/ic_menuselect_wb_auto.png diff --git a/res/drawable-hdpi/ic_menuselect_wb_cloudy.png b/res/drawable-hdpi/ic_menuselect_wb_cloudy.png Binary files differnew file mode 100644 index 0000000..ccd7948 --- /dev/null +++ b/res/drawable-hdpi/ic_menuselect_wb_cloudy.png diff --git a/res/drawable-hdpi/ic_menuselect_wb_daylight.png b/res/drawable-hdpi/ic_menuselect_wb_daylight.png Binary files differnew file mode 100644 index 0000000..815280b --- /dev/null +++ b/res/drawable-hdpi/ic_menuselect_wb_daylight.png diff --git a/res/drawable-hdpi/ic_menuselect_wb_fluorescent.png b/res/drawable-hdpi/ic_menuselect_wb_fluorescent.png Binary files differnew file mode 100644 index 0000000..886af77 --- /dev/null +++ b/res/drawable-hdpi/ic_menuselect_wb_fluorescent.png diff --git a/res/drawable-hdpi/ic_menuselect_wb_incandescent.png b/res/drawable-hdpi/ic_menuselect_wb_incandescent.png Binary files differnew file mode 100644 index 0000000..0b95bec --- /dev/null +++ b/res/drawable-hdpi/ic_menuselect_wb_incandescent.png diff --git a/res/drawable-hdpi/ic_viewfinder_flash_auto.png b/res/drawable-hdpi/ic_viewfinder_flash_auto.png Binary files differindex 27968e7..0bf7495 100755..100644 --- a/res/drawable-hdpi/ic_viewfinder_flash_auto.png +++ b/res/drawable-hdpi/ic_viewfinder_flash_auto.png diff --git a/res/drawable-hdpi/ic_viewfinder_flash_off.png b/res/drawable-hdpi/ic_viewfinder_flash_off.png Binary files differindex 4a8bb95..afcfbad 100644 --- a/res/drawable-hdpi/ic_viewfinder_flash_off.png +++ b/res/drawable-hdpi/ic_viewfinder_flash_off.png diff --git a/res/drawable-hdpi/ic_viewfinder_flash_on.png b/res/drawable-hdpi/ic_viewfinder_flash_on.png Binary files differindex 71ee867..827c98f 100755..100644 --- a/res/drawable-hdpi/ic_viewfinder_flash_on.png +++ b/res/drawable-hdpi/ic_viewfinder_flash_on.png diff --git a/res/drawable-hdpi/ic_viewfinder_focus_auto.png b/res/drawable-hdpi/ic_viewfinder_focus_auto.png Binary files differnew file mode 100644 index 0000000..0a669cb --- /dev/null +++ b/res/drawable-hdpi/ic_viewfinder_focus_auto.png diff --git a/res/drawable-hdpi/ic_viewfinder_focus_infinity.png b/res/drawable-hdpi/ic_viewfinder_focus_infinity.png Binary files differindex 82112fc..99f8638 100644 --- a/res/drawable-hdpi/ic_viewfinder_focus_infinity.png +++ b/res/drawable-hdpi/ic_viewfinder_focus_infinity.png diff --git a/res/drawable-hdpi/ic_viewfinder_focus_macro.png b/res/drawable-hdpi/ic_viewfinder_focus_macro.png Binary files differindex 85d4a55..adbc25a 100644 --- a/res/drawable-hdpi/ic_viewfinder_focus_macro.png +++ b/res/drawable-hdpi/ic_viewfinder_focus_macro.png diff --git a/res/drawable-hdpi/ic_viewfinder_gps_no_signal.png b/res/drawable-hdpi/ic_viewfinder_gps_no_signal.png Binary files differnew file mode 100644 index 0000000..4a0d016 --- /dev/null +++ b/res/drawable-hdpi/ic_viewfinder_gps_no_signal.png diff --git a/res/drawable-hdpi/ic_viewfinder_gps_off.png b/res/drawable-hdpi/ic_viewfinder_gps_off.png Binary files differnew file mode 100644 index 0000000..64636b9 --- /dev/null +++ b/res/drawable-hdpi/ic_viewfinder_gps_off.png diff --git a/res/drawable-hdpi/ic_viewfinder_gps_on.png b/res/drawable-hdpi/ic_viewfinder_gps_on.png Binary files differnew file mode 100644 index 0000000..be84985 --- /dev/null +++ b/res/drawable-hdpi/ic_viewfinder_gps_on.png diff --git a/res/drawable-hdpi/ic_viewfinder_iconbar.9.png b/res/drawable-hdpi/ic_viewfinder_iconbar.9.png Binary files differnew file mode 100644 index 0000000..0cd4dfe --- /dev/null +++ b/res/drawable-hdpi/ic_viewfinder_iconbar.9.png diff --git a/res/drawable-hdpi/ic_viewfinder_iconbar_highlight.9.png b/res/drawable-hdpi/ic_viewfinder_iconbar_highlight.9.png Binary files differnew file mode 100644 index 0000000..1b08fdf --- /dev/null +++ b/res/drawable-hdpi/ic_viewfinder_iconbar_highlight.9.png diff --git a/res/drawable-hdpi/ic_viewfinder_settings.png b/res/drawable-hdpi/ic_viewfinder_settings.png Binary files differnew file mode 100644 index 0000000..1a53019 --- /dev/null +++ b/res/drawable-hdpi/ic_viewfinder_settings.png diff --git a/res/drawable-hdpi/ic_viewfinder_wb_auto.png b/res/drawable-hdpi/ic_viewfinder_wb_auto.png Binary files differnew file mode 100644 index 0000000..cf24be4 --- /dev/null +++ b/res/drawable-hdpi/ic_viewfinder_wb_auto.png diff --git a/res/drawable-hdpi/ic_viewfinder_wb_cloudy.png b/res/drawable-hdpi/ic_viewfinder_wb_cloudy.png Binary files differindex 9766f7b..fd6f2e1 100644 --- a/res/drawable-hdpi/ic_viewfinder_wb_cloudy.png +++ b/res/drawable-hdpi/ic_viewfinder_wb_cloudy.png diff --git a/res/drawable-hdpi/ic_viewfinder_wb_daylight.png b/res/drawable-hdpi/ic_viewfinder_wb_daylight.png Binary files differindex 3ecebe6..e9706c3 100644 --- a/res/drawable-hdpi/ic_viewfinder_wb_daylight.png +++ b/res/drawable-hdpi/ic_viewfinder_wb_daylight.png diff --git a/res/drawable-hdpi/ic_viewfinder_wb_fluorescent.png b/res/drawable-hdpi/ic_viewfinder_wb_fluorescent.png Binary files differindex 889f607..49b9ea6 100644 --- a/res/drawable-hdpi/ic_viewfinder_wb_fluorescent.png +++ b/res/drawable-hdpi/ic_viewfinder_wb_fluorescent.png diff --git a/res/drawable-hdpi/ic_viewfinder_wb_incandescent.png b/res/drawable-hdpi/ic_viewfinder_wb_incandescent.png Binary files differindex 0fce7ce..026bf14 100644 --- a/res/drawable-hdpi/ic_viewfinder_wb_incandescent.png +++ b/res/drawable-hdpi/ic_viewfinder_wb_incandescent.png diff --git a/res/drawable-hdpi/menu_popup.9.png b/res/drawable-hdpi/menu_popup.9.png Binary files differnew file mode 100644 index 0000000..534d3cf --- /dev/null +++ b/res/drawable-hdpi/menu_popup.9.png diff --git a/res/drawable-hdpi/menu_popup_triangle.png b/res/drawable-hdpi/menu_popup_triangle.png Binary files differnew file mode 100644 index 0000000..59e2f99 --- /dev/null +++ b/res/drawable-hdpi/menu_popup_triangle.png diff --git a/res/drawable-hdpi/optionheader_background.9.png b/res/drawable-hdpi/optionheader_background.9.png Binary files differnew file mode 100644 index 0000000..22991e7 --- /dev/null +++ b/res/drawable-hdpi/optionheader_background.9.png diff --git a/res/drawable-hdpi/optionitem_highlight.9.png b/res/drawable-hdpi/optionitem_highlight.9.png Binary files differnew file mode 100644 index 0000000..3a17a86 --- /dev/null +++ b/res/drawable-hdpi/optionitem_highlight.9.png diff --git a/res/drawable-hdpi/scrollbar_handle_vertical.9.png b/res/drawable-hdpi/scrollbar_handle_vertical.9.png Binary files differnew file mode 100644 index 0000000..3ec0791 --- /dev/null +++ b/res/drawable-hdpi/scrollbar_handle_vertical.9.png diff --git a/res/drawable-hdpi/zoom_shape.png b/res/drawable-hdpi/zoom_shape.png Binary files differnew file mode 100644 index 0000000..c12ce6b --- /dev/null +++ b/res/drawable-hdpi/zoom_shape.png diff --git a/res/drawable-hdpi/zoom_slider.png b/res/drawable-hdpi/zoom_slider.png Binary files differnew file mode 100644 index 0000000..6fcaecf --- /dev/null +++ b/res/drawable-hdpi/zoom_slider.png diff --git a/res/drawable-hdpi/zoom_tickmarks.png b/res/drawable-hdpi/zoom_tickmarks.png Binary files differnew file mode 100644 index 0000000..a4191d7 --- /dev/null +++ b/res/drawable-hdpi/zoom_tickmarks.png diff --git a/res/drawable-mdpi/ic_menuselect_flash_auto.png b/res/drawable-mdpi/ic_menuselect_flash_auto.png Binary files differnew file mode 100644 index 0000000..39e1eed --- /dev/null +++ b/res/drawable-mdpi/ic_menuselect_flash_auto.png diff --git a/res/drawable-mdpi/ic_menuselect_flash_off.png b/res/drawable-mdpi/ic_menuselect_flash_off.png Binary files differnew file mode 100644 index 0000000..44b6e95 --- /dev/null +++ b/res/drawable-mdpi/ic_menuselect_flash_off.png diff --git a/res/drawable-mdpi/ic_menuselect_flash_on.png b/res/drawable-mdpi/ic_menuselect_flash_on.png Binary files differnew file mode 100644 index 0000000..bb1343a --- /dev/null +++ b/res/drawable-mdpi/ic_menuselect_flash_on.png diff --git a/res/drawable-mdpi/ic_menuselect_focus_auto.png b/res/drawable-mdpi/ic_menuselect_focus_auto.png Binary files differnew file mode 100644 index 0000000..2eb28da --- /dev/null +++ b/res/drawable-mdpi/ic_menuselect_focus_auto.png diff --git a/res/drawable-mdpi/ic_menuselect_focus_infinity.png b/res/drawable-mdpi/ic_menuselect_focus_infinity.png Binary files differnew file mode 100644 index 0000000..c5f902e --- /dev/null +++ b/res/drawable-mdpi/ic_menuselect_focus_infinity.png diff --git a/res/drawable-mdpi/ic_menuselect_focus_macro.png b/res/drawable-mdpi/ic_menuselect_focus_macro.png Binary files differnew file mode 100644 index 0000000..dd3ecb3 --- /dev/null +++ b/res/drawable-mdpi/ic_menuselect_focus_macro.png diff --git a/res/drawable-mdpi/ic_menuselect_gps_off.png b/res/drawable-mdpi/ic_menuselect_gps_off.png Binary files differnew file mode 100644 index 0000000..6f36907 --- /dev/null +++ b/res/drawable-mdpi/ic_menuselect_gps_off.png diff --git a/res/drawable-mdpi/ic_menuselect_gps_on.png b/res/drawable-mdpi/ic_menuselect_gps_on.png Binary files differnew file mode 100644 index 0000000..19d4c85 --- /dev/null +++ b/res/drawable-mdpi/ic_menuselect_gps_on.png diff --git a/res/drawable-mdpi/ic_menuselect_off.png b/res/drawable-mdpi/ic_menuselect_off.png Binary files differnew file mode 100644 index 0000000..80baf0e --- /dev/null +++ b/res/drawable-mdpi/ic_menuselect_off.png diff --git a/res/drawable-mdpi/ic_menuselect_on.png b/res/drawable-mdpi/ic_menuselect_on.png Binary files differnew file mode 100644 index 0000000..1f027fd --- /dev/null +++ b/res/drawable-mdpi/ic_menuselect_on.png diff --git a/res/drawable-mdpi/ic_menuselect_settings.png b/res/drawable-mdpi/ic_menuselect_settings.png Binary files differnew file mode 100644 index 0000000..495d854 --- /dev/null +++ b/res/drawable-mdpi/ic_menuselect_settings.png diff --git a/res/drawable-mdpi/ic_menuselect_wb_auto.png b/res/drawable-mdpi/ic_menuselect_wb_auto.png Binary files differnew file mode 100644 index 0000000..65f5d18 --- /dev/null +++ b/res/drawable-mdpi/ic_menuselect_wb_auto.png diff --git a/res/drawable-mdpi/ic_menuselect_wb_cloudy.png b/res/drawable-mdpi/ic_menuselect_wb_cloudy.png Binary files differnew file mode 100644 index 0000000..1867c9b --- /dev/null +++ b/res/drawable-mdpi/ic_menuselect_wb_cloudy.png diff --git a/res/drawable-mdpi/ic_menuselect_wb_daylight.png b/res/drawable-mdpi/ic_menuselect_wb_daylight.png Binary files differnew file mode 100644 index 0000000..d16a930 --- /dev/null +++ b/res/drawable-mdpi/ic_menuselect_wb_daylight.png diff --git a/res/drawable-mdpi/ic_menuselect_wb_fluorescent.png b/res/drawable-mdpi/ic_menuselect_wb_fluorescent.png Binary files differnew file mode 100644 index 0000000..b788229 --- /dev/null +++ b/res/drawable-mdpi/ic_menuselect_wb_fluorescent.png diff --git a/res/drawable-mdpi/ic_menuselect_wb_incandescent.png b/res/drawable-mdpi/ic_menuselect_wb_incandescent.png Binary files differnew file mode 100644 index 0000000..af78583 --- /dev/null +++ b/res/drawable-mdpi/ic_menuselect_wb_incandescent.png diff --git a/res/drawable-mdpi/ic_viewfinder_flash_auto.png b/res/drawable-mdpi/ic_viewfinder_flash_auto.png Binary files differindex 79e36b7..4a86340 100755 --- a/res/drawable-mdpi/ic_viewfinder_flash_auto.png +++ b/res/drawable-mdpi/ic_viewfinder_flash_auto.png diff --git a/res/drawable-mdpi/ic_viewfinder_flash_off.png b/res/drawable-mdpi/ic_viewfinder_flash_off.png Binary files differindex d076636..830fb23 100644 --- a/res/drawable-mdpi/ic_viewfinder_flash_off.png +++ b/res/drawable-mdpi/ic_viewfinder_flash_off.png diff --git a/res/drawable-mdpi/ic_viewfinder_flash_on.png b/res/drawable-mdpi/ic_viewfinder_flash_on.png Binary files differindex 91dfe24..8f600e5 100755 --- a/res/drawable-mdpi/ic_viewfinder_flash_on.png +++ b/res/drawable-mdpi/ic_viewfinder_flash_on.png diff --git a/res/drawable-mdpi/ic_viewfinder_focus_auto.png b/res/drawable-mdpi/ic_viewfinder_focus_auto.png Binary files differnew file mode 100644 index 0000000..076b434 --- /dev/null +++ b/res/drawable-mdpi/ic_viewfinder_focus_auto.png diff --git a/res/drawable-mdpi/ic_viewfinder_focus_infinity.png b/res/drawable-mdpi/ic_viewfinder_focus_infinity.png Binary files differindex afa0902..7eb90cb 100644 --- a/res/drawable-mdpi/ic_viewfinder_focus_infinity.png +++ b/res/drawable-mdpi/ic_viewfinder_focus_infinity.png diff --git a/res/drawable-mdpi/ic_viewfinder_focus_macro.png b/res/drawable-mdpi/ic_viewfinder_focus_macro.png Binary files differindex e5c560f..dd3ecb3 100644 --- a/res/drawable-mdpi/ic_viewfinder_focus_macro.png +++ b/res/drawable-mdpi/ic_viewfinder_focus_macro.png diff --git a/res/drawable-mdpi/ic_viewfinder_gps_no_signal.png b/res/drawable-mdpi/ic_viewfinder_gps_no_signal.png Binary files differnew file mode 100644 index 0000000..fbdea07 --- /dev/null +++ b/res/drawable-mdpi/ic_viewfinder_gps_no_signal.png diff --git a/res/drawable-mdpi/ic_viewfinder_gps_off.png b/res/drawable-mdpi/ic_viewfinder_gps_off.png Binary files differnew file mode 100644 index 0000000..918c914 --- /dev/null +++ b/res/drawable-mdpi/ic_viewfinder_gps_off.png diff --git a/res/drawable-mdpi/ic_viewfinder_gps_on.png b/res/drawable-mdpi/ic_viewfinder_gps_on.png Binary files differnew file mode 100644 index 0000000..4bbb47b --- /dev/null +++ b/res/drawable-mdpi/ic_viewfinder_gps_on.png diff --git a/res/drawable-mdpi/ic_viewfinder_iconbar.9.png b/res/drawable-mdpi/ic_viewfinder_iconbar.9.png Binary files differnew file mode 100644 index 0000000..0cd4dfe --- /dev/null +++ b/res/drawable-mdpi/ic_viewfinder_iconbar.9.png diff --git a/res/drawable-mdpi/ic_viewfinder_iconbar_highlight.9.png b/res/drawable-mdpi/ic_viewfinder_iconbar_highlight.9.png Binary files differnew file mode 100644 index 0000000..1b08fdf --- /dev/null +++ b/res/drawable-mdpi/ic_viewfinder_iconbar_highlight.9.png diff --git a/res/drawable-mdpi/ic_viewfinder_settings.png b/res/drawable-mdpi/ic_viewfinder_settings.png Binary files differnew file mode 100644 index 0000000..8687eec --- /dev/null +++ b/res/drawable-mdpi/ic_viewfinder_settings.png diff --git a/res/drawable-mdpi/ic_viewfinder_wb_auto.png b/res/drawable-mdpi/ic_viewfinder_wb_auto.png Binary files differnew file mode 100644 index 0000000..0f18ca5 --- /dev/null +++ b/res/drawable-mdpi/ic_viewfinder_wb_auto.png diff --git a/res/drawable-mdpi/ic_viewfinder_wb_cloudy.png b/res/drawable-mdpi/ic_viewfinder_wb_cloudy.png Binary files differindex 2d02af3..8a45c53 100644 --- a/res/drawable-mdpi/ic_viewfinder_wb_cloudy.png +++ b/res/drawable-mdpi/ic_viewfinder_wb_cloudy.png diff --git a/res/drawable-mdpi/ic_viewfinder_wb_daylight.png b/res/drawable-mdpi/ic_viewfinder_wb_daylight.png Binary files differindex 77e4a6e..35268a0 100644 --- a/res/drawable-mdpi/ic_viewfinder_wb_daylight.png +++ b/res/drawable-mdpi/ic_viewfinder_wb_daylight.png diff --git a/res/drawable-mdpi/ic_viewfinder_wb_fluorescent.png b/res/drawable-mdpi/ic_viewfinder_wb_fluorescent.png Binary files differindex 3a5f6ef..acfd9a7 100644 --- a/res/drawable-mdpi/ic_viewfinder_wb_fluorescent.png +++ b/res/drawable-mdpi/ic_viewfinder_wb_fluorescent.png diff --git a/res/drawable-mdpi/ic_viewfinder_wb_incandescent.png b/res/drawable-mdpi/ic_viewfinder_wb_incandescent.png Binary files differindex cddb4a1..fe5a60f 100644 --- a/res/drawable-mdpi/ic_viewfinder_wb_incandescent.png +++ b/res/drawable-mdpi/ic_viewfinder_wb_incandescent.png diff --git a/res/drawable-mdpi/menu_popup.9.png b/res/drawable-mdpi/menu_popup.9.png Binary files differnew file mode 100644 index 0000000..534d3cf --- /dev/null +++ b/res/drawable-mdpi/menu_popup.9.png diff --git a/res/drawable-mdpi/menu_popup_triangle.png b/res/drawable-mdpi/menu_popup_triangle.png Binary files differnew file mode 100644 index 0000000..59e2f99 --- /dev/null +++ b/res/drawable-mdpi/menu_popup_triangle.png diff --git a/res/drawable-mdpi/on_screen_hint_frame.9.png b/res/drawable-mdpi/on_screen_hint_frame.9.png Binary files differindex 08c4f86..8b78d67 100644 --- a/res/drawable-mdpi/on_screen_hint_frame.9.png +++ b/res/drawable-mdpi/on_screen_hint_frame.9.png diff --git a/res/drawable-mdpi/optionheader_background.9.png b/res/drawable-mdpi/optionheader_background.9.png Binary files differnew file mode 100644 index 0000000..22991e7 --- /dev/null +++ b/res/drawable-mdpi/optionheader_background.9.png diff --git a/res/drawable-mdpi/optionitem_highlight.9.png b/res/drawable-mdpi/optionitem_highlight.9.png Binary files differnew file mode 100644 index 0000000..3a17a86 --- /dev/null +++ b/res/drawable-mdpi/optionitem_highlight.9.png diff --git a/res/drawable-mdpi/scrollbar_handle_vertical.9.png b/res/drawable-mdpi/scrollbar_handle_vertical.9.png Binary files differnew file mode 100644 index 0000000..3ec0791 --- /dev/null +++ b/res/drawable-mdpi/scrollbar_handle_vertical.9.png diff --git a/res/drawable-mdpi/zoom_shape.png b/res/drawable-mdpi/zoom_shape.png Binary files differnew file mode 100644 index 0000000..c3155f7 --- /dev/null +++ b/res/drawable-mdpi/zoom_shape.png diff --git a/res/drawable-mdpi/zoom_slider.png b/res/drawable-mdpi/zoom_slider.png Binary files differnew file mode 100644 index 0000000..c9f7c5b --- /dev/null +++ b/res/drawable-mdpi/zoom_slider.png diff --git a/res/drawable-mdpi/zoom_tickmarks.png b/res/drawable-mdpi/zoom_tickmarks.png Binary files differnew file mode 100644 index 0000000..32f207e --- /dev/null +++ b/res/drawable-mdpi/zoom_tickmarks.png 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); + } +} |