diff --git a/product/modules/agents/android/client/AndroidManifest.xml b/product/modules/agents/android/client/AndroidManifest.xml
new file mode 100644
index 0000000000..5b7a6fdd95
--- /dev/null
+++ b/product/modules/agents/android/client/AndroidManifest.xml
@@ -0,0 +1,183 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/README.md b/product/modules/agents/android/client/README.md
new file mode 100644
index 0000000000..b66bd049b9
--- /dev/null
+++ b/product/modules/agents/android/client/README.md
@@ -0,0 +1,18 @@
+WSO2 EMM Agent
+=================
+
+Configure and build the Android client application
+----------------------
+Follow the instructions below to configure and build the Android client application:
+
+1. Get a Git clone of the project.
+2. Download Android ADT plugin and configure it in your Eclipse.
+3. Open the project in your Eclipse IDE.
+4. Import the project as an Android project using "File-->Import-->Existing Android Code Into Workspace"
+5. Two projects will show, a library and the agent. Clean the Library first and build it.
+6. Open the file properties of the Agent project.
+7. Under "Android" scroll down (past the Build targets).
+8. The library project will show with a red "X" next to it. Remove it.
+9. Add the library project you just built in step 3
+10. Ensure the Library is also on your "Java Build Path" under Libraries.
+11. Clean and build.
diff --git a/product/modules/agents/android/client/assets/config.properties b/product/modules/agents/android/client/assets/config.properties
new file mode 100644
index 0000000000..7d9009c35f
--- /dev/null
+++ b/product/modules/agents/android/client/assets/config.properties
@@ -0,0 +1 @@
+SHOP_URL=""
\ No newline at end of file
diff --git a/product/modules/agents/android/client/bin/AndroidManifest.xml b/product/modules/agents/android/client/bin/AndroidManifest.xml
new file mode 100644
index 0000000000..5b7a6fdd95
--- /dev/null
+++ b/product/modules/agents/android/client/bin/AndroidManifest.xml
@@ -0,0 +1,183 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/bin/R.txt b/product/modules/agents/android/client/bin/R.txt
new file mode 100644
index 0000000000..3f5cf9fc6b
--- /dev/null
+++ b/product/modules/agents/android/client/bin/R.txt
@@ -0,0 +1,911 @@
+int attr actionBarDivider 0x7f01000e
+int attr actionBarItemBackground 0x7f01000f
+int attr actionBarSize 0x7f01000d
+int attr actionBarSplitStyle 0x7f01000b
+int attr actionBarStyle 0x7f01000a
+int attr actionBarTabBarStyle 0x7f010007
+int attr actionBarTabStyle 0x7f010006
+int attr actionBarTabTextStyle 0x7f010008
+int attr actionBarWidgetTheme 0x7f01000c
+int attr actionButtonStyle 0x7f01003a
+int attr actionDropDownStyle 0x7f010039
+int attr actionMenuTextAppearance 0x7f010010
+int attr actionMenuTextColor 0x7f010011
+int attr actionModeBackground 0x7f010014
+int attr actionModeCloseButtonStyle 0x7f010013
+int attr actionModeCloseDrawable 0x7f010016
+int attr actionModePopupWindowStyle 0x7f010018
+int attr actionModeShareDrawable 0x7f010017
+int attr actionModeSplitBackground 0x7f010015
+int attr actionModeStyle 0x7f010012
+int attr actionOverflowButtonStyle 0x7f010009
+int attr actionSpinnerItemStyle 0x7f01003f
+int attr activatedBackgroundIndicator 0x7f010047
+int attr activityChooserViewStyle 0x7f010046
+int attr background 0x7f010002
+int attr backgroundSplit 0x7f010003
+int attr backgroundStacked 0x7f01004e
+int attr buttonStyleSmall 0x7f010019
+int attr customNavigationLayout 0x7f01004f
+int attr displayOptions 0x7f010049
+int attr divider 0x7f010005
+int attr dividerVertical 0x7f010038
+int attr dropDownListViewStyle 0x7f01003c
+int attr dropdownListPreferredItemHeight 0x7f01003e
+int attr expandActivityOverflowButtonDrawable 0x7f01005e
+int attr headerBackground 0x7f010058
+int attr height 0x7f010004
+int attr homeAsUpIndicator 0x7f01003b
+int attr homeLayout 0x7f010050
+int attr horizontalDivider 0x7f010056
+int attr icon 0x7f01004c
+int attr iconifiedByDefault 0x7f01005f
+int attr indeterminateProgressStyle 0x7f010052
+int attr initialActivityCount 0x7f01005d
+int attr itemBackground 0x7f010059
+int attr itemIconDisabledAlpha 0x7f01005b
+int attr itemPadding 0x7f010054
+int attr itemTextAppearance 0x7f010055
+int attr listPopupWindowStyle 0x7f010045
+int attr listPreferredItemHeightSmall 0x7f010032
+int attr listPreferredItemPaddingLeft 0x7f010033
+int attr listPreferredItemPaddingRight 0x7f010034
+int attr logo 0x7f01004d
+int attr navigationMode 0x7f010048
+int attr popupMenuStyle 0x7f01003d
+int attr preserveIconSpacing 0x7f01005c
+int attr progressBarPadding 0x7f010053
+int attr progressBarStyle 0x7f010051
+int attr queryHint 0x7f010060
+int attr searchAutoCompleteTextView 0x7f010024
+int attr searchDropdownBackground 0x7f010025
+int attr searchResultListItemHeight 0x7f01002f
+int attr searchViewCloseIcon 0x7f010026
+int attr searchViewEditQuery 0x7f01002a
+int attr searchViewEditQueryBackground 0x7f01002b
+int attr searchViewGoIcon 0x7f010027
+int attr searchViewSearchIcon 0x7f010028
+int attr searchViewTextField 0x7f01002c
+int attr searchViewTextFieldRight 0x7f01002d
+int attr searchViewVoiceIcon 0x7f010029
+int attr selectableItemBackground 0x7f01001a
+int attr spinnerDropDownItemStyle 0x7f010023
+int attr spinnerItemStyle 0x7f010022
+int attr subtitle 0x7f01004b
+int attr subtitleTextStyle 0x7f010001
+int attr textAppearanceLargePopupMenu 0x7f01001c
+int attr textAppearanceListItemSmall 0x7f010035
+int attr textAppearanceSearchResultSubtitle 0x7f010031
+int attr textAppearanceSearchResultTitle 0x7f010030
+int attr textAppearanceSmall 0x7f01001e
+int attr textAppearanceSmallPopupMenu 0x7f01001d
+int attr textColorPrimary 0x7f01001f
+int attr textColorPrimaryDisableOnly 0x7f010020
+int attr textColorPrimaryInverse 0x7f010021
+int attr textColorSearchUrl 0x7f01002e
+int attr title 0x7f01004a
+int attr titleTextStyle 0x7f010000
+int attr verticalDivider 0x7f010057
+int attr windowActionBar 0x7f010041
+int attr windowActionBarOverlay 0x7f010042
+int attr windowActionModeOverlay 0x7f010043
+int attr windowAnimationStyle 0x7f01005a
+int attr windowContentOverlay 0x7f01001b
+int attr windowMinWidthMajor 0x7f010036
+int attr windowMinWidthMinor 0x7f010037
+int attr windowNoTitle 0x7f010040
+int attr windowSplitActionBar 0x7f010044
+int bool abs__action_bar_embed_tabs 0x7f070000
+int bool abs__action_bar_expanded_action_views_exclusive 0x7f070002
+int bool abs__config_actionMenuItemAllCaps 0x7f070004
+int bool abs__config_allowActionMenuItemTextWithIcon 0x7f070005
+int bool abs__config_showMenuShortcutsWhenKeyboardPresent 0x7f070003
+int bool abs__split_action_bar_is_narrow 0x7f070001
+int color abs__background_holo_dark 0x7f080000
+int color abs__background_holo_light 0x7f080001
+int color abs__bright_foreground_disabled_holo_dark 0x7f080004
+int color abs__bright_foreground_disabled_holo_light 0x7f080005
+int color abs__bright_foreground_holo_dark 0x7f080002
+int color abs__bright_foreground_holo_light 0x7f080003
+int color abs__bright_foreground_inverse_holo_dark 0x7f080006
+int color abs__bright_foreground_inverse_holo_light 0x7f080007
+int color abs__holo_blue_light 0x7f080008
+int color abs__primary_text_disable_only_holo_dark 0x7f08000c
+int color abs__primary_text_disable_only_holo_light 0x7f08000d
+int color abs__primary_text_holo_dark 0x7f08000e
+int color abs__primary_text_holo_light 0x7f08000f
+int color black 0x7f08000b
+int color light_grey 0x7f080009
+int color white 0x7f08000a
+int dimen abs__action_bar_default_height 0x7f090001
+int dimen abs__action_bar_icon_vertical_padding 0x7f090002
+int dimen abs__action_bar_subtitle_bottom_margin 0x7f090006
+int dimen abs__action_bar_subtitle_text_size 0x7f090004
+int dimen abs__action_bar_subtitle_top_margin 0x7f090005
+int dimen abs__action_bar_title_text_size 0x7f090003
+int dimen abs__action_button_min_width 0x7f090007
+int dimen abs__alert_dialog_title_height 0x7f090008
+int dimen abs__config_prefDialogWidth 0x7f090000
+int dimen abs__dialog_min_width_major 0x7f090009
+int dimen abs__dialog_min_width_minor 0x7f09000a
+int dimen abs__dropdownitem_icon_width 0x7f09000d
+int dimen abs__dropdownitem_text_padding_left 0x7f09000b
+int dimen abs__dropdownitem_text_padding_right 0x7f09000c
+int dimen abs__search_view_preferred_width 0x7f09000f
+int dimen abs__search_view_text_min_width 0x7f09000e
+int dimen action_button_min_width 0x7f090010
+int dimen activity_horizontal_margin 0x7f090011
+int dimen activity_vertical_margin 0x7f090012
+int dimen top_bar_height 0x7f090013
+int drawable abs__ab_bottom_solid_dark_holo 0x7f020000
+int drawable abs__ab_bottom_solid_inverse_holo 0x7f020001
+int drawable abs__ab_bottom_solid_light_holo 0x7f020002
+int drawable abs__ab_bottom_transparent_dark_holo 0x7f020003
+int drawable abs__ab_bottom_transparent_light_holo 0x7f020004
+int drawable abs__ab_share_pack_holo_dark 0x7f020005
+int drawable abs__ab_share_pack_holo_light 0x7f020006
+int drawable abs__ab_solid_dark_holo 0x7f020007
+int drawable abs__ab_solid_light_holo 0x7f020008
+int drawable abs__ab_solid_shadow_holo 0x7f020009
+int drawable abs__ab_stacked_solid_dark_holo 0x7f02000a
+int drawable abs__ab_stacked_solid_light_holo 0x7f02000b
+int drawable abs__ab_stacked_transparent_dark_holo 0x7f02000c
+int drawable abs__ab_stacked_transparent_light_holo 0x7f02000d
+int drawable abs__ab_transparent_dark_holo 0x7f02000e
+int drawable abs__ab_transparent_light_holo 0x7f02000f
+int drawable abs__activated_background_holo_dark 0x7f020010
+int drawable abs__activated_background_holo_light 0x7f020011
+int drawable abs__btn_cab_done_default_holo_dark 0x7f020012
+int drawable abs__btn_cab_done_default_holo_light 0x7f020013
+int drawable abs__btn_cab_done_focused_holo_dark 0x7f020014
+int drawable abs__btn_cab_done_focused_holo_light 0x7f020015
+int drawable abs__btn_cab_done_holo_dark 0x7f020016
+int drawable abs__btn_cab_done_holo_light 0x7f020017
+int drawable abs__btn_cab_done_pressed_holo_dark 0x7f020018
+int drawable abs__btn_cab_done_pressed_holo_light 0x7f020019
+int drawable abs__cab_background_bottom_holo_dark 0x7f02001a
+int drawable abs__cab_background_bottom_holo_light 0x7f02001b
+int drawable abs__cab_background_top_holo_dark 0x7f02001c
+int drawable abs__cab_background_top_holo_light 0x7f02001d
+int drawable abs__dialog_full_holo_dark 0x7f02001e
+int drawable abs__dialog_full_holo_light 0x7f02001f
+int drawable abs__ic_ab_back_holo_dark 0x7f020020
+int drawable abs__ic_ab_back_holo_light 0x7f020021
+int drawable abs__ic_cab_done_holo_dark 0x7f020022
+int drawable abs__ic_cab_done_holo_light 0x7f020023
+int drawable abs__ic_clear 0x7f020024
+int drawable abs__ic_clear_disabled 0x7f020025
+int drawable abs__ic_clear_holo_light 0x7f020026
+int drawable abs__ic_clear_normal 0x7f020027
+int drawable abs__ic_clear_search_api_disabled_holo_light 0x7f020028
+int drawable abs__ic_clear_search_api_holo_light 0x7f020029
+int drawable abs__ic_go 0x7f02002a
+int drawable abs__ic_go_search_api_holo_light 0x7f02002b
+int drawable abs__ic_menu_moreoverflow_holo_dark 0x7f02002c
+int drawable abs__ic_menu_moreoverflow_holo_light 0x7f02002d
+int drawable abs__ic_menu_moreoverflow_normal_holo_dark 0x7f02002e
+int drawable abs__ic_menu_moreoverflow_normal_holo_light 0x7f02002f
+int drawable abs__ic_menu_share_holo_dark 0x7f020030
+int drawable abs__ic_menu_share_holo_light 0x7f020031
+int drawable abs__ic_search 0x7f020032
+int drawable abs__ic_search_api_holo_light 0x7f020033
+int drawable abs__ic_voice_search 0x7f020034
+int drawable abs__ic_voice_search_api_holo_light 0x7f020035
+int drawable abs__item_background_holo_dark 0x7f020036
+int drawable abs__item_background_holo_light 0x7f020037
+int drawable abs__list_activated_holo 0x7f020038
+int drawable abs__list_divider_holo_dark 0x7f020039
+int drawable abs__list_divider_holo_light 0x7f02003a
+int drawable abs__list_focused_holo 0x7f02003b
+int drawable abs__list_longpressed_holo 0x7f02003c
+int drawable abs__list_pressed_holo_dark 0x7f02003d
+int drawable abs__list_pressed_holo_light 0x7f02003e
+int drawable abs__list_selector_background_transition_holo_dark 0x7f02003f
+int drawable abs__list_selector_background_transition_holo_light 0x7f020040
+int drawable abs__list_selector_disabled_holo_dark 0x7f020041
+int drawable abs__list_selector_disabled_holo_light 0x7f020042
+int drawable abs__list_selector_holo_dark 0x7f020043
+int drawable abs__list_selector_holo_light 0x7f020044
+int drawable abs__menu_dropdown_panel_holo_dark 0x7f020045
+int drawable abs__menu_dropdown_panel_holo_light 0x7f020046
+int drawable abs__progress_bg_holo_dark 0x7f020047
+int drawable abs__progress_bg_holo_light 0x7f020048
+int drawable abs__progress_horizontal_holo_dark 0x7f020049
+int drawable abs__progress_horizontal_holo_light 0x7f02004a
+int drawable abs__progress_medium_holo 0x7f02004b
+int drawable abs__progress_primary_holo_dark 0x7f02004c
+int drawable abs__progress_primary_holo_light 0x7f02004d
+int drawable abs__progress_secondary_holo_dark 0x7f02004e
+int drawable abs__progress_secondary_holo_light 0x7f02004f
+int drawable abs__search_dropdown_dark 0x7f020050
+int drawable abs__search_dropdown_light 0x7f020051
+int drawable abs__spinner_48_inner_holo 0x7f020052
+int drawable abs__spinner_48_outer_holo 0x7f020053
+int drawable abs__spinner_ab_default_holo_dark 0x7f020054
+int drawable abs__spinner_ab_default_holo_light 0x7f020055
+int drawable abs__spinner_ab_disabled_holo_dark 0x7f020056
+int drawable abs__spinner_ab_disabled_holo_light 0x7f020057
+int drawable abs__spinner_ab_focused_holo_dark 0x7f020058
+int drawable abs__spinner_ab_focused_holo_light 0x7f020059
+int drawable abs__spinner_ab_holo_dark 0x7f02005a
+int drawable abs__spinner_ab_holo_light 0x7f02005b
+int drawable abs__spinner_ab_pressed_holo_dark 0x7f02005c
+int drawable abs__spinner_ab_pressed_holo_light 0x7f02005d
+int drawable abs__tab_indicator_ab_holo 0x7f02005e
+int drawable abs__tab_selected_focused_holo 0x7f02005f
+int drawable abs__tab_selected_holo 0x7f020060
+int drawable abs__tab_selected_pressed_holo 0x7f020061
+int drawable abs__tab_unselected_pressed_holo 0x7f020062
+int drawable abs__textfield_search_default_holo_dark 0x7f020063
+int drawable abs__textfield_search_default_holo_light 0x7f020064
+int drawable abs__textfield_search_right_default_holo_dark 0x7f020065
+int drawable abs__textfield_search_right_default_holo_light 0x7f020066
+int drawable abs__textfield_search_right_selected_holo_dark 0x7f020067
+int drawable abs__textfield_search_right_selected_holo_light 0x7f020068
+int drawable abs__textfield_search_selected_holo_dark 0x7f020069
+int drawable abs__textfield_search_selected_holo_light 0x7f02006a
+int drawable abs__textfield_searchview_holo_dark 0x7f02006b
+int drawable abs__textfield_searchview_holo_light 0x7f02006c
+int drawable abs__textfield_searchview_right_holo_dark 0x7f02006d
+int drawable abs__textfield_searchview_right_holo_light 0x7f02006e
+int drawable appinstall 0x7f02006f
+int drawable applist 0x7f020070
+int drawable appuninstall 0x7f020071
+int drawable btn_grey 0x7f020072
+int drawable btn_orange 0x7f020073
+int drawable camera 0x7f020074
+int drawable changepassword 0x7f020075
+int drawable custom_checkbox 0x7f020076
+int drawable dot 0x7f020077
+int drawable encrypt 0x7f020078
+int drawable ic_bookmark 0x7f020079
+int drawable ic_check_default 0x7f02007a
+int drawable ic_check_selected 0x7f02007b
+int drawable ic_launcher 0x7f02007c
+int drawable ic_logo 0x7f02007d
+int drawable ic_logo_dark 0x7f02007e
+int drawable ic_stat_gcm 0x7f02007f
+int drawable info 0x7f020080
+int drawable location 0x7f020081
+int drawable lock 0x7f020082
+int drawable mdm_logo 0x7f020083
+int drawable mute 0x7f020084
+int drawable notification 0x7f020085
+int drawable option_icon 0x7f020086
+int drawable repeat_bg 0x7f020087
+int drawable top_bar 0x7f020088
+int drawable wifi 0x7f020089
+int drawable wipe 0x7f02008a
+int id TextView01 0x7f060091
+int id abs__action_bar 0x7f06004e
+int id abs__action_bar_container 0x7f06004d
+int id abs__action_bar_subtitle 0x7f06003d
+int id abs__action_bar_title 0x7f06003c
+int id abs__action_context_bar 0x7f06004f
+int id abs__action_menu_divider 0x7f06000c
+int id abs__action_menu_presenter 0x7f06000d
+int id abs__action_mode_bar 0x7f060052
+int id abs__action_mode_bar_stub 0x7f060051
+int id abs__action_mode_close_button 0x7f060040
+int id abs__activity_chooser_view_content 0x7f060041
+int id abs__checkbox 0x7f06004a
+int id abs__content 0x7f060049
+int id abs__default_activity_button 0x7f060044
+int id abs__expand_activities_button 0x7f060042
+int id abs__home 0x7f06000a
+int id abs__icon 0x7f060046
+int id abs__image 0x7f060043
+int id abs__imageButton 0x7f06003e
+int id abs__list_item 0x7f060045
+int id abs__progress_circular 0x7f06000e
+int id abs__progress_horizontal 0x7f06000f
+int id abs__radio 0x7f06004c
+int id abs__search_badge 0x7f060055
+int id abs__search_bar 0x7f060054
+int id abs__search_button 0x7f060056
+int id abs__search_close_btn 0x7f06005b
+int id abs__search_edit_frame 0x7f060057
+int id abs__search_go_btn 0x7f06005d
+int id abs__search_mag_icon 0x7f060058
+int id abs__search_plate 0x7f060059
+int id abs__search_src_text 0x7f06005a
+int id abs__search_voice_btn 0x7f06005e
+int id abs__shortcut 0x7f06004b
+int id abs__split_action_bar 0x7f060050
+int id abs__submit_area 0x7f06005c
+int id abs__textButton 0x7f06003f
+int id abs__title 0x7f060047
+int id abs__titleDivider 0x7f060048
+int id abs__up 0x7f06000b
+int id action_settings 0x7f060095
+int id background_container 0x7f06001f
+int id blocks_now 0x7f06001e
+int id blocks_ruler 0x7f06001d
+int id btnEnroll 0x7f060082
+int id btnLogin 0x7f060090
+int id btnOK 0x7f060064
+int id btnRefresh 0x7f06007d
+int id btnRegister 0x7f060070
+int id btnReset 0x7f06007e
+int id btnSetPin 0x7f060087
+int id btnStartRegistration 0x7f06008b
+int id btnTryAgain 0x7f060073
+int id btnUnreg 0x7f060065
+int id btnUnregister 0x7f060088
+int id button_layout 0x7f06008d
+int id debug_log 0x7f06009b
+int id dialogButtonCancel 0x7f06008f
+int id dialogButtonOK 0x7f06008e
+int id dialog_discard_confirm 0x7f06001a
+int id dialog_moderator 0x7f06001b
+int id dialog_wave 0x7f06001c
+int id disableHome 0x7f060009
+int id edit_query 0x7f060053
+int id enrollPanel 0x7f060081
+int id error 0x7f060072
+int id etDomain 0x7f06006a
+int id etPassword 0x7f06006c
+int id etUsername 0x7f06006b
+int id evServerIP 0x7f06008a
+int id footer 0x7f060071
+int id footerlogo 0x7f060068
+int id fragment_container 0x7f060034
+int id gridview 0x7f060014
+int id homeAsUp 0x7f060006
+int id incompatibleError 0x7f060074
+int id info 0x7f060098
+int id info_setting 0x7f060099
+int id ip_setting 0x7f060097
+int id layout_topbar 0x7f060067
+int id lblPin 0x7f060084
+int id linInner 0x7f060062
+int id linearLayout1 0x7f060069
+int id linearLayoutText 0x7f060080
+int id listMode 0x7f060002
+int id listview 0x7f060060
+int id logo 0x7f06005f
+int id more 0x7f060096
+int id normal 0x7f060001
+int id notify 0x7f060092
+int id option_button 0x7f06007c
+int id pin_setting 0x7f06009a
+int id preference_brand_view 0x7f06002d
+int id preference_empty_view 0x7f06002c
+int id radioBYOD 0x7f06006e
+int id radioCOPE 0x7f06006f
+int id radioGroupType 0x7f06006d
+int id rowImage 0x7f060093
+int id rowTextView 0x7f060094
+int id scroller 0x7f060061
+int id setting_invite_email_button 0x7f060030
+int id setting_invite_email_edittext 0x7f06002f
+int id setting_invite_email_imageview 0x7f060031
+int id setting_invite_email_layout 0x7f06002e
+int id setting_invite_email_textview 0x7f060032
+int id sg_button1 0x7f060039
+int id sg_button2 0x7f06003a
+int id sg_button3 0x7f06003b
+int id sg_category_popup 0x7f060035
+int id sg_city 0x7f060038
+int id sg_tag_command 0x7f060036
+int id sg_tag_payload 0x7f060037
+int id showCustom 0x7f060008
+int id showHome 0x7f060005
+int id showTitle 0x7f060007
+int id sp_edittext_city 0x7f060025
+int id sp_edittext_email 0x7f060023
+int id sp_edittext_location 0x7f060022
+int id sp_edittext_name 0x7f060021
+int id sp_edittext_other 0x7f06002a
+int id sp_edittext_phone 0x7f060029
+int id sp_edittext_state 0x7f060026
+int id sp_edittext_street 0x7f060024
+int id sp_edittext_zipcode 0x7f060027
+int id sp_textview_country 0x7f060028
+int id sp_textview_gpspick 0x7f06002b
+int id startRegistration 0x7f060083
+int id swipeable 0x7f060013
+int id swipeable_bottom 0x7f060010
+int id swipeable_container 0x7f060012
+int id swipeable_top 0x7f060011
+int id tabMode 0x7f060003
+int id text 0x7f06008c
+int id title_bar_layout 0x7f060020
+int id title_container 0x7f060016
+int id title_logo 0x7f060017
+int id title_option 0x7f060019
+int id title_text 0x7f060018
+int id tvSeverAddress 0x7f060089
+int id txtDevice 0x7f060076
+int id txtId 0x7f060075
+int id txtLog 0x7f06007f
+int id txtMessage 0x7f060063
+int id txtModel 0x7f060077
+int id txtOS 0x7f06007a
+int id txtOldPinCode 0x7f060085
+int id txtOperator 0x7f060078
+int id txtPinCode 0x7f060086
+int id txtRegText 0x7f060066
+int id txtRoot 0x7f06007b
+int id txtSDK 0x7f060079
+int id useLogo 0x7f060004
+int id user_edit_location 0x7f060033
+int id webview 0x7f060015
+int id wrap_content 0x7f060000
+int integer abs__max_action_buttons 0x7f0a0000
+int layout abs__action_bar_home 0x7f030000
+int layout abs__action_bar_tab 0x7f030001
+int layout abs__action_bar_tab_bar_view 0x7f030002
+int layout abs__action_bar_title_item 0x7f030003
+int layout abs__action_menu_item_layout 0x7f030004
+int layout abs__action_menu_layout 0x7f030005
+int layout abs__action_mode_bar 0x7f030006
+int layout abs__action_mode_close_item 0x7f030007
+int layout abs__activity_chooser_view 0x7f030008
+int layout abs__activity_chooser_view_list_item 0x7f030009
+int layout abs__dialog_title_holo 0x7f03000a
+int layout abs__list_menu_item_checkbox 0x7f03000b
+int layout abs__list_menu_item_icon 0x7f03000c
+int layout abs__list_menu_item_layout 0x7f03000d
+int layout abs__list_menu_item_radio 0x7f03000e
+int layout abs__popup_menu_item_layout 0x7f03000f
+int layout abs__screen_action_bar 0x7f030010
+int layout abs__screen_action_bar_overlay 0x7f030011
+int layout abs__screen_simple 0x7f030012
+int layout abs__screen_simple_overlay_action_mode 0x7f030013
+int layout abs__search_dropdown_item_icons_2line 0x7f030014
+int layout abs__search_view 0x7f030015
+int layout abs__simple_dropdown_hint 0x7f030016
+int layout activity_agent_settings 0x7f030017
+int layout activity_alert 0x7f030018
+int layout activity_already_registered 0x7f030019
+int layout activity_authentication 0x7f03001a
+int layout activity_authentication_error 0x7f03001b
+int layout activity_available_operations 0x7f03001c
+int layout activity_display_device_info 0x7f03001d
+int layout activity_entry 0x7f03001e
+int layout activity_log 0x7f03001f
+int layout activity_main 0x7f030020
+int layout activity_notification 0x7f030021
+int layout activity_pin_code 0x7f030022
+int layout activity_register_successful 0x7f030023
+int layout activity_settings 0x7f030024
+int layout custom_sherlock_bar 0x7f030025
+int layout custom_terms_popup 0x7f030026
+int layout footer_repeat 0x7f030027
+int layout header_gradient 0x7f030028
+int layout login 0x7f030029
+int layout main 0x7f03002a
+int layout notify 0x7f03002b
+int layout row_with_icon 0x7f03002c
+int layout sherlock_spinner_dropdown_item 0x7f03002d
+int layout sherlock_spinner_item 0x7f03002e
+int layout simplerow 0x7f03002f
+int menu agent_settings 0x7f0d0000
+int menu alert 0x7f0d0001
+int menu all_ready_registered 0x7f0d0002
+int menu auth_sherlock_menu 0x7f0d0003
+int menu authentication 0x7f0d0004
+int menu authentication_error 0x7f0d0005
+int menu available_operations 0x7f0d0006
+int menu display_device_info 0x7f0d0007
+int menu entry 0x7f0d0008
+int menu log 0x7f0d0009
+int menu main 0x7f0d000a
+int menu notification 0x7f0d000b
+int menu notify 0x7f0d000c
+int menu options_menu 0x7f0d000d
+int menu pin_code 0x7f0d000e
+int menu register_successful 0x7f0d000f
+int menu settings 0x7f0d0010
+int menu sherlock_menu 0x7f0d0011
+int menu sherlock_menu_debug 0x7f0d0012
+int raw emm_truststore 0x7f050000
+int string abs__action_bar_home_description 0x7f0b0000
+int string abs__action_bar_up_description 0x7f0b0001
+int string abs__action_menu_overflow_description 0x7f0b0002
+int string abs__action_mode_done 0x7f0b0003
+int string abs__activity_chooser_view_dialog_title_default 0x7f0b0005
+int string abs__activity_chooser_view_see_all 0x7f0b0004
+int string abs__activitychooserview_choose_application 0x7f0b0007
+int string abs__searchview_description_clear 0x7f0b000c
+int string abs__searchview_description_query 0x7f0b000b
+int string abs__searchview_description_search 0x7f0b000a
+int string abs__searchview_description_submit 0x7f0b000d
+int string abs__searchview_description_voice 0x7f0b000e
+int string abs__share_action_provider_share_with 0x7f0b0006
+int string abs__shareactionprovider_share_with 0x7f0b0008
+int string abs__shareactionprovider_share_with_application 0x7f0b0009
+int string action_settings 0x7f0b0025
+int string already_registered 0x7f0b0012
+int string app_name 0x7f0b0023
+int string application_mgr_download_file_name 0x7f0b0097
+int string application_mgr_download_location 0x7f0b0096
+int string application_mgr_mime 0x7f0b0098
+int string application_package_launcher_action 0x7f0b009a
+int string application_package_prefix 0x7f0b0099
+int string button_cancel 0x7f0b0095
+int string button_ok 0x7f0b0094
+int string client_id 0x7f0b000f
+int string client_secreat 0x7f0b0010
+int string device_admin_disabled 0x7f0b0030
+int string device_admin_enable_alert 0x7f0b003f
+int string device_admin_enabled 0x7f0b002f
+int string device_compatible 0x7f0b0033
+int string device_enroll_type_byod 0x7f0b0040
+int string device_enroll_type_cope 0x7f0b0041
+int string device_not_compatible_error 0x7f0b0031
+int string device_not_compatible_error_os 0x7f0b0032
+int string device_not_compatible_error_root 0x7f0b0034
+int string dialog_authenticate 0x7f0b0075
+int string dialog_checking_reg 0x7f0b0077
+int string dialog_enrolling 0x7f0b007a
+int string dialog_init_confirmation 0x7f0b007d
+int string dialog_init_device_type 0x7f0b007f
+int string dialog_init_end 0x7f0b0080
+int string dialog_init_end_general 0x7f0b0081
+int string dialog_init_middle 0x7f0b007e
+int string dialog_license_agreement 0x7f0b0079
+int string dialog_message_please_wait 0x7f0b0089
+int string dialog_message_unregistering 0x7f0b0088
+int string dialog_pin_confirmation 0x7f0b007b
+int string dialog_pin_confirmation_end 0x7f0b007c
+int string dialog_please_wait 0x7f0b0076
+int string dialog_sender_id 0x7f0b0078
+int string dialog_unregister 0x7f0b0082
+int string empty_app_title 0x7f0b0024
+int string error_auth_failed_detail 0x7f0b006c
+int string error_authentication_failed 0x7f0b006d
+int string error_authorization_failed 0x7f0b0069
+int string error_config 0x7f0b0011
+int string error_connect_to_server 0x7f0b0065
+int string error_enrollment_failed 0x7f0b006a
+int string error_enrollment_failed_detail 0x7f0b006b
+int string error_for_all_unknown_authentication_failures 0x7f0b0070
+int string error_for_all_unknown_notification_failures 0x7f0b0072
+int string error_for_all_unknown_registration_failures 0x7f0b006f
+int string error_for_all_unknown_unregister_failures 0x7f0b0071
+int string error_heading_connection 0x7f0b0066
+int string error_internal_server 0x7f0b0073
+int string error_invalid_server_address 0x7f0b0074
+int string error_network_unavailable 0x7f0b006e
+int string error_registration_failed 0x7f0b0067
+int string error_unregistration_failed 0x7f0b0068
+int string gcm_deleted 0x7f0b0018
+int string gcm_error 0x7f0b0016
+int string gcm_message 0x7f0b0015
+int string gcm_recoverable_error 0x7f0b0017
+int string gcm_registered 0x7f0b0013
+int string gcm_unregistered 0x7f0b0014
+int string hello_world 0x7f0b0026
+int string hint_new_pin 0x7f0b0045
+int string info_label_device 0x7f0b008b
+int string info_label_imei 0x7f0b008a
+int string info_label_imsi 0x7f0b008f
+int string info_label_model 0x7f0b008c
+int string info_label_no_sim 0x7f0b008d
+int string info_label_operator 0x7f0b008e
+int string info_label_os 0x7f0b0090
+int string info_label_rooted 0x7f0b0091
+int string intent_extra_fresh_reg_flag 0x7f0b004f
+int string intent_extra_from_activity 0x7f0b004b
+int string intent_extra_main_activity 0x7f0b004d
+int string intent_extra_message 0x7f0b004e
+int string intent_extra_notification 0x7f0b0051
+int string intent_extra_regid 0x7f0b004c
+int string intent_extra_username 0x7f0b0050
+int string menu_item_change_ip 0x7f0b0087
+int string menu_item_change_pin 0x7f0b0086
+int string menu_item_ip 0x7f0b003c
+int string menu_item_log 0x7f0b003d
+int string menu_item_operations 0x7f0b003a
+int string menu_item_phone_info 0x7f0b0085
+int string menu_item_pin 0x7f0b003b
+int string no 0x7f0b0093
+int string options_clear 0x7f0b0021
+int string options_exit 0x7f0b0022
+int string options_register 0x7f0b001e
+int string options_unregister 0x7f0b001f
+int string register_button_text 0x7f0b0083
+int string register_text_view_text_unregister 0x7f0b0084
+int string registration_heading 0x7f0b009e
+int string server_register_error 0x7f0b001c
+int string server_registered 0x7f0b001a
+int string server_registering 0x7f0b0019
+int string server_unregister_error 0x7f0b001d
+int string server_unregistered 0x7f0b001b
+int string server_util_req_type_get 0x7f0b009c
+int string server_util_req_type_post 0x7f0b009b
+int string shared_pref_client_id 0x7f0b0063
+int string shared_pref_client_secret 0x7f0b0064
+int string shared_pref_device_active 0x7f0b0062
+int string shared_pref_eula 0x7f0b0057
+int string shared_pref_interval 0x7f0b0060
+int string shared_pref_ip 0x7f0b0056
+int string shared_pref_isagreed 0x7f0b0054
+int string shared_pref_message_mode 0x7f0b005f
+int string shared_pref_package 0x7f0b0052
+int string shared_pref_pin 0x7f0b005a
+int string shared_pref_policy 0x7f0b0053
+int string shared_pref_regId 0x7f0b0058
+int string shared_pref_reg_fail 0x7f0b005c
+int string shared_pref_reg_success 0x7f0b005b
+int string shared_pref_reg_type 0x7f0b005d
+int string shared_pref_registered 0x7f0b0055
+int string shared_pref_sender_id 0x7f0b005e
+int string shared_pref_username 0x7f0b0059
+int string string_content 0x7f0b0020
+int string title_activity_agent_settings 0x7f0b0038
+int string title_activity_alert 0x7f0b003e
+int string title_activity_authentication_error 0x7f0b0035
+int string title_activity_available_operations 0x7f0b0039
+int string title_activity_display_device_info 0x7f0b0027
+int string title_activity_entry 0x7f0b002a
+int string title_activity_log 0x7f0b009d
+int string title_activity_notification 0x7f0b0036
+int string title_activity_notify 0x7f0b0037
+int string title_activity_register_successful 0x7f0b0029
+int string title_head_authentication_error 0x7f0b002b
+int string title_head_confirm_pin 0x7f0b0044
+int string title_head_connection_error 0x7f0b002d
+int string title_head_init_error 0x7f0b0043
+int string title_head_notification_error 0x7f0b002e
+int string title_head_registration_error 0x7f0b002c
+int string title_init_msg_error 0x7f0b0042
+int string toast_error_password 0x7f0b004a
+int string toast_error_username 0x7f0b0049
+int string toast_message_enter_server_address 0x7f0b0048
+int string toast_message_pin_change_failed 0x7f0b0047
+int string toast_message_pin_change_success 0x7f0b0046
+int string url 0x7f0b0028
+int string username 0x7f0b0061
+int string validation_pin_confirm 0x7f0b009f
+int string yes 0x7f0b0092
+int style AppBaseTheme 0x7f0c0059
+int style AppTheme 0x7f0c005a
+int style ButtonText 0x7f0c005f
+int style DialogWindowTitle_Sherlock 0x7f0c0036
+int style DialogWindowTitle_Sherlock_Light 0x7f0c0037
+int style Sherlock___TextAppearance_Small 0x7f0c004a
+int style Sherlock___Theme 0x7f0c004e
+int style Sherlock___Theme_DarkActionBar 0x7f0c0050
+int style Sherlock___Theme_Dialog 0x7f0c0051
+int style Sherlock___Theme_Light 0x7f0c004f
+int style Sherlock___Widget_ActionBar 0x7f0c0001
+int style Sherlock___Widget_ActionMode 0x7f0c0016
+int style Sherlock___Widget_ActivityChooserView 0x7f0c001e
+int style Sherlock___Widget_Holo_DropDownItem 0x7f0c0029
+int style Sherlock___Widget_Holo_ListView 0x7f0c0026
+int style Sherlock___Widget_Holo_Spinner 0x7f0c0023
+int style Sherlock___Widget_SearchAutoCompleteTextView 0x7f0c0033
+int style TextAppearance_Sherlock_DialogWindowTitle 0x7f0c0048
+int style TextAppearance_Sherlock_Light_DialogWindowTitle 0x7f0c0049
+int style TextAppearance_Sherlock_Light_Small 0x7f0c004c
+int style TextAppearance_Sherlock_Light_Widget_PopupMenu_Large 0x7f0c0043
+int style TextAppearance_Sherlock_Light_Widget_PopupMenu_Small 0x7f0c0045
+int style TextAppearance_Sherlock_Small 0x7f0c004b
+int style TextAppearance_Sherlock_Widget_ActionBar_Menu 0x7f0c0038
+int style TextAppearance_Sherlock_Widget_ActionBar_Subtitle 0x7f0c003b
+int style TextAppearance_Sherlock_Widget_ActionBar_Subtitle_Inverse 0x7f0c003c
+int style TextAppearance_Sherlock_Widget_ActionBar_Title 0x7f0c0039
+int style TextAppearance_Sherlock_Widget_ActionBar_Title_Inverse 0x7f0c003a
+int style TextAppearance_Sherlock_Widget_ActionMode_Subtitle 0x7f0c003f
+int style TextAppearance_Sherlock_Widget_ActionMode_Subtitle_Inverse 0x7f0c0040
+int style TextAppearance_Sherlock_Widget_ActionMode_Title 0x7f0c003d
+int style TextAppearance_Sherlock_Widget_ActionMode_Title_Inverse 0x7f0c003e
+int style TextAppearance_Sherlock_Widget_DropDownHint 0x7f0c004d
+int style TextAppearance_Sherlock_Widget_DropDownItem 0x7f0c0047
+int style TextAppearance_Sherlock_Widget_PopupMenu 0x7f0c0041
+int style TextAppearance_Sherlock_Widget_PopupMenu_Large 0x7f0c0042
+int style TextAppearance_Sherlock_Widget_PopupMenu_Small 0x7f0c0044
+int style TextAppearance_Sherlock_Widget_TextView_SpinnerItem 0x7f0c0046
+int style Theme_Sherlock 0x7f0c0052
+int style Theme_Sherlock_Dialog 0x7f0c0057
+int style Theme_Sherlock_Light 0x7f0c0053
+int style Theme_Sherlock_Light_DarkActionBar 0x7f0c0054
+int style Theme_Sherlock_Light_Dialog 0x7f0c0058
+int style Theme_Sherlock_Light_NoActionBar 0x7f0c0056
+int style Theme_Sherlock_NoActionBar 0x7f0c0055
+int style TitleBar 0x7f0c005c
+int style TitleBarLogo 0x7f0c005d
+int style TitleBarOption 0x7f0c005e
+int style TopBarTheme 0x7f0c005b
+int style Widget 0x7f0c0000
+int style Widget_Sherlock_ActionBar 0x7f0c0002
+int style Widget_Sherlock_ActionBar_Solid 0x7f0c0003
+int style Widget_Sherlock_ActionBar_TabBar 0x7f0c000a
+int style Widget_Sherlock_ActionBar_TabText 0x7f0c000d
+int style Widget_Sherlock_ActionBar_TabView 0x7f0c0007
+int style Widget_Sherlock_ActionButton 0x7f0c0010
+int style Widget_Sherlock_ActionButton_CloseMode 0x7f0c0012
+int style Widget_Sherlock_ActionButton_Overflow 0x7f0c0014
+int style Widget_Sherlock_ActionMode 0x7f0c0017
+int style Widget_Sherlock_ActivityChooserView 0x7f0c001f
+int style Widget_Sherlock_Button_Small 0x7f0c0021
+int style Widget_Sherlock_DropDownItem_Spinner 0x7f0c002a
+int style Widget_Sherlock_Light_ActionBar 0x7f0c0004
+int style Widget_Sherlock_Light_ActionBar_Solid 0x7f0c0005
+int style Widget_Sherlock_Light_ActionBar_Solid_Inverse 0x7f0c0006
+int style Widget_Sherlock_Light_ActionBar_TabBar 0x7f0c000b
+int style Widget_Sherlock_Light_ActionBar_TabBar_Inverse 0x7f0c000c
+int style Widget_Sherlock_Light_ActionBar_TabText 0x7f0c000e
+int style Widget_Sherlock_Light_ActionBar_TabText_Inverse 0x7f0c000f
+int style Widget_Sherlock_Light_ActionBar_TabView 0x7f0c0008
+int style Widget_Sherlock_Light_ActionBar_TabView_Inverse 0x7f0c0009
+int style Widget_Sherlock_Light_ActionButton 0x7f0c0011
+int style Widget_Sherlock_Light_ActionButton_CloseMode 0x7f0c0013
+int style Widget_Sherlock_Light_ActionButton_Overflow 0x7f0c0015
+int style Widget_Sherlock_Light_ActionMode 0x7f0c0018
+int style Widget_Sherlock_Light_ActionMode_Inverse 0x7f0c0019
+int style Widget_Sherlock_Light_ActivityChooserView 0x7f0c0020
+int style Widget_Sherlock_Light_Button_Small 0x7f0c0022
+int style Widget_Sherlock_Light_DropDownItem_Spinner 0x7f0c002b
+int style Widget_Sherlock_Light_ListPopupWindow 0x7f0c001b
+int style Widget_Sherlock_Light_ListView_DropDown 0x7f0c0028
+int style Widget_Sherlock_Light_PopupMenu 0x7f0c001d
+int style Widget_Sherlock_Light_PopupWindow_ActionMode 0x7f0c002d
+int style Widget_Sherlock_Light_ProgressBar 0x7f0c002f
+int style Widget_Sherlock_Light_ProgressBar_Horizontal 0x7f0c0031
+int style Widget_Sherlock_Light_SearchAutoCompleteTextView 0x7f0c0035
+int style Widget_Sherlock_Light_Spinner_DropDown_ActionBar 0x7f0c0025
+int style Widget_Sherlock_ListPopupWindow 0x7f0c001a
+int style Widget_Sherlock_ListView_DropDown 0x7f0c0027
+int style Widget_Sherlock_PopupMenu 0x7f0c001c
+int style Widget_Sherlock_PopupWindow_ActionMode 0x7f0c002c
+int style Widget_Sherlock_ProgressBar 0x7f0c002e
+int style Widget_Sherlock_ProgressBar_Horizontal 0x7f0c0030
+int style Widget_Sherlock_SearchAutoCompleteTextView 0x7f0c0034
+int style Widget_Sherlock_Spinner_DropDown_ActionBar 0x7f0c0024
+int style Widget_Sherlock_TextView_SpinnerItem 0x7f0c0032
+int[] styleable SherlockActionBar { 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, 0x7f010004, 0x7f010005, 0x7f010048, 0x7f010049, 0x7f01004a, 0x7f01004b, 0x7f01004c, 0x7f01004d, 0x7f01004e, 0x7f01004f, 0x7f010050, 0x7f010051, 0x7f010052, 0x7f010053, 0x7f010054 }
+int styleable SherlockActionBar_background 2
+int styleable SherlockActionBar_backgroundSplit 3
+int styleable SherlockActionBar_backgroundStacked 12
+int styleable SherlockActionBar_customNavigationLayout 13
+int styleable SherlockActionBar_displayOptions 7
+int styleable SherlockActionBar_divider 5
+int styleable SherlockActionBar_height 4
+int styleable SherlockActionBar_homeLayout 14
+int styleable SherlockActionBar_icon 10
+int styleable SherlockActionBar_indeterminateProgressStyle 16
+int styleable SherlockActionBar_itemPadding 18
+int styleable SherlockActionBar_logo 11
+int styleable SherlockActionBar_navigationMode 6
+int styleable SherlockActionBar_progressBarPadding 17
+int styleable SherlockActionBar_progressBarStyle 15
+int styleable SherlockActionBar_subtitle 9
+int styleable SherlockActionBar_subtitleTextStyle 1
+int styleable SherlockActionBar_title 8
+int styleable SherlockActionBar_titleTextStyle 0
+int[] styleable SherlockActionMenuItemView { 0x0101013f }
+int styleable SherlockActionMenuItemView_android_minWidth 0
+int[] styleable SherlockActionMode { 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, 0x7f010004 }
+int styleable SherlockActionMode_background 2
+int styleable SherlockActionMode_backgroundSplit 3
+int styleable SherlockActionMode_height 4
+int styleable SherlockActionMode_subtitleTextStyle 1
+int styleable SherlockActionMode_titleTextStyle 0
+int[] styleable SherlockActivityChooserView { 0x010100d4, 0x7f01005d, 0x7f01005e }
+int styleable SherlockActivityChooserView_android_background 0
+int styleable SherlockActivityChooserView_expandActivityOverflowButtonDrawable 2
+int styleable SherlockActivityChooserView_initialActivityCount 1
+int[] styleable SherlockMenuGroup { 0x0101000e, 0x010100d0, 0x01010194, 0x010101de, 0x010101df, 0x010101e0 }
+int styleable SherlockMenuGroup_android_checkableBehavior 5
+int styleable SherlockMenuGroup_android_enabled 0
+int styleable SherlockMenuGroup_android_id 1
+int styleable SherlockMenuGroup_android_menuCategory 3
+int styleable SherlockMenuGroup_android_orderInCategory 4
+int styleable SherlockMenuGroup_android_visible 2
+int[] styleable SherlockMenuItem { 0x01010002, 0x0101000e, 0x010100d0, 0x01010106, 0x01010194, 0x010101de, 0x010101df, 0x010101e1, 0x010101e2, 0x010101e3, 0x010101e4, 0x010101e5, 0x0101026f, 0x010102d9, 0x010102fb, 0x010102fc, 0x01010389 }
+int styleable SherlockMenuItem_android_actionLayout 14
+int styleable SherlockMenuItem_android_actionProviderClass 16
+int styleable SherlockMenuItem_android_actionViewClass 15
+int styleable SherlockMenuItem_android_alphabeticShortcut 9
+int styleable SherlockMenuItem_android_checkable 11
+int styleable SherlockMenuItem_android_checked 3
+int styleable SherlockMenuItem_android_enabled 1
+int styleable SherlockMenuItem_android_icon 0
+int styleable SherlockMenuItem_android_id 2
+int styleable SherlockMenuItem_android_menuCategory 5
+int styleable SherlockMenuItem_android_numericShortcut 10
+int styleable SherlockMenuItem_android_onClick 12
+int styleable SherlockMenuItem_android_orderInCategory 6
+int styleable SherlockMenuItem_android_showAsAction 13
+int styleable SherlockMenuItem_android_title 7
+int styleable SherlockMenuItem_android_titleCondensed 8
+int styleable SherlockMenuItem_android_visible 4
+int[] styleable SherlockMenuView { 0x7f010055, 0x7f010056, 0x7f010057, 0x7f010058, 0x7f010059, 0x7f01005a, 0x7f01005b, 0x7f01005c }
+int styleable SherlockMenuView_headerBackground 3
+int styleable SherlockMenuView_horizontalDivider 1
+int styleable SherlockMenuView_itemBackground 4
+int styleable SherlockMenuView_itemIconDisabledAlpha 6
+int styleable SherlockMenuView_itemTextAppearance 0
+int styleable SherlockMenuView_preserveIconSpacing 7
+int styleable SherlockMenuView_verticalDivider 2
+int styleable SherlockMenuView_windowAnimationStyle 5
+int[] styleable SherlockSearchView { 0x0101011f, 0x01010220, 0x01010264, 0x7f01005f, 0x7f010060 }
+int styleable SherlockSearchView_android_imeOptions 2
+int styleable SherlockSearchView_android_inputType 1
+int styleable SherlockSearchView_android_maxWidth 0
+int styleable SherlockSearchView_iconifiedByDefault 3
+int styleable SherlockSearchView_queryHint 4
+int[] styleable SherlockSpinner { 0x010100af, 0x01010175, 0x01010176, 0x0101017b, 0x01010262, 0x010102ac, 0x010102ad, 0x0101043a }
+int styleable SherlockSpinner_android_dropDownHorizontalOffset 5
+int styleable SherlockSpinner_android_dropDownSelector 1
+int styleable SherlockSpinner_android_dropDownVerticalOffset 6
+int styleable SherlockSpinner_android_dropDownWidth 4
+int styleable SherlockSpinner_android_gravity 0
+int styleable SherlockSpinner_android_popupBackground 2
+int styleable SherlockSpinner_android_popupPromptView 7
+int styleable SherlockSpinner_android_prompt 3
+int[] styleable SherlockTheme { 0x01010057, 0x7f010006, 0x7f010007, 0x7f010008, 0x7f010009, 0x7f01000a, 0x7f01000b, 0x7f01000c, 0x7f01000d, 0x7f01000e, 0x7f01000f, 0x7f010010, 0x7f010011, 0x7f010012, 0x7f010013, 0x7f010014, 0x7f010015, 0x7f010016, 0x7f010017, 0x7f010018, 0x7f010019, 0x7f01001a, 0x7f01001b, 0x7f01001c, 0x7f01001d, 0x7f01001e, 0x7f01001f, 0x7f010020, 0x7f010021, 0x7f010022, 0x7f010023, 0x7f010024, 0x7f010025, 0x7f010026, 0x7f010027, 0x7f010028, 0x7f010029, 0x7f01002a, 0x7f01002b, 0x7f01002c, 0x7f01002d, 0x7f01002e, 0x7f01002f, 0x7f010030, 0x7f010031, 0x7f010032, 0x7f010033, 0x7f010034, 0x7f010035, 0x7f010036, 0x7f010037, 0x7f010038, 0x7f010039, 0x7f01003a, 0x7f01003b, 0x7f01003c, 0x7f01003d, 0x7f01003e, 0x7f01003f, 0x7f010040, 0x7f010041, 0x7f010042, 0x7f010043, 0x7f010044, 0x7f010045, 0x7f010046, 0x7f010047 }
+int styleable SherlockTheme_actionBarDivider 9
+int styleable SherlockTheme_actionBarItemBackground 10
+int styleable SherlockTheme_actionBarSize 8
+int styleable SherlockTheme_actionBarSplitStyle 6
+int styleable SherlockTheme_actionBarStyle 5
+int styleable SherlockTheme_actionBarTabBarStyle 2
+int styleable SherlockTheme_actionBarTabStyle 1
+int styleable SherlockTheme_actionBarTabTextStyle 3
+int styleable SherlockTheme_actionBarWidgetTheme 7
+int styleable SherlockTheme_actionButtonStyle 53
+int styleable SherlockTheme_actionDropDownStyle 52
+int styleable SherlockTheme_actionMenuTextAppearance 11
+int styleable SherlockTheme_actionMenuTextColor 12
+int styleable SherlockTheme_actionModeBackground 15
+int styleable SherlockTheme_actionModeCloseButtonStyle 14
+int styleable SherlockTheme_actionModeCloseDrawable 17
+int styleable SherlockTheme_actionModePopupWindowStyle 19
+int styleable SherlockTheme_actionModeShareDrawable 18
+int styleable SherlockTheme_actionModeSplitBackground 16
+int styleable SherlockTheme_actionModeStyle 13
+int styleable SherlockTheme_actionOverflowButtonStyle 4
+int styleable SherlockTheme_actionSpinnerItemStyle 58
+int styleable SherlockTheme_activatedBackgroundIndicator 66
+int styleable SherlockTheme_activityChooserViewStyle 65
+int styleable SherlockTheme_android_windowIsFloating 0
+int styleable SherlockTheme_buttonStyleSmall 20
+int styleable SherlockTheme_dividerVertical 51
+int styleable SherlockTheme_dropDownListViewStyle 55
+int styleable SherlockTheme_dropdownListPreferredItemHeight 57
+int styleable SherlockTheme_homeAsUpIndicator 54
+int styleable SherlockTheme_listPopupWindowStyle 64
+int styleable SherlockTheme_listPreferredItemHeightSmall 45
+int styleable SherlockTheme_listPreferredItemPaddingLeft 46
+int styleable SherlockTheme_listPreferredItemPaddingRight 47
+int styleable SherlockTheme_popupMenuStyle 56
+int styleable SherlockTheme_searchAutoCompleteTextView 31
+int styleable SherlockTheme_searchDropdownBackground 32
+int styleable SherlockTheme_searchResultListItemHeight 42
+int styleable SherlockTheme_searchViewCloseIcon 33
+int styleable SherlockTheme_searchViewEditQuery 37
+int styleable SherlockTheme_searchViewEditQueryBackground 38
+int styleable SherlockTheme_searchViewGoIcon 34
+int styleable SherlockTheme_searchViewSearchIcon 35
+int styleable SherlockTheme_searchViewTextField 39
+int styleable SherlockTheme_searchViewTextFieldRight 40
+int styleable SherlockTheme_searchViewVoiceIcon 36
+int styleable SherlockTheme_selectableItemBackground 21
+int styleable SherlockTheme_spinnerDropDownItemStyle 30
+int styleable SherlockTheme_spinnerItemStyle 29
+int styleable SherlockTheme_textAppearanceLargePopupMenu 23
+int styleable SherlockTheme_textAppearanceListItemSmall 48
+int styleable SherlockTheme_textAppearanceSearchResultSubtitle 44
+int styleable SherlockTheme_textAppearanceSearchResultTitle 43
+int styleable SherlockTheme_textAppearanceSmall 25
+int styleable SherlockTheme_textAppearanceSmallPopupMenu 24
+int styleable SherlockTheme_textColorPrimary 26
+int styleable SherlockTheme_textColorPrimaryDisableOnly 27
+int styleable SherlockTheme_textColorPrimaryInverse 28
+int styleable SherlockTheme_textColorSearchUrl 41
+int styleable SherlockTheme_windowActionBar 60
+int styleable SherlockTheme_windowActionBarOverlay 61
+int styleable SherlockTheme_windowActionModeOverlay 62
+int styleable SherlockTheme_windowContentOverlay 22
+int styleable SherlockTheme_windowMinWidthMajor 49
+int styleable SherlockTheme_windowMinWidthMinor 50
+int styleable SherlockTheme_windowNoTitle 59
+int styleable SherlockTheme_windowSplitActionBar 63
+int[] styleable SherlockView { 0x010100da }
+int styleable SherlockView_android_focusable 0
+int xml wso2_device_admin 0x7f040000
diff --git a/product/modules/agents/android/client/bin/cdm-agent.apk b/product/modules/agents/android/client/bin/cdm-agent.apk
new file mode 100644
index 0000000000..7813e3aa98
Binary files /dev/null and b/product/modules/agents/android/client/bin/cdm-agent.apk differ
diff --git a/product/modules/agents/android/client/bin/classes.dex b/product/modules/agents/android/client/bin/classes.dex
new file mode 100644
index 0000000000..51faaee6de
Binary files /dev/null and b/product/modules/agents/android/client/bin/classes.dex differ
diff --git a/product/modules/agents/android/client/bin/jarlist.cache b/product/modules/agents/android/client/bin/jarlist.cache
new file mode 100644
index 0000000000..0b3d02d697
--- /dev/null
+++ b/product/modules/agents/android/client/bin/jarlist.cache
@@ -0,0 +1,5 @@
+# cache for current jar dependency. DO NOT EDIT.
+# format is
+# Encoding is UTF-8
+1421928865000 484258 bd6479f5dd592790607e0504e66e0f31c2b4d308 /home/inoshp/Documents/work/EMM 2.0/Source/CDM/product-mdm/product/modules/agents/android/client/libs/android-support-v4.jar
+1421928865000 484258 bd6479f5dd592790607e0504e66e0f31c2b4d308 /home/inoshp/Documents/work/EMM 2.0/Source/CDM/product-mdm/product/modules/agents/android/client/plugins/ActionBarSherlock/library/libs/android-support-v4.jar
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_bookmark.png b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_bookmark.png
new file mode 100644
index 0000000000..e39a023452
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_bookmark.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_check_default.png b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_check_default.png
new file mode 100644
index 0000000000..74c9a0d459
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_check_default.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_check_selected.png b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_check_selected.png
new file mode 100644
index 0000000000..5482cfb175
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_check_selected.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_launcher.png b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000000..dc581a5c3a
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_launcher.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_logo.png b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_logo.png
new file mode 100644
index 0000000000..731d461bd4
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_logo.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_logo_dark.png b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_logo_dark.png
new file mode 100644
index 0000000000..731d461bd4
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_logo_dark.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_stat_gcm.png b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_stat_gcm.png
new file mode 100644
index 0000000000..c031369cc7
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/ic_stat_gcm.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/option_icon.png b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/option_icon.png
new file mode 100644
index 0000000000..c06067326a
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/option_icon.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/repeat_bg.png b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/repeat_bg.png
new file mode 100644
index 0000000000..70274e215f
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/repeat_bg.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/top_bar.png b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/top_bar.png
new file mode 100644
index 0000000000..79621a1269
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-hdpi/top_bar.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_bookmark.png b/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_bookmark.png
new file mode 100644
index 0000000000..6f3192e8dd
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_bookmark.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_check_default.png b/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_check_default.png
new file mode 100644
index 0000000000..2e04208666
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_check_default.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_check_selected.png b/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_check_selected.png
new file mode 100644
index 0000000000..51cc587f23
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_check_selected.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_launcher.png b/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000000..096b26b45f
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_launcher.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_logo.png b/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_logo.png
new file mode 100644
index 0000000000..846d41318a
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_logo.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_logo_dark.png b/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_logo_dark.png
new file mode 100644
index 0000000000..846d41318a
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/ic_logo_dark.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/option_icon.png b/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/option_icon.png
new file mode 100644
index 0000000000..bbcd11bde0
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/option_icon.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/top_bar.png b/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/top_bar.png
new file mode 100644
index 0000000000..1a10ae8e0b
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-mdpi/top_bar.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/appinstall.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/appinstall.png
new file mode 100644
index 0000000000..540a3132de
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/appinstall.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/applist.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/applist.png
new file mode 100644
index 0000000000..639d648523
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/applist.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/appuninstall.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/appuninstall.png
new file mode 100644
index 0000000000..8f6ffef13e
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/appuninstall.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/camera.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/camera.png
new file mode 100644
index 0000000000..a4900b9292
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/camera.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/changepassword.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/changepassword.png
new file mode 100644
index 0000000000..e1bde62f55
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/changepassword.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/encrypt.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/encrypt.png
new file mode 100644
index 0000000000..21c3c0ef86
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/encrypt.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_bookmark.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_bookmark.png
new file mode 100644
index 0000000000..f72001d8ec
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_bookmark.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_check_default.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_check_default.png
new file mode 100644
index 0000000000..aa466a928e
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_check_default.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_check_selected.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_check_selected.png
new file mode 100644
index 0000000000..3a7b7f9fad
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_check_selected.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_launcher.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000000..39b9a8f217
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_launcher.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_logo.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_logo.png
new file mode 100644
index 0000000000..380beb5a90
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_logo.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_logo_dark.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_logo_dark.png
new file mode 100644
index 0000000000..380beb5a90
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/ic_logo_dark.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/info.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/info.png
new file mode 100644
index 0000000000..ec33699635
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/info.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/location.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/location.png
new file mode 100644
index 0000000000..7df708d9b6
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/location.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/lock.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/lock.png
new file mode 100644
index 0000000000..9537c9a8df
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/lock.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/mute.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/mute.png
new file mode 100644
index 0000000000..94c0417e2a
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/mute.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/notification.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/notification.png
new file mode 100644
index 0000000000..f41ba8bef0
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/notification.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/repeat_bg.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/repeat_bg.png
new file mode 100644
index 0000000000..5984849fbe
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/repeat_bg.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/wifi.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/wifi.png
new file mode 100644
index 0000000000..217d097604
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/wifi.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/wipe.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/wipe.png
new file mode 100644
index 0000000000..e48edc69c2
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xhdpi/wipe.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_bookmark.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_bookmark.png
new file mode 100644
index 0000000000..b3cc1456a1
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_bookmark.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_check_default.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_check_default.png
new file mode 100644
index 0000000000..6401f3a7a7
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_check_default.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_check_selected.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_check_selected.png
new file mode 100644
index 0000000000..6a07c92553
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_check_selected.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_launcher.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..12fe35986b
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_launcher.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_logo.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_logo.png
new file mode 100644
index 0000000000..0a305fefe7
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_logo.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_logo_dark.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_logo_dark.png
new file mode 100644
index 0000000000..0a305fefe7
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/ic_logo_dark.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/repeat_bg.png b/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/repeat_bg.png
new file mode 100644
index 0000000000..4b43a1295f
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable-xxhdpi/repeat_bg.png differ
diff --git a/product/modules/agents/android/client/bin/res/crunch/drawable/dot.png b/product/modules/agents/android/client/bin/res/crunch/drawable/dot.png
new file mode 100644
index 0000000000..9b8761da4a
Binary files /dev/null and b/product/modules/agents/android/client/bin/res/crunch/drawable/dot.png differ
diff --git a/product/modules/agents/android/client/bin/resources.ap_ b/product/modules/agents/android/client/bin/resources.ap_
new file mode 100644
index 0000000000..a4b3d3c533
Binary files /dev/null and b/product/modules/agents/android/client/bin/resources.ap_ differ
diff --git a/product/modules/agents/android/client/lint.xml b/product/modules/agents/android/client/lint.xml
new file mode 100644
index 0000000000..ee0eead5bb
--- /dev/null
+++ b/product/modules/agents/android/client/lint.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/.gitignore b/product/modules/agents/android/client/plugins/ActionBarSherlock/.gitignore
new file mode 100644
index 0000000000..6d0dc1c163
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/.gitignore
@@ -0,0 +1,34 @@
+#Android generated
+bin
+gen
+lint.xml
+
+#Eclipse
+.project
+.classpath
+.settings
+.checkstyle
+
+#IntelliJ IDEA
+.idea
+*.iml
+*.ipr
+*.iws
+classes
+gen-external-apklibs
+
+#Maven
+target
+release.properties
+pom.xml.*
+
+#Ant
+build.xml
+ant.properties
+local.properties
+proguard.cfg
+proguard-project.txt
+
+#Other
+.DS_Store
+tmp
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/.travis.yml b/product/modules/agents/android/client/plugins/ActionBarSherlock/.travis.yml
new file mode 100644
index 0000000000..e9a32d4fac
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/.travis.yml
@@ -0,0 +1,11 @@
+language: java
+
+notifications:
+ email: false
+
+before_install:
+ - wget http://dl.google.com/android/android-sdk_r20.0.3-linux.tgz
+ - tar -zxf android-sdk_r20.0.3-linux.tgz
+ - export ANDROID_HOME=~/builds/JakeWharton/ActionBarSherlock/android-sdk-linux
+ - export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools
+ - android update sdk --filter 1,5 --no-ui --force
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/CHANGELOG.md b/product/modules/agents/android/client/plugins/ActionBarSherlock/CHANGELOG.md
new file mode 100644
index 0000000000..432230bf02
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/CHANGELOG.md
@@ -0,0 +1,469 @@
+Change Log
+===============================================================================
+
+Version 4.2.0 *(2012-10-07)*
+----------------------------
+
+**Maven `artifactId` is now to 'actionbarsherlock'.**
+
+Note: The `.Dialog` themes are now deprecated. These will be removed in a future
+version of the library.
+
+ * Add `SearchView` widget for standard search interaction (API 8+ only)
+ * Fix: `ShareActionProvider` in the split action bar no longer fills the entire
+ screen.
+ * Fix: `ShareActionProvider` now does file I/O on a background thread.
+ * Fix: Automatically correct `ColorDrawable` not respecting bounds when used as
+ a stacked background.
+ * Fix: Ensure fragments collection is present before dispatching events.
+ * Fix: XML-defined `onClick` searches the correct context for the declared
+ method.
+ * Fix: Ensure action mode start/finish callbacks are invoked on the activity
+ for the native action bar.
+ * Fix: Allow tab callbacks to have a fragment transaction instance for any
+ `FragmentActivity`.
+ * Fix: Ensure `CollapsibleActionView` callbacks are dispatched in both native
+ and compatbility action bars.
+ * Fix: Remove `.ForceOverflow` themes. These never should have been included.
+
+
+Version 4.1.0 *(2012-05-17)*
+----------------------------
+
+ * Fix: Altered technique used for menu event dispatching through the fragment
+ manager for greater control.
+ * Fix: Do not dispatch menu creation event if the activity has been destroyed.
+ * Fix: Correct potential `NullPointerException` when expanding an action item.
+ * Fix: Correct potential `NullPointerException` when the hardware menu key was
+ pressed in an activity that is forcing the overflow menu.
+ * Fix: Do not set a listener on the native action bar tab wrapper unless a
+ compatibility listener has been set.
+ * Fix: Ensure the compatibility animation framework is always available on
+ views even if they were previously detached from the view hierarchy.
+
+
+Version 4.0.2 *(2012-04-15)*
+----------------------------
+
+ * Upgrade to r7 support library.
+ * Fix: Do not trigger menu creation after `onCreate` if activity is finishing.
+ * Fix: Prevent overflow from displaying if there are no overflow action items.
+ * Fix: Long-pressing menu key no longer triggers overflow.
+ * Fix: Use proper tab state-list drawable to mimic ICS.
+ * Fix: Ensure dispatching menu creation and preparation to fragments can
+ properly return `false` when appropriate to avoid rendering artifacts.
+ * Fix: Properly save and fetch action mode tag on ICS.
+ * Fix: Add missing density-specific resources for certain asssets and remove
+ unused assets.
+
+
+Version 4.0.1 *(2012-03-25)*
+----------------------------
+
+ * Add `ShareActionProvider` widget for use as action items.
+ * Re-add 'Styled' sample to provide a more comprehensive theming example.
+ * Fix: Do not dispatch options item selection to fragments if the activity
+ handles the callback.
+ * Fix: Prevent menu key from opening the overflow menu when an action mode is
+ currently displayed.
+ * Fix: Ensure fragment transaction instance is not `null` on initial tab
+ selection callback.
+ * Fix: Displaying an action mode while using stacked tab navigation no longer
+ throws an exception.
+ * Fix: Using expandable action item callbacks no longer results in a possible
+ exception on older devices.
+
+
+Version 4.0.0 *(2012-03-07)*
+----------------------------
+
+Complete rewrite of the library to backport the Android 4.0 action bar.
+
+ * The minimum supported version of Android is now 2.1 (API 7).
+ * New base activities are provided (e.g., `SherlockActivity` and
+ `SherlockFragmentActivity`) which extend from the native activities.
+ * The support library sources are no longer included in the library. You must
+ include `android-support-v4.jar` in your project separately.
+ * Theming now mirrors that of the native action bar through the use of multiple
+ styles rather than through `ab`- and `am`-prefixed attributes in the theme.
+ * The action bar can be statically attached to an activity view without the
+ requirement of using one of the provided base activities.
+
+
+Version 3.5.1 *(2012-01-03)*
+----------------------------
+
+ * Fix: `NullPointerException` in `FragmentManager` can no longer occur when an
+ attempt is being made to save to a `Bundle` that has not yet been created.
+ * Fix: Pre-3.0 action item submenu dialogs now properly dismiss themselves when
+ an item of theirs is selected.
+
+
+Version 3.5.0 *(2011-12-18)*
+----------------------------
+
+ * Library now uses the `r6` version of the compatibility library for its base.
+ Ice Cream Sandwich-specific implementations are currently disabled, however,
+ but will be added in a future version of the library.
+
+ `MenuCompat`, `MenuItemCompat`, and `ActivityCompat` have be added back in
+ to ease transition to this library but all their methods and the classes
+ themselves have been deprecated.
+ * Rewritten menu and action item support from Ice Cream Sandwich.
+
+ * Removed the need for the custom `Window.FEATURE_ACTION_ITEM_TEXT` flag.
+ You should now use the `showAsAction` attribute and/or the
+ `setShowAsAction(int)` method on each `MenuItem` to control whether or
+ not text is shown
+ * Action item dividers are now added automatically only when necessary
+ to distinguish possible confusion between action items.
+ * Fix: Action views now properly size themselves within the bounded space
+ of the menu.
+
+ * Fix: List navigation no longer becomes unusable on certain device
+ configurations.
+ * Fix: `SubMenu`'s `findItem(int)` method now properly returns the support
+ version of `MenuItem`.
+ * Fix: Invisible sub-menu items are no longer shown on the pre-3.0 popup list.
+
+
+Version 3.4.2 *(2001-11-09)*
+----------------------------
+
+ * Fix: Stacked action bar now properly sets the tab bar background based on
+ the theme.
+
+
+Version 3.4.1 *(2011-11-09)*
+----------------------------
+
+ * The `makeFragmentName` method in `FragmentPagerAdapter` has been changed to
+ `public` scope to allow for easier access to your fragments that it is
+ managing.
+ * Action bar will now animate when calling `show()` or `hide()`.
+ * `SherlockPreferenceActivity` now provides full fragment and loader support.
+ * Examples for the plugins are now in their own sample application.
+ * Fix: Home icon no longer erroneously clipped when it exceeds the size of the
+ action bar.
+ * Fix: Tabs will now scroll horizontally to mimic the native action bar
+ behavior.
+ * Fix: Plugins now properly DO NOT inline their `R.java` integer constants.
+ * Fix: Tabs below the action bar are now styled with a default background so
+ that they do not incorrectly inherit an applied background unless explicity
+ declared.
+
+
+Version 3.4.0 *(2011-10-30)*
+----------------------------
+
+ * Library now uses the `r4` version of the compatibility library for its base.
+ Ice Cream Sandwich-specific implementations are currently disabled, however,
+ but will be added in a future version of the library.
+ * Context menu callbacks now use the support version of `MenuItem` to maintain
+ consistency.
+ * Added preference plugin which provides an action bar enhanced preference
+ screen.
+ * Fix: `abHomeLayout` theme attribute is now honored.
+ * Fix: `onPrepareOptionsMenu` is now properly dispatched upon menu
+ invalidation.
+
+
+Version 3.3.1 *(2011-10-20)*
+----------------------------
+
+ADT 14 is now required. Maven 3 is required if building from the command line.
+
+ * XML-defined `onClick` attributes will now check for an `onClick` method that
+ takes an `android.support.v4.view.MenuItem` instance.
+ * Tabs on medium screens in landscape now display inline rather than below the
+ action bar to mirror how Android 4.0 behaves with the same configuration.
+ * Fix: Menu inflater properly checks activity context for `onClick` method
+ declared in the XML.
+ * Fix: Dialog fragment properly saves its `showDialog` state when not being
+ used as a popup.
+ * Fix: Return `-1` when in tab navigation but no tab is selected. This brings
+ the library in line with the post-3.0 behavior.
+ * Fix: Removing a menu group no longer throws an `IndexOutOfBoundsException`.
+ * Fix: `getSelectedTab` and `getTabAt` no longer throw `NullPointerException`s
+ on post-3.0 when no tab was selected or no tab existed at the specified
+ position, respectively.
+ * Fix: `findFragmentById` now properly returns fragments attached to
+ `android.R.id.content` when run on pre-3.0 devices.
+
+
+Version 3.3.0 *(2011-10-11)*
+----------------------------
+
+ * Tabs are now displayed below the action bar on all medium-screen devices and
+ portrait large-screen devices.
+ * Fix: Dialog fragments no longer throw an `IllegalStateException` when being
+ used as a regular fragment (i.e., not as a popup). See
+ [StackOverflow](http://stackoverflow.com/questions/5637894/dialogfragments-with-devices-api-level-11/7560686#7560686)
+ for more information.
+ * Fix: Popping a fragment off of the back stack now properly assigns its parent
+ activity.
+ * Fix: An activity result no longer causes a `NullPointerException` when the
+ target fragment no longer exists.
+ * Fix: Action item dividers are now properly initially hidden when their
+ associated action items are as well.
+
+
+Version 3.2.3 *(2011-09-16)*
+----------------------------
+
+ * Fix: Fragments in a `ViewPager` that contributed items to the options menu
+ were caught in a race condition causing inconsistent results when a new page
+ was selected. This regression was introduced in version 3.2.2.
+
+
+Version 3.2.2 *(2011-09-15)*
+----------------------------
+
+ * Fix: Side-effects related to using `FragmentMapActivity` due to how it was
+ referencing resources from the main library.
+ * Fix: Fragments adjacent to the currently selected fragment in a `ViewPager`
+ no longer receive context menu events.
+ * Fix: Eliminate exception when inflating context menus on 3.0+ when using
+ `getMenuInflater()`.
+ * Fix: `ViewPager` now determines whether or not an activity menu invalidation
+ is required independently of whether or not fragments were created or
+ destroyed. This should fix an edge case where an activity with a `ViewPager`
+ containing only two fragments would not get its menu properly invalidated.
+
+
+Version 3.2.1 *(2011-09-12)*
+----------------------------
+
+ * Fix: Action mode API incorrectly using the native `Menu` and `MenuItem`
+ classes causing an easy pitfall for `ClassCastExceptions`.
+ * Fix: Large action bar backgrounds increasing the size beyond that alloted in
+ the theme.
+
+
+Version 3.2.0 *(2011-09-05)*
+----------------------------
+
+ * Added support for `MapView` and the Google APIs through the use of
+ `FragmentMapActivity`. If you are using a map within a fragment you must
+ ensure it is always attached to an activity which extends from this new base
+ class.
+
+ Since supporting maps requires compiling against the Google APIs, this
+ functionality is implemented in the form of a plugin which is to be used
+ alongside the normal library. You can choose to add it as an additional
+ library project or by including it as a `.jar`. Maven users may simply
+ include the additional dependency (artifactId: `plugin-maps`).
+ * Fix: Fragments adjacent to the currently selected fragment in a `ViewPager`
+ no longer contribute to the activity menu.
+ * `ActionBar.Tab` has been changed from an interface to an abstract class to
+ mirror its native counterpart.
+
+
+Version 3.1.3 *(2011-08-14)*
+----------------------------
+
+ * Renamed all resources to be prefixed with `abs__` to avoid conflicts when
+ including in your project.
+ * Fix: Action bar background being set on two views causing artifacts to remain
+ on screen when the action bar was hidden.
+ * Fix: Incorrect sub-menu item being selected by default when the sub-menu was
+ triggered from the native options menu on pre-3.0.
+ * Fix: `MenuItem.setVisible` now properly updates the associated action item and
+ native menu item visible state.
+ * Fix: Adding items to a menu now honors its ordering and category.
+ * Fix: Fragment options item selected callback now uses the proper version of
+ `MenuItem`.
+
+
+Version 3.1.2 *(2011-08-07)*
+----------------------------
+
+ * Fix: `MenuItem.getMenuInfo()` was throwing runtime exception. Will now just
+ return `null`.
+ * Fix: Dragging over a `WebView` contained in a `ViewPager` would not register.
+ * Fix: Inflation of context menu incorrectly being handled by the custom menu
+ inflater for the library.
+
+
+Version 3.1.1 *(2011-07-31)*
+----------------------------
+
+ * Fix: `MenuItem.getSubMenu` now returns a support instance rather than a
+ native instance.
+ * Fix: Fragment methods `onAttach` and `onInflate` incorrectly regressed to use
+ `Activity` instead of a `FragmentActivity` in their method signatures.
+ * Fix: Retained fragments not being re-attached on pre-3.0 when attached to
+ `android.R.id.content` upon activity recreation.
+ * Fix: `onPrepareOptionsMenu` not dispatched to fragments. This still will only
+ occur if the activity method returns true (which is the default).
+ * Fix: `Menu.findItem` not returning `null` when the item was not found on
+ Android 3.0+.
+
+
+Version 3.1.0 *(2011-07-22)*
+----------------------------
+
+Due to shortcomings in the Android theming system, a small change must be made
+in how this library handles themes. If you were using a custom style for
+`actionBarStyle` you must now specify its attributes in the root of the theme
+and prefix them with 'ab'.
+
+You can see an example of this in the `SherlockCustom` theme in
+`samples/demos/res/values/styles.xml`.
+
+ * Library now uses the `r3` version of the compatibility library for its base.
+ * `actionBarStyle` is no longer a valid theme attribute (see note above).
+ * Added the demo project included with the new compatibility library under
+ `samples/demos/` and merged in the old 'featuredemo'.
+ * Dividers are now shown on pre-3.0 devices between all action items.
+ * `Window.FEATURE_ACTION_BAR_OVERLAY` is now honored on pre-3.0 devices.
+ * Inflation of XML menu resources will now honor `android:actionLayout` and
+ `android:actionViewClass` attributes.
+ * Buttons for displaying the determinate and indeterminate progress bars have
+ been added to the feature toggle demo.
+ * Added support for indeterminate progress bar. Due to the `final` modifier on
+ the native type, you must use `setIndeterminateProgressBarVisibility(Boolean)`
+ and pass `Boolean.TRUE` or `Boolean.FALSE`.
+ * Fix: `MenuBuilder#removeItem(int)` and `MenuBuilder#findItem(int)` throwing
+ `IndexOutOfBoundsException`s when the item was not found.
+ * Fix: Theme attributes for home item data (e.g., icon, logo) will not be
+ overwritten by the special `MenuItem` instance for home.
+ * Fix: Native strings can now be specified for an XML menu `` in
+ `android:title` and `android:titleCondensed`.
+ * `Window.FEATURE_ENABLE_ACTION_BAR_WATSON_TEXT` is now
+ `Window.FEATURE_ACTION_BAR_ITEM_TEXT`.
+ * `Widget.Sherlock.Spinner.DropDown.ActionBar` and
+ `Widget.Sherlock.Light.Spinner.DropDown.ActionBar` styles are now
+ `Widget.Sherlock.Spinner` and `Widget.Sherlock.Light.Spinner`, respectively.
+ * `Widget.Sherlock.ActionBarView_TabXXX` styles are now
+ `Widget.Sherlock.ActionBar.TabXXX`.
+
+
+Version 3.0.3 *(2011-07-17)*
+----------------------------
+
+This version is a hotfix for incompatibilities introduced with the SDKs for
+3.1 r2 and 3.2 r1. Due to unavoidable changes in the underlying SDK, the library
+must now be compiled against API level 13.
+
+ * `actionModeStyle` and `actionModePopupWindowStyle` are no longer valid theme
+ attributes.
+
+
+Version 3.0.2 *(2011-06-23)*
+----------------------------
+
+ * Sub-menus for action items are now shown in a list dialog.
+ * Moved certain classes to the `com.actionbarsherlock.internal` package which
+ were not meant for public consumption. Despite being given `public` scope in
+ this new package, these classes should **NOT** be used under any circumstances
+ as their API can be considered highly volatile and is subject to change often
+ and without warning.
+
+
+Version 3.0.1 *(2011-06-08)*
+----------------------------
+
+ * Fix: `onOptionsItemSelected()` not being called in fragments if the activity
+ version returns `false`.
+ * Fix: `onCreateOptionsMenu()` not being called in fragments on Android 3.0+.
+ * New: Enable action item text display on pre-Android 3.0 by calling
+ `requestWindowFeature` with `Window.FEATURE_ENABLE_ACTION_BAR_WATSON_TEXT`.
+ * Fix: `setCustomView()` no longer automatically enables the custom view on
+ pre-3.0. You must call `setDisplayShowCustomEnabled()` in order to display
+ the view.
+
+
+Version 3.0.0 *(2011-06-05)*
+----------------------------
+
+The API has been rewritten to mimic that of the native action bar. As a result,
+usage now only requires changing a few imports to use the support versions
+of classes and calling `getSupportActionBar()`. See the README for more info.
+
+The rewrite necessitated tight interaction with the
+[compatibility library](http://android-developers.blogspot.com/2011/03/fragments-for-all.html)
+to the point where its sources are now included. You are no longer required to
+have the standalone `.jar` file.
+
+Also included is a default custom action bar for use by default on pre-3.0
+devices. This custom implementation is based off of Johan Nilsson's
+[Android-ActionBar](https://github.com/johannilsson/android-actionbar) and the
+[work that I have done](https://github.com/johannilsson/android-actionbar/pull/25)
+on it.
+
+More details are available at http://actionbarsherlock.com
+
+
+Version 2.1.1 *(2011-03-21)*
+----------------------------
+
+**No changes to library code.**
+
+ * Moved library to the root of the repository.
+ * Added `samples/dependencies.py` script to automatically download the needed
+ dependencies for the sample projects.
+
+
+Version 2.1.0 *(2011-03-21)*
+----------------------------
+
+**WARNING**: The
+[Android Compatibility Library (v4)](http://android-developers.blogspot.com/2011/03/fragments-for-all.html)
+is now required.
+
+ * Added `ActionBarSherlock.Activity`, `ActionBarSherlock.FragmentActivity`,
+ and `ActionBarSherlock.ListActivity` for extension by implementing
+ activities, the latter of which is deprecated. This affords a much tighter
+ integration and allows for the use of other new features listed below.
+ * New API method: `layout(Fragment)` will use the fragment argument as the
+ content to the activity.
+ * New API method: `menu(int)` allows for the inflation of menu XMLs from a
+ resource. For the non-native implementation, the XML can be inflated to a
+ custom Menu which can then be applied appropriately to the third-party
+ action bar. Sub-menus are also supported. Third-party action bar handlers
+ should implement `ActionBarSherlock.HasMenu` for this functionality. *This
+ feature requires that activities extend from one of the provided activity
+ base classes.*
+ * New API method: `homeAsUp(boolean)`. This mimics the native method
+ `setDisplayHomeAsUpEnalbed` on the native action bar. Third-party action bar
+ handlers should implement `ActionBarSherlock.HasHomeAsUp` for this
+ functionality.
+ * New API method: `useLogo(boolean)` will trigger the action bar to hide the
+ application icon/home button and title and show a larger logo representing
+ the application. Third-party action bar handlers should implement
+ `ActionBarSherlock.HasLogo` for this functionality.
+ * New API method: `listNavigation(SpinnerAdapter, OnNavigationListener)`. Tells
+ the action bar to use drop-down style navigation with the specified list of
+ items and callback listener. Third-party action bar handlers should
+ implement `ActionBarSherlock.HasListNavigation` for this functionality.
+ * Javadocs are now available at
+ [jakewharton.github.com/ActionBarSherlock](http://jakewharton.github.com/ActionBarSherlock/).
+ * A standalone JAR is now available via the
+ [GitHub downloads page](https://github.com/JakeWharton/ActionBarSherlock/downloads)
+ or in my
+ [personal maven repository](http://r.jakewharton.com/maven/)
+ as `com.jakewharton:android-actionbarsherlock:2.1.0`.
+
+
+Version 2.0.1 *(2011-03-11)*
+----------------------------
+
+ * Use `Class.forName()` for detection of native action bar. This provides
+ compatability all the way back to Android 1.5.
+
+
+Version 2.0.0 *(2011-03-09)*
+----------------------------
+Complete rewrite!
+
+ * New and better API.
+ * More sane logic and attachment to activity.
+ * Extensible via generics. Implement any ActionBar or roll your own with
+ minimal effort.
+ * Now a library project for easy inclusion in applications.
+
+
+Version 1.0.0 *(2011-03-07)*
+----------------------------
+Initial release.
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/CONTRIBUTING.md b/product/modules/agents/android/client/plugins/ActionBarSherlock/CONTRIBUTING.md
new file mode 100644
index 0000000000..30d383364e
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/CONTRIBUTING.md
@@ -0,0 +1,11 @@
+Contributing
+============
+
+If you would like to contribute code to ActionBarSherlock you can do so through
+GitHub by forking the repository and sending a pull request.
+
+When submitting code, please make every effort to follow existing conventions
+and style in order to keep the code as readable as possible. Please also make
+sure your code compiles by running `mvn clean verify`. Checkstyle failures
+during compilation indicate errors in your style and can be viewed in the
+`checkstyle-result.xml` file.
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/LICENSE.txt b/product/modules/agents/android/client/plugins/ActionBarSherlock/LICENSE.txt
new file mode 100644
index 0000000000..d645695673
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/README.md b/product/modules/agents/android/client/plugins/ActionBarSherlock/README.md
new file mode 100644
index 0000000000..6506c361d2
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/README.md
@@ -0,0 +1,60 @@
+ActionBarSherlock
+=================
+
+ActionBarSherlock is an standalone library designed to facilitate the use of
+the action bar design pattern across all versions of Android through a single
+API.
+
+The library will automatically use the [native ActionBar][2] implementation on
+Android 4.0 or later. For previous versions which do not include ActionBar, a
+custom action bar implementation based on the sources of Ice Cream Sandwich
+will automatically be wrapped around the layout. This allows you to easily
+develop an application with an action bar for every version of Android from 2.x
+and up.
+
+**See http://actionbarsherlock.com for more information.**
+
+![Example Image][3]
+
+Try out the sample applications on the Android Market: [Feature Demos][4],
+[Fragments][5], and [RoboGuice][6].
+
+Continuous integration is provided by [Travis CI][7].
+
+
+
+Developed By
+============
+
+* Jake Wharton -
+
+
+
+License
+=======
+
+ Copyright 2012 Jake Wharton
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+
+
+
+ [1]: http://android-developers.blogspot.com/2011/03/fragments-for-all.html
+ [2]: http://developer.android.com/guide/topics/ui/actionbar.html
+ [3]: http://actionbarsherlock.com/static/feature.png
+ [4]: https://play.google.com/store/apps/details?id=com.actionbarsherlock.sample.demos
+ [5]: https://play.google.com/store/apps/details?id=com.actionbarsherlock.sample.fragments
+ [6]: https://play.google.com/store/apps/details?id=com.actionbarsherlock.sample.roboguice
+ [7]: https://travis-ci.org/JakeWharton/ActionBarSherlock
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/checkstyle.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/checkstyle.xml
new file mode 100644
index 0000000000..cfde0eaf74
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/checkstyle.xml
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/AndroidManifest.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/AndroidManifest.xml
new file mode 100644
index 0000000000..7b8a848240
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/README.md b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/README.md
new file mode 100644
index 0000000000..e8a2c080e6
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/README.md
@@ -0,0 +1,15 @@
+ActionBarSherlock Library
+=========================
+
+This folder contains the main library which should be linked against as an
+Android library project in your application.
+
+For more information see the "Including In Your Project" section of the
+[usage page][1].
+
+
+
+
+
+
+ [1]: http://actionbarsherlock.com/usage.html
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/build.gradle b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/build.gradle
new file mode 100644
index 0000000000..88ae49ebf3
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/build.gradle
@@ -0,0 +1,32 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:0.4'
+ }
+}
+apply plugin: 'android-library'
+
+dependencies {
+ compile fileTree(dir: 'libs', include: '*.jar')
+}
+
+android {
+ compileSdkVersion 15
+ buildToolsVersion "18.0.1"
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ resources.srcDirs = ['src']
+ aidl.srcDirs = ['src']
+ renderscript.srcDirs = ['src']
+ res.srcDirs = ['res']
+ assets.srcDirs = ['assets']
+ }
+
+ instrumentTest.setRoot('tests')
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/pom.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/pom.xml
new file mode 100644
index 0000000000..3b6ce40ce1
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/pom.xml
@@ -0,0 +1,148 @@
+
+
+
+ 4.0.0
+
+ actionbarsherlock
+ ActionBarSherlock
+ apklib
+
+
+ com.actionbarsherlock
+ parent
+ 4.2.0
+ ../pom.xml
+
+
+
+
+ com.google.android
+ android
+ provided
+
+
+ com.google.android
+ support-v4
+
+
+
+ junit
+ junit
+ test
+
+
+
+
+ src
+ test
+
+
+
+ com.jayway.maven.plugins.android.generation2
+ android-maven-plugin
+ true
+
+ ignored
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+ true
+
+
+
+
+ com.google.code.maven-replacer-plugin
+ maven-replacer-plugin
+ 1.4.0
+
+
+ process-sources
+
+ replace
+
+
+
+
+ false
+ target/generated-sources/r/com/actionbarsherlock/R.java
+ target/generated-sources/r/com/actionbarsherlock/R.java
+ false
+ static final int
+ static int
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+ ../checkstyle.xml
+
+
+
+ verify
+
+ checkstyle
+
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 1.7
+
+
+ package
+
+ attach-artifact
+
+
+
+
+ jar
+ ${project.build.directory}/${project.build.finalName}.jar
+
+
+
+
+
+
+
+
+
+
+
+
+ org.eclipse.m2e
+ lifecycle-mapping
+ 1.0.0
+
+
+
+
+
+ com.google.code.maven-replacer-plugin
+ maven-replacer-plugin
+ [1.4.0,)
+
+ replace
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/project.properties b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/project.properties
new file mode 100644
index 0000000000..f28bc833e1
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/project.properties
@@ -0,0 +1,12 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+
+android.library=true
+# Project target.
+target=android-15
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/color/abs__primary_text_disable_only_holo_dark.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/color/abs__primary_text_disable_only_holo_dark.xml
new file mode 100644
index 0000000000..ea7459aaf5
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/color/abs__primary_text_disable_only_holo_dark.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/color/abs__primary_text_disable_only_holo_light.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/color/abs__primary_text_disable_only_holo_light.xml
new file mode 100644
index 0000000000..0edb33b4be
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/color/abs__primary_text_disable_only_holo_light.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/color/abs__primary_text_holo_dark.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/color/abs__primary_text_holo_dark.xml
new file mode 100644
index 0000000000..2bcfd0b630
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/color/abs__primary_text_holo_dark.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/color/abs__primary_text_holo_light.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/color/abs__primary_text_holo_light.xml
new file mode 100644
index 0000000000..198384fede
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/color/abs__primary_text_holo_light.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_bottom_solid_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_bottom_solid_dark_holo.9.png
new file mode 100644
index 0000000000..769463b369
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_bottom_solid_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_bottom_solid_inverse_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_bottom_solid_inverse_holo.9.png
new file mode 100644
index 0000000000..88f11dcb91
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_bottom_solid_inverse_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png
new file mode 100644
index 0000000000..73050476e7
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_bottom_transparent_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_bottom_transparent_dark_holo.9.png
new file mode 100644
index 0000000000..712a551ece
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_bottom_transparent_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_bottom_transparent_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_bottom_transparent_light_holo.9.png
new file mode 100644
index 0000000000..bf3b9438b1
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_bottom_transparent_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_share_pack_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_share_pack_holo_dark.9.png
new file mode 100644
index 0000000000..81b87b86c9
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_share_pack_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_share_pack_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_share_pack_holo_light.9.png
new file mode 100644
index 0000000000..8fc83e22ef
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_share_pack_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_solid_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_solid_dark_holo.9.png
new file mode 100644
index 0000000000..cbbaec588e
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_solid_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_solid_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_solid_light_holo.9.png
new file mode 100644
index 0000000000..af917e5b6f
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_solid_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_solid_shadow_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_solid_shadow_holo.9.png
new file mode 100644
index 0000000000..2d59f354ee
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_solid_shadow_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_stacked_solid_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_stacked_solid_dark_holo.9.png
new file mode 100644
index 0000000000..0520e5a2f6
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_stacked_solid_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_stacked_solid_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_stacked_solid_light_holo.9.png
new file mode 100644
index 0000000000..e3e3f93b9f
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_stacked_solid_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_stacked_transparent_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_stacked_transparent_dark_holo.9.png
new file mode 100644
index 0000000000..1e39572224
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_stacked_transparent_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_stacked_transparent_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_stacked_transparent_light_holo.9.png
new file mode 100644
index 0000000000..a16db853e9
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_stacked_transparent_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_transparent_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_transparent_dark_holo.9.png
new file mode 100644
index 0000000000..0eff695d82
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_transparent_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_transparent_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_transparent_light_holo.9.png
new file mode 100644
index 0000000000..219b170fa6
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ab_transparent_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_default_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_default_holo_dark.9.png
new file mode 100644
index 0000000000..b0dc31fb3d
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_default_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_default_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_default_holo_light.9.png
new file mode 100644
index 0000000000..4bc2683b15
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_default_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_focused_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_focused_holo_dark.9.png
new file mode 100644
index 0000000000..4af38fb70b
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_focused_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_focused_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_focused_holo_light.9.png
new file mode 100644
index 0000000000..d32f74cf4c
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_focused_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_dark.9.png
new file mode 100644
index 0000000000..66adffed63
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_light.9.png
new file mode 100644
index 0000000000..caeff9c331
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__cab_background_bottom_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__cab_background_bottom_holo_dark.9.png
new file mode 100644
index 0000000000..1d836f65a1
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__cab_background_bottom_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__cab_background_bottom_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__cab_background_bottom_holo_light.9.png
new file mode 100644
index 0000000000..5818666d4e
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__cab_background_bottom_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__cab_background_top_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__cab_background_top_holo_dark.9.png
new file mode 100644
index 0000000000..564fb34b43
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__cab_background_top_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__cab_background_top_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__cab_background_top_holo_light.9.png
new file mode 100644
index 0000000000..ae21b760fb
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__cab_background_top_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__dialog_full_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__dialog_full_holo_dark.9.png
new file mode 100644
index 0000000000..79e56f522b
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__dialog_full_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__dialog_full_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__dialog_full_holo_light.9.png
new file mode 100644
index 0000000000..e029f210b9
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__dialog_full_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_ab_back_holo_dark.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_ab_back_holo_dark.png
new file mode 100644
index 0000000000..897a1c11a0
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_ab_back_holo_dark.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_ab_back_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_ab_back_holo_light.png
new file mode 100644
index 0000000000..0c89f71407
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_ab_back_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_cab_done_holo_dark.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_cab_done_holo_dark.png
new file mode 100644
index 0000000000..d8662e3f0f
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_cab_done_holo_dark.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_cab_done_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_cab_done_holo_light.png
new file mode 100644
index 0000000000..ed03f620f8
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_cab_done_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_clear_disabled.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_clear_disabled.png
new file mode 100644
index 0000000000..d97c342d53
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_clear_disabled.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_clear_normal.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_clear_normal.png
new file mode 100644
index 0000000000..33ad8d4b89
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_clear_normal.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_clear_search_api_disabled_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_clear_search_api_disabled_holo_light.png
new file mode 100644
index 0000000000..3edbd74085
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_clear_search_api_disabled_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_clear_search_api_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_clear_search_api_holo_light.png
new file mode 100644
index 0000000000..90db01b5bc
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_clear_search_api_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_go.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_go.png
new file mode 100644
index 0000000000..97b825e831
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_go.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_go_search_api_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_go_search_api_holo_light.png
new file mode 100644
index 0000000000..7e1ba2adc6
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_go_search_api_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png
new file mode 100644
index 0000000000..2abc45809c
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_light.png
new file mode 100644
index 0000000000..bb6aef1d06
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_menu_share_holo_dark.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_menu_share_holo_dark.png
new file mode 100644
index 0000000000..6f747c8f06
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_menu_share_holo_dark.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_menu_share_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_menu_share_holo_light.png
new file mode 100644
index 0000000000..682b2fdec4
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_menu_share_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_search.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_search.png
new file mode 100644
index 0000000000..4be72f108b
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_search.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_search_api_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_search_api_holo_light.png
new file mode 100644
index 0000000000..72e207bc5d
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_search_api_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_voice_search.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_voice_search.png
new file mode 100644
index 0000000000..66d14aec0c
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_voice_search.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_voice_search_api_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_voice_search_api_holo_light.png
new file mode 100644
index 0000000000..3481c98286
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__ic_voice_search_api_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_activated_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_activated_holo.9.png
new file mode 100644
index 0000000000..4ea7afa00e
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_activated_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_divider_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_divider_holo_dark.9.png
new file mode 100644
index 0000000000..986ab0b974
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_divider_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_divider_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_divider_holo_light.9.png
new file mode 100644
index 0000000000..0279e17a12
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_divider_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_focused_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_focused_holo.9.png
new file mode 100644
index 0000000000..516f5c7399
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_focused_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_longpressed_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_longpressed_holo.9.png
new file mode 100644
index 0000000000..4ea7afa00e
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_longpressed_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_pressed_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_pressed_holo_dark.9.png
new file mode 100644
index 0000000000..5654cd6942
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_pressed_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_pressed_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_pressed_holo_light.9.png
new file mode 100644
index 0000000000..5654cd6942
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_pressed_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_selector_disabled_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_selector_disabled_holo_dark.9.png
new file mode 100644
index 0000000000..f6fd30dcdc
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_selector_disabled_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_selector_disabled_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_selector_disabled_holo_light.9.png
new file mode 100644
index 0000000000..ca8e9a2778
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__list_selector_disabled_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__menu_dropdown_panel_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__menu_dropdown_panel_holo_dark.9.png
new file mode 100644
index 0000000000..4d3d208578
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__menu_dropdown_panel_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__menu_dropdown_panel_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__menu_dropdown_panel_holo_light.9.png
new file mode 100644
index 0000000000..924a99d173
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__menu_dropdown_panel_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_bg_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_bg_holo_dark.9.png
new file mode 100644
index 0000000000..310c368e7a
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_bg_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_bg_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_bg_holo_light.9.png
new file mode 100644
index 0000000000..70cb7fc7e0
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_bg_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_primary_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_primary_holo_dark.9.png
new file mode 100644
index 0000000000..1c269205e8
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_primary_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_primary_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_primary_holo_light.9.png
new file mode 100644
index 0000000000..1c269205e8
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_primary_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_secondary_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_secondary_holo_dark.9.png
new file mode 100644
index 0000000000..40d0d1645c
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_secondary_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_secondary_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_secondary_holo_light.9.png
new file mode 100644
index 0000000000..40d0d1645c
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__progress_secondary_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_48_inner_holo.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_48_inner_holo.png
new file mode 100644
index 0000000000..c8358e9cef
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_48_inner_holo.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_48_outer_holo.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_48_outer_holo.png
new file mode 100644
index 0000000000..f62f74bb38
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_48_outer_holo.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_default_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_default_holo_dark.9.png
new file mode 100644
index 0000000000..eb28ff9a55
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_default_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_default_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_default_holo_light.9.png
new file mode 100644
index 0000000000..d281adb553
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_default_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_disabled_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_disabled_holo_dark.9.png
new file mode 100644
index 0000000000..b298586090
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_disabled_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_disabled_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_disabled_holo_light.9.png
new file mode 100644
index 0000000000..4215396dd4
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_disabled_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_focused_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_focused_holo_dark.9.png
new file mode 100644
index 0000000000..a280eabf59
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_focused_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_focused_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_focused_holo_light.9.png
new file mode 100644
index 0000000000..f8d619b4d4
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_focused_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_pressed_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_pressed_holo_dark.9.png
new file mode 100644
index 0000000000..955a2f3406
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_pressed_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_pressed_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_pressed_holo_light.9.png
new file mode 100644
index 0000000000..6c22e223ac
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__spinner_ab_pressed_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__tab_selected_focused_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__tab_selected_focused_holo.9.png
new file mode 100644
index 0000000000..673e3bf10d
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__tab_selected_focused_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__tab_selected_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__tab_selected_holo.9.png
new file mode 100644
index 0000000000..d57df98b50
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__tab_selected_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__tab_selected_pressed_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__tab_selected_pressed_holo.9.png
new file mode 100644
index 0000000000..6278eef472
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__tab_selected_pressed_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__tab_unselected_pressed_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__tab_unselected_pressed_holo.9.png
new file mode 100644
index 0000000000..aadc6f87b2
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__tab_unselected_pressed_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_default_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_default_holo_dark.9.png
new file mode 100644
index 0000000000..70c0e7396e
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_default_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_default_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_default_holo_light.9.png
new file mode 100644
index 0000000000..36e71d85d0
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_default_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_right_default_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_right_default_holo_dark.9.png
new file mode 100644
index 0000000000..4be4af5fab
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_right_default_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_right_default_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_right_default_holo_light.9.png
new file mode 100644
index 0000000000..e72193f592
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_right_default_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_right_selected_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_right_selected_holo_dark.9.png
new file mode 100644
index 0000000000..8f20b9d267
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_right_selected_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_right_selected_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_right_selected_holo_light.9.png
new file mode 100644
index 0000000000..04f657e1db
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_right_selected_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_selected_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_selected_holo_dark.9.png
new file mode 100644
index 0000000000..99309ef6d3
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_selected_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_selected_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_selected_holo_light.9.png
new file mode 100644
index 0000000000..9bde7fbdce
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-hdpi/abs__textfield_search_selected_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_bottom_solid_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_bottom_solid_dark_holo.9.png
new file mode 100644
index 0000000000..b2293670b7
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_bottom_solid_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_bottom_solid_inverse_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_bottom_solid_inverse_holo.9.png
new file mode 100644
index 0000000000..c65f443e33
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_bottom_solid_inverse_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png
new file mode 100644
index 0000000000..0706c8af65
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_bottom_transparent_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_bottom_transparent_dark_holo.9.png
new file mode 100644
index 0000000000..d814d02d31
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_bottom_transparent_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_bottom_transparent_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_bottom_transparent_light_holo.9.png
new file mode 100644
index 0000000000..b139c8e491
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_bottom_transparent_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_share_pack_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_share_pack_holo_dark.9.png
new file mode 100644
index 0000000000..738cb38d07
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_share_pack_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_share_pack_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_share_pack_holo_light.9.png
new file mode 100644
index 0000000000..2ed75a767a
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_share_pack_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_solid_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_solid_dark_holo.9.png
new file mode 100644
index 0000000000..743d00b6cd
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_solid_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_solid_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_solid_light_holo.9.png
new file mode 100644
index 0000000000..17c1fb921f
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_solid_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_solid_shadow_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_solid_shadow_holo.9.png
new file mode 100644
index 0000000000..ddfc8e3d5c
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_solid_shadow_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_stacked_solid_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_stacked_solid_dark_holo.9.png
new file mode 100644
index 0000000000..007a4b2392
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_stacked_solid_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_stacked_solid_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_stacked_solid_light_holo.9.png
new file mode 100644
index 0000000000..ad6e1a4d9f
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_stacked_solid_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_stacked_transparent_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_stacked_transparent_dark_holo.9.png
new file mode 100644
index 0000000000..0ad6c888b4
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_stacked_transparent_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_stacked_transparent_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_stacked_transparent_light_holo.9.png
new file mode 100644
index 0000000000..19b50abcb5
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_stacked_transparent_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_transparent_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_transparent_dark_holo.9.png
new file mode 100644
index 0000000000..ad980b13fc
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_transparent_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_transparent_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_transparent_light_holo.9.png
new file mode 100644
index 0000000000..60e6c52786
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ab_transparent_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_default_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_default_holo_dark.9.png
new file mode 100644
index 0000000000..5461b9c00f
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_default_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_default_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_default_holo_light.9.png
new file mode 100644
index 0000000000..5dc6f804ae
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_default_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_focused_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_focused_holo_dark.9.png
new file mode 100644
index 0000000000..a70b53c59a
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_focused_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_focused_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_focused_holo_light.9.png
new file mode 100644
index 0000000000..c7a9896b0d
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_focused_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_dark.9.png
new file mode 100644
index 0000000000..85d7aadd4d
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_light.9.png
new file mode 100644
index 0000000000..f7b01e012f
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__cab_background_bottom_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__cab_background_bottom_holo_dark.9.png
new file mode 100644
index 0000000000..d8f1c8bd54
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__cab_background_bottom_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__cab_background_bottom_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__cab_background_bottom_holo_light.9.png
new file mode 100644
index 0000000000..31e49894ad
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__cab_background_bottom_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__cab_background_top_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__cab_background_top_holo_dark.9.png
new file mode 100644
index 0000000000..7c2cbe5356
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__cab_background_top_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__cab_background_top_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__cab_background_top_holo_light.9.png
new file mode 100644
index 0000000000..30cbdc174a
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__cab_background_top_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__dialog_full_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__dialog_full_holo_dark.9.png
new file mode 100644
index 0000000000..fb3660eab1
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__dialog_full_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__dialog_full_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__dialog_full_holo_light.9.png
new file mode 100644
index 0000000000..f18050ea58
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__dialog_full_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_ab_back_holo_dark.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_ab_back_holo_dark.png
new file mode 100644
index 0000000000..df2d3d158e
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_ab_back_holo_dark.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_ab_back_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_ab_back_holo_light.png
new file mode 100644
index 0000000000..b2aa9c265b
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_ab_back_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_cab_done_holo_dark.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_cab_done_holo_dark.png
new file mode 100644
index 0000000000..a17b6a7892
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_cab_done_holo_dark.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_cab_done_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_cab_done_holo_light.png
new file mode 100644
index 0000000000..b28b3b54f4
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_cab_done_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_clear_disabled.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_clear_disabled.png
new file mode 100644
index 0000000000..79228baed0
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_clear_disabled.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_clear_normal.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_clear_normal.png
new file mode 100644
index 0000000000..86944a879b
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_clear_normal.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_clear_search_api_disabled_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_clear_search_api_disabled_holo_light.png
new file mode 100644
index 0000000000..c0bdf0641a
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_clear_search_api_disabled_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_clear_search_api_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_clear_search_api_holo_light.png
new file mode 100644
index 0000000000..15b86cbb21
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_clear_search_api_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_go.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_go.png
new file mode 100644
index 0000000000..bf19833f2e
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_go.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_go_search_api_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_go_search_api_holo_light.png
new file mode 100644
index 0000000000..8518498eb6
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_go_search_api_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png
new file mode 100644
index 0000000000..ba704b67e3
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_light.png
new file mode 100644
index 0000000000..01d681697f
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_menu_share_holo_dark.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_menu_share_holo_dark.png
new file mode 100644
index 0000000000..6bf21e307e
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_menu_share_holo_dark.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_menu_share_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_menu_share_holo_light.png
new file mode 100644
index 0000000000..70fe31aa22
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_menu_share_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_search.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_search.png
new file mode 100644
index 0000000000..4be72f108b
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_search.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_search_api_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_search_api_holo_light.png
new file mode 100644
index 0000000000..f2e26f8838
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_search_api_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_voice_search.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_voice_search.png
new file mode 100644
index 0000000000..73c6be654a
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_voice_search.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_voice_search_api_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_voice_search_api_holo_light.png
new file mode 100644
index 0000000000..71d838e736
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__ic_voice_search_api_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_activated_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_activated_holo.9.png
new file mode 100644
index 0000000000..3bf8e03623
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_activated_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_divider_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_divider_holo_dark.9.png
new file mode 100644
index 0000000000..986ab0b974
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_divider_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_divider_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_divider_holo_light.9.png
new file mode 100644
index 0000000000..0279e17a12
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_divider_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_focused_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_focused_holo.9.png
new file mode 100644
index 0000000000..7c0599e3a6
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_focused_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_longpressed_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_longpressed_holo.9.png
new file mode 100644
index 0000000000..3bf8e03623
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_longpressed_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_pressed_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_pressed_holo_dark.9.png
new file mode 100644
index 0000000000..6e77525d2d
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_pressed_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_pressed_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_pressed_holo_light.9.png
new file mode 100644
index 0000000000..6e77525d2d
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_pressed_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_selector_disabled_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_selector_disabled_holo_dark.9.png
new file mode 100644
index 0000000000..92da2f0dd3
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_selector_disabled_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_selector_disabled_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_selector_disabled_holo_light.9.png
new file mode 100644
index 0000000000..42cb6463e4
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__list_selector_disabled_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__menu_dropdown_panel_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__menu_dropdown_panel_holo_dark.9.png
new file mode 100644
index 0000000000..460ec46eb0
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__menu_dropdown_panel_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__menu_dropdown_panel_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__menu_dropdown_panel_holo_light.9.png
new file mode 100644
index 0000000000..e84adf2d41
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__menu_dropdown_panel_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_bg_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_bg_holo_dark.9.png
new file mode 100644
index 0000000000..3d946e545d
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_bg_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_bg_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_bg_holo_light.9.png
new file mode 100644
index 0000000000..4bb22f0e10
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_bg_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_primary_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_primary_holo_dark.9.png
new file mode 100644
index 0000000000..ab8ec69844
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_primary_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_primary_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_primary_holo_light.9.png
new file mode 100644
index 0000000000..ab8ec69844
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_primary_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_secondary_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_secondary_holo_dark.9.png
new file mode 100644
index 0000000000..7274274b17
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_secondary_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_secondary_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_secondary_holo_light.9.png
new file mode 100644
index 0000000000..7274274b17
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__progress_secondary_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_48_inner_holo.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_48_inner_holo.png
new file mode 100644
index 0000000000..9458668f02
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_48_inner_holo.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_48_outer_holo.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_48_outer_holo.png
new file mode 100644
index 0000000000..4ce73edce7
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_48_outer_holo.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_default_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_default_holo_dark.9.png
new file mode 100644
index 0000000000..29aff4d43f
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_default_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_default_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_default_holo_light.9.png
new file mode 100644
index 0000000000..4055f70539
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_default_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_disabled_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_disabled_holo_dark.9.png
new file mode 100644
index 0000000000..ea4ee042ea
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_disabled_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_disabled_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_disabled_holo_light.9.png
new file mode 100644
index 0000000000..f74c02b9e1
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_disabled_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_focused_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_focused_holo_dark.9.png
new file mode 100644
index 0000000000..09a2992cca
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_focused_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_focused_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_focused_holo_light.9.png
new file mode 100644
index 0000000000..6536ee6332
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_focused_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_pressed_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_pressed_holo_dark.9.png
new file mode 100644
index 0000000000..202b5b72ee
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_pressed_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_pressed_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_pressed_holo_light.9.png
new file mode 100644
index 0000000000..6de0ba8841
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__spinner_ab_pressed_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__tab_selected_focused_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__tab_selected_focused_holo.9.png
new file mode 100644
index 0000000000..c9972e74bb
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__tab_selected_focused_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__tab_selected_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__tab_selected_holo.9.png
new file mode 100644
index 0000000000..587337caf7
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__tab_selected_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__tab_selected_pressed_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__tab_selected_pressed_holo.9.png
new file mode 100644
index 0000000000..155c4fc753
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__tab_selected_pressed_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__tab_unselected_pressed_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__tab_unselected_pressed_holo.9.png
new file mode 100644
index 0000000000..b1223fe3c4
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__tab_unselected_pressed_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_default_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_default_holo_dark.9.png
new file mode 100644
index 0000000000..081657ee7b
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_default_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_default_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_default_holo_light.9.png
new file mode 100644
index 0000000000..3f312b4651
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_default_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_right_default_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_right_default_holo_dark.9.png
new file mode 100644
index 0000000000..b086fae873
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_right_default_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_right_default_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_right_default_holo_light.9.png
new file mode 100644
index 0000000000..73c336a77a
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_right_default_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_right_selected_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_right_selected_holo_dark.9.png
new file mode 100644
index 0000000000..726e0ff427
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_right_selected_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_right_selected_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_right_selected_holo_light.9.png
new file mode 100644
index 0000000000..726e0ff427
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_right_selected_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_selected_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_selected_holo_dark.9.png
new file mode 100644
index 0000000000..1767c169ee
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_selected_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_selected_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_selected_holo_light.9.png
new file mode 100644
index 0000000000..1767c169ee
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-mdpi/abs__textfield_search_selected_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-v11/abs__progress_medium_holo.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-v11/abs__progress_medium_holo.xml
new file mode 100644
index 0000000000..6bcbdb83f1
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-v11/abs__progress_medium_holo.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_bottom_solid_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_bottom_solid_dark_holo.9.png
new file mode 100644
index 0000000000..5753346996
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_bottom_solid_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_bottom_solid_inverse_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_bottom_solid_inverse_holo.9.png
new file mode 100644
index 0000000000..7e6c047d66
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_bottom_solid_inverse_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png
new file mode 100644
index 0000000000..8155fe8405
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_bottom_transparent_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_bottom_transparent_dark_holo.9.png
new file mode 100644
index 0000000000..6cee9a128d
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_bottom_transparent_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_bottom_transparent_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_bottom_transparent_light_holo.9.png
new file mode 100644
index 0000000000..fa4d76af93
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_bottom_transparent_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_share_pack_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_share_pack_holo_dark.9.png
new file mode 100644
index 0000000000..9a70a5d1e3
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_share_pack_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_share_pack_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_share_pack_holo_light.9.png
new file mode 100644
index 0000000000..14fbee101d
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_share_pack_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_solid_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_solid_dark_holo.9.png
new file mode 100644
index 0000000000..6622cbad34
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_solid_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_solid_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_solid_light_holo.9.png
new file mode 100644
index 0000000000..c427297833
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_solid_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_solid_shadow_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_solid_shadow_holo.9.png
new file mode 100644
index 0000000000..d0df29d8b3
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_solid_shadow_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_stacked_solid_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_stacked_solid_dark_holo.9.png
new file mode 100644
index 0000000000..a0d9c1b957
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_stacked_solid_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_stacked_solid_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_stacked_solid_light_holo.9.png
new file mode 100644
index 0000000000..d36f99fecf
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_stacked_solid_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_stacked_transparent_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_stacked_transparent_dark_holo.9.png
new file mode 100644
index 0000000000..5ad475dc3f
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_stacked_transparent_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_stacked_transparent_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_stacked_transparent_light_holo.9.png
new file mode 100644
index 0000000000..6ade5eeb37
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_stacked_transparent_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_transparent_dark_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_transparent_dark_holo.9.png
new file mode 100644
index 0000000000..719b9234df
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_transparent_dark_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_transparent_light_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_transparent_light_holo.9.png
new file mode 100644
index 0000000000..6da264db26
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ab_transparent_light_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_default_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_default_holo_dark.9.png
new file mode 100644
index 0000000000..7ef2db75e2
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_default_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_default_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_default_holo_light.9.png
new file mode 100644
index 0000000000..2283b4c01f
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_default_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_dark.9.png
new file mode 100644
index 0000000000..6d2039e284
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_light.9.png
new file mode 100644
index 0000000000..3c909b5130
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_dark.9.png
new file mode 100644
index 0000000000..131d1030c9
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_light.9.png
new file mode 100644
index 0000000000..3e7dcdfdba
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__cab_background_bottom_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__cab_background_bottom_holo_dark.9.png
new file mode 100644
index 0000000000..0bd09806f5
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__cab_background_bottom_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__cab_background_bottom_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__cab_background_bottom_holo_light.9.png
new file mode 100644
index 0000000000..43ed26d478
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__cab_background_bottom_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__cab_background_top_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__cab_background_top_holo_dark.9.png
new file mode 100644
index 0000000000..6b3157985e
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__cab_background_top_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__cab_background_top_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__cab_background_top_holo_light.9.png
new file mode 100644
index 0000000000..df0121bb35
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__cab_background_top_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__dialog_full_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__dialog_full_holo_dark.9.png
new file mode 100644
index 0000000000..f4970ad1c3
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__dialog_full_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__dialog_full_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__dialog_full_holo_light.9.png
new file mode 100644
index 0000000000..172fc3b5e3
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__dialog_full_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_ab_back_holo_dark.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_ab_back_holo_dark.png
new file mode 100644
index 0000000000..8ded62fb7b
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_ab_back_holo_dark.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_ab_back_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_ab_back_holo_light.png
new file mode 100644
index 0000000000..517e9f72d0
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_ab_back_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_cab_done_holo_dark.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_cab_done_holo_dark.png
new file mode 100644
index 0000000000..2e06dd01be
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_cab_done_holo_dark.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_cab_done_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_cab_done_holo_light.png
new file mode 100644
index 0000000000..bb19810bc2
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_cab_done_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_clear_disabled.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_clear_disabled.png
new file mode 100644
index 0000000000..e35c5f05ef
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_clear_disabled.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_clear_search_api_disabled_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_clear_search_api_disabled_holo_light.png
new file mode 100644
index 0000000000..7fd7aeb2a6
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_clear_search_api_disabled_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_clear_search_api_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_clear_search_api_holo_light.png
new file mode 100644
index 0000000000..53cfbd3115
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_clear_search_api_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_go.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_go.png
new file mode 100644
index 0000000000..1e2dcfa020
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_go.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_go_search_api_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_go_search_api_holo_light.png
new file mode 100644
index 0000000000..f12eafcdcf
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_go_search_api_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png
new file mode 100644
index 0000000000..a92fb1d4af
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_light.png
new file mode 100644
index 0000000000..930ca8d95e
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_menu_share_holo_dark.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_menu_share_holo_dark.png
new file mode 100644
index 0000000000..45a0f1da0d
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_menu_share_holo_dark.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_menu_share_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_menu_share_holo_light.png
new file mode 100644
index 0000000000..528e554abe
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_menu_share_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_search.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_search.png
new file mode 100644
index 0000000000..998f91be9c
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_search.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_search_api_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_search_api_holo_light.png
new file mode 100644
index 0000000000..a4cdf1c792
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_search_api_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_voice_search.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_voice_search.png
new file mode 100644
index 0000000000..c625a3602b
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_voice_search.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_voice_search_api_holo_light.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_voice_search_api_holo_light.png
new file mode 100644
index 0000000000..c332ba08c2
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__ic_voice_search_api_holo_light.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_activated_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_activated_holo.9.png
new file mode 100644
index 0000000000..eda10e6123
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_activated_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_divider_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_divider_holo_dark.9.png
new file mode 100644
index 0000000000..e62f011d45
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_divider_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_divider_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_divider_holo_light.9.png
new file mode 100644
index 0000000000..65061c0f45
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_divider_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_focused_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_focused_holo.9.png
new file mode 100644
index 0000000000..690cb1eb61
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_focused_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_longpressed_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_longpressed_holo.9.png
new file mode 100644
index 0000000000..eda10e6123
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_longpressed_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_pressed_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_pressed_holo_dark.9.png
new file mode 100644
index 0000000000..e4b33935a3
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_pressed_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_pressed_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_pressed_holo_light.9.png
new file mode 100644
index 0000000000..e4b33935a3
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_pressed_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_selector_disabled_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_selector_disabled_holo_dark.9.png
new file mode 100644
index 0000000000..88726b6916
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_selector_disabled_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_selector_disabled_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_selector_disabled_holo_light.9.png
new file mode 100644
index 0000000000..c6a7d4d87c
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__list_selector_disabled_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_dark.9.png
new file mode 100644
index 0000000000..e2aff72f48
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_light.9.png
new file mode 100644
index 0000000000..93066c8403
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_bg_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_bg_holo_dark.9.png
new file mode 100644
index 0000000000..345f5d3067
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_bg_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_bg_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_bg_holo_light.9.png
new file mode 100644
index 0000000000..c843ef3af2
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_bg_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_primary_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_primary_holo_dark.9.png
new file mode 100644
index 0000000000..c6c3f1ec24
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_primary_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_primary_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_primary_holo_light.9.png
new file mode 100644
index 0000000000..c6c3f1ec24
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_primary_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_secondary_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_secondary_holo_dark.9.png
new file mode 100644
index 0000000000..205b66e2cd
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_secondary_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_secondary_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_secondary_holo_light.9.png
new file mode 100644
index 0000000000..205b66e2cd
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__progress_secondary_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_48_inner_holo.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_48_inner_holo.png
new file mode 100644
index 0000000000..19517c4b0a
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_48_inner_holo.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_48_outer_holo.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_48_outer_holo.png
new file mode 100644
index 0000000000..14143c51c3
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_48_outer_holo.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_default_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_default_holo_dark.9.png
new file mode 100644
index 0000000000..d8929fcd18
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_default_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_default_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_default_holo_light.9.png
new file mode 100644
index 0000000000..9174c4e4bc
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_default_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_dark.9.png
new file mode 100644
index 0000000000..3015d30708
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_light.9.png
new file mode 100644
index 0000000000..126637d119
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_focused_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_focused_holo_dark.9.png
new file mode 100644
index 0000000000..d45c7a864d
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_focused_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_focused_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_focused_holo_light.9.png
new file mode 100644
index 0000000000..29036b907a
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_focused_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_dark.9.png
new file mode 100644
index 0000000000..2cb34d7f60
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_light.9.png
new file mode 100644
index 0000000000..82f752fdc2
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__tab_selected_focused_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__tab_selected_focused_holo.9.png
new file mode 100644
index 0000000000..03cfb0945d
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__tab_selected_focused_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__tab_selected_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__tab_selected_holo.9.png
new file mode 100644
index 0000000000..e4229f26b2
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__tab_selected_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__tab_selected_pressed_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__tab_selected_pressed_holo.9.png
new file mode 100644
index 0000000000..e862cb1215
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__tab_selected_pressed_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__tab_unselected_pressed_holo.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__tab_unselected_pressed_holo.9.png
new file mode 100644
index 0000000000..f1eb67323a
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__tab_unselected_pressed_holo.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_default_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_default_holo_dark.9.png
new file mode 100644
index 0000000000..8fdbbf3ad7
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_default_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_default_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_default_holo_light.9.png
new file mode 100644
index 0000000000..4e9ae43c25
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_default_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_right_default_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_right_default_holo_dark.9.png
new file mode 100644
index 0000000000..98f4871bb5
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_right_default_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_right_default_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_right_default_holo_light.9.png
new file mode 100644
index 0000000000..733373ed38
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_right_default_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_right_selected_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_right_selected_holo_dark.9.png
new file mode 100644
index 0000000000..0c6bb036db
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_right_selected_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_right_selected_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_right_selected_holo_light.9.png
new file mode 100644
index 0000000000..0c6bb036db
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_right_selected_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_selected_holo_dark.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_selected_holo_dark.9.png
new file mode 100644
index 0000000000..e5bfd8ad33
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_selected_holo_dark.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_selected_holo_light.9.png b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_selected_holo_light.9.png
new file mode 100644
index 0000000000..1743da6b4e
Binary files /dev/null and b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable-xhdpi/abs__textfield_search_selected_holo_light.9.png differ
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__activated_background_holo_dark.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__activated_background_holo_dark.xml
new file mode 100644
index 0000000000..85c2c02129
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__activated_background_holo_dark.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__activated_background_holo_light.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__activated_background_holo_light.xml
new file mode 100644
index 0000000000..85c2c02129
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__activated_background_holo_light.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__btn_cab_done_holo_dark.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__btn_cab_done_holo_dark.xml
new file mode 100644
index 0000000000..cab896283c
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__btn_cab_done_holo_dark.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__btn_cab_done_holo_light.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__btn_cab_done_holo_light.xml
new file mode 100644
index 0000000000..42ba8a0df0
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__btn_cab_done_holo_light.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__ic_clear.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__ic_clear.xml
new file mode 100644
index 0000000000..a16f4b22e8
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__ic_clear.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__ic_clear_holo_light.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__ic_clear_holo_light.xml
new file mode 100644
index 0000000000..256de80fb3
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__ic_clear_holo_light.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__ic_menu_moreoverflow_holo_dark.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__ic_menu_moreoverflow_holo_dark.xml
new file mode 100644
index 0000000000..2588a492db
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__ic_menu_moreoverflow_holo_dark.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__ic_menu_moreoverflow_holo_light.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__ic_menu_moreoverflow_holo_light.xml
new file mode 100644
index 0000000000..e2078c9679
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__ic_menu_moreoverflow_holo_light.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__item_background_holo_dark.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__item_background_holo_dark.xml
new file mode 100644
index 0000000000..d99b7a426b
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__item_background_holo_dark.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__item_background_holo_light.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__item_background_holo_light.xml
new file mode 100644
index 0000000000..da5fb2e86e
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__item_background_holo_light.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__list_selector_background_transition_holo_dark.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__list_selector_background_transition_holo_dark.xml
new file mode 100644
index 0000000000..b2ce4f0f77
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__list_selector_background_transition_holo_dark.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__list_selector_background_transition_holo_light.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__list_selector_background_transition_holo_light.xml
new file mode 100644
index 0000000000..d7e31b1d1f
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__list_selector_background_transition_holo_light.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__list_selector_holo_dark.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__list_selector_holo_dark.xml
new file mode 100644
index 0000000000..08b8b12f37
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__list_selector_holo_dark.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__list_selector_holo_light.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__list_selector_holo_light.xml
new file mode 100644
index 0000000000..ada490bf9f
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__list_selector_holo_light.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__progress_horizontal_holo_dark.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__progress_horizontal_holo_dark.xml
new file mode 100644
index 0000000000..bd19140abf
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__progress_horizontal_holo_dark.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__progress_horizontal_holo_light.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__progress_horizontal_holo_light.xml
new file mode 100644
index 0000000000..321f07c8b2
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__progress_horizontal_holo_light.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__progress_medium_holo.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__progress_medium_holo.xml
new file mode 100644
index 0000000000..6d4814f862
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__progress_medium_holo.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__search_dropdown_dark.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__search_dropdown_dark.xml
new file mode 100644
index 0000000000..26284187a7
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__search_dropdown_dark.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__search_dropdown_light.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__search_dropdown_light.xml
new file mode 100644
index 0000000000..0d00c58788
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__search_dropdown_light.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__spinner_ab_holo_dark.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__spinner_ab_holo_dark.xml
new file mode 100644
index 0000000000..4af5e22a90
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__spinner_ab_holo_dark.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__spinner_ab_holo_light.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__spinner_ab_holo_light.xml
new file mode 100644
index 0000000000..b785084782
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__spinner_ab_holo_light.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__tab_indicator_ab_holo.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__tab_indicator_ab_holo.xml
new file mode 100644
index 0000000000..d34e208117
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__tab_indicator_ab_holo.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__textfield_searchview_holo_dark.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__textfield_searchview_holo_dark.xml
new file mode 100644
index 0000000000..b6d58c040a
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__textfield_searchview_holo_dark.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__textfield_searchview_holo_light.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__textfield_searchview_holo_light.xml
new file mode 100644
index 0000000000..3d6acf8085
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__textfield_searchview_holo_light.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__textfield_searchview_right_holo_dark.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__textfield_searchview_right_holo_dark.xml
new file mode 100644
index 0000000000..05ff4eda55
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__textfield_searchview_right_holo_dark.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__textfield_searchview_right_holo_light.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__textfield_searchview_right_holo_light.xml
new file mode 100644
index 0000000000..f6d61e57ab
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/drawable/abs__textfield_searchview_right_holo_light.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout-large/abs__action_mode_close_item.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout-large/abs__action_mode_close_item.xml
new file mode 100644
index 0000000000..8811dad8d6
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout-large/abs__action_mode_close_item.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout-v14/sherlock_spinner_dropdown_item.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout-v14/sherlock_spinner_dropdown_item.xml
new file mode 100644
index 0000000000..6c183c0596
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout-v14/sherlock_spinner_dropdown_item.xml
@@ -0,0 +1,26 @@
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout-v14/sherlock_spinner_item.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout-v14/sherlock_spinner_item.xml
new file mode 100644
index 0000000000..61dc02527f
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout-v14/sherlock_spinner_item.xml
@@ -0,0 +1,26 @@
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout-xlarge/abs__screen_action_bar.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout-xlarge/abs__screen_action_bar.xml
new file mode 100644
index 0000000000..040df44abb
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout-xlarge/abs__screen_action_bar.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout-xlarge/abs__screen_action_bar_overlay.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout-xlarge/abs__screen_action_bar_overlay.xml
new file mode 100644
index 0000000000..c64ef141b3
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout-xlarge/abs__screen_action_bar_overlay.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_bar_home.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_bar_home.xml
new file mode 100644
index 0000000000..5c1e9ec4b3
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_bar_home.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_bar_tab.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_bar_tab.xml
new file mode 100644
index 0000000000..f46f7a044b
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_bar_tab.xml
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_bar_tab_bar_view.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_bar_tab_bar_view.xml
new file mode 100644
index 0000000000..0d51220c90
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_bar_tab_bar_view.xml
@@ -0,0 +1,6 @@
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_bar_title_item.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_bar_title_item.xml
new file mode 100644
index 0000000000..dd69acadaa
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_bar_title_item.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_menu_item_layout.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_menu_item_layout.xml
new file mode 100644
index 0000000000..13149fd630
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_menu_item_layout.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_menu_layout.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_menu_layout.xml
new file mode 100644
index 0000000000..a6f8e53f8a
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_menu_layout.xml
@@ -0,0 +1,23 @@
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_mode_bar.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_mode_bar.xml
new file mode 100644
index 0000000000..7168dc77fd
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_mode_bar.xml
@@ -0,0 +1,24 @@
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_mode_close_item.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_mode_close_item.xml
new file mode 100644
index 0000000000..875ec3e1b0
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__action_mode_close_item.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__activity_chooser_view.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__activity_chooser_view.xml
new file mode 100644
index 0000000000..6a0ac9ece0
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__activity_chooser_view.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__activity_chooser_view_list_item.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__activity_chooser_view_list_item.xml
new file mode 100644
index 0000000000..b430032a14
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__activity_chooser_view_list_item.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__dialog_title_holo.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__dialog_title_holo.xml
new file mode 100644
index 0000000000..ab2b0ee6ce
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__dialog_title_holo.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__list_menu_item_checkbox.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__list_menu_item_checkbox.xml
new file mode 100644
index 0000000000..39aca3a8dd
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__list_menu_item_checkbox.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__list_menu_item_icon.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__list_menu_item_icon.xml
new file mode 100644
index 0000000000..55ab28a24d
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__list_menu_item_icon.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__list_menu_item_layout.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__list_menu_item_layout.xml
new file mode 100644
index 0000000000..147f36fe85
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__list_menu_item_layout.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__list_menu_item_radio.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__list_menu_item_radio.xml
new file mode 100644
index 0000000000..ff54bbecd1
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__list_menu_item_radio.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__popup_menu_item_layout.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__popup_menu_item_layout.xml
new file mode 100644
index 0000000000..d42425ad32
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__popup_menu_item_layout.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__screen_action_bar.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__screen_action_bar.xml
new file mode 100644
index 0000000000..1fb82fe9a4
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__screen_action_bar.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__screen_action_bar_overlay.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__screen_action_bar_overlay.xml
new file mode 100644
index 0000000000..0961ef561a
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__screen_action_bar_overlay.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__screen_simple.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__screen_simple.xml
new file mode 100644
index 0000000000..33e2dea0de
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__screen_simple.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__screen_simple_overlay_action_mode.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__screen_simple_overlay_action_mode.xml
new file mode 100644
index 0000000000..f8b9fb1859
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__screen_simple_overlay_action_mode.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__search_dropdown_item_icons_2line.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__search_dropdown_item_icons_2line.xml
new file mode 100644
index 0000000000..e1d3dc49cb
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__search_dropdown_item_icons_2line.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__search_view.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__search_view.xml
new file mode 100644
index 0000000000..6ba319121c
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__search_view.xml
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__simple_dropdown_hint.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__simple_dropdown_hint.xml
new file mode 100644
index 0000000000..8fc0eb12cb
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/abs__simple_dropdown_hint.xml
@@ -0,0 +1,29 @@
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/sherlock_spinner_dropdown_item.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/sherlock_spinner_dropdown_item.xml
new file mode 100644
index 0000000000..a6c6252d26
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/sherlock_spinner_dropdown_item.xml
@@ -0,0 +1,26 @@
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/sherlock_spinner_item.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/sherlock_spinner_item.xml
new file mode 100644
index 0000000000..bea7401781
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/layout/sherlock_spinner_item.xml
@@ -0,0 +1,26 @@
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-land/abs__dimens.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-land/abs__dimens.xml
new file mode 100644
index 0000000000..502cc16a30
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-land/abs__dimens.xml
@@ -0,0 +1,33 @@
+
+
+
+
+ 40dip
+
+ 4dip
+
+ 16dp
+
+ 12dp
+
+ -2dp
+
+ 4dip
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-large-hdpi-1024x600/abs__dimens.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-large-hdpi-1024x600/abs__dimens.xml
new file mode 100644
index 0000000000..3312cfa7fd
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-large-hdpi-1024x600/abs__dimens.xml
@@ -0,0 +1,33 @@
+
+
+
+
+ 48dip
+
+ 8dip
+
+ 18dp
+
+ 14dp
+
+ -3dp
+
+ 5dip
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-large-land-hdpi-1024x600/abs__dimens.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-large-land-hdpi-1024x600/abs__dimens.xml
new file mode 100644
index 0000000000..502cc16a30
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-large-land-hdpi-1024x600/abs__dimens.xml
@@ -0,0 +1,33 @@
+
+
+
+
+ 40dip
+
+ 4dip
+
+ 16dp
+
+ 12dp
+
+ -2dp
+
+ 4dip
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-large-land-mdpi-1024x600/abs__dimens.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-large-land-mdpi-1024x600/abs__dimens.xml
new file mode 100644
index 0000000000..3312cfa7fd
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-large-land-mdpi-1024x600/abs__dimens.xml
@@ -0,0 +1,33 @@
+
+
+
+
+ 48dip
+
+ 8dip
+
+ 18dp
+
+ 14dp
+
+ -3dp
+
+ 5dip
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-large-mdpi-1024x600/abs__dimens.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-large-mdpi-1024x600/abs__dimens.xml
new file mode 100644
index 0000000000..35910333b2
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-large-mdpi-1024x600/abs__dimens.xml
@@ -0,0 +1,36 @@
+
+
+
+
+ 56dip
+
+ 4dip
+
+ 18dp
+
+ 14dp
+
+ -3dp
+
+ 9dip
+
+
+ 64dip
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-large/abs__dimens.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-large/abs__dimens.xml
new file mode 100644
index 0000000000..63b12f7f3b
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-large/abs__dimens.xml
@@ -0,0 +1,29 @@
+
+
+
+
+ 55%
+
+ 80%
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-sw600dp/abs__bools.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-sw600dp/abs__bools.xml
new file mode 100644
index 0000000000..7a48e1542e
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-sw600dp/abs__bools.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ false
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-sw600dp/abs__dimens.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-sw600dp/abs__dimens.xml
new file mode 100644
index 0000000000..f678538173
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-sw600dp/abs__dimens.xml
@@ -0,0 +1,38 @@
+
+
+
+
+ 56dip
+
+ 4dip
+
+ 18dp
+
+ 14dp
+
+ -3dp
+
+ 9dip
+
+ 5
+
+
+ 64dip
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-v11/abs__themes.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-v11/abs__themes.xml
new file mode 100644
index 0000000000..03473572c2
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-v11/abs__themes.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-v14/abs__styles.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-v14/abs__styles.xml
new file mode 100644
index 0000000000..88a60dd92d
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-v14/abs__styles.xml
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-v14/abs__themes.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-v14/abs__themes.xml
new file mode 100644
index 0000000000..5fac1ab584
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-v14/abs__themes.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-w360dp/abs__dimens.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-w360dp/abs__dimens.xml
new file mode 100644
index 0000000000..6f49d7e47b
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-w360dp/abs__dimens.xml
@@ -0,0 +1,22 @@
+
+
+
+ 3
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-w480dp/abs__bools.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-w480dp/abs__bools.xml
new file mode 100644
index 0000000000..3eaf4aee9d
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-w480dp/abs__bools.xml
@@ -0,0 +1,22 @@
+
+
+
+ true
+ false
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-w480dp/abs__config.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-w480dp/abs__config.xml
new file mode 100644
index 0000000000..88357b0a76
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-w480dp/abs__config.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+ true
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-w500dp/abs__dimens.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-w500dp/abs__dimens.xml
new file mode 100644
index 0000000000..2fd4deea2d
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-w500dp/abs__dimens.xml
@@ -0,0 +1,22 @@
+
+
+
+ 4
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-w600dp/abs__dimens.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-w600dp/abs__dimens.xml
new file mode 100644
index 0000000000..b085952d32
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-w600dp/abs__dimens.xml
@@ -0,0 +1,22 @@
+
+
+
+ 5
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-xlarge/abs__dimens.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-xlarge/abs__dimens.xml
new file mode 100644
index 0000000000..bfc535de16
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values-xlarge/abs__dimens.xml
@@ -0,0 +1,45 @@
+
+
+
+
+ 56dip
+
+ 4dip
+
+ 18dp
+
+ 14dp
+
+ -3dp
+
+ 9dip
+
+
+ 64dip
+
+
+ 45%
+
+ 72%
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__attrs.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__attrs.xml
new file mode 100644
index 0000000000..32631ca8d3
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__attrs.xml
@@ -0,0 +1,432 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__bools.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__bools.xml
new file mode 100644
index 0000000000..0b432448d9
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__bools.xml
@@ -0,0 +1,22 @@
+
+
+
+
+ false
+ true
+ true
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__colors.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__colors.xml
new file mode 100644
index 0000000000..625c632ff7
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__colors.xml
@@ -0,0 +1,27 @@
+
+
+
+
+ #ff000000
+ #fff3f3f3
+ @color/abs__background_holo_light
+ @color/abs__background_holo_dark
+ #ff4c4c4c
+ #ffb2b2b2
+ @color/abs__bright_foreground_holo_light
+ @color/abs__bright_foreground_holo_dark
+ #ff33b5e5
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__config.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__config.xml
new file mode 100644
index 0000000000..4c7b5d4598
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__config.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+ 320dp
+
+
+ false
+
+
+ true
+
+
+ false
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__dimens.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__dimens.xml
new file mode 100644
index 0000000000..831289e073
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__dimens.xml
@@ -0,0 +1,67 @@
+
+
+
+
+ 48dip
+
+ 8dip
+
+ 18dp
+
+ 14dp
+
+ -3dp
+
+ 5dip
+
+ 2
+
+
+ 56dip
+
+
+ 64dip
+
+
+ 65%
+
+ 95%
+
+
+
+ 8dip
+
+
+ 8dip
+
+
+ 32dip
+
+
+
+ 160dip
+
+
+ 320dip
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__ids.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__ids.xml
new file mode 100644
index 0000000000..f9f56045b3
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__ids.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__strings.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__strings.xml
new file mode 100644
index 0000000000..06a2a2af4e
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__strings.xml
@@ -0,0 +1,53 @@
+
+
+
+
+ Navigate home
+
+ Navigate up
+
+ More options
+
+
+ Done
+
+
+ See all...
+
+ Select activity
+
+ Share with...
+
+ Choose an application
+
+ Share with
+
+ Share with %s
+
+
+ Search
+
+ Search query
+
+ Clear query
+
+ Submit query
+
+ Voice search
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__styles.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__styles.xml
new file mode 100644
index 0000000000..45a18c1833
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__styles.xml
@@ -0,0 +1,412 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__themes.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__themes.xml
new file mode 100644
index 0000000000..634fa798b0
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/res/values/abs__themes.xml
@@ -0,0 +1,239 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/android/support/v4/app/Watson.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/android/support/v4/app/Watson.java
new file mode 100644
index 0000000000..d93de4c6a1
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/android/support/v4/app/Watson.java
@@ -0,0 +1,144 @@
+package android.support.v4.app;
+
+import android.util.Log;
+import android.view.View;
+import android.view.Window;
+import com.actionbarsherlock.ActionBarSherlock.OnCreatePanelMenuListener;
+import com.actionbarsherlock.ActionBarSherlock.OnMenuItemSelectedListener;
+import com.actionbarsherlock.ActionBarSherlock.OnPreparePanelListener;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+
+import java.util.ArrayList;
+
+/** I'm in ur package. Stealing ur variables. */
+public abstract class Watson extends FragmentActivity implements OnCreatePanelMenuListener, OnPreparePanelListener, OnMenuItemSelectedListener {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "Watson";
+
+ /** Fragment interface for menu creation callback. */
+ public interface OnCreateOptionsMenuListener {
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater);
+ }
+ /** Fragment interface for menu preparation callback. */
+ public interface OnPrepareOptionsMenuListener {
+ public void onPrepareOptionsMenu(Menu menu);
+ }
+ /** Fragment interface for menu item selection callback. */
+ public interface OnOptionsItemSelectedListener {
+ public boolean onOptionsItemSelected(MenuItem item);
+ }
+
+ private ArrayList mCreatedMenus;
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Sherlock menu handling
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public boolean onCreatePanelMenu(int featureId, Menu menu) {
+ if (DEBUG) Log.d(TAG, "[onCreatePanelMenu] featureId: " + featureId + ", menu: " + menu);
+
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+ boolean result = onCreateOptionsMenu(menu);
+ if (DEBUG) Log.d(TAG, "[onCreatePanelMenu] activity create result: " + result);
+
+ MenuInflater inflater = getSupportMenuInflater();
+ boolean show = false;
+ ArrayList newMenus = null;
+ if (mFragments.mAdded != null) {
+ for (int i = 0; i < mFragments.mAdded.size(); i++) {
+ Fragment f = mFragments.mAdded.get(i);
+ if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible && f instanceof OnCreateOptionsMenuListener) {
+ show = true;
+ ((OnCreateOptionsMenuListener)f).onCreateOptionsMenu(menu, inflater);
+ if (newMenus == null) {
+ newMenus = new ArrayList();
+ }
+ newMenus.add(f);
+ }
+ }
+ }
+
+ if (mCreatedMenus != null) {
+ for (int i = 0; i < mCreatedMenus.size(); i++) {
+ Fragment f = mCreatedMenus.get(i);
+ if (newMenus == null || !newMenus.contains(f)) {
+ f.onDestroyOptionsMenu();
+ }
+ }
+ }
+
+ mCreatedMenus = newMenus;
+
+ if (DEBUG) Log.d(TAG, "[onCreatePanelMenu] fragments create result: " + show);
+ result |= show;
+
+ if (DEBUG) Log.d(TAG, "[onCreatePanelMenu] returning " + result);
+ return result;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onPreparePanel(int featureId, View view, Menu menu) {
+ if (DEBUG) Log.d(TAG, "[onPreparePanel] featureId: " + featureId + ", view: " + view + " menu: " + menu);
+
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+ boolean result = onPrepareOptionsMenu(menu);
+ if (DEBUG) Log.d(TAG, "[onPreparePanel] activity prepare result: " + result);
+
+ boolean show = false;
+ if (mFragments.mAdded != null) {
+ for (int i = 0; i < mFragments.mAdded.size(); i++) {
+ Fragment f = mFragments.mAdded.get(i);
+ if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible && f instanceof OnPrepareOptionsMenuListener) {
+ show = true;
+ ((OnPrepareOptionsMenuListener)f).onPrepareOptionsMenu(menu);
+ }
+ }
+ }
+
+ if (DEBUG) Log.d(TAG, "[onPreparePanel] fragments prepare result: " + show);
+ result |= show;
+
+ result &= menu.hasVisibleItems();
+ if (DEBUG) Log.d(TAG, "[onPreparePanel] returning " + result);
+ return result;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ if (DEBUG) Log.d(TAG, "[onMenuItemSelected] featureId: " + featureId + ", item: " + item);
+
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+ if (onOptionsItemSelected(item)) {
+ return true;
+ }
+
+ if (mFragments.mAdded != null) {
+ for (int i = 0; i < mFragments.mAdded.size(); i++) {
+ Fragment f = mFragments.mAdded.get(i);
+ if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible && f instanceof OnOptionsItemSelectedListener) {
+ if (((OnOptionsItemSelectedListener)f).onOptionsItemSelected(item)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public abstract boolean onCreateOptionsMenu(Menu menu);
+
+ public abstract boolean onPrepareOptionsMenu(Menu menu);
+
+ public abstract boolean onOptionsItemSelected(MenuItem item);
+
+ public abstract MenuInflater getSupportMenuInflater();
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/ActionBarSherlock.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/ActionBarSherlock.java
new file mode 100644
index 0000000000..ab160f8360
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/ActionBarSherlock.java
@@ -0,0 +1,794 @@
+package com.actionbarsherlock;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Iterator;
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.internal.ActionBarSherlockCompat;
+import com.actionbarsherlock.internal.ActionBarSherlockNative;
+import com.actionbarsherlock.view.ActionMode;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+
+/**
+ *
Helper for implementing the action bar design pattern across all versions
+ * of Android.
+ *
+ *
This class will manage interaction with a custom action bar based on the
+ * Android 4.0 source code. The exposed API mirrors that of its native
+ * counterpart and you should refer to its documentation for instruction.
+ *
+ * @author Jake Wharton
+ */
+public abstract class ActionBarSherlock {
+ protected static final String TAG = "ActionBarSherlock";
+ protected static final boolean DEBUG = false;
+
+ private static final Class>[] CONSTRUCTOR_ARGS = new Class[] { Activity.class, int.class };
+ private static final HashMap> IMPLEMENTATIONS =
+ new HashMap>();
+
+ static {
+ //Register our two built-in implementations
+ registerImplementation(ActionBarSherlockCompat.class);
+ registerImplementation(ActionBarSherlockNative.class);
+ }
+
+
+ /**
+ *
Denotes an implementation of ActionBarSherlock which provides an
+ * action bar-enhanced experience.
+ */
+ @Target(ElementType.TYPE)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface Implementation {
+ static final int DEFAULT_API = -1;
+ static final int DEFAULT_DPI = -1;
+
+ int api() default DEFAULT_API;
+ int dpi() default DEFAULT_DPI;
+ }
+
+
+ /** Activity interface for menu creation callback. */
+ public interface OnCreatePanelMenuListener {
+ public boolean onCreatePanelMenu(int featureId, Menu menu);
+ }
+ /** Activity interface for menu creation callback. */
+ public interface OnCreateOptionsMenuListener {
+ public boolean onCreateOptionsMenu(Menu menu);
+ }
+ /** Activity interface for menu item selection callback. */
+ public interface OnMenuItemSelectedListener {
+ public boolean onMenuItemSelected(int featureId, MenuItem item);
+ }
+ /** Activity interface for menu item selection callback. */
+ public interface OnOptionsItemSelectedListener {
+ public boolean onOptionsItemSelected(MenuItem item);
+ }
+ /** Activity interface for menu preparation callback. */
+ public interface OnPreparePanelListener {
+ public boolean onPreparePanel(int featureId, View view, Menu menu);
+ }
+ /** Activity interface for menu preparation callback. */
+ public interface OnPrepareOptionsMenuListener {
+ public boolean onPrepareOptionsMenu(Menu menu);
+ }
+ /** Activity interface for action mode finished callback. */
+ public interface OnActionModeFinishedListener {
+ public void onActionModeFinished(ActionMode mode);
+ }
+ /** Activity interface for action mode started callback. */
+ public interface OnActionModeStartedListener {
+ public void onActionModeStarted(ActionMode mode);
+ }
+
+
+ /**
+ * If set, the logic in these classes will assume that an {@link Activity}
+ * is dispatching all of the required events to the class. This flag should
+ * only be used internally or if you are creating your own base activity
+ * modeled after one of the included types (e.g., {@code SherlockActivity}).
+ */
+ public static final int FLAG_DELEGATE = 1;
+
+
+ /**
+ * Register an ActionBarSherlock implementation.
+ *
+ * @param implementationClass Target implementation class which extends
+ * {@link ActionBarSherlock}. This class must also be annotated with
+ * {@link Implementation}.
+ */
+ public static void registerImplementation(Class extends ActionBarSherlock> implementationClass) {
+ if (!implementationClass.isAnnotationPresent(Implementation.class)) {
+ throw new IllegalArgumentException("Class " + implementationClass.getSimpleName() + " is not annotated with @Implementation");
+ } else if (IMPLEMENTATIONS.containsValue(implementationClass)) {
+ if (DEBUG) Log.w(TAG, "Class " + implementationClass.getSimpleName() + " already registered");
+ return;
+ }
+
+ Implementation impl = implementationClass.getAnnotation(Implementation.class);
+ if (DEBUG) Log.i(TAG, "Registering " + implementationClass.getSimpleName() + " with qualifier " + impl);
+ IMPLEMENTATIONS.put(impl, implementationClass);
+ }
+
+ /**
+ * Unregister an ActionBarSherlock implementation. This should be
+ * considered very volatile and you should only use it if you know what
+ * you are doing. You have been warned.
+ *
+ * @param implementationClass Target implementation class.
+ * @return Boolean indicating whether the class was removed.
+ */
+ public static boolean unregisterImplementation(Class extends ActionBarSherlock> implementationClass) {
+ return IMPLEMENTATIONS.values().remove(implementationClass);
+ }
+
+ /**
+ * Wrap an activity with an action bar abstraction which will enable the
+ * use of a custom implementation on platforms where a native version does
+ * not exist.
+ *
+ * @param activity Activity to wrap.
+ * @return Instance to interact with the action bar.
+ */
+ public static ActionBarSherlock wrap(Activity activity) {
+ return wrap(activity, 0);
+ }
+
+ /**
+ * Wrap an activity with an action bar abstraction which will enable the
+ * use of a custom implementation on platforms where a native version does
+ * not exist.
+ *
+ * @param activity Owning activity.
+ * @param flags Option flags to control behavior.
+ * @return Instance to interact with the action bar.
+ */
+ public static ActionBarSherlock wrap(Activity activity, int flags) {
+ //Create a local implementation map we can modify
+ HashMap> impls =
+ new HashMap>(IMPLEMENTATIONS);
+ boolean hasQualfier;
+
+ /* DPI FILTERING */
+ hasQualfier = false;
+ for (Implementation key : impls.keySet()) {
+ //Only honor TVDPI as a specific qualifier
+ if (key.dpi() == DisplayMetrics.DENSITY_TV) {
+ hasQualfier = true;
+ break;
+ }
+ }
+ if (hasQualfier) {
+ final boolean isTvDpi = activity.getResources().getDisplayMetrics().densityDpi == DisplayMetrics.DENSITY_TV;
+ for (Iterator keys = impls.keySet().iterator(); keys.hasNext(); ) {
+ int keyDpi = keys.next().dpi();
+ if ((isTvDpi && keyDpi != DisplayMetrics.DENSITY_TV)
+ || (!isTvDpi && keyDpi == DisplayMetrics.DENSITY_TV)) {
+ keys.remove();
+ }
+ }
+ }
+
+ /* API FILTERING */
+ hasQualfier = false;
+ for (Implementation key : impls.keySet()) {
+ if (key.api() != Implementation.DEFAULT_API) {
+ hasQualfier = true;
+ break;
+ }
+ }
+ if (hasQualfier) {
+ final int runtimeApi = Build.VERSION.SDK_INT;
+ int bestApi = 0;
+ for (Iterator keys = impls.keySet().iterator(); keys.hasNext(); ) {
+ int keyApi = keys.next().api();
+ if (keyApi > runtimeApi) {
+ keys.remove();
+ } else if (keyApi > bestApi) {
+ bestApi = keyApi;
+ }
+ }
+ for (Iterator keys = impls.keySet().iterator(); keys.hasNext(); ) {
+ if (keys.next().api() != bestApi) {
+ keys.remove();
+ }
+ }
+ }
+
+ if (impls.size() > 1) {
+ throw new IllegalStateException("More than one implementation matches configuration.");
+ }
+ if (impls.isEmpty()) {
+ throw new IllegalStateException("No implementations match configuration.");
+ }
+ Class extends ActionBarSherlock> impl = impls.values().iterator().next();
+ if (DEBUG) Log.i(TAG, "Using implementation: " + impl.getSimpleName());
+
+ try {
+ Constructor extends ActionBarSherlock> ctor = impl.getConstructor(CONSTRUCTOR_ARGS);
+ return ctor.newInstance(activity, flags);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ /** Activity which is displaying the action bar. Also used for context. */
+ protected final Activity mActivity;
+ /** Whether delegating actions for the activity or managing ourselves. */
+ protected final boolean mIsDelegate;
+
+ /** Reference to our custom menu inflater which supports action items. */
+ protected MenuInflater mMenuInflater;
+
+
+
+ protected ActionBarSherlock(Activity activity, int flags) {
+ if (DEBUG) Log.d(TAG, "[] activity: " + activity + ", flags: " + flags);
+
+ mActivity = activity;
+ mIsDelegate = (flags & FLAG_DELEGATE) != 0;
+ }
+
+
+ /**
+ * Get the current action bar instance.
+ *
+ * @return Action bar instance.
+ */
+ public abstract ActionBar getActionBar();
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Lifecycle and interaction callbacks when delegating
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Notify action bar of a configuration change event. Should be dispatched
+ * after the call to the superclass implementation.
+ *
+ *
+ *
+ * @param newConfig The new device configuration.
+ */
+ public void dispatchConfigurationChanged(Configuration newConfig) {}
+
+ /**
+ * Notify the action bar that the activity has finished its resuming. This
+ * should be dispatched after the call to the superclass implementation.
+ *
+ *
+ */
+ public void dispatchPostResume() {}
+
+ /**
+ * Notify the action bar that the activity is pausing. This should be
+ * dispatched before the call to the superclass implementation.
+ *
+ *
+ */
+ public void dispatchPause() {}
+
+ /**
+ * Notify the action bar that the activity is stopping. This should be
+ * called before the superclass implementation.
+ *
+ *
+ */
+ public void dispatchStop() {}
+
+ /**
+ * Indicate that the menu should be recreated by calling
+ * {@link OnCreateOptionsMenuListener#onCreateOptionsMenu(com.actionbarsherlock.view.Menu)}.
+ */
+ public abstract void dispatchInvalidateOptionsMenu();
+
+ /**
+ * Notify the action bar that it should display its overflow menu if it is
+ * appropriate for the device. The implementation should conditionally
+ * call the superclass method only if this method returns {@code false}.
+ *
+ *
+ *
+ * @return {@code true} if the opening of the menu was handled internally.
+ */
+ public boolean dispatchOpenOptionsMenu() {
+ return false;
+ }
+
+ /**
+ * Notify the action bar that it should close its overflow menu if it is
+ * appropriate for the device. This implementation should conditionally
+ * call the superclass method only if this method returns {@code false}.
+ *
+ *
+ *
+ * @return {@code true} if the closing of the menu was handled internally.
+ */
+ public boolean dispatchCloseOptionsMenu() {
+ return false;
+ }
+
+ /**
+ * Notify the class that the activity has finished its creation. This
+ * should be called after the superclass implementation.
+ *
+ *
+ *
+ * @param savedInstanceState If the activity is being re-initialized after
+ * previously being shut down then this Bundle
+ * contains the data it most recently supplied in
+ * {@link Activity#}onSaveInstanceState(Bundle)}.
+ * Note: Otherwise it is null.
+ */
+ public void dispatchPostCreate(Bundle savedInstanceState) {}
+
+ /**
+ * Notify the action bar that the title has changed and the action bar
+ * should be updated to reflect the change. This should be called before
+ * the superclass implementation.
+ *
+ *
+ *
+ * @param title New activity title.
+ * @param color New activity color.
+ */
+ public void dispatchTitleChanged(CharSequence title, int color) {}
+
+ /**
+ * Notify the action bar the user has created a key event. This is used to
+ * toggle the display of the overflow action item with the menu key and to
+ * close the action mode or expanded action item with the back key.
+ *
+ *
+ *
+ * @param event Description of the key event.
+ * @return {@code true} if the event was handled.
+ */
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * Notify the action bar that the Activity has triggered a menu creation
+ * which should happen on the conclusion of {@link Activity#onCreate}. This
+ * will be used to gain a reference to the native menu for native and
+ * overflow binding as well as to indicate when compatibility create should
+ * occur for the first time.
+ *
+ * @param menu Activity native menu.
+ * @return {@code true} since we always want to say that we have a native
+ */
+ public abstract boolean dispatchCreateOptionsMenu(android.view.Menu menu);
+
+ /**
+ * Notify the action bar that the Activity has triggered a menu preparation
+ * which usually means that the user has requested the overflow menu via a
+ * hardware menu key. You should return the result of this method call and
+ * not call the superclass implementation.
+ *
+ *
+ * @Override
+ * public final boolean onPrepareOptionsMenu(android.view.Menu menu) {
+ * return mSherlock.dispatchPrepareOptionsMenu(menu);
+ * }
+ *
+ *
+ * @param menu Activity native menu.
+ * @return {@code true} if menu display should proceed.
+ */
+ public abstract boolean dispatchPrepareOptionsMenu(android.view.Menu menu);
+
+ /**
+ * Notify the action bar that a native options menu item has been selected.
+ * The implementation should return the result of this method call.
+ *
+ *
+ * @Override
+ * public final boolean onOptionsItemSelected(android.view.MenuItem item) {
+ * return mSherlock.dispatchOptionsItemSelected(item);
+ * }
+ *
+ *
+ * @param item Options menu item.
+ * @return @{code true} if the selection was handled.
+ */
+ public abstract boolean dispatchOptionsItemSelected(android.view.MenuItem item);
+
+ /**
+ * Notify the action bar that the overflow menu has been opened. The
+ * implementation should conditionally return {@code true} if this method
+ * returns {@code true}, otherwise return the result of the superclass
+ * method.
+ *
+ *
+ *
+ * @param featureId Window feature which triggered the event.
+ * @param menu Activity native menu.
+ * @return {@code true} if the event was handled by this method.
+ */
+ public boolean dispatchMenuOpened(int featureId, android.view.Menu menu) {
+ return false;
+ }
+
+ /**
+ * Notify the action bar that the overflow menu has been closed. This
+ * method should be called before the superclass implementation.
+ *
+ *
+ *
+ * @param featureId
+ * @param menu
+ */
+ public void dispatchPanelClosed(int featureId, android.view.Menu menu) {}
+
+ /**
+ * Notify the action bar that the activity has been destroyed. This method
+ * should be called before the superclass implementation.
+ *
+ *
+ */
+ public void dispatchDestroy() {}
+
+ public void dispatchSaveInstanceState(Bundle outState) {}
+
+ public void dispatchRestoreInstanceState(Bundle savedInstanceState) {}
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Internal method to trigger the menu creation process.
+ *
+ * @return {@code true} if menu creation should proceed.
+ */
+ protected final boolean callbackCreateOptionsMenu(Menu menu) {
+ if (DEBUG) Log.d(TAG, "[callbackCreateOptionsMenu] menu: " + menu);
+
+ boolean result = true;
+ if (mActivity instanceof OnCreatePanelMenuListener) {
+ OnCreatePanelMenuListener listener = (OnCreatePanelMenuListener)mActivity;
+ result = listener.onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu);
+ } else if (mActivity instanceof OnCreateOptionsMenuListener) {
+ OnCreateOptionsMenuListener listener = (OnCreateOptionsMenuListener)mActivity;
+ result = listener.onCreateOptionsMenu(menu);
+ }
+
+ if (DEBUG) Log.d(TAG, "[callbackCreateOptionsMenu] returning " + result);
+ return result;
+ }
+
+ /**
+ * Internal method to trigger the menu preparation process.
+ *
+ * @return {@code true} if menu preparation should proceed.
+ */
+ protected final boolean callbackPrepareOptionsMenu(Menu menu) {
+ if (DEBUG) Log.d(TAG, "[callbackPrepareOptionsMenu] menu: " + menu);
+
+ boolean result = true;
+ if (mActivity instanceof OnPreparePanelListener) {
+ OnPreparePanelListener listener = (OnPreparePanelListener)mActivity;
+ result = listener.onPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu);
+ } else if (mActivity instanceof OnPrepareOptionsMenuListener) {
+ OnPrepareOptionsMenuListener listener = (OnPrepareOptionsMenuListener)mActivity;
+ result = listener.onPrepareOptionsMenu(menu);
+ }
+
+ if (DEBUG) Log.d(TAG, "[callbackPrepareOptionsMenu] returning " + result);
+ return result;
+ }
+
+ /**
+ * Internal method for dispatching options menu selection to the owning
+ * activity callback.
+ *
+ * @param item Selected options menu item.
+ * @return {@code true} if the item selection was handled in the callback.
+ */
+ protected final boolean callbackOptionsItemSelected(MenuItem item) {
+ if (DEBUG) Log.d(TAG, "[callbackOptionsItemSelected] item: " + item.getTitleCondensed());
+
+ boolean result = false;
+ if (mActivity instanceof OnMenuItemSelectedListener) {
+ OnMenuItemSelectedListener listener = (OnMenuItemSelectedListener)mActivity;
+ result = listener.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item);
+ } else if (mActivity instanceof OnOptionsItemSelectedListener) {
+ OnOptionsItemSelectedListener listener = (OnOptionsItemSelectedListener)mActivity;
+ result = listener.onOptionsItemSelected(item);
+ }
+
+ if (DEBUG) Log.d(TAG, "[callbackOptionsItemSelected] returning " + result);
+ return result;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Query for the availability of a certain feature.
+ *
+ * @param featureId The feature ID to check.
+ * @return {@code true} if feature is enabled, {@code false} otherwise.
+ */
+ public abstract boolean hasFeature(int featureId);
+
+ /**
+ * Enable extended screen features. This must be called before
+ * {@code setContentView()}. May be called as many times as desired as long
+ * as it is before {@code setContentView()}. If not called, no extended
+ * features will be available. You can not turn off a feature once it is
+ * requested.
+ *
+ * @param featureId The desired features, defined as constants by Window.
+ * @return Returns true if the requested feature is supported and now
+ * enabled.
+ */
+ public abstract boolean requestFeature(int featureId);
+
+ /**
+ * Set extra options that will influence the UI for this window.
+ *
+ * @param uiOptions Flags specifying extra options for this window.
+ */
+ public abstract void setUiOptions(int uiOptions);
+
+ /**
+ * Set extra options that will influence the UI for this window. Only the
+ * bits filtered by mask will be modified.
+ *
+ * @param uiOptions Flags specifying extra options for this window.
+ * @param mask Flags specifying which options should be modified. Others
+ * will remain unchanged.
+ */
+ public abstract void setUiOptions(int uiOptions, int mask);
+
+ /**
+ * Set the content of the activity inside the action bar.
+ *
+ * @param layoutResId Layout resource ID.
+ */
+ public abstract void setContentView(int layoutResId);
+
+ /**
+ * Set the content of the activity inside the action bar.
+ *
+ * @param view The desired content to display.
+ */
+ public void setContentView(View view) {
+ if (DEBUG) Log.d(TAG, "[setContentView] view: " + view);
+
+ setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ }
+
+ /**
+ * Set the content of the activity inside the action bar.
+ *
+ * @param view The desired content to display.
+ * @param params Layout parameters to apply to the view.
+ */
+ public abstract void setContentView(View view, ViewGroup.LayoutParams params);
+
+ /**
+ * Variation on {@link #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}
+ * to add an additional content view to the screen. Added after any
+ * existing ones on the screen -- existing views are NOT removed.
+ *
+ * @param view The desired content to display.
+ * @param params Layout parameters for the view.
+ */
+ public abstract void addContentView(View view, ViewGroup.LayoutParams params);
+
+ /**
+ * Change the title associated with this activity.
+ */
+ public abstract void setTitle(CharSequence title);
+
+ /**
+ * Change the title associated with this activity.
+ */
+ public void setTitle(int resId) {
+ if (DEBUG) Log.d(TAG, "[setTitle] resId: " + resId);
+
+ setTitle(mActivity.getString(resId));
+ }
+
+ /**
+ * Sets the visibility of the progress bar in the title.
+ *
+ * In order for the progress bar to be shown, the feature must be requested
+ * via {@link #requestWindowFeature(int)}.
+ *
+ * @param visible Whether to show the progress bars in the title.
+ */
+ public abstract void setProgressBarVisibility(boolean visible);
+
+ /**
+ * Sets the visibility of the indeterminate progress bar in the title.
+ *
+ * In order for the progress bar to be shown, the feature must be requested
+ * via {@link #requestWindowFeature(int)}.
+ *
+ * @param visible Whether to show the progress bars in the title.
+ */
+ public abstract void setProgressBarIndeterminateVisibility(boolean visible);
+
+ /**
+ * Sets whether the horizontal progress bar in the title should be indeterminate (the circular
+ * is always indeterminate).
+ *
+ * In order for the progress bar to be shown, the feature must be requested
+ * via {@link #requestWindowFeature(int)}.
+ *
+ * @param indeterminate Whether the horizontal progress bar should be indeterminate.
+ */
+ public abstract void setProgressBarIndeterminate(boolean indeterminate);
+
+ /**
+ * Sets the progress for the progress bars in the title.
+ *
+ * In order for the progress bar to be shown, the feature must be requested
+ * via {@link #requestWindowFeature(int)}.
+ *
+ * @param progress The progress for the progress bar. Valid ranges are from
+ * 0 to 10000 (both inclusive). If 10000 is given, the progress
+ * bar will be completely filled and will fade out.
+ */
+ public abstract void setProgress(int progress);
+
+ /**
+ * Sets the secondary progress for the progress bar in the title. This
+ * progress is drawn between the primary progress (set via
+ * {@link #setProgress(int)} and the background. It can be ideal for media
+ * scenarios such as showing the buffering progress while the default
+ * progress shows the play progress.
+ *
+ * In order for the progress bar to be shown, the feature must be requested
+ * via {@link #requestWindowFeature(int)}.
+ *
+ * @param secondaryProgress The secondary progress for the progress bar. Valid ranges are from
+ * 0 to 10000 (both inclusive).
+ */
+ public abstract void setSecondaryProgress(int secondaryProgress);
+
+ /**
+ * Get a menu inflater instance which supports the newer menu attributes.
+ *
+ * @return Menu inflater instance.
+ */
+ public MenuInflater getMenuInflater() {
+ if (DEBUG) Log.d(TAG, "[getMenuInflater]");
+
+ // Make sure that action views can get an appropriate theme.
+ if (mMenuInflater == null) {
+ if (getActionBar() != null) {
+ mMenuInflater = new MenuInflater(getThemedContext(), mActivity);
+ } else {
+ mMenuInflater = new MenuInflater(mActivity);
+ }
+ }
+ return mMenuInflater;
+ }
+
+ protected abstract Context getThemedContext();
+
+ /**
+ * Start an action mode.
+ *
+ * @param callback Callback that will manage lifecycle events for this
+ * context mode.
+ * @return The ContextMode that was started, or null if it was canceled.
+ * @see ActionMode
+ */
+ public abstract ActionMode startActionMode(ActionMode.Callback callback);
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/ActionBar.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/ActionBar.java
new file mode 100644
index 0000000000..03755be2b0
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/ActionBar.java
@@ -0,0 +1,956 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.app;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.support.v4.app.FragmentTransaction;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.widget.SpinnerAdapter;
+
+/**
+ * A window feature at the top of the activity that may display the activity title, navigation
+ * modes, and other interactive items.
+ *
Beginning with Android 3.0 (API level 11), the action bar appears at the top of an
+ * activity's window when the activity uses the system's {@link
+ * android.R.style#Theme_Holo Holo} theme (or one of its descendant themes), which is the default.
+ * You may otherwise add the action bar by calling {@link
+ * android.view.Window#requestFeature requestFeature(FEATURE_ACTION_BAR)} or by declaring it in a
+ * custom theme with the {@link android.R.styleable#Theme_windowActionBar windowActionBar} property.
+ *
By default, the action bar shows the application icon on
+ * the left, followed by the activity title. If your activity has an options menu, you can make
+ * select items accessible directly from the action bar as "action items". You can also
+ * modify various characteristics of the action bar or remove it completely.
+ *
From your activity, you can retrieve an instance of {@link ActionBar} by calling {@link
+ * android.app.Activity#getActionBar getActionBar()}.
+ *
In some cases, the action bar may be overlayed by another bar that enables contextual actions,
+ * using an {@link android.view.ActionMode}. For example, when the user selects one or more items in
+ * your activity, you can enable an action mode that offers actions specific to the selected
+ * items, with a UI that temporarily replaces the action bar. Although the UI may occupy the
+ * same space, the {@link android.view.ActionMode} APIs are distinct and independent from those for
+ * {@link ActionBar}.
+ *
+ *
Developer Guides
+ *
For information about how to use the action bar, including how to add action items, navigation
+ * modes and more, read the Action
+ * Bar developer guide.
+ *
+ */
+public abstract class ActionBar {
+ /**
+ * Standard navigation mode. Consists of either a logo or icon
+ * and title text with an optional subtitle. Clicking any of these elements
+ * will dispatch onOptionsItemSelected to the host Activity with
+ * a MenuItem with item ID android.R.id.home.
+ */
+ public static final int NAVIGATION_MODE_STANDARD = android.app.ActionBar.NAVIGATION_MODE_STANDARD;
+
+ /**
+ * List navigation mode. Instead of static title text this mode
+ * presents a list menu for navigation within the activity.
+ * e.g. this might be presented to the user as a dropdown list.
+ */
+ public static final int NAVIGATION_MODE_LIST = android.app.ActionBar.NAVIGATION_MODE_LIST;
+
+ /**
+ * Tab navigation mode. Instead of static title text this mode
+ * presents a series of tabs for navigation within the activity.
+ */
+ public static final int NAVIGATION_MODE_TABS = android.app.ActionBar.NAVIGATION_MODE_TABS;
+
+ /**
+ * Use logo instead of icon if available. This flag will cause appropriate
+ * navigation modes to use a wider logo in place of the standard icon.
+ *
+ * @see #setDisplayOptions(int)
+ * @see #setDisplayOptions(int, int)
+ */
+ public static final int DISPLAY_USE_LOGO = android.app.ActionBar.DISPLAY_USE_LOGO;
+
+ /**
+ * Show 'home' elements in this action bar, leaving more space for other
+ * navigation elements. This includes logo and icon.
+ *
+ * @see #setDisplayOptions(int)
+ * @see #setDisplayOptions(int, int)
+ */
+ public static final int DISPLAY_SHOW_HOME = android.app.ActionBar.DISPLAY_SHOW_HOME;
+
+ /**
+ * Display the 'home' element such that it appears as an 'up' affordance.
+ * e.g. show an arrow to the left indicating the action that will be taken.
+ *
+ * Set this flag if selecting the 'home' button in the action bar to return
+ * up by a single level in your UI rather than back to the top level or front page.
+ *
+ *
Setting this option will implicitly enable interaction with the home/up
+ * button. See {@link #setHomeButtonEnabled(boolean)}.
+ *
+ * @see #setDisplayOptions(int)
+ * @see #setDisplayOptions(int, int)
+ */
+ public static final int DISPLAY_HOME_AS_UP = android.app.ActionBar.DISPLAY_HOME_AS_UP;
+
+ /**
+ * Show the activity title and subtitle, if present.
+ *
+ * @see #setTitle(CharSequence)
+ * @see #setTitle(int)
+ * @see #setSubtitle(CharSequence)
+ * @see #setSubtitle(int)
+ * @see #setDisplayOptions(int)
+ * @see #setDisplayOptions(int, int)
+ */
+ public static final int DISPLAY_SHOW_TITLE = android.app.ActionBar.DISPLAY_SHOW_TITLE;
+
+ /**
+ * Show the custom view if one has been set.
+ * @see #setCustomView(View)
+ * @see #setDisplayOptions(int)
+ * @see #setDisplayOptions(int, int)
+ */
+ public static final int DISPLAY_SHOW_CUSTOM = android.app.ActionBar.DISPLAY_SHOW_CUSTOM;
+
+ /**
+ * Set the action bar into custom navigation mode, supplying a view
+ * for custom navigation.
+ *
+ * Custom navigation views appear between the application icon and
+ * any action buttons and may use any space available there. Common
+ * use cases for custom navigation views might include an auto-suggesting
+ * address bar for a browser or other navigation mechanisms that do not
+ * translate well to provided navigation modes.
+ *
+ * @param view Custom navigation view to place in the ActionBar.
+ */
+ public abstract void setCustomView(View view);
+
+ /**
+ * Set the action bar into custom navigation mode, supplying a view
+ * for custom navigation.
+ *
+ *
Custom navigation views appear between the application icon and
+ * any action buttons and may use any space available there. Common
+ * use cases for custom navigation views might include an auto-suggesting
+ * address bar for a browser or other navigation mechanisms that do not
+ * translate well to provided navigation modes.
+ *
+ *
The display option {@link #DISPLAY_SHOW_CUSTOM} must be set for
+ * the custom view to be displayed.
+ *
+ * @param view Custom navigation view to place in the ActionBar.
+ * @param layoutParams How this custom view should layout in the bar.
+ *
+ * @see #setDisplayOptions(int, int)
+ */
+ public abstract void setCustomView(View view, LayoutParams layoutParams);
+
+ /**
+ * Set the action bar into custom navigation mode, supplying a view
+ * for custom navigation.
+ *
+ *
Custom navigation views appear between the application icon and
+ * any action buttons and may use any space available there. Common
+ * use cases for custom navigation views might include an auto-suggesting
+ * address bar for a browser or other navigation mechanisms that do not
+ * translate well to provided navigation modes.
+ *
+ *
The display option {@link #DISPLAY_SHOW_CUSTOM} must be set for
+ * the custom view to be displayed.
+ *
+ * @param resId Resource ID of a layout to inflate into the ActionBar.
+ *
+ * @see #setDisplayOptions(int, int)
+ */
+ public abstract void setCustomView(int resId);
+
+ /**
+ * Set the icon to display in the 'home' section of the action bar.
+ * The action bar will use an icon specified by its style or the
+ * activity icon by default.
+ *
+ * Whether the home section shows an icon or logo is controlled
+ * by the display option {@link #DISPLAY_USE_LOGO}.
+ *
+ * @param resId Resource ID of a drawable to show as an icon.
+ *
+ * @see #setDisplayUseLogoEnabled(boolean)
+ * @see #setDisplayShowHomeEnabled(boolean)
+ */
+ public abstract void setIcon(int resId);
+
+ /**
+ * Set the icon to display in the 'home' section of the action bar.
+ * The action bar will use an icon specified by its style or the
+ * activity icon by default.
+ *
+ * Whether the home section shows an icon or logo is controlled
+ * by the display option {@link #DISPLAY_USE_LOGO}.
+ *
+ * @param icon Drawable to show as an icon.
+ *
+ * @see #setDisplayUseLogoEnabled(boolean)
+ * @see #setDisplayShowHomeEnabled(boolean)
+ */
+ public abstract void setIcon(Drawable icon);
+
+ /**
+ * Set the logo to display in the 'home' section of the action bar.
+ * The action bar will use a logo specified by its style or the
+ * activity logo by default.
+ *
+ * Whether the home section shows an icon or logo is controlled
+ * by the display option {@link #DISPLAY_USE_LOGO}.
+ *
+ * @param resId Resource ID of a drawable to show as a logo.
+ *
+ * @see #setDisplayUseLogoEnabled(boolean)
+ * @see #setDisplayShowHomeEnabled(boolean)
+ */
+ public abstract void setLogo(int resId);
+
+ /**
+ * Set the logo to display in the 'home' section of the action bar.
+ * The action bar will use a logo specified by its style or the
+ * activity logo by default.
+ *
+ * Whether the home section shows an icon or logo is controlled
+ * by the display option {@link #DISPLAY_USE_LOGO}.
+ *
+ * @param logo Drawable to show as a logo.
+ *
+ * @see #setDisplayUseLogoEnabled(boolean)
+ * @see #setDisplayShowHomeEnabled(boolean)
+ */
+ public abstract void setLogo(Drawable logo);
+
+ /**
+ * Set the adapter and navigation callback for list navigation mode.
+ *
+ * The supplied adapter will provide views for the expanded list as well as
+ * the currently selected item. (These may be displayed differently.)
+ *
+ * The supplied OnNavigationListener will alert the application when the user
+ * changes the current list selection.
+ *
+ * @param adapter An adapter that will provide views both to display
+ * the current navigation selection and populate views
+ * within the dropdown navigation menu.
+ * @param callback An OnNavigationListener that will receive events when the user
+ * selects a navigation item.
+ */
+ public abstract void setListNavigationCallbacks(SpinnerAdapter adapter,
+ OnNavigationListener callback);
+
+ /**
+ * Set the selected navigation item in list or tabbed navigation modes.
+ *
+ * @param position Position of the item to select.
+ */
+ public abstract void setSelectedNavigationItem(int position);
+
+ /**
+ * Get the position of the selected navigation item in list or tabbed navigation modes.
+ *
+ * @return Position of the selected item.
+ */
+ public abstract int getSelectedNavigationIndex();
+
+ /**
+ * Get the number of navigation items present in the current navigation mode.
+ *
+ * @return Number of navigation items.
+ */
+ public abstract int getNavigationItemCount();
+
+ /**
+ * Set the action bar's title. This will only be displayed if
+ * {@link #DISPLAY_SHOW_TITLE} is set.
+ *
+ * @param title Title to set
+ *
+ * @see #setTitle(int)
+ * @see #setDisplayOptions(int, int)
+ */
+ public abstract void setTitle(CharSequence title);
+
+ /**
+ * Set the action bar's title. This will only be displayed if
+ * {@link #DISPLAY_SHOW_TITLE} is set.
+ *
+ * @param resId Resource ID of title string to set
+ *
+ * @see #setTitle(CharSequence)
+ * @see #setDisplayOptions(int, int)
+ */
+ public abstract void setTitle(int resId);
+
+ /**
+ * Set the action bar's subtitle. This will only be displayed if
+ * {@link #DISPLAY_SHOW_TITLE} is set. Set to null to disable the
+ * subtitle entirely.
+ *
+ * @param subtitle Subtitle to set
+ *
+ * @see #setSubtitle(int)
+ * @see #setDisplayOptions(int, int)
+ */
+ public abstract void setSubtitle(CharSequence subtitle);
+
+ /**
+ * Set the action bar's subtitle. This will only be displayed if
+ * {@link #DISPLAY_SHOW_TITLE} is set.
+ *
+ * @param resId Resource ID of subtitle string to set
+ *
+ * @see #setSubtitle(CharSequence)
+ * @see #setDisplayOptions(int, int)
+ */
+ public abstract void setSubtitle(int resId);
+
+ /**
+ * Set display options. This changes all display option bits at once. To change
+ * a limited subset of display options, see {@link #setDisplayOptions(int, int)}.
+ *
+ * @param options A combination of the bits defined by the DISPLAY_ constants
+ * defined in ActionBar.
+ */
+ public abstract void setDisplayOptions(int options);
+
+ /**
+ * Set selected display options. Only the options specified by mask will be changed.
+ * To change all display option bits at once, see {@link #setDisplayOptions(int)}.
+ *
+ *
Example: setDisplayOptions(0, DISPLAY_SHOW_HOME) will disable the
+ * {@link #DISPLAY_SHOW_HOME} option.
+ * setDisplayOptions(DISPLAY_SHOW_HOME, DISPLAY_SHOW_HOME | DISPLAY_USE_LOGO)
+ * will enable {@link #DISPLAY_SHOW_HOME} and disable {@link #DISPLAY_USE_LOGO}.
+ *
+ * @param options A combination of the bits defined by the DISPLAY_ constants
+ * defined in ActionBar.
+ * @param mask A bit mask declaring which display options should be changed.
+ */
+ public abstract void setDisplayOptions(int options, int mask);
+
+ /**
+ * Set whether to display the activity logo rather than the activity icon.
+ * A logo is often a wider, more detailed image.
+ *
+ *
To set several display options at once, see the setDisplayOptions methods.
+ *
+ * @param useLogo true to use the activity logo, false to use the activity icon.
+ *
+ * @see #setDisplayOptions(int)
+ * @see #setDisplayOptions(int, int)
+ */
+ public abstract void setDisplayUseLogoEnabled(boolean useLogo);
+
+ /**
+ * Set whether to include the application home affordance in the action bar.
+ * Home is presented as either an activity icon or logo.
+ *
+ *
To set several display options at once, see the setDisplayOptions methods.
+ *
+ * @param showHome true to show home, false otherwise.
+ *
+ * @see #setDisplayOptions(int)
+ * @see #setDisplayOptions(int, int)
+ */
+ public abstract void setDisplayShowHomeEnabled(boolean showHome);
+
+ /**
+ * Set whether home should be displayed as an "up" affordance.
+ * Set this to true if selecting "home" returns up by a single level in your UI
+ * rather than back to the top level or front page.
+ *
+ *
To set several display options at once, see the setDisplayOptions methods.
+ *
+ * @param showHomeAsUp true to show the user that selecting home will return one
+ * level up rather than to the top level of the app.
+ *
+ * @see #setDisplayOptions(int)
+ * @see #setDisplayOptions(int, int)
+ */
+ public abstract void setDisplayHomeAsUpEnabled(boolean showHomeAsUp);
+
+ /**
+ * Set whether an activity title/subtitle should be displayed.
+ *
+ *
To set several display options at once, see the setDisplayOptions methods.
+ *
+ * @param showTitle true to display a title/subtitle if present.
+ *
+ * @see #setDisplayOptions(int)
+ * @see #setDisplayOptions(int, int)
+ */
+ public abstract void setDisplayShowTitleEnabled(boolean showTitle);
+
+ /**
+ * Set whether a custom view should be displayed, if set.
+ *
+ *
To set several display options at once, see the setDisplayOptions methods.
+ *
+ * @param showCustom true if the currently set custom view should be displayed, false otherwise.
+ *
+ * @see #setDisplayOptions(int)
+ * @see #setDisplayOptions(int, int)
+ */
+ public abstract void setDisplayShowCustomEnabled(boolean showCustom);
+
+ /**
+ * Set the ActionBar's background. This will be used for the primary
+ * action bar.
+ *
+ * @param d Background drawable
+ * @see #setStackedBackgroundDrawable(Drawable)
+ * @see #setSplitBackgroundDrawable(Drawable)
+ */
+ public abstract void setBackgroundDrawable(Drawable d);
+
+ /**
+ * Set the ActionBar's stacked background. This will appear
+ * in the second row/stacked bar on some devices and configurations.
+ *
+ * @param d Background drawable for the stacked row
+ */
+ public void setStackedBackgroundDrawable(Drawable d) { }
+
+ /**
+ * Set the ActionBar's split background. This will appear in
+ * the split action bar containing menu-provided action buttons
+ * on some devices and configurations.
+ *
You can enable split action bar with {@link android.R.attr#uiOptions}
+ *
+ * @param d Background drawable for the split bar
+ */
+ public void setSplitBackgroundDrawable(Drawable d) { }
+
+ /**
+ * @return The current custom view.
+ */
+ public abstract View getCustomView();
+
+ /**
+ * Returns the current ActionBar title in standard mode.
+ * Returns null if {@link #getNavigationMode()} would not return
+ * {@link #NAVIGATION_MODE_STANDARD}.
+ *
+ * @return The current ActionBar title or null.
+ */
+ public abstract CharSequence getTitle();
+
+ /**
+ * Returns the current ActionBar subtitle in standard mode.
+ * Returns null if {@link #getNavigationMode()} would not return
+ * {@link #NAVIGATION_MODE_STANDARD}.
+ *
+ * @return The current ActionBar subtitle or null.
+ */
+ public abstract CharSequence getSubtitle();
+
+ /**
+ * Returns the current navigation mode. The result will be one of:
+ *
+ *
{@link #NAVIGATION_MODE_STANDARD}
+ *
{@link #NAVIGATION_MODE_LIST}
+ *
{@link #NAVIGATION_MODE_TABS}
+ *
+ *
+ * @return The current navigation mode.
+ */
+ public abstract int getNavigationMode();
+
+ /**
+ * Set the current navigation mode.
+ *
+ * @param mode The new mode to set.
+ * @see #NAVIGATION_MODE_STANDARD
+ * @see #NAVIGATION_MODE_LIST
+ * @see #NAVIGATION_MODE_TABS
+ */
+ public abstract void setNavigationMode(int mode);
+
+ /**
+ * @return The current set of display options.
+ */
+ public abstract int getDisplayOptions();
+
+ /**
+ * Create and return a new {@link Tab}.
+ * This tab will not be included in the action bar until it is added.
+ *
+ *
Very often tabs will be used to switch between {@link Fragment}
+ * objects. Here is a typical implementation of such tabs:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabs.java
+ * complete}
+ *
+ * @return A new Tab
+ *
+ * @see #addTab(Tab)
+ */
+ public abstract Tab newTab();
+
+ /**
+ * Add a tab for use in tabbed navigation mode. The tab will be added at the end of the list.
+ * If this is the first tab to be added it will become the selected tab.
+ *
+ * @param tab Tab to add
+ */
+ public abstract void addTab(Tab tab);
+
+ /**
+ * Add a tab for use in tabbed navigation mode. The tab will be added at the end of the list.
+ *
+ * @param tab Tab to add
+ * @param setSelected True if the added tab should become the selected tab.
+ */
+ public abstract void addTab(Tab tab, boolean setSelected);
+
+ /**
+ * Add a tab for use in tabbed navigation mode. The tab will be inserted at
+ * position. If this is the first tab to be added it will become
+ * the selected tab.
+ *
+ * @param tab The tab to add
+ * @param position The new position of the tab
+ */
+ public abstract void addTab(Tab tab, int position);
+
+ /**
+ * Add a tab for use in tabbed navigation mode. The tab will be insterted at
+ * position.
+ *
+ * @param tab The tab to add
+ * @param position The new position of the tab
+ * @param setSelected True if the added tab should become the selected tab.
+ */
+ public abstract void addTab(Tab tab, int position, boolean setSelected);
+
+ /**
+ * Remove a tab from the action bar. If the removed tab was selected it will be deselected
+ * and another tab will be selected if present.
+ *
+ * @param tab The tab to remove
+ */
+ public abstract void removeTab(Tab tab);
+
+ /**
+ * Remove a tab from the action bar. If the removed tab was selected it will be deselected
+ * and another tab will be selected if present.
+ *
+ * @param position Position of the tab to remove
+ */
+ public abstract void removeTabAt(int position);
+
+ /**
+ * Remove all tabs from the action bar and deselect the current tab.
+ */
+ public abstract void removeAllTabs();
+
+ /**
+ * Select the specified tab. If it is not a child of this action bar it will be added.
+ *
+ *
Note: If you want to select by index, use {@link #setSelectedNavigationItem(int)}.
+ *
+ * @param tab Tab to select
+ */
+ public abstract void selectTab(Tab tab);
+
+ /**
+ * Returns the currently selected tab if in tabbed navigation mode and there is at least
+ * one tab present.
+ *
+ * @return The currently selected tab or null
+ */
+ public abstract Tab getSelectedTab();
+
+ /**
+ * Returns the tab at the specified index.
+ *
+ * @param index Index value in the range 0-get
+ * @return
+ */
+ public abstract Tab getTabAt(int index);
+
+ /**
+ * Returns the number of tabs currently registered with the action bar.
+ * @return Tab count
+ */
+ public abstract int getTabCount();
+
+ /**
+ * Retrieve the current height of the ActionBar.
+ *
+ * @return The ActionBar's height
+ */
+ public abstract int getHeight();
+
+ /**
+ * Show the ActionBar if it is not currently showing.
+ * If the window hosting the ActionBar does not have the feature
+ * {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application
+ * content to fit the new space available.
+ */
+ public abstract void show();
+
+ /**
+ * Hide the ActionBar if it is currently showing.
+ * If the window hosting the ActionBar does not have the feature
+ * {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application
+ * content to fit the new space available.
+ */
+ public abstract void hide();
+
+ /**
+ * @return true if the ActionBar is showing, false otherwise.
+ */
+ public abstract boolean isShowing();
+
+ /**
+ * Add a listener that will respond to menu visibility change events.
+ *
+ * @param listener The new listener to add
+ */
+ public abstract void addOnMenuVisibilityListener(OnMenuVisibilityListener listener);
+
+ /**
+ * Remove a menu visibility listener. This listener will no longer receive menu
+ * visibility change events.
+ *
+ * @param listener A listener to remove that was previously added
+ */
+ public abstract void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener);
+
+ /**
+ * Enable or disable the "home" button in the corner of the action bar. (Note that this
+ * is the application home/up affordance on the action bar, not the systemwide home
+ * button.)
+ *
+ *
This defaults to true for packages targeting < API 14. For packages targeting
+ * API 14 or greater, the application should call this method to enable interaction
+ * with the home/up affordance.
+ *
+ *
Setting the {@link #DISPLAY_HOME_AS_UP} display option will automatically enable
+ * the home button.
+ *
+ * @param enabled true to enable the home button, false to disable the home button.
+ */
+ public void setHomeButtonEnabled(boolean enabled) { }
+
+ /**
+ * Returns a {@link Context} with an appropriate theme for creating views that
+ * will appear in the action bar. If you are inflating or instantiating custom views
+ * that will appear in an action bar, you should use the Context returned by this method.
+ * (This includes adapters used for list navigation mode.)
+ * This will ensure that views contrast properly against the action bar.
+ *
+ * @return A themed Context for creating views
+ */
+ public Context getThemedContext() { return null; }
+
+ /**
+ * Listener interface for ActionBar navigation events.
+ */
+ public interface OnNavigationListener {
+ /**
+ * This method is called whenever a navigation item in your action bar
+ * is selected.
+ *
+ * @param itemPosition Position of the item clicked.
+ * @param itemId ID of the item clicked.
+ * @return True if the event was handled, false otherwise.
+ */
+ public boolean onNavigationItemSelected(int itemPosition, long itemId);
+ }
+
+ /**
+ * Listener for receiving events when action bar menus are shown or hidden.
+ */
+ public interface OnMenuVisibilityListener {
+ /**
+ * Called when an action bar menu is shown or hidden. Applications may want to use
+ * this to tune auto-hiding behavior for the action bar or pause/resume video playback,
+ * gameplay, or other activity within the main content area.
+ *
+ * @param isVisible True if an action bar menu is now visible, false if no action bar
+ * menus are visible.
+ */
+ public void onMenuVisibilityChanged(boolean isVisible);
+ }
+
+ /**
+ * A tab in the action bar.
+ *
+ *
Tabs manage the hiding and showing of {@link Fragment}s.
+ */
+ public static abstract class Tab {
+ /**
+ * An invalid position for a tab.
+ *
+ * @see #getPosition()
+ */
+ public static final int INVALID_POSITION = -1;
+
+ /**
+ * Return the current position of this tab in the action bar.
+ *
+ * @return Current position, or {@link #INVALID_POSITION} if this tab is not currently in
+ * the action bar.
+ */
+ public abstract int getPosition();
+
+ /**
+ * Return the icon associated with this tab.
+ *
+ * @return The tab's icon
+ */
+ public abstract Drawable getIcon();
+
+ /**
+ * Return the text of this tab.
+ *
+ * @return The tab's text
+ */
+ public abstract CharSequence getText();
+
+ /**
+ * Set the icon displayed on this tab.
+ *
+ * @param icon The drawable to use as an icon
+ * @return The current instance for call chaining
+ */
+ public abstract Tab setIcon(Drawable icon);
+
+ /**
+ * Set the icon displayed on this tab.
+ *
+ * @param resId Resource ID referring to the drawable to use as an icon
+ * @return The current instance for call chaining
+ */
+ public abstract Tab setIcon(int resId);
+
+ /**
+ * Set the text displayed on this tab. Text may be truncated if there is not
+ * room to display the entire string.
+ *
+ * @param text The text to display
+ * @return The current instance for call chaining
+ */
+ public abstract Tab setText(CharSequence text);
+
+ /**
+ * Set the text displayed on this tab. Text may be truncated if there is not
+ * room to display the entire string.
+ *
+ * @param resId A resource ID referring to the text that should be displayed
+ * @return The current instance for call chaining
+ */
+ public abstract Tab setText(int resId);
+
+ /**
+ * Set a custom view to be used for this tab. This overrides values set by
+ * {@link #setText(CharSequence)} and {@link #setIcon(Drawable)}.
+ *
+ * @param view Custom view to be used as a tab.
+ * @return The current instance for call chaining
+ */
+ public abstract Tab setCustomView(View view);
+
+ /**
+ * Set a custom view to be used for this tab. This overrides values set by
+ * {@link #setText(CharSequence)} and {@link #setIcon(Drawable)}.
+ *
+ * @param layoutResId A layout resource to inflate and use as a custom tab view
+ * @return The current instance for call chaining
+ */
+ public abstract Tab setCustomView(int layoutResId);
+
+ /**
+ * Retrieve a previously set custom view for this tab.
+ *
+ * @return The custom view set by {@link #setCustomView(View)}.
+ */
+ public abstract View getCustomView();
+
+ /**
+ * Give this Tab an arbitrary object to hold for later use.
+ *
+ * @param obj Object to store
+ * @return The current instance for call chaining
+ */
+ public abstract Tab setTag(Object obj);
+
+ /**
+ * @return This Tab's tag object.
+ */
+ public abstract Object getTag();
+
+ /**
+ * Set the {@link TabListener} that will handle switching to and from this tab.
+ * All tabs must have a TabListener set before being added to the ActionBar.
+ *
+ * @param listener Listener to handle tab selection events
+ * @return The current instance for call chaining
+ */
+ public abstract Tab setTabListener(TabListener listener);
+
+ /**
+ * Select this tab. Only valid if the tab has been added to the action bar.
+ */
+ public abstract void select();
+
+ /**
+ * Set a description of this tab's content for use in accessibility support.
+ * If no content description is provided the title will be used.
+ *
+ * @param resId A resource ID referring to the description text
+ * @return The current instance for call chaining
+ * @see #setContentDescription(CharSequence)
+ * @see #getContentDescription()
+ */
+ public abstract Tab setContentDescription(int resId);
+
+ /**
+ * Set a description of this tab's content for use in accessibility support.
+ * If no content description is provided the title will be used.
+ *
+ * @param contentDesc Description of this tab's content
+ * @return The current instance for call chaining
+ * @see #setContentDescription(int)
+ * @see #getContentDescription()
+ */
+ public abstract Tab setContentDescription(CharSequence contentDesc);
+
+ /**
+ * Gets a brief description of this tab's content for use in accessibility support.
+ *
+ * @return Description of this tab's content
+ * @see #setContentDescription(CharSequence)
+ * @see #setContentDescription(int)
+ */
+ public abstract CharSequence getContentDescription();
+ }
+
+ /**
+ * Callback interface invoked when a tab is focused, unfocused, added, or removed.
+ */
+ public interface TabListener {
+ /**
+ * Called when a tab enters the selected state.
+ *
+ * @param tab The tab that was selected
+ * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
+ * during a tab switch. The previous tab's unselect and this tab's select will be
+ * executed in a single transaction. This FragmentTransaction does not support
+ * being added to the back stack.
+ */
+ public void onTabSelected(Tab tab, FragmentTransaction ft);
+
+ /**
+ * Called when a tab exits the selected state.
+ *
+ * @param tab The tab that was unselected
+ * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
+ * during a tab switch. This tab's unselect and the newly selected tab's select
+ * will be executed in a single transaction. This FragmentTransaction does not
+ * support being added to the back stack.
+ */
+ public void onTabUnselected(Tab tab, FragmentTransaction ft);
+
+ /**
+ * Called when a tab that is already selected is chosen again by the user.
+ * Some applications may use this action to return to the top level of a category.
+ *
+ * @param tab The tab that was reselected.
+ * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
+ * once this method returns. This FragmentTransaction does not support
+ * being added to the back stack.
+ */
+ public void onTabReselected(Tab tab, FragmentTransaction ft);
+ }
+
+ /**
+ * Per-child layout information associated with action bar custom views.
+ *
+ * @attr ref android.R.styleable#ActionBar_LayoutParams_layout_gravity
+ */
+ public static class LayoutParams extends MarginLayoutParams {
+ private static final int[] ATTRS = new int[] {
+ android.R.attr.layout_gravity
+ };
+
+ /**
+ * Gravity for the view associated with these LayoutParams.
+ *
+ * @see android.view.Gravity
+ */
+ @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.IntToString(from = -1, to = "NONE"),
+ @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"),
+ @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"),
+ @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"),
+ @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"),
+ @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"),
+ @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"),
+ @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"),
+ @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
+ @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"),
+ @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"),
+ @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL")
+ })
+ public int gravity = -1;
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+
+ TypedArray a = c.obtainStyledAttributes(attrs, ATTRS);
+ gravity = a.getInt(0, -1);
+ a.recycle();
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ this.gravity = Gravity.CENTER_VERTICAL | Gravity.LEFT;
+ }
+
+ public LayoutParams(int width, int height, int gravity) {
+ super(width, height);
+ this.gravity = gravity;
+ }
+
+ public LayoutParams(int gravity) {
+ this(WRAP_CONTENT, FILL_PARENT, gravity);
+ }
+
+ public LayoutParams(LayoutParams source) {
+ super(source);
+
+ this.gravity = source.gravity;
+ }
+
+ public LayoutParams(ViewGroup.LayoutParams source) {
+ super(source);
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockActivity.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockActivity.java
new file mode 100644
index 0000000000..7b45436405
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockActivity.java
@@ -0,0 +1,270 @@
+package com.actionbarsherlock.app;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.ViewGroup.LayoutParams;
+import com.actionbarsherlock.ActionBarSherlock;
+import com.actionbarsherlock.ActionBarSherlock.OnActionModeFinishedListener;
+import com.actionbarsherlock.ActionBarSherlock.OnActionModeStartedListener;
+import com.actionbarsherlock.ActionBarSherlock.OnCreatePanelMenuListener;
+import com.actionbarsherlock.ActionBarSherlock.OnMenuItemSelectedListener;
+import com.actionbarsherlock.ActionBarSherlock.OnPreparePanelListener;
+import com.actionbarsherlock.view.ActionMode;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+
+public abstract class SherlockActivity extends Activity implements OnCreatePanelMenuListener, OnPreparePanelListener, OnMenuItemSelectedListener, OnActionModeStartedListener, OnActionModeFinishedListener {
+ private ActionBarSherlock mSherlock;
+
+ protected final ActionBarSherlock getSherlock() {
+ if (mSherlock == null) {
+ mSherlock = ActionBarSherlock.wrap(this, ActionBarSherlock.FLAG_DELEGATE);
+ }
+ return mSherlock;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Action bar and mode
+ ///////////////////////////////////////////////////////////////////////////
+
+ public ActionBar getSupportActionBar() {
+ return getSherlock().getActionBar();
+ }
+
+ public ActionMode startActionMode(ActionMode.Callback callback) {
+ return getSherlock().startActionMode(callback);
+ }
+
+ @Override
+ public void onActionModeStarted(ActionMode mode) {}
+
+ @Override
+ public void onActionModeFinished(ActionMode mode) {}
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // General lifecycle/callback dispatching
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ getSherlock().dispatchConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onPostResume() {
+ super.onPostResume();
+ getSherlock().dispatchPostResume();
+ }
+
+ @Override
+ protected void onPause() {
+ getSherlock().dispatchPause();
+ super.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ getSherlock().dispatchStop();
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ getSherlock().dispatchDestroy();
+ super.onDestroy();
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ getSherlock().dispatchPostCreate(savedInstanceState);
+ super.onPostCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onTitleChanged(CharSequence title, int color) {
+ getSherlock().dispatchTitleChanged(title, color);
+ super.onTitleChanged(title, color);
+ }
+
+ @Override
+ public final boolean onMenuOpened(int featureId, android.view.Menu menu) {
+ if (getSherlock().dispatchMenuOpened(featureId, menu)) {
+ return true;
+ }
+ return super.onMenuOpened(featureId, menu);
+ }
+
+ @Override
+ public void onPanelClosed(int featureId, android.view.Menu menu) {
+ getSherlock().dispatchPanelClosed(featureId, menu);
+ super.onPanelClosed(featureId, menu);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (getSherlock().dispatchKeyEvent(event)) {
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ getSherlock().dispatchSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ getSherlock().dispatchRestoreInstanceState(savedInstanceState);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Native menu handling
+ ///////////////////////////////////////////////////////////////////////////
+
+ public MenuInflater getSupportMenuInflater() {
+ return getSherlock().getMenuInflater();
+ }
+
+ public void invalidateOptionsMenu() {
+ getSherlock().dispatchInvalidateOptionsMenu();
+ }
+
+ public void supportInvalidateOptionsMenu() {
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public final boolean onCreateOptionsMenu(android.view.Menu menu) {
+ return getSherlock().dispatchCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public final boolean onPrepareOptionsMenu(android.view.Menu menu) {
+ return getSherlock().dispatchPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public final boolean onOptionsItemSelected(android.view.MenuItem item) {
+ return getSherlock().dispatchOptionsItemSelected(item);
+ }
+
+ @Override
+ public void openOptionsMenu() {
+ if (!getSherlock().dispatchOpenOptionsMenu()) {
+ super.openOptionsMenu();
+ }
+ }
+
+ @Override
+ public void closeOptionsMenu() {
+ if (!getSherlock().dispatchCloseOptionsMenu()) {
+ super.closeOptionsMenu();
+ }
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Sherlock menu handling
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public boolean onCreatePanelMenu(int featureId, Menu menu) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+ return onCreateOptionsMenu(menu);
+ }
+ return false;
+ }
+
+ public boolean onCreateOptionsMenu(Menu menu) {
+ return true;
+ }
+
+ @Override
+ public boolean onPreparePanel(int featureId, View view, Menu menu) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+ return onPrepareOptionsMenu(menu);
+ }
+ return false;
+ }
+
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+ return onOptionsItemSelected(item);
+ }
+ return false;
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return false;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Content
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void addContentView(View view, LayoutParams params) {
+ getSherlock().addContentView(view, params);
+ }
+
+ @Override
+ public void setContentView(int layoutResId) {
+ getSherlock().setContentView(layoutResId);
+ }
+
+ @Override
+ public void setContentView(View view, LayoutParams params) {
+ getSherlock().setContentView(view, params);
+ }
+
+ @Override
+ public void setContentView(View view) {
+ getSherlock().setContentView(view);
+ }
+
+ public void requestWindowFeature(long featureId) {
+ getSherlock().requestFeature((int)featureId);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Progress Indication
+ ///////////////////////////////////////////////////////////////////////////
+
+ public void setSupportProgress(int progress) {
+ getSherlock().setProgress(progress);
+ }
+
+ public void setSupportProgressBarIndeterminate(boolean indeterminate) {
+ getSherlock().setProgressBarIndeterminate(indeterminate);
+ }
+
+ public void setSupportProgressBarIndeterminateVisibility(boolean visible) {
+ getSherlock().setProgressBarIndeterminateVisibility(visible);
+ }
+
+ public void setSupportProgressBarVisibility(boolean visible) {
+ getSherlock().setProgressBarVisibility(visible);
+ }
+
+ public void setSupportSecondaryProgress(int secondaryProgress) {
+ getSherlock().setSecondaryProgress(secondaryProgress);
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockDialogFragment.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockDialogFragment.java
new file mode 100644
index 0000000000..a7c856bf02
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockDialogFragment.java
@@ -0,0 +1,68 @@
+package com.actionbarsherlock.app;
+
+import android.app.Activity;
+import android.support.v4.app.DialogFragment;
+import com.actionbarsherlock.internal.view.menu.MenuItemWrapper;
+import com.actionbarsherlock.internal.view.menu.MenuWrapper;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+
+import static com.actionbarsherlock.app.SherlockFragmentActivity.OnCreateOptionsMenuListener;
+import static com.actionbarsherlock.app.SherlockFragmentActivity.OnOptionsItemSelectedListener;
+import static com.actionbarsherlock.app.SherlockFragmentActivity.OnPrepareOptionsMenuListener;
+
+public class SherlockDialogFragment extends DialogFragment implements OnCreateOptionsMenuListener, OnPrepareOptionsMenuListener, OnOptionsItemSelectedListener {
+ private SherlockFragmentActivity mActivity;
+
+ public SherlockFragmentActivity getSherlockActivity() {
+ return mActivity;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ if (!(activity instanceof SherlockFragmentActivity)) {
+ throw new IllegalStateException(getClass().getSimpleName() + " must be attached to a SherlockFragmentActivity.");
+ }
+ mActivity = (SherlockFragmentActivity)activity;
+
+ super.onAttach(activity);
+ }
+
+ @Override
+ public void onDetach() {
+ mActivity = null;
+ super.onDetach();
+ }
+
+ @Override
+ public final void onCreateOptionsMenu(android.view.Menu menu, android.view.MenuInflater inflater) {
+ onCreateOptionsMenu(new MenuWrapper(menu), mActivity.getSupportMenuInflater());
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ //Nothing to see here.
+ }
+
+ @Override
+ public final void onPrepareOptionsMenu(android.view.Menu menu) {
+ onPrepareOptionsMenu(new MenuWrapper(menu));
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ //Nothing to see here.
+ }
+
+ @Override
+ public final boolean onOptionsItemSelected(android.view.MenuItem item) {
+ return onOptionsItemSelected(new MenuItemWrapper(item));
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ //Nothing to see here.
+ return false;
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockExpandableListActivity.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockExpandableListActivity.java
new file mode 100644
index 0000000000..078f9b0ca1
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockExpandableListActivity.java
@@ -0,0 +1,259 @@
+package com.actionbarsherlock.app;
+
+import android.app.ExpandableListActivity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.view.Window;
+import com.actionbarsherlock.ActionBarSherlock;
+import com.actionbarsherlock.ActionBarSherlock.OnActionModeFinishedListener;
+import com.actionbarsherlock.ActionBarSherlock.OnActionModeStartedListener;
+import com.actionbarsherlock.ActionBarSherlock.OnCreatePanelMenuListener;
+import com.actionbarsherlock.ActionBarSherlock.OnMenuItemSelectedListener;
+import com.actionbarsherlock.ActionBarSherlock.OnPreparePanelListener;
+import com.actionbarsherlock.view.ActionMode;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+
+public abstract class SherlockExpandableListActivity extends ExpandableListActivity implements OnCreatePanelMenuListener, OnPreparePanelListener, OnMenuItemSelectedListener, OnActionModeStartedListener, OnActionModeFinishedListener {
+ private ActionBarSherlock mSherlock;
+
+ protected final ActionBarSherlock getSherlock() {
+ if (mSherlock == null) {
+ mSherlock = ActionBarSherlock.wrap(this, ActionBarSherlock.FLAG_DELEGATE);
+ }
+ return mSherlock;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Action bar and mode
+ ///////////////////////////////////////////////////////////////////////////
+
+ public ActionBar getSupportActionBar() {
+ return getSherlock().getActionBar();
+ }
+
+ public ActionMode startActionMode(ActionMode.Callback callback) {
+ return getSherlock().startActionMode(callback);
+ }
+
+ @Override
+ public void onActionModeStarted(ActionMode mode) {}
+
+ @Override
+ public void onActionModeFinished(ActionMode mode) {}
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // General lifecycle/callback dispatching
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ getSherlock().dispatchConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onPostResume() {
+ super.onPostResume();
+ getSherlock().dispatchPostResume();
+ }
+
+ @Override
+ protected void onPause() {
+ getSherlock().dispatchPause();
+ super.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ getSherlock().dispatchStop();
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ getSherlock().dispatchDestroy();
+ super.onDestroy();
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ getSherlock().dispatchPostCreate(savedInstanceState);
+ super.onPostCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onTitleChanged(CharSequence title, int color) {
+ getSherlock().dispatchTitleChanged(title, color);
+ super.onTitleChanged(title, color);
+ }
+
+ @Override
+ public final boolean onMenuOpened(int featureId, android.view.Menu menu) {
+ if (getSherlock().dispatchMenuOpened(featureId, menu)) {
+ return true;
+ }
+ return super.onMenuOpened(featureId, menu);
+ }
+
+ @Override
+ public void onPanelClosed(int featureId, android.view.Menu menu) {
+ getSherlock().dispatchPanelClosed(featureId, menu);
+ super.onPanelClosed(featureId, menu);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (getSherlock().dispatchKeyEvent(event)) {
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Native menu handling
+ ///////////////////////////////////////////////////////////////////////////
+
+ public MenuInflater getSupportMenuInflater() {
+ return getSherlock().getMenuInflater();
+ }
+
+ public void invalidateOptionsMenu() {
+ getSherlock().dispatchInvalidateOptionsMenu();
+ }
+
+ public void supportInvalidateOptionsMenu() {
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public final boolean onCreateOptionsMenu(android.view.Menu menu) {
+ return getSherlock().dispatchCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public final boolean onPrepareOptionsMenu(android.view.Menu menu) {
+ return getSherlock().dispatchPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public final boolean onOptionsItemSelected(android.view.MenuItem item) {
+ return getSherlock().dispatchOptionsItemSelected(item);
+ }
+
+ @Override
+ public void openOptionsMenu() {
+ if (!getSherlock().dispatchOpenOptionsMenu()) {
+ super.openOptionsMenu();
+ }
+ }
+
+ @Override
+ public void closeOptionsMenu() {
+ if (!getSherlock().dispatchCloseOptionsMenu()) {
+ super.closeOptionsMenu();
+ }
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Sherlock menu handling
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public boolean onCreatePanelMenu(int featureId, Menu menu) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+ return onCreateOptionsMenu(menu);
+ }
+ return false;
+ }
+
+ public boolean onCreateOptionsMenu(Menu menu) {
+ return true;
+ }
+
+ @Override
+ public boolean onPreparePanel(int featureId, View view, Menu menu) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+ return onPrepareOptionsMenu(menu);
+ }
+ return false;
+ }
+
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+ return onOptionsItemSelected(item);
+ }
+ return false;
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return false;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Content
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void addContentView(View view, LayoutParams params) {
+ getSherlock().addContentView(view, params);
+ }
+
+ @Override
+ public void setContentView(int layoutResId) {
+ getSherlock().setContentView(layoutResId);
+ }
+
+ @Override
+ public void setContentView(View view, LayoutParams params) {
+ getSherlock().setContentView(view, params);
+ }
+
+ @Override
+ public void setContentView(View view) {
+ getSherlock().setContentView(view);
+ }
+
+ public void requestWindowFeature(long featureId) {
+ getSherlock().requestFeature((int)featureId);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Progress Indication
+ ///////////////////////////////////////////////////////////////////////////
+
+ public void setSupportProgress(int progress) {
+ getSherlock().setProgress(progress);
+ }
+
+ public void setSupportProgressBarIndeterminate(boolean indeterminate) {
+ getSherlock().setProgressBarIndeterminate(indeterminate);
+ }
+
+ public void setSupportProgressBarIndeterminateVisibility(boolean visible) {
+ getSherlock().setProgressBarIndeterminateVisibility(visible);
+ }
+
+ public void setSupportProgressBarVisibility(boolean visible) {
+ getSherlock().setProgressBarVisibility(visible);
+ }
+
+ public void setSupportSecondaryProgress(int secondaryProgress) {
+ getSherlock().setSecondaryProgress(secondaryProgress);
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockFragment.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockFragment.java
new file mode 100644
index 0000000000..0f24e9c856
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockFragment.java
@@ -0,0 +1,68 @@
+package com.actionbarsherlock.app;
+
+import android.app.Activity;
+import android.support.v4.app.Fragment;
+import com.actionbarsherlock.internal.view.menu.MenuItemWrapper;
+import com.actionbarsherlock.internal.view.menu.MenuWrapper;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+
+import static com.actionbarsherlock.app.SherlockFragmentActivity.OnCreateOptionsMenuListener;
+import static com.actionbarsherlock.app.SherlockFragmentActivity.OnOptionsItemSelectedListener;
+import static com.actionbarsherlock.app.SherlockFragmentActivity.OnPrepareOptionsMenuListener;
+
+public class SherlockFragment extends Fragment implements OnCreateOptionsMenuListener, OnPrepareOptionsMenuListener, OnOptionsItemSelectedListener {
+ private SherlockFragmentActivity mActivity;
+
+ public SherlockFragmentActivity getSherlockActivity() {
+ return mActivity;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ if (!(activity instanceof SherlockFragmentActivity)) {
+ throw new IllegalStateException(getClass().getSimpleName() + " must be attached to a SherlockFragmentActivity.");
+ }
+ mActivity = (SherlockFragmentActivity)activity;
+
+ super.onAttach(activity);
+ }
+
+ @Override
+ public void onDetach() {
+ mActivity = null;
+ super.onDetach();
+ }
+
+ @Override
+ public final void onCreateOptionsMenu(android.view.Menu menu, android.view.MenuInflater inflater) {
+ onCreateOptionsMenu(new MenuWrapper(menu), mActivity.getSupportMenuInflater());
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ //Nothing to see here.
+ }
+
+ @Override
+ public final void onPrepareOptionsMenu(android.view.Menu menu) {
+ onPrepareOptionsMenu(new MenuWrapper(menu));
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ //Nothing to see here.
+ }
+
+ @Override
+ public final boolean onOptionsItemSelected(android.view.MenuItem item) {
+ return onOptionsItemSelected(new MenuItemWrapper(item));
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ //Nothing to see here.
+ return false;
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockFragmentActivity.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockFragmentActivity.java
new file mode 100644
index 0000000000..3d092f033a
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockFragmentActivity.java
@@ -0,0 +1,303 @@
+package com.actionbarsherlock.app;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.support.v4.app.Watson;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.view.Window;
+import com.actionbarsherlock.ActionBarSherlock;
+import com.actionbarsherlock.view.ActionMode;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+
+import static com.actionbarsherlock.ActionBarSherlock.OnActionModeFinishedListener;
+import static com.actionbarsherlock.ActionBarSherlock.OnActionModeStartedListener;
+
+/** @see {@link android.support.v4.app.Watson} */
+public class SherlockFragmentActivity extends Watson implements OnActionModeStartedListener, OnActionModeFinishedListener {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "SherlockFragmentActivity";
+
+ private ActionBarSherlock mSherlock;
+ private boolean mIgnoreNativeCreate = false;
+ private boolean mIgnoreNativePrepare = false;
+ private boolean mIgnoreNativeSelected = false;
+
+ protected final ActionBarSherlock getSherlock() {
+ if (mSherlock == null) {
+ mSherlock = ActionBarSherlock.wrap(this, ActionBarSherlock.FLAG_DELEGATE);
+ }
+ return mSherlock;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Action bar and mode
+ ///////////////////////////////////////////////////////////////////////////
+
+ public ActionBar getSupportActionBar() {
+ return getSherlock().getActionBar();
+ }
+
+ public ActionMode startActionMode(ActionMode.Callback callback) {
+ return getSherlock().startActionMode(callback);
+ }
+
+ @Override
+ public void onActionModeStarted(ActionMode mode) {}
+
+ @Override
+ public void onActionModeFinished(ActionMode mode) {}
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // General lifecycle/callback dispatching
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ getSherlock().dispatchConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onPostResume() {
+ super.onPostResume();
+ getSherlock().dispatchPostResume();
+ }
+
+ @Override
+ protected void onPause() {
+ getSherlock().dispatchPause();
+ super.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ getSherlock().dispatchStop();
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ getSherlock().dispatchDestroy();
+ super.onDestroy();
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ getSherlock().dispatchPostCreate(savedInstanceState);
+ super.onPostCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onTitleChanged(CharSequence title, int color) {
+ getSherlock().dispatchTitleChanged(title, color);
+ super.onTitleChanged(title, color);
+ }
+
+ @Override
+ public final boolean onMenuOpened(int featureId, android.view.Menu menu) {
+ if (getSherlock().dispatchMenuOpened(featureId, menu)) {
+ return true;
+ }
+ return super.onMenuOpened(featureId, menu);
+ }
+
+ @Override
+ public void onPanelClosed(int featureId, android.view.Menu menu) {
+ getSherlock().dispatchPanelClosed(featureId, menu);
+ super.onPanelClosed(featureId, menu);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (getSherlock().dispatchKeyEvent(event)) {
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ getSherlock().dispatchSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ getSherlock().dispatchRestoreInstanceState(savedInstanceState);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Native menu handling
+ ///////////////////////////////////////////////////////////////////////////
+
+ public MenuInflater getSupportMenuInflater() {
+ if (DEBUG) Log.d(TAG, "[getSupportMenuInflater]");
+
+ return getSherlock().getMenuInflater();
+ }
+
+ public void invalidateOptionsMenu() {
+ if (DEBUG) Log.d(TAG, "[invalidateOptionsMenu]");
+
+ getSherlock().dispatchInvalidateOptionsMenu();
+ }
+
+ public void supportInvalidateOptionsMenu() {
+ if (DEBUG) Log.d(TAG, "[supportInvalidateOptionsMenu]");
+
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public final boolean onCreatePanelMenu(int featureId, android.view.Menu menu) {
+ if (DEBUG) Log.d(TAG, "[onCreatePanelMenu] featureId: " + featureId + ", menu: " + menu);
+
+ if (featureId == Window.FEATURE_OPTIONS_PANEL && !mIgnoreNativeCreate) {
+ mIgnoreNativeCreate = true;
+ boolean result = getSherlock().dispatchCreateOptionsMenu(menu);
+ mIgnoreNativeCreate = false;
+
+ if (DEBUG) Log.d(TAG, "[onCreatePanelMenu] returning " + result);
+ return result;
+ }
+ return super.onCreatePanelMenu(featureId, menu);
+ }
+
+ @Override
+ public final boolean onCreateOptionsMenu(android.view.Menu menu) {
+ return true;
+ }
+
+ @Override
+ public final boolean onPreparePanel(int featureId, View view, android.view.Menu menu) {
+ if (DEBUG) Log.d(TAG, "[onPreparePanel] featureId: " + featureId + ", view: " + view + ", menu: " + menu);
+
+ if (featureId == Window.FEATURE_OPTIONS_PANEL && !mIgnoreNativePrepare) {
+ mIgnoreNativePrepare = true;
+ boolean result = getSherlock().dispatchPrepareOptionsMenu(menu);
+ mIgnoreNativePrepare = false;
+
+ if (DEBUG) Log.d(TAG, "[onPreparePanel] returning " + result);
+ return result;
+ }
+ return super.onPreparePanel(featureId, view, menu);
+ }
+
+ @Override
+ public final boolean onPrepareOptionsMenu(android.view.Menu menu) {
+ return true;
+ }
+
+ @Override
+ public final boolean onMenuItemSelected(int featureId, android.view.MenuItem item) {
+ if (DEBUG) Log.d(TAG, "[onMenuItemSelected] featureId: " + featureId + ", item: " + item);
+
+ if (featureId == Window.FEATURE_OPTIONS_PANEL && !mIgnoreNativeSelected) {
+ mIgnoreNativeSelected = true;
+ boolean result = getSherlock().dispatchOptionsItemSelected(item);
+ mIgnoreNativeSelected = false;
+
+ if (DEBUG) Log.d(TAG, "[onMenuItemSelected] returning " + result);
+ return result;
+ }
+ return super.onMenuItemSelected(featureId, item);
+ }
+
+ @Override
+ public final boolean onOptionsItemSelected(android.view.MenuItem item) {
+ return false;
+ }
+
+ @Override
+ public void openOptionsMenu() {
+ if (!getSherlock().dispatchOpenOptionsMenu()) {
+ super.openOptionsMenu();
+ }
+ }
+
+ @Override
+ public void closeOptionsMenu() {
+ if (!getSherlock().dispatchCloseOptionsMenu()) {
+ super.closeOptionsMenu();
+ }
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Sherlock menu handling
+ ///////////////////////////////////////////////////////////////////////////
+
+ public boolean onCreateOptionsMenu(Menu menu) {
+ return true;
+ }
+
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ return true;
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return false;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Content
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void addContentView(View view, LayoutParams params) {
+ getSherlock().addContentView(view, params);
+ }
+
+ @Override
+ public void setContentView(int layoutResId) {
+ getSherlock().setContentView(layoutResId);
+ }
+
+ @Override
+ public void setContentView(View view, LayoutParams params) {
+ getSherlock().setContentView(view, params);
+ }
+
+ @Override
+ public void setContentView(View view) {
+ getSherlock().setContentView(view);
+ }
+
+ public void requestWindowFeature(long featureId) {
+ getSherlock().requestFeature((int)featureId);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Progress Indication
+ ///////////////////////////////////////////////////////////////////////////
+
+ public void setSupportProgress(int progress) {
+ getSherlock().setProgress(progress);
+ }
+
+ public void setSupportProgressBarIndeterminate(boolean indeterminate) {
+ getSherlock().setProgressBarIndeterminate(indeterminate);
+ }
+
+ public void setSupportProgressBarIndeterminateVisibility(boolean visible) {
+ getSherlock().setProgressBarIndeterminateVisibility(visible);
+ }
+
+ public void setSupportProgressBarVisibility(boolean visible) {
+ getSherlock().setProgressBarVisibility(visible);
+ }
+
+ public void setSupportSecondaryProgress(int secondaryProgress) {
+ getSherlock().setSecondaryProgress(secondaryProgress);
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockListActivity.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockListActivity.java
new file mode 100644
index 0000000000..aba6d85e88
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockListActivity.java
@@ -0,0 +1,270 @@
+package com.actionbarsherlock.app;
+
+import android.app.ListActivity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.ViewGroup.LayoutParams;
+import com.actionbarsherlock.ActionBarSherlock;
+import com.actionbarsherlock.ActionBarSherlock.OnActionModeFinishedListener;
+import com.actionbarsherlock.ActionBarSherlock.OnActionModeStartedListener;
+import com.actionbarsherlock.ActionBarSherlock.OnCreatePanelMenuListener;
+import com.actionbarsherlock.ActionBarSherlock.OnMenuItemSelectedListener;
+import com.actionbarsherlock.ActionBarSherlock.OnPreparePanelListener;
+import com.actionbarsherlock.view.ActionMode;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+
+public abstract class SherlockListActivity extends ListActivity implements OnCreatePanelMenuListener, OnPreparePanelListener, OnMenuItemSelectedListener, OnActionModeStartedListener, OnActionModeFinishedListener {
+ private ActionBarSherlock mSherlock;
+
+ protected final ActionBarSherlock getSherlock() {
+ if (mSherlock == null) {
+ mSherlock = ActionBarSherlock.wrap(this, ActionBarSherlock.FLAG_DELEGATE);
+ }
+ return mSherlock;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Action bar and mode
+ ///////////////////////////////////////////////////////////////////////////
+
+ public ActionBar getSupportActionBar() {
+ return getSherlock().getActionBar();
+ }
+
+ public ActionMode startActionMode(ActionMode.Callback callback) {
+ return getSherlock().startActionMode(callback);
+ }
+
+ @Override
+ public void onActionModeStarted(ActionMode mode) {}
+
+ @Override
+ public void onActionModeFinished(ActionMode mode) {}
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // General lifecycle/callback dispatching
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ getSherlock().dispatchConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onPostResume() {
+ super.onPostResume();
+ getSherlock().dispatchPostResume();
+ }
+
+ @Override
+ protected void onPause() {
+ getSherlock().dispatchPause();
+ super.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ getSherlock().dispatchStop();
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ getSherlock().dispatchDestroy();
+ super.onDestroy();
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ getSherlock().dispatchPostCreate(savedInstanceState);
+ super.onPostCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onTitleChanged(CharSequence title, int color) {
+ getSherlock().dispatchTitleChanged(title, color);
+ super.onTitleChanged(title, color);
+ }
+
+ @Override
+ public final boolean onMenuOpened(int featureId, android.view.Menu menu) {
+ if (getSherlock().dispatchMenuOpened(featureId, menu)) {
+ return true;
+ }
+ return super.onMenuOpened(featureId, menu);
+ }
+
+ @Override
+ public void onPanelClosed(int featureId, android.view.Menu menu) {
+ getSherlock().dispatchPanelClosed(featureId, menu);
+ super.onPanelClosed(featureId, menu);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (getSherlock().dispatchKeyEvent(event)) {
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ getSherlock().dispatchSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ getSherlock().dispatchRestoreInstanceState(savedInstanceState);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Native menu handling
+ ///////////////////////////////////////////////////////////////////////////
+
+ public MenuInflater getSupportMenuInflater() {
+ return getSherlock().getMenuInflater();
+ }
+
+ public void invalidateOptionsMenu() {
+ getSherlock().dispatchInvalidateOptionsMenu();
+ }
+
+ public void supportInvalidateOptionsMenu() {
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public final boolean onCreateOptionsMenu(android.view.Menu menu) {
+ return getSherlock().dispatchCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public final boolean onPrepareOptionsMenu(android.view.Menu menu) {
+ return getSherlock().dispatchPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public final boolean onOptionsItemSelected(android.view.MenuItem item) {
+ return getSherlock().dispatchOptionsItemSelected(item);
+ }
+
+ @Override
+ public void openOptionsMenu() {
+ if (!getSherlock().dispatchOpenOptionsMenu()) {
+ super.openOptionsMenu();
+ }
+ }
+
+ @Override
+ public void closeOptionsMenu() {
+ if (!getSherlock().dispatchCloseOptionsMenu()) {
+ super.closeOptionsMenu();
+ }
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Sherlock menu handling
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public boolean onCreatePanelMenu(int featureId, Menu menu) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+ return onCreateOptionsMenu(menu);
+ }
+ return false;
+ }
+
+ public boolean onCreateOptionsMenu(Menu menu) {
+ return true;
+ }
+
+ @Override
+ public boolean onPreparePanel(int featureId, View view, Menu menu) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+ return onPrepareOptionsMenu(menu);
+ }
+ return false;
+ }
+
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+ return onOptionsItemSelected(item);
+ }
+ return false;
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return false;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Content
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void addContentView(View view, LayoutParams params) {
+ getSherlock().addContentView(view, params);
+ }
+
+ @Override
+ public void setContentView(int layoutResId) {
+ getSherlock().setContentView(layoutResId);
+ }
+
+ @Override
+ public void setContentView(View view, LayoutParams params) {
+ getSherlock().setContentView(view, params);
+ }
+
+ @Override
+ public void setContentView(View view) {
+ getSherlock().setContentView(view);
+ }
+
+ public void requestWindowFeature(long featureId) {
+ getSherlock().requestFeature((int)featureId);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Progress Indication
+ ///////////////////////////////////////////////////////////////////////////
+
+ public void setSupportProgress(int progress) {
+ getSherlock().setProgress(progress);
+ }
+
+ public void setSupportProgressBarIndeterminate(boolean indeterminate) {
+ getSherlock().setProgressBarIndeterminate(indeterminate);
+ }
+
+ public void setSupportProgressBarIndeterminateVisibility(boolean visible) {
+ getSherlock().setProgressBarIndeterminateVisibility(visible);
+ }
+
+ public void setSupportProgressBarVisibility(boolean visible) {
+ getSherlock().setProgressBarVisibility(visible);
+ }
+
+ public void setSupportSecondaryProgress(int secondaryProgress) {
+ getSherlock().setSecondaryProgress(secondaryProgress);
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockListFragment.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockListFragment.java
new file mode 100644
index 0000000000..13ca3c49fb
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockListFragment.java
@@ -0,0 +1,68 @@
+package com.actionbarsherlock.app;
+
+import android.app.Activity;
+import android.support.v4.app.ListFragment;
+import com.actionbarsherlock.internal.view.menu.MenuItemWrapper;
+import com.actionbarsherlock.internal.view.menu.MenuWrapper;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+
+import static com.actionbarsherlock.app.SherlockFragmentActivity.OnCreateOptionsMenuListener;
+import static com.actionbarsherlock.app.SherlockFragmentActivity.OnOptionsItemSelectedListener;
+import static com.actionbarsherlock.app.SherlockFragmentActivity.OnPrepareOptionsMenuListener;
+
+public class SherlockListFragment extends ListFragment implements OnCreateOptionsMenuListener, OnPrepareOptionsMenuListener, OnOptionsItemSelectedListener {
+ private SherlockFragmentActivity mActivity;
+
+ public SherlockFragmentActivity getSherlockActivity() {
+ return mActivity;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ if (!(activity instanceof SherlockFragmentActivity)) {
+ throw new IllegalStateException(getClass().getSimpleName() + " must be attached to a SherlockFragmentActivity.");
+ }
+ mActivity = (SherlockFragmentActivity)activity;
+
+ super.onAttach(activity);
+ }
+
+ @Override
+ public void onDetach() {
+ mActivity = null;
+ super.onDetach();
+ }
+
+ @Override
+ public final void onCreateOptionsMenu(android.view.Menu menu, android.view.MenuInflater inflater) {
+ onCreateOptionsMenu(new MenuWrapper(menu), mActivity.getSupportMenuInflater());
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ //Nothing to see here.
+ }
+
+ @Override
+ public final void onPrepareOptionsMenu(android.view.Menu menu) {
+ onPrepareOptionsMenu(new MenuWrapper(menu));
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ //Nothing to see here.
+ }
+
+ @Override
+ public final boolean onOptionsItemSelected(android.view.MenuItem item) {
+ return onOptionsItemSelected(new MenuItemWrapper(item));
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ //Nothing to see here.
+ return false;
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockPreferenceActivity.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockPreferenceActivity.java
new file mode 100644
index 0000000000..bee72cb258
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/app/SherlockPreferenceActivity.java
@@ -0,0 +1,270 @@
+package com.actionbarsherlock.app;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.view.Window;
+import com.actionbarsherlock.ActionBarSherlock;
+import com.actionbarsherlock.ActionBarSherlock.OnActionModeFinishedListener;
+import com.actionbarsherlock.ActionBarSherlock.OnActionModeStartedListener;
+import com.actionbarsherlock.ActionBarSherlock.OnCreatePanelMenuListener;
+import com.actionbarsherlock.ActionBarSherlock.OnMenuItemSelectedListener;
+import com.actionbarsherlock.ActionBarSherlock.OnPreparePanelListener;
+import com.actionbarsherlock.view.ActionMode;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+
+public abstract class SherlockPreferenceActivity extends PreferenceActivity implements OnCreatePanelMenuListener, OnPreparePanelListener, OnMenuItemSelectedListener, OnActionModeStartedListener, OnActionModeFinishedListener {
+ private ActionBarSherlock mSherlock;
+
+ protected final ActionBarSherlock getSherlock() {
+ if (mSherlock == null) {
+ mSherlock = ActionBarSherlock.wrap(this, ActionBarSherlock.FLAG_DELEGATE);
+ }
+ return mSherlock;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Action bar and mode
+ ///////////////////////////////////////////////////////////////////////////
+
+ public ActionBar getSupportActionBar() {
+ return getSherlock().getActionBar();
+ }
+
+ public ActionMode startActionMode(ActionMode.Callback callback) {
+ return getSherlock().startActionMode(callback);
+ }
+
+ @Override
+ public void onActionModeStarted(ActionMode mode) {}
+
+ @Override
+ public void onActionModeFinished(ActionMode mode) {}
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // General lifecycle/callback dispatching
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ getSherlock().dispatchConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onPostResume() {
+ super.onPostResume();
+ getSherlock().dispatchPostResume();
+ }
+
+ @Override
+ protected void onPause() {
+ getSherlock().dispatchPause();
+ super.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ getSherlock().dispatchStop();
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ getSherlock().dispatchDestroy();
+ super.onDestroy();
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ getSherlock().dispatchPostCreate(savedInstanceState);
+ super.onPostCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onTitleChanged(CharSequence title, int color) {
+ getSherlock().dispatchTitleChanged(title, color);
+ super.onTitleChanged(title, color);
+ }
+
+ @Override
+ public final boolean onMenuOpened(int featureId, android.view.Menu menu) {
+ if (getSherlock().dispatchMenuOpened(featureId, menu)) {
+ return true;
+ }
+ return super.onMenuOpened(featureId, menu);
+ }
+
+ @Override
+ public void onPanelClosed(int featureId, android.view.Menu menu) {
+ getSherlock().dispatchPanelClosed(featureId, menu);
+ super.onPanelClosed(featureId, menu);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (getSherlock().dispatchKeyEvent(event)) {
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ getSherlock().dispatchSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ getSherlock().dispatchRestoreInstanceState(savedInstanceState);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Native menu handling
+ ///////////////////////////////////////////////////////////////////////////
+
+ public MenuInflater getSupportMenuInflater() {
+ return getSherlock().getMenuInflater();
+ }
+
+ public void invalidateOptionsMenu() {
+ getSherlock().dispatchInvalidateOptionsMenu();
+ }
+
+ public void supportInvalidateOptionsMenu() {
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public final boolean onCreateOptionsMenu(android.view.Menu menu) {
+ return getSherlock().dispatchCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public final boolean onPrepareOptionsMenu(android.view.Menu menu) {
+ return getSherlock().dispatchPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public final boolean onOptionsItemSelected(android.view.MenuItem item) {
+ return getSherlock().dispatchOptionsItemSelected(item);
+ }
+
+ @Override
+ public void openOptionsMenu() {
+ if (!getSherlock().dispatchOpenOptionsMenu()) {
+ super.openOptionsMenu();
+ }
+ }
+
+ @Override
+ public void closeOptionsMenu() {
+ if (!getSherlock().dispatchCloseOptionsMenu()) {
+ super.closeOptionsMenu();
+ }
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Sherlock menu handling
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public boolean onCreatePanelMenu(int featureId, Menu menu) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+ return onCreateOptionsMenu(menu);
+ }
+ return false;
+ }
+
+ public boolean onCreateOptionsMenu(Menu menu) {
+ return true;
+ }
+
+ @Override
+ public boolean onPreparePanel(int featureId, View view, Menu menu) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+ return onPrepareOptionsMenu(menu);
+ }
+ return false;
+ }
+
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
+ return onOptionsItemSelected(item);
+ }
+ return false;
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return false;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Content
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void addContentView(View view, LayoutParams params) {
+ getSherlock().addContentView(view, params);
+ }
+
+ @Override
+ public void setContentView(int layoutResId) {
+ getSherlock().setContentView(layoutResId);
+ }
+
+ @Override
+ public void setContentView(View view, LayoutParams params) {
+ getSherlock().setContentView(view, params);
+ }
+
+ @Override
+ public void setContentView(View view) {
+ getSherlock().setContentView(view);
+ }
+
+ public void requestWindowFeature(long featureId) {
+ getSherlock().requestFeature((int)featureId);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Progress Indication
+ ///////////////////////////////////////////////////////////////////////////
+
+ public void setSupportProgress(int progress) {
+ getSherlock().setProgress(progress);
+ }
+
+ public void setSupportProgressBarIndeterminate(boolean indeterminate) {
+ getSherlock().setProgressBarIndeterminate(indeterminate);
+ }
+
+ public void setSupportProgressBarIndeterminateVisibility(boolean visible) {
+ getSherlock().setProgressBarIndeterminateVisibility(visible);
+ }
+
+ public void setSupportProgressBarVisibility(boolean visible) {
+ getSherlock().setProgressBarVisibility(visible);
+ }
+
+ public void setSupportSecondaryProgress(int secondaryProgress) {
+ getSherlock().setSecondaryProgress(secondaryProgress);
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/ActionBarSherlockCompat.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/ActionBarSherlockCompat.java
new file mode 100644
index 0000000000..5e69275c7c
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/ActionBarSherlockCompat.java
@@ -0,0 +1,1203 @@
+package com.actionbarsherlock.internal;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getBoolean;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import org.xmlpull.v1.XmlPullParser;
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Bundle;
+import android.util.AndroidRuntimeException;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewStub;
+import android.view.Window;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+import com.actionbarsherlock.ActionBarSherlock;
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.internal.app.ActionBarImpl;
+import com.actionbarsherlock.internal.view.StandaloneActionMode;
+import com.actionbarsherlock.internal.view.menu.ActionMenuPresenter;
+import com.actionbarsherlock.internal.view.menu.MenuBuilder;
+import com.actionbarsherlock.internal.view.menu.MenuItemImpl;
+import com.actionbarsherlock.internal.view.menu.MenuPresenter;
+import com.actionbarsherlock.internal.widget.ActionBarContainer;
+import com.actionbarsherlock.internal.widget.ActionBarContextView;
+import com.actionbarsherlock.internal.widget.ActionBarView;
+import com.actionbarsherlock.internal.widget.IcsProgressBar;
+import com.actionbarsherlock.view.ActionMode;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+
+@ActionBarSherlock.Implementation(api = 7)
+public class ActionBarSherlockCompat extends ActionBarSherlock implements MenuBuilder.Callback, com.actionbarsherlock.view.Window.Callback, MenuPresenter.Callback, android.view.MenuItem.OnMenuItemClickListener {
+ /** Window features which are enabled by default. */
+ protected static final int DEFAULT_FEATURES = 0;
+
+ static private final String PANELS_TAG = "sherlock:Panels";
+
+ public ActionBarSherlockCompat(Activity activity, int flags) {
+ super(activity, flags);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Properties
+ ///////////////////////////////////////////////////////////////////////////
+
+ /** Whether or not the device has a dedicated menu key button. */
+ private boolean mReserveOverflow;
+ /** Lazy-load indicator for {@link #mReserveOverflow}. */
+ private boolean mReserveOverflowSet = false;
+
+ /** Current menu instance for managing action items. */
+ private MenuBuilder mMenu;
+ /** Map between native options items and sherlock items. */
+ protected HashMap mNativeItemMap;
+
+ /** Parent view of the window decoration (action bar, mode, etc.). */
+ private ViewGroup mDecor;
+ /** Parent view of the activity content. */
+ private ViewGroup mContentParent;
+
+ /** Whether or not the title is stable and can be displayed. */
+ private boolean mIsTitleReady = false;
+ /** Whether or not the parent activity has been destroyed. */
+ private boolean mIsDestroyed = false;
+
+ /* Emulate PanelFeatureState */
+ private boolean mClosingActionMenu;
+ private boolean mMenuIsPrepared;
+ private boolean mMenuRefreshContent;
+ private Bundle mMenuFrozenActionViewState;
+
+ /** Implementation which backs the action bar interface API. */
+ private ActionBarImpl aActionBar;
+ /** Main action bar view which displays the core content. */
+ private ActionBarView wActionBar;
+ /** Relevant window and action bar features flags. */
+ private int mFeatures = DEFAULT_FEATURES;
+ /** Relevant user interface option flags. */
+ private int mUiOptions = 0;
+
+ /** Decor indeterminate progress indicator. */
+ private IcsProgressBar mCircularProgressBar;
+ /** Decor progress indicator. */
+ private IcsProgressBar mHorizontalProgressBar;
+
+ /** Current displayed context action bar, if any. */
+ private ActionMode mActionMode;
+ /** Parent view in which the context action bar is displayed. */
+ private ActionBarContextView mActionModeView;
+
+ /** Title view used with dialogs. */
+ private TextView mTitleView;
+ /** Current activity title. */
+ private CharSequence mTitle = null;
+ /** Whether or not this "activity" is floating (i.e., a dialog) */
+ private boolean mIsFloating;
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Instance methods
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public ActionBar getActionBar() {
+ if (DEBUG) Log.d(TAG, "[getActionBar]");
+
+ initActionBar();
+ return aActionBar;
+ }
+
+ private void initActionBar() {
+ if (DEBUG) Log.d(TAG, "[initActionBar]");
+
+ // Initializing the window decor can change window feature flags.
+ // Make sure that we have the correct set before performing the test below.
+ if (mDecor == null) {
+ installDecor();
+ }
+
+ if ((aActionBar != null) || !hasFeature(Window.FEATURE_ACTION_BAR) || hasFeature(Window.FEATURE_NO_TITLE) || mActivity.isChild()) {
+ return;
+ }
+
+ aActionBar = new ActionBarImpl(mActivity, mFeatures);
+
+ if (!mIsDelegate) {
+ //We may never get another chance to set the title
+ wActionBar.setWindowTitle(mActivity.getTitle());
+ }
+ }
+
+ @Override
+ protected Context getThemedContext() {
+ return aActionBar.getThemedContext();
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ if (DEBUG) Log.d(TAG, "[setTitle] title: " + title);
+
+ dispatchTitleChanged(title, 0);
+ }
+
+ @Override
+ public ActionMode startActionMode(ActionMode.Callback callback) {
+ if (DEBUG) Log.d(TAG, "[startActionMode] callback: " + callback);
+
+ if (mActionMode != null) {
+ mActionMode.finish();
+ }
+
+ final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback);
+ ActionMode mode = null;
+
+ //Emulate Activity's onWindowStartingActionMode:
+ initActionBar();
+ if (aActionBar != null) {
+ mode = aActionBar.startActionMode(wrappedCallback);
+ }
+
+ if (mode != null) {
+ mActionMode = mode;
+ } else {
+ if (mActionModeView == null) {
+ ViewStub stub = (ViewStub)mDecor.findViewById(R.id.abs__action_mode_bar_stub);
+ if (stub != null) {
+ mActionModeView = (ActionBarContextView)stub.inflate();
+ }
+ }
+ if (mActionModeView != null) {
+ mActionModeView.killMode();
+ mode = new StandaloneActionMode(mActivity, mActionModeView, wrappedCallback, true);
+ if (callback.onCreateActionMode(mode, mode.getMenu())) {
+ mode.invalidate();
+ mActionModeView.initForMode(mode);
+ mActionModeView.setVisibility(View.VISIBLE);
+ mActionMode = mode;
+ mActionModeView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ } else {
+ mActionMode = null;
+ }
+ }
+ }
+ if (mActionMode != null && mActivity instanceof OnActionModeStartedListener) {
+ ((OnActionModeStartedListener)mActivity).onActionModeStarted(mActionMode);
+ }
+ return mActionMode;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Lifecycle and interaction callbacks for delegation
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void dispatchConfigurationChanged(Configuration newConfig) {
+ if (DEBUG) Log.d(TAG, "[dispatchConfigurationChanged] newConfig: " + newConfig);
+
+ if (aActionBar != null) {
+ aActionBar.onConfigurationChanged(newConfig);
+ }
+ }
+
+ @Override
+ public void dispatchPostResume() {
+ if (DEBUG) Log.d(TAG, "[dispatchPostResume]");
+
+ if (aActionBar != null) {
+ aActionBar.setShowHideAnimationEnabled(true);
+ }
+ }
+
+ @Override
+ public void dispatchPause() {
+ if (DEBUG) Log.d(TAG, "[dispatchPause]");
+
+ if (wActionBar != null && wActionBar.isOverflowMenuShowing()) {
+ wActionBar.hideOverflowMenu();
+ }
+ }
+
+ @Override
+ public void dispatchStop() {
+ if (DEBUG) Log.d(TAG, "[dispatchStop]");
+
+ if (aActionBar != null) {
+ aActionBar.setShowHideAnimationEnabled(false);
+ }
+ }
+
+ @Override
+ public void dispatchInvalidateOptionsMenu() {
+ if (DEBUG) Log.d(TAG, "[dispatchInvalidateOptionsMenu]");
+
+ Bundle savedActionViewStates = null;
+ if (mMenu != null) {
+ savedActionViewStates = new Bundle();
+ mMenu.saveActionViewStates(savedActionViewStates);
+ if (savedActionViewStates.size() > 0) {
+ mMenuFrozenActionViewState = savedActionViewStates;
+ }
+ // This will be started again when the panel is prepared.
+ mMenu.stopDispatchingItemsChanged();
+ mMenu.clear();
+ }
+ mMenuRefreshContent = true;
+
+ // Prepare the options panel if we have an action bar
+ if (wActionBar != null) {
+ mMenuIsPrepared = false;
+ preparePanel();
+ }
+ }
+
+ @Override
+ public boolean dispatchOpenOptionsMenu() {
+ if (DEBUG) Log.d(TAG, "[dispatchOpenOptionsMenu]");
+
+ if (!isReservingOverflow()) {
+ return false;
+ }
+
+ return wActionBar.showOverflowMenu();
+ }
+
+ @Override
+ public boolean dispatchCloseOptionsMenu() {
+ if (DEBUG) Log.d(TAG, "[dispatchCloseOptionsMenu]");
+
+ if (!isReservingOverflow()) {
+ return false;
+ }
+
+ if (wActionBar != null) {
+ return wActionBar.hideOverflowMenu();
+ }
+ return false;
+ }
+
+ @Override
+ public void dispatchPostCreate(Bundle savedInstanceState) {
+ if (DEBUG) Log.d(TAG, "[dispatchOnPostCreate]");
+
+ if (mIsDelegate) {
+ mIsTitleReady = true;
+ }
+
+ if (mDecor == null) {
+ initActionBar();
+ }
+ }
+
+ @Override
+ public boolean dispatchCreateOptionsMenu(android.view.Menu menu) {
+ if (DEBUG) {
+ Log.d(TAG, "[dispatchCreateOptionsMenu] android.view.Menu: " + menu);
+ Log.d(TAG, "[dispatchCreateOptionsMenu] returning true");
+ }
+ return true;
+ }
+
+ @Override
+ public boolean dispatchPrepareOptionsMenu(android.view.Menu menu) {
+ if (DEBUG) Log.d(TAG, "[dispatchPrepareOptionsMenu] android.view.Menu: " + menu);
+
+ if (mActionMode != null) {
+ return false;
+ }
+
+ mMenuIsPrepared = false;
+ if (!preparePanel()) {
+ return false;
+ }
+
+ if (isReservingOverflow()) {
+ return false;
+ }
+
+ if (mNativeItemMap == null) {
+ mNativeItemMap = new HashMap();
+ } else {
+ mNativeItemMap.clear();
+ }
+
+ if (mMenu == null) {
+ return false;
+ }
+
+ boolean result = mMenu.bindNativeOverflow(menu, this, mNativeItemMap);
+ if (DEBUG) Log.d(TAG, "[dispatchPrepareOptionsMenu] returning " + result);
+ return result;
+ }
+
+ @Override
+ public boolean dispatchOptionsItemSelected(android.view.MenuItem item) {
+ throw new IllegalStateException("Native callback invoked. Create a test case and report!");
+ }
+
+ @Override
+ public boolean dispatchMenuOpened(int featureId, android.view.Menu menu) {
+ if (DEBUG) Log.d(TAG, "[dispatchMenuOpened] featureId: " + featureId + ", menu: " + menu);
+
+ if (featureId == Window.FEATURE_ACTION_BAR || featureId == Window.FEATURE_OPTIONS_PANEL) {
+ if (aActionBar != null) {
+ aActionBar.dispatchMenuVisibilityChanged(true);
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void dispatchPanelClosed(int featureId, android.view.Menu menu){
+ if (DEBUG) Log.d(TAG, "[dispatchPanelClosed] featureId: " + featureId + ", menu: " + menu);
+
+ if (featureId == Window.FEATURE_ACTION_BAR || featureId == Window.FEATURE_OPTIONS_PANEL) {
+ if (aActionBar != null) {
+ aActionBar.dispatchMenuVisibilityChanged(false);
+ }
+ }
+ }
+
+ @Override
+ public void dispatchTitleChanged(CharSequence title, int color) {
+ if (DEBUG) Log.d(TAG, "[dispatchTitleChanged] title: " + title + ", color: " + color);
+
+ if (!mIsDelegate || mIsTitleReady) {
+ if (mTitleView != null) {
+ mTitleView.setText(title);
+ } else if (wActionBar != null) {
+ wActionBar.setWindowTitle(title);
+ }
+ }
+
+ mTitle = title;
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (DEBUG) Log.d(TAG, "[dispatchKeyEvent] event: " + event);
+
+ final int keyCode = event.getKeyCode();
+
+ // Not handled by the view hierarchy, does the action bar want it
+ // to cancel out of something special?
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ final int action = event.getAction();
+ // Back cancels action modes first.
+ if (mActionMode != null) {
+ if (action == KeyEvent.ACTION_UP) {
+ mActionMode.finish();
+ }
+ if (DEBUG) Log.d(TAG, "[dispatchKeyEvent] returning true");
+ return true;
+ }
+
+ // Next collapse any expanded action views.
+ if (wActionBar != null && wActionBar.hasExpandedActionView()) {
+ if (action == KeyEvent.ACTION_UP) {
+ wActionBar.collapseActionView();
+ }
+ if (DEBUG) Log.d(TAG, "[dispatchKeyEvent] returning true");
+ return true;
+ }
+ }
+
+ if (DEBUG) Log.d(TAG, "[dispatchKeyEvent] returning false");
+ return false;
+ }
+
+ @Override
+ public void dispatchDestroy() {
+ mIsDestroyed = true;
+ }
+
+ @Override
+ public void dispatchSaveInstanceState(Bundle outState) {
+ if (mMenu != null) {
+ mMenuFrozenActionViewState = new Bundle();
+ mMenu.saveActionViewStates(mMenuFrozenActionViewState);
+ }
+ outState.putParcelable(PANELS_TAG, mMenuFrozenActionViewState);
+ }
+
+ @Override
+ public void dispatchRestoreInstanceState(Bundle savedInstanceState) {
+ mMenuFrozenActionViewState = savedInstanceState.getParcelable(PANELS_TAG);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Menu callback lifecycle and creation
+ ///////////////////////////////////////////////////////////////////////////
+
+ private boolean preparePanel() {
+ // Already prepared (isPrepared will be reset to false later)
+ if (mMenuIsPrepared) {
+ return true;
+ }
+
+ // Init the panel state's menu--return false if init failed
+ if (mMenu == null || mMenuRefreshContent) {
+ if (mMenu == null) {
+ if (!initializePanelMenu() || (mMenu == null)) {
+ return false;
+ }
+ }
+
+ if (wActionBar != null) {
+ wActionBar.setMenu(mMenu, this);
+ }
+
+ // Call callback, and return if it doesn't want to display menu.
+
+ // Creating the panel menu will involve a lot of manipulation;
+ // don't dispatch change events to presenters until we're done.
+ mMenu.stopDispatchingItemsChanged();
+ if (!callbackCreateOptionsMenu(mMenu)) {
+ // Ditch the menu created above
+ mMenu = null;
+
+ if (wActionBar != null) {
+ // Don't show it in the action bar either
+ wActionBar.setMenu(null, this);
+ }
+
+ return false;
+ }
+
+ mMenuRefreshContent = false;
+ }
+
+ // Callback and return if the callback does not want to show the menu
+
+ // Preparing the panel menu can involve a lot of manipulation;
+ // don't dispatch change events to presenters until we're done.
+ mMenu.stopDispatchingItemsChanged();
+
+ // Restore action view state before we prepare. This gives apps
+ // an opportunity to override frozen/restored state in onPrepare.
+ if (mMenuFrozenActionViewState != null) {
+ mMenu.restoreActionViewStates(mMenuFrozenActionViewState);
+ mMenuFrozenActionViewState = null;
+ }
+
+ if (!callbackPrepareOptionsMenu(mMenu)) {
+ if (wActionBar != null) {
+ // The app didn't want to show the menu for now but it still exists.
+ // Clear it out of the action bar.
+ wActionBar.setMenu(null, this);
+ }
+ mMenu.startDispatchingItemsChanged();
+ return false;
+ }
+
+ // Set the proper keymap
+ KeyCharacterMap kmap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+ mMenu.setQwertyMode(kmap.getKeyboardType() != KeyCharacterMap.NUMERIC);
+ mMenu.startDispatchingItemsChanged();
+
+ // Set other state
+ mMenuIsPrepared = true;
+
+ return true;
+ }
+
+ public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
+ return callbackOptionsItemSelected(item);
+ }
+
+ public void onMenuModeChange(MenuBuilder menu) {
+ reopenMenu(true);
+ }
+
+ private void reopenMenu(boolean toggleMenuMode) {
+ if (wActionBar != null && wActionBar.isOverflowReserved()) {
+ if (!wActionBar.isOverflowMenuShowing() || !toggleMenuMode) {
+ if (wActionBar.getVisibility() == View.VISIBLE) {
+ if (callbackPrepareOptionsMenu(mMenu)) {
+ wActionBar.showOverflowMenu();
+ }
+ }
+ } else {
+ wActionBar.hideOverflowMenu();
+ }
+ return;
+ }
+ }
+
+ private boolean initializePanelMenu() {
+ Context context = mActivity;//getContext();
+
+ // If we have an action bar, initialize the menu with a context themed for it.
+ if (wActionBar != null) {
+ TypedValue outValue = new TypedValue();
+ Resources.Theme currentTheme = context.getTheme();
+ currentTheme.resolveAttribute(R.attr.actionBarWidgetTheme,
+ outValue, true);
+ final int targetThemeRes = outValue.resourceId;
+
+ if (targetThemeRes != 0 /*&& context.getThemeResId() != targetThemeRes*/) {
+ context = new ContextThemeWrapper(context, targetThemeRes);
+ }
+ }
+
+ mMenu = new MenuBuilder(context);
+ mMenu.setCallback(this);
+
+ return true;
+ }
+
+ void checkCloseActionMenu(Menu menu) {
+ if (mClosingActionMenu) {
+ return;
+ }
+
+ mClosingActionMenu = true;
+ wActionBar.dismissPopupMenus();
+ //Callback cb = getCallback();
+ //if (cb != null && !isDestroyed()) {
+ // cb.onPanelClosed(FEATURE_ACTION_BAR, menu);
+ //}
+ mClosingActionMenu = false;
+ }
+
+ @Override
+ public boolean onOpenSubMenu(MenuBuilder subMenu) {
+ return true;
+ }
+
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ checkCloseActionMenu(menu);
+ }
+
+ @Override
+ public boolean onMenuItemClick(android.view.MenuItem item) {
+ if (DEBUG) Log.d(TAG, "[mNativeItemListener.onMenuItemClick] item: " + item);
+
+ final MenuItemImpl sherlockItem = mNativeItemMap.get(item);
+ if (sherlockItem != null) {
+ sherlockItem.invoke();
+ } else {
+ Log.e(TAG, "Options item \"" + item + "\" not found in mapping");
+ }
+
+ return true; //Do not allow continuation of native handling
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ return callbackOptionsItemSelected(item);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Progress bar interaction and internal handling
+ ///////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void setProgressBarVisibility(boolean visible) {
+ if (DEBUG) Log.d(TAG, "[setProgressBarVisibility] visible: " + visible);
+
+ setFeatureInt(Window.FEATURE_PROGRESS, visible ? Window.PROGRESS_VISIBILITY_ON :
+ Window.PROGRESS_VISIBILITY_OFF);
+ }
+
+ @Override
+ public void setProgressBarIndeterminateVisibility(boolean visible) {
+ if (DEBUG) Log.d(TAG, "[setProgressBarIndeterminateVisibility] visible: " + visible);
+
+ setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
+ visible ? Window.PROGRESS_VISIBILITY_ON : Window.PROGRESS_VISIBILITY_OFF);
+ }
+
+ @Override
+ public void setProgressBarIndeterminate(boolean indeterminate) {
+ if (DEBUG) Log.d(TAG, "[setProgressBarIndeterminate] indeterminate: " + indeterminate);
+
+ setFeatureInt(Window.FEATURE_PROGRESS,
+ indeterminate ? Window.PROGRESS_INDETERMINATE_ON : Window.PROGRESS_INDETERMINATE_OFF);
+ }
+
+ @Override
+ public void setProgress(int progress) {
+ if (DEBUG) Log.d(TAG, "[setProgress] progress: " + progress);
+
+ setFeatureInt(Window.FEATURE_PROGRESS, progress + Window.PROGRESS_START);
+ }
+
+ @Override
+ public void setSecondaryProgress(int secondaryProgress) {
+ if (DEBUG) Log.d(TAG, "[setSecondaryProgress] secondaryProgress: " + secondaryProgress);
+
+ setFeatureInt(Window.FEATURE_PROGRESS,
+ secondaryProgress + Window.PROGRESS_SECONDARY_START);
+ }
+
+ private void setFeatureInt(int featureId, int value) {
+ updateInt(featureId, value, false);
+ }
+
+ private void updateInt(int featureId, int value, boolean fromResume) {
+ // Do nothing if the decor is not yet installed... an update will
+ // need to be forced when we eventually become active.
+ if (mContentParent == null) {
+ return;
+ }
+
+ final int featureMask = 1 << featureId;
+
+ if ((getFeatures() & featureMask) == 0 && !fromResume) {
+ return;
+ }
+
+ onIntChanged(featureId, value);
+ }
+
+ private void onIntChanged(int featureId, int value) {
+ if (featureId == Window.FEATURE_PROGRESS || featureId == Window.FEATURE_INDETERMINATE_PROGRESS) {
+ updateProgressBars(value);
+ }
+ }
+
+ private void updateProgressBars(int value) {
+ IcsProgressBar circularProgressBar = getCircularProgressBar(true);
+ IcsProgressBar horizontalProgressBar = getHorizontalProgressBar(true);
+
+ final int features = mFeatures;//getLocalFeatures();
+ if (value == Window.PROGRESS_VISIBILITY_ON) {
+ if ((features & (1 << Window.FEATURE_PROGRESS)) != 0) {
+ int level = horizontalProgressBar.getProgress();
+ int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ?
+ View.VISIBLE : View.INVISIBLE;
+ horizontalProgressBar.setVisibility(visibility);
+ }
+ if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0) {
+ circularProgressBar.setVisibility(View.VISIBLE);
+ }
+ } else if (value == Window.PROGRESS_VISIBILITY_OFF) {
+ if ((features & (1 << Window.FEATURE_PROGRESS)) != 0) {
+ horizontalProgressBar.setVisibility(View.GONE);
+ }
+ if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0) {
+ circularProgressBar.setVisibility(View.GONE);
+ }
+ } else if (value == Window.PROGRESS_INDETERMINATE_ON) {
+ horizontalProgressBar.setIndeterminate(true);
+ } else if (value == Window.PROGRESS_INDETERMINATE_OFF) {
+ horizontalProgressBar.setIndeterminate(false);
+ } else if (Window.PROGRESS_START <= value && value <= Window.PROGRESS_END) {
+ // We want to set the progress value before testing for visibility
+ // so that when the progress bar becomes visible again, it has the
+ // correct level.
+ horizontalProgressBar.setProgress(value - Window.PROGRESS_START);
+
+ if (value < Window.PROGRESS_END) {
+ showProgressBars(horizontalProgressBar, circularProgressBar);
+ } else {
+ hideProgressBars(horizontalProgressBar, circularProgressBar);
+ }
+ } else if (Window.PROGRESS_SECONDARY_START <= value && value <= Window.PROGRESS_SECONDARY_END) {
+ horizontalProgressBar.setSecondaryProgress(value - Window.PROGRESS_SECONDARY_START);
+
+ showProgressBars(horizontalProgressBar, circularProgressBar);
+ }
+ }
+
+ private void showProgressBars(IcsProgressBar horizontalProgressBar, IcsProgressBar spinnyProgressBar) {
+ final int features = mFeatures;//getLocalFeatures();
+ if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0 &&
+ spinnyProgressBar.getVisibility() == View.INVISIBLE) {
+ spinnyProgressBar.setVisibility(View.VISIBLE);
+ }
+ // Only show the progress bars if the primary progress is not complete
+ if ((features & (1 << Window.FEATURE_PROGRESS)) != 0 &&
+ horizontalProgressBar.getProgress() < 10000) {
+ horizontalProgressBar.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private void hideProgressBars(IcsProgressBar horizontalProgressBar, IcsProgressBar spinnyProgressBar) {
+ final int features = mFeatures;//getLocalFeatures();
+ Animation anim = AnimationUtils.loadAnimation(mActivity, android.R.anim.fade_out);
+ anim.setDuration(1000);
+ if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0 &&
+ spinnyProgressBar.getVisibility() == View.VISIBLE) {
+ spinnyProgressBar.startAnimation(anim);
+ spinnyProgressBar.setVisibility(View.INVISIBLE);
+ }
+ if ((features & (1 << Window.FEATURE_PROGRESS)) != 0 &&
+ horizontalProgressBar.getVisibility() == View.VISIBLE) {
+ horizontalProgressBar.startAnimation(anim);
+ horizontalProgressBar.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ private IcsProgressBar getCircularProgressBar(boolean shouldInstallDecor) {
+ if (mCircularProgressBar != null) {
+ return mCircularProgressBar;
+ }
+ if (mContentParent == null && shouldInstallDecor) {
+ installDecor();
+ }
+ mCircularProgressBar = (IcsProgressBar)mDecor.findViewById(R.id.abs__progress_circular);
+ if (mCircularProgressBar != null) {
+ mCircularProgressBar.setVisibility(View.INVISIBLE);
+ }
+ return mCircularProgressBar;
+ }
+
+ private IcsProgressBar getHorizontalProgressBar(boolean shouldInstallDecor) {
+ if (mHorizontalProgressBar != null) {
+ return mHorizontalProgressBar;
+ }
+ if (mContentParent == null && shouldInstallDecor) {
+ installDecor();
+ }
+ mHorizontalProgressBar = (IcsProgressBar)mDecor.findViewById(R.id.abs__progress_horizontal);
+ if (mHorizontalProgressBar != null) {
+ mHorizontalProgressBar.setVisibility(View.INVISIBLE);
+ }
+ return mHorizontalProgressBar;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Feature management and content interaction and creation
+ ///////////////////////////////////////////////////////////////////////////
+
+ private int getFeatures() {
+ if (DEBUG) Log.d(TAG, "[getFeatures] returning " + mFeatures);
+
+ return mFeatures;
+ }
+
+ @Override
+ public boolean hasFeature(int featureId) {
+ if (DEBUG) Log.d(TAG, "[hasFeature] featureId: " + featureId);
+
+ boolean result = (mFeatures & (1 << featureId)) != 0;
+ if (DEBUG) Log.d(TAG, "[hasFeature] returning " + result);
+ return result;
+ }
+
+ @Override
+ public boolean requestFeature(int featureId) {
+ if (DEBUG) Log.d(TAG, "[requestFeature] featureId: " + featureId);
+
+ if (mContentParent != null) {
+ throw new AndroidRuntimeException("requestFeature() must be called before adding content");
+ }
+
+ switch (featureId) {
+ case Window.FEATURE_ACTION_BAR:
+ case Window.FEATURE_ACTION_BAR_OVERLAY:
+ case Window.FEATURE_ACTION_MODE_OVERLAY:
+ case Window.FEATURE_INDETERMINATE_PROGRESS:
+ case Window.FEATURE_NO_TITLE:
+ case Window.FEATURE_PROGRESS:
+ mFeatures |= (1 << featureId);
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public void setUiOptions(int uiOptions) {
+ if (DEBUG) Log.d(TAG, "[setUiOptions] uiOptions: " + uiOptions);
+
+ mUiOptions = uiOptions;
+ }
+
+ @Override
+ public void setUiOptions(int uiOptions, int mask) {
+ if (DEBUG) Log.d(TAG, "[setUiOptions] uiOptions: " + uiOptions + ", mask: " + mask);
+
+ mUiOptions = (mUiOptions & ~mask) | (uiOptions & mask);
+ }
+
+ @Override
+ public void setContentView(int layoutResId) {
+ if (DEBUG) Log.d(TAG, "[setContentView] layoutResId: " + layoutResId);
+
+ if (mContentParent == null) {
+ installDecor();
+ } else {
+ mContentParent.removeAllViews();
+ }
+ mActivity.getLayoutInflater().inflate(layoutResId, mContentParent);
+
+ android.view.Window.Callback callback = mActivity.getWindow().getCallback();
+ if (callback != null) {
+ callback.onContentChanged();
+ }
+
+ initActionBar();
+ }
+
+ @Override
+ public void setContentView(View view, ViewGroup.LayoutParams params) {
+ if (DEBUG) Log.d(TAG, "[setContentView] view: " + view + ", params: " + params);
+
+ if (mContentParent == null) {
+ installDecor();
+ } else {
+ mContentParent.removeAllViews();
+ }
+ mContentParent.addView(view, params);
+
+ android.view.Window.Callback callback = mActivity.getWindow().getCallback();
+ if (callback != null) {
+ callback.onContentChanged();
+ }
+
+ initActionBar();
+ }
+
+ @Override
+ public void addContentView(View view, ViewGroup.LayoutParams params) {
+ if (DEBUG) Log.d(TAG, "[addContentView] view: " + view + ", params: " + params);
+
+ if (mContentParent == null) {
+ installDecor();
+ }
+ mContentParent.addView(view, params);
+
+ initActionBar();
+ }
+
+ private void installDecor() {
+ if (DEBUG) Log.d(TAG, "[installDecor]");
+
+ if (mDecor == null) {
+ mDecor = (ViewGroup)mActivity.getWindow().getDecorView().findViewById(android.R.id.content);
+ }
+ if (mContentParent == null) {
+ //Since we are not operating at the window level we need to take
+ //into account the fact that the true decor may have already been
+ //initialized and had content attached to it. If that is the case,
+ //copy over its children to our new content container.
+ List views = null;
+ if (mDecor.getChildCount() > 0) {
+ views = new ArrayList(1); //Usually there's only one child
+ for (int i = 0, children = mDecor.getChildCount(); i < children; i++) {
+ View child = mDecor.getChildAt(0);
+ mDecor.removeView(child);
+ views.add(child);
+ }
+ }
+
+ mContentParent = generateLayout();
+
+ //Copy over the old children. See above for explanation.
+ if (views != null) {
+ for (View child : views) {
+ mContentParent.addView(child);
+ }
+ }
+
+ mTitleView = (TextView)mDecor.findViewById(android.R.id.title);
+ if (mTitleView != null) {
+ if (hasFeature(Window.FEATURE_NO_TITLE)) {
+ mTitleView.setVisibility(View.GONE);
+ if (mContentParent instanceof FrameLayout) {
+ ((FrameLayout)mContentParent).setForeground(null);
+ }
+ } else {
+ mTitleView.setText(mTitle);
+ }
+ } else {
+ wActionBar = (ActionBarView)mDecor.findViewById(R.id.abs__action_bar);
+ if (wActionBar != null) {
+ wActionBar.setWindowCallback(this);
+ if (wActionBar.getTitle() == null) {
+ wActionBar.setWindowTitle(mActivity.getTitle());
+ }
+ if (hasFeature(Window.FEATURE_PROGRESS)) {
+ wActionBar.initProgress();
+ }
+ if (hasFeature(Window.FEATURE_INDETERMINATE_PROGRESS)) {
+ wActionBar.initIndeterminateProgress();
+ }
+
+ //Since we don't require onCreate dispatching, parse for uiOptions here
+ int uiOptions = loadUiOptionsFromManifest(mActivity);
+ if (uiOptions != 0) {
+ mUiOptions = uiOptions;
+ }
+
+ boolean splitActionBar = false;
+ final boolean splitWhenNarrow = (mUiOptions & ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW) != 0;
+ if (splitWhenNarrow) {
+ splitActionBar = getResources_getBoolean(mActivity, R.bool.abs__split_action_bar_is_narrow);
+ } else {
+ splitActionBar = mActivity.getTheme()
+ .obtainStyledAttributes(R.styleable.SherlockTheme)
+ .getBoolean(R.styleable.SherlockTheme_windowSplitActionBar, false);
+ }
+ final ActionBarContainer splitView = (ActionBarContainer)mDecor.findViewById(R.id.abs__split_action_bar);
+ if (splitView != null) {
+ wActionBar.setSplitView(splitView);
+ wActionBar.setSplitActionBar(splitActionBar);
+ wActionBar.setSplitWhenNarrow(splitWhenNarrow);
+
+ mActionModeView = (ActionBarContextView)mDecor.findViewById(R.id.abs__action_context_bar);
+ mActionModeView.setSplitView(splitView);
+ mActionModeView.setSplitActionBar(splitActionBar);
+ mActionModeView.setSplitWhenNarrow(splitWhenNarrow);
+ } else if (splitActionBar) {
+ Log.e(TAG, "Requested split action bar with incompatible window decor! Ignoring request.");
+ }
+
+ // Post the panel invalidate for later; avoid application onCreateOptionsMenu
+ // being called in the middle of onCreate or similar.
+ mDecor.post(new Runnable() {
+ @Override
+ public void run() {
+ //Invalidate if the panel menu hasn't been created before this.
+ if (!mIsDestroyed && !mActivity.isFinishing() && mMenu == null) {
+ dispatchInvalidateOptionsMenu();
+ }
+ }
+ });
+ }
+ }
+ }
+ }
+
+ private ViewGroup generateLayout() {
+ if (DEBUG) Log.d(TAG, "[generateLayout]");
+
+ // Apply data from current theme.
+
+ TypedArray a = mActivity.getTheme().obtainStyledAttributes(R.styleable.SherlockTheme);
+
+ mIsFloating = a.getBoolean(R.styleable.SherlockTheme_android_windowIsFloating, false);
+
+ if (!a.hasValue(R.styleable.SherlockTheme_windowActionBar)) {
+ throw new IllegalStateException("You must use Theme.Sherlock, Theme.Sherlock.Light, Theme.Sherlock.Light.DarkActionBar, or a derivative.");
+ }
+
+ if (a.getBoolean(R.styleable.SherlockTheme_windowNoTitle, false)) {
+ requestFeature(Window.FEATURE_NO_TITLE);
+ } else if (a.getBoolean(R.styleable.SherlockTheme_windowActionBar, false)) {
+ // Don't allow an action bar if there is no title.
+ requestFeature(Window.FEATURE_ACTION_BAR);
+ }
+
+ if (a.getBoolean(R.styleable.SherlockTheme_windowActionBarOverlay, false)) {
+ requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
+ }
+
+ if (a.getBoolean(R.styleable.SherlockTheme_windowActionModeOverlay, false)) {
+ requestFeature(Window.FEATURE_ACTION_MODE_OVERLAY);
+ }
+
+ a.recycle();
+
+ int layoutResource;
+ if (!hasFeature(Window.FEATURE_NO_TITLE)) {
+ if (mIsFloating) {
+ //Trash original dialog LinearLayout
+ mDecor = (ViewGroup)mDecor.getParent();
+ mDecor.removeAllViews();
+
+ layoutResource = R.layout.abs__dialog_title_holo;
+ } else {
+ if (hasFeature(Window.FEATURE_ACTION_BAR_OVERLAY)) {
+ layoutResource = R.layout.abs__screen_action_bar_overlay;
+ } else {
+ layoutResource = R.layout.abs__screen_action_bar;
+ }
+ }
+ } else if (hasFeature(Window.FEATURE_ACTION_MODE_OVERLAY) && !hasFeature(Window.FEATURE_NO_TITLE)) {
+ layoutResource = R.layout.abs__screen_simple_overlay_action_mode;
+ } else {
+ layoutResource = R.layout.abs__screen_simple;
+ }
+
+ if (DEBUG) Log.d(TAG, "[generateLayout] using screen XML " + mActivity.getResources().getString(layoutResource));
+ View in = mActivity.getLayoutInflater().inflate(layoutResource, null);
+ mDecor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+ ViewGroup contentParent = (ViewGroup)mDecor.findViewById(R.id.abs__content);
+ if (contentParent == null) {
+ throw new RuntimeException("Couldn't find content container view");
+ }
+
+ //Make our new child the true content view (for fragments). VERY VOLATILE!
+ mDecor.setId(View.NO_ID);
+ contentParent.setId(android.R.id.content);
+
+ if (hasFeature(Window.FEATURE_INDETERMINATE_PROGRESS)) {
+ IcsProgressBar progress = getCircularProgressBar(false);
+ if (progress != null) {
+ progress.setIndeterminate(true);
+ }
+ }
+
+ return contentParent;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Miscellaneous
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Determine whether or not the device has a dedicated menu key.
+ *
+ * @return {@code true} if native menu key is present.
+ */
+ private boolean isReservingOverflow() {
+ if (!mReserveOverflowSet) {
+ mReserveOverflow = ActionMenuPresenter.reserveOverflow(mActivity);
+ mReserveOverflowSet = true;
+ }
+ return mReserveOverflow;
+ }
+
+ private static int loadUiOptionsFromManifest(Activity activity) {
+ int uiOptions = 0;
+ try {
+ final String thisPackage = activity.getClass().getName();
+ if (DEBUG) Log.i(TAG, "Parsing AndroidManifest.xml for " + thisPackage);
+
+ final String packageName = activity.getApplicationInfo().packageName;
+ final AssetManager am = activity.createPackageContext(packageName, 0).getAssets();
+ final XmlResourceParser xml = am.openXmlResourceParser("AndroidManifest.xml");
+
+ int eventType = xml.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ String name = xml.getName();
+
+ if ("application".equals(name)) {
+ //Check if the has the attribute
+ if (DEBUG) Log.d(TAG, "Got ");
+
+ for (int i = xml.getAttributeCount() - 1; i >= 0; i--) {
+ if (DEBUG) Log.d(TAG, xml.getAttributeName(i) + ": " + xml.getAttributeValue(i));
+
+ if ("uiOptions".equals(xml.getAttributeName(i))) {
+ uiOptions = xml.getAttributeIntValue(i, 0);
+ break; //out of for loop
+ }
+ }
+ } else if ("activity".equals(name)) {
+ //Check if the is us and has the attribute
+ if (DEBUG) Log.d(TAG, "Got ");
+ Integer activityUiOptions = null;
+ String activityPackage = null;
+ boolean isOurActivity = false;
+
+ for (int i = xml.getAttributeCount() - 1; i >= 0; i--) {
+ if (DEBUG) Log.d(TAG, xml.getAttributeName(i) + ": " + xml.getAttributeValue(i));
+
+ //We need both uiOptions and name attributes
+ String attrName = xml.getAttributeName(i);
+ if ("uiOptions".equals(attrName)) {
+ activityUiOptions = xml.getAttributeIntValue(i, 0);
+ } else if ("name".equals(attrName)) {
+ activityPackage = cleanActivityName(packageName, xml.getAttributeValue(i));
+ if (!thisPackage.equals(activityPackage)) {
+ break; //out of for loop
+ }
+ isOurActivity = true;
+ }
+
+ //Make sure we have both attributes before processing
+ if ((activityUiOptions != null) && (activityPackage != null)) {
+ //Our activity, uiOptions specified, override with our value
+ uiOptions = activityUiOptions.intValue();
+ }
+ }
+ if (isOurActivity) {
+ //If we matched our activity but it had no logo don't
+ //do any more processing of the manifest
+ break;
+ }
+ }
+ }
+ eventType = xml.nextToken();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ if (DEBUG) Log.i(TAG, "Returning " + Integer.toHexString(uiOptions));
+ return uiOptions;
+ }
+
+ public static String cleanActivityName(String manifestPackage, String activityName) {
+ if (activityName.charAt(0) == '.') {
+ //Relative activity name (e.g., android:name=".ui.SomeClass")
+ return manifestPackage + activityName;
+ }
+ if (activityName.indexOf('.', 1) == -1) {
+ //Unqualified activity name (e.g., android:name="SomeClass")
+ return manifestPackage + "." + activityName;
+ }
+ //Fully-qualified activity name (e.g., "com.my.package.SomeClass")
+ return activityName;
+ }
+
+ /**
+ * Clears out internal reference when the action mode is destroyed.
+ */
+ private class ActionModeCallbackWrapper implements ActionMode.Callback {
+ private final ActionMode.Callback mWrapped;
+
+ public ActionModeCallbackWrapper(ActionMode.Callback wrapped) {
+ mWrapped = wrapped;
+ }
+
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ return mWrapped.onCreateActionMode(mode, menu);
+ }
+
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return mWrapped.onPrepareActionMode(mode, menu);
+ }
+
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return mWrapped.onActionItemClicked(mode, item);
+ }
+
+ public void onDestroyActionMode(ActionMode mode) {
+ mWrapped.onDestroyActionMode(mode);
+ if (mActionModeView != null) {
+ mActionModeView.setVisibility(View.GONE);
+ mActionModeView.removeAllViews();
+ }
+ if (mActivity instanceof OnActionModeFinishedListener) {
+ ((OnActionModeFinishedListener)mActivity).onActionModeFinished(mActionMode);
+ }
+ mActionMode = null;
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/ActionBarSherlockNative.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/ActionBarSherlockNative.java
new file mode 100644
index 0000000000..0824d3848f
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/ActionBarSherlockNative.java
@@ -0,0 +1,336 @@
+package com.actionbarsherlock.internal;
+
+import com.actionbarsherlock.ActionBarSherlock;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.internal.app.ActionBarWrapper;
+import com.actionbarsherlock.internal.view.menu.MenuWrapper;
+import com.actionbarsherlock.view.ActionMode;
+import com.actionbarsherlock.view.MenuInflater;
+import android.app.Activity;
+import android.content.Context;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
+import android.view.View;
+import android.view.Window;
+import android.view.ViewGroup.LayoutParams;
+
+@ActionBarSherlock.Implementation(api = 14)
+public class ActionBarSherlockNative extends ActionBarSherlock {
+ private ActionBarWrapper mActionBar;
+ private ActionModeWrapper mActionMode;
+ private MenuWrapper mMenu;
+
+ public ActionBarSherlockNative(Activity activity, int flags) {
+ super(activity, flags);
+ }
+
+
+ @Override
+ public ActionBar getActionBar() {
+ if (DEBUG) Log.d(TAG, "[getActionBar]");
+
+ initActionBar();
+ return mActionBar;
+ }
+
+ private void initActionBar() {
+ if (mActionBar != null || mActivity.getActionBar() == null) {
+ return;
+ }
+
+ mActionBar = new ActionBarWrapper(mActivity);
+ }
+
+ @Override
+ public void dispatchInvalidateOptionsMenu() {
+ if (DEBUG) Log.d(TAG, "[dispatchInvalidateOptionsMenu]");
+
+ mActivity.getWindow().invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
+ }
+
+ @Override
+ public boolean dispatchCreateOptionsMenu(android.view.Menu menu) {
+ if (DEBUG) Log.d(TAG, "[dispatchCreateOptionsMenu] menu: " + menu);
+
+ if (mMenu == null || menu != mMenu.unwrap()) {
+ mMenu = new MenuWrapper(menu);
+ }
+
+ final boolean result = callbackCreateOptionsMenu(mMenu);
+ if (DEBUG) Log.d(TAG, "[dispatchCreateOptionsMenu] returning " + result);
+ return result;
+ }
+
+ @Override
+ public boolean dispatchPrepareOptionsMenu(android.view.Menu menu) {
+ if (DEBUG) Log.d(TAG, "[dispatchPrepareOptionsMenu] menu: " + menu);
+
+ final boolean result = callbackPrepareOptionsMenu(mMenu);
+ if (DEBUG) Log.d(TAG, "[dispatchPrepareOptionsMenu] returning " + result);
+ return result;
+ }
+
+ @Override
+ public boolean dispatchOptionsItemSelected(android.view.MenuItem item) {
+ if (DEBUG) Log.d(TAG, "[dispatchOptionsItemSelected] item: " + item.getTitleCondensed());
+
+ final boolean result = callbackOptionsItemSelected(mMenu.findItem(item));
+ if (DEBUG) Log.d(TAG, "[dispatchOptionsItemSelected] returning " + result);
+ return result;
+ }
+
+ @Override
+ public boolean hasFeature(int feature) {
+ if (DEBUG) Log.d(TAG, "[hasFeature] feature: " + feature);
+
+ final boolean result = mActivity.getWindow().hasFeature(feature);
+ if (DEBUG) Log.d(TAG, "[hasFeature] returning " + result);
+ return result;
+ }
+
+ @Override
+ public boolean requestFeature(int featureId) {
+ if (DEBUG) Log.d(TAG, "[requestFeature] featureId: " + featureId);
+
+ final boolean result = mActivity.getWindow().requestFeature(featureId);
+ if (DEBUG) Log.d(TAG, "[requestFeature] returning " + result);
+ return result;
+ }
+
+ @Override
+ public void setUiOptions(int uiOptions) {
+ if (DEBUG) Log.d(TAG, "[setUiOptions] uiOptions: " + uiOptions);
+
+ mActivity.getWindow().setUiOptions(uiOptions);
+ }
+
+ @Override
+ public void setUiOptions(int uiOptions, int mask) {
+ if (DEBUG) Log.d(TAG, "[setUiOptions] uiOptions: " + uiOptions + ", mask: " + mask);
+
+ mActivity.getWindow().setUiOptions(uiOptions, mask);
+ }
+
+ @Override
+ public void setContentView(int layoutResId) {
+ if (DEBUG) Log.d(TAG, "[setContentView] layoutResId: " + layoutResId);
+
+ mActivity.getWindow().setContentView(layoutResId);
+ initActionBar();
+ }
+
+ @Override
+ public void setContentView(View view, LayoutParams params) {
+ if (DEBUG) Log.d(TAG, "[setContentView] view: " + view + ", params: " + params);
+
+ mActivity.getWindow().setContentView(view, params);
+ initActionBar();
+ }
+
+ @Override
+ public void addContentView(View view, LayoutParams params) {
+ if (DEBUG) Log.d(TAG, "[addContentView] view: " + view + ", params: " + params);
+
+ mActivity.getWindow().addContentView(view, params);
+ initActionBar();
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ if (DEBUG) Log.d(TAG, "[setTitle] title: " + title);
+
+ mActivity.getWindow().setTitle(title);
+ }
+
+ @Override
+ public void setProgressBarVisibility(boolean visible) {
+ if (DEBUG) Log.d(TAG, "[setProgressBarVisibility] visible: " + visible);
+
+ mActivity.setProgressBarVisibility(visible);
+ }
+
+ @Override
+ public void setProgressBarIndeterminateVisibility(boolean visible) {
+ if (DEBUG) Log.d(TAG, "[setProgressBarIndeterminateVisibility] visible: " + visible);
+
+ mActivity.setProgressBarIndeterminateVisibility(visible);
+ }
+
+ @Override
+ public void setProgressBarIndeterminate(boolean indeterminate) {
+ if (DEBUG) Log.d(TAG, "[setProgressBarIndeterminate] indeterminate: " + indeterminate);
+
+ mActivity.setProgressBarIndeterminate(indeterminate);
+ }
+
+ @Override
+ public void setProgress(int progress) {
+ if (DEBUG) Log.d(TAG, "[setProgress] progress: " + progress);
+
+ mActivity.setProgress(progress);
+ }
+
+ @Override
+ public void setSecondaryProgress(int secondaryProgress) {
+ if (DEBUG) Log.d(TAG, "[setSecondaryProgress] secondaryProgress: " + secondaryProgress);
+
+ mActivity.setSecondaryProgress(secondaryProgress);
+ }
+
+ @Override
+ protected Context getThemedContext() {
+ Context context = mActivity;
+ TypedValue outValue = new TypedValue();
+ mActivity.getTheme().resolveAttribute(android.R.attr.actionBarWidgetTheme, outValue, true);
+ if (outValue.resourceId != 0) {
+ //We are unable to test if this is the same as our current theme
+ //so we just wrap it and hope that if the attribute was specified
+ //then the user is intentionally specifying an alternate theme.
+ context = new ContextThemeWrapper(context, outValue.resourceId);
+ }
+ return context;
+ }
+
+ @Override
+ public ActionMode startActionMode(com.actionbarsherlock.view.ActionMode.Callback callback) {
+ if (DEBUG) Log.d(TAG, "[startActionMode] callback: " + callback);
+
+ if (mActionMode != null) {
+ mActionMode.finish();
+ }
+ ActionModeCallbackWrapper wrapped = null;
+ if (callback != null) {
+ wrapped = new ActionModeCallbackWrapper(callback);
+ }
+
+ //Calling this will trigger the callback wrapper's onCreate which
+ //is where we will set the new instance to mActionMode since we need
+ //to pass it through to the sherlock callbacks and the call below
+ //will not have returned yet to store its value.
+ if (mActivity.startActionMode(wrapped) == null) {
+ mActionMode = null;
+ }
+ if (mActivity instanceof OnActionModeStartedListener && mActionMode != null) {
+ ((OnActionModeStartedListener)mActivity).onActionModeStarted(mActionMode);
+ }
+
+ return mActionMode;
+ }
+
+ private class ActionModeCallbackWrapper implements android.view.ActionMode.Callback {
+ private final ActionMode.Callback mCallback;
+
+ public ActionModeCallbackWrapper(ActionMode.Callback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public boolean onCreateActionMode(android.view.ActionMode mode, android.view.Menu menu) {
+ //See ActionBarSherlockNative#startActionMode
+ mActionMode = new ActionModeWrapper(mode);
+
+ return mCallback.onCreateActionMode(mActionMode, mActionMode.getMenu());
+ }
+
+ @Override
+ public boolean onPrepareActionMode(android.view.ActionMode mode, android.view.Menu menu) {
+ return mCallback.onPrepareActionMode(mActionMode, mActionMode.getMenu());
+ }
+
+ @Override
+ public boolean onActionItemClicked(android.view.ActionMode mode, android.view.MenuItem item) {
+ return mCallback.onActionItemClicked(mActionMode, mActionMode.getMenu().findItem(item));
+ }
+
+ @Override
+ public void onDestroyActionMode(android.view.ActionMode mode) {
+ mCallback.onDestroyActionMode(mActionMode);
+ if (mActivity instanceof OnActionModeFinishedListener) {
+ ((OnActionModeFinishedListener)mActivity).onActionModeFinished(mActionMode);
+ }
+ }
+ }
+
+ private class ActionModeWrapper extends ActionMode {
+ private final android.view.ActionMode mActionMode;
+ private MenuWrapper mMenu = null;
+
+ ActionModeWrapper(android.view.ActionMode actionMode) {
+ mActionMode = actionMode;
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ mActionMode.setTitle(title);
+ }
+
+ @Override
+ public void setTitle(int resId) {
+ mActionMode.setTitle(resId);
+ }
+
+ @Override
+ public void setSubtitle(CharSequence subtitle) {
+ mActionMode.setSubtitle(subtitle);
+ }
+
+ @Override
+ public void setSubtitle(int resId) {
+ mActionMode.setSubtitle(resId);
+ }
+
+ @Override
+ public void setCustomView(View view) {
+ mActionMode.setCustomView(view);
+ }
+
+ @Override
+ public void invalidate() {
+ mActionMode.invalidate();
+ }
+
+ @Override
+ public void finish() {
+ mActionMode.finish();
+ }
+
+ @Override
+ public MenuWrapper getMenu() {
+ if (mMenu == null) {
+ mMenu = new MenuWrapper(mActionMode.getMenu());
+ }
+ return mMenu;
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mActionMode.getTitle();
+ }
+
+ @Override
+ public CharSequence getSubtitle() {
+ return mActionMode.getSubtitle();
+ }
+
+ @Override
+ public View getCustomView() {
+ return mActionMode.getCustomView();
+ }
+
+ @Override
+ public MenuInflater getMenuInflater() {
+ return ActionBarSherlockNative.this.getMenuInflater();
+ }
+
+ @Override
+ public void setTag(Object tag) {
+ mActionMode.setTag(tag);
+ }
+
+ @Override
+ public Object getTag() {
+ return mActionMode.getTag();
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/ResourcesCompat.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/ResourcesCompat.java
new file mode 100644
index 0000000000..8e1efe8c54
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/ResourcesCompat.java
@@ -0,0 +1,95 @@
+package com.actionbarsherlock.internal;
+
+import android.content.Context;
+import android.os.Build;
+import android.util.DisplayMetrics;
+import com.actionbarsherlock.R;
+
+public final class ResourcesCompat {
+ //No instances
+ private ResourcesCompat() {}
+
+
+ /**
+ * Support implementation of {@code getResources().getBoolean()} that we
+ * can use to simulate filtering based on width and smallest width
+ * qualifiers on pre-3.2.
+ *
+ * @param context Context to load booleans from on 3.2+ and to fetch the
+ * display metrics.
+ * @param id Id of boolean to load.
+ * @return Associated boolean value as reflected by the current display
+ * metrics.
+ */
+ public static boolean getResources_getBoolean(Context context, int id) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
+ return context.getResources().getBoolean(id);
+ }
+
+ DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ float widthDp = metrics.widthPixels / metrics.density;
+ float heightDp = metrics.heightPixels / metrics.density;
+ float smallestWidthDp = (widthDp < heightDp) ? widthDp : heightDp;
+
+ if (id == R.bool.abs__action_bar_embed_tabs) {
+ if (widthDp >= 480) {
+ return true; //values-w480dp
+ }
+ return false; //values
+ }
+ if (id == R.bool.abs__split_action_bar_is_narrow) {
+ if (widthDp >= 480) {
+ return false; //values-w480dp
+ }
+ return true; //values
+ }
+ if (id == R.bool.abs__action_bar_expanded_action_views_exclusive) {
+ if (smallestWidthDp >= 600) {
+ return false; //values-sw600dp
+ }
+ return true; //values
+ }
+ if (id == R.bool.abs__config_allowActionMenuItemTextWithIcon) {
+ if (widthDp >= 480) {
+ return true; //values-w480dp
+ }
+ return false; //values
+ }
+
+ throw new IllegalArgumentException("Unknown boolean resource ID " + id);
+ }
+
+ /**
+ * Support implementation of {@code getResources().getInteger()} that we
+ * can use to simulate filtering based on width qualifiers on pre-3.2.
+ *
+ * @param context Context to load integers from on 3.2+ and to fetch the
+ * display metrics.
+ * @param id Id of integer to load.
+ * @return Associated integer value as reflected by the current display
+ * metrics.
+ */
+ public static int getResources_getInteger(Context context, int id) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
+ return context.getResources().getInteger(id);
+ }
+
+ DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ float widthDp = metrics.widthPixels / metrics.density;
+
+ if (id == R.integer.abs__max_action_buttons) {
+ if (widthDp >= 600) {
+ return 5; //values-w600dp
+ }
+ if (widthDp >= 500) {
+ return 4; //values-w500dp
+ }
+ if (widthDp >= 360) {
+ return 3; //values-w360dp
+ }
+ return 2; //values
+ }
+
+ throw new IllegalArgumentException("Unknown integer resource ID " + id);
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/app/ActionBarImpl.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/app/ActionBarImpl.java
new file mode 100644
index 0000000000..d022a24659
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/app/ActionBarImpl.java
@@ -0,0 +1,1026 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.app;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Handler;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentTransaction;
+import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.SpinnerAdapter;
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.internal.nineoldandroids.animation.Animator;
+import com.actionbarsherlock.internal.nineoldandroids.animation.AnimatorListenerAdapter;
+import com.actionbarsherlock.internal.nineoldandroids.animation.AnimatorSet;
+import com.actionbarsherlock.internal.nineoldandroids.animation.ObjectAnimator;
+import com.actionbarsherlock.internal.nineoldandroids.animation.Animator.AnimatorListener;
+import com.actionbarsherlock.internal.nineoldandroids.widget.NineFrameLayout;
+import com.actionbarsherlock.internal.view.menu.MenuBuilder;
+import com.actionbarsherlock.internal.view.menu.MenuPopupHelper;
+import com.actionbarsherlock.internal.view.menu.SubMenuBuilder;
+import com.actionbarsherlock.internal.widget.ActionBarContainer;
+import com.actionbarsherlock.internal.widget.ActionBarContextView;
+import com.actionbarsherlock.internal.widget.ActionBarView;
+import com.actionbarsherlock.internal.widget.ScrollingTabContainerView;
+import com.actionbarsherlock.view.ActionMode;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getBoolean;
+
+/**
+ * ActionBarImpl is the ActionBar implementation used
+ * by devices of all screen sizes. If it detects a compatible decor,
+ * it will split contextual modes across both the ActionBarView at
+ * the top of the screen and a horizontal LinearLayout at the bottom
+ * which is normally hidden.
+ */
+public class ActionBarImpl extends ActionBar {
+ //UNUSED private static final String TAG = "ActionBarImpl";
+
+ private Context mContext;
+ private Context mThemedContext;
+ private Activity mActivity;
+ //UNUSED private Dialog mDialog;
+
+ private ActionBarContainer mContainerView;
+ private ActionBarView mActionView;
+ private ActionBarContextView mContextView;
+ private ActionBarContainer mSplitView;
+ private NineFrameLayout mContentView;
+ private ScrollingTabContainerView mTabScrollView;
+
+ private ArrayList mTabs = new ArrayList();
+
+ private TabImpl mSelectedTab;
+ private int mSavedTabPosition = INVALID_POSITION;
+
+ ActionModeImpl mActionMode;
+ ActionMode mDeferredDestroyActionMode;
+ ActionMode.Callback mDeferredModeDestroyCallback;
+
+ private boolean mLastMenuVisibility;
+ private ArrayList mMenuVisibilityListeners =
+ new ArrayList();
+
+ private static final int CONTEXT_DISPLAY_NORMAL = 0;
+ private static final int CONTEXT_DISPLAY_SPLIT = 1;
+
+ private static final int INVALID_POSITION = -1;
+
+ private int mContextDisplayMode;
+ private boolean mHasEmbeddedTabs;
+
+ final Handler mHandler = new Handler();
+ Runnable mTabSelector;
+
+ private Animator mCurrentShowAnim;
+ private Animator mCurrentModeAnim;
+ private boolean mShowHideAnimationEnabled;
+ boolean mWasHiddenBeforeMode;
+
+ final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mContentView != null) {
+ mContentView.setTranslationY(0);
+ mContainerView.setTranslationY(0);
+ }
+ if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) {
+ mSplitView.setVisibility(View.GONE);
+ }
+ mContainerView.setVisibility(View.GONE);
+ mContainerView.setTransitioning(false);
+ mCurrentShowAnim = null;
+ completeDeferredDestroyActionMode();
+ }
+ };
+
+ final AnimatorListener mShowListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCurrentShowAnim = null;
+ mContainerView.requestLayout();
+ }
+ };
+
+ public ActionBarImpl(Activity activity, int features) {
+ mActivity = activity;
+ Window window = activity.getWindow();
+ View decor = window.getDecorView();
+ init(decor);
+
+ //window.hasFeature() workaround for pre-3.0
+ if ((features & (1 << Window.FEATURE_ACTION_BAR_OVERLAY)) == 0) {
+ mContentView = (NineFrameLayout)decor.findViewById(android.R.id.content);
+ }
+ }
+
+ public ActionBarImpl(Dialog dialog) {
+ //UNUSED mDialog = dialog;
+ init(dialog.getWindow().getDecorView());
+ }
+
+ private void init(View decor) {
+ mContext = decor.getContext();
+ mActionView = (ActionBarView) decor.findViewById(R.id.abs__action_bar);
+ mContextView = (ActionBarContextView) decor.findViewById(
+ R.id.abs__action_context_bar);
+ mContainerView = (ActionBarContainer) decor.findViewById(
+ R.id.abs__action_bar_container);
+ mSplitView = (ActionBarContainer) decor.findViewById(
+ R.id.abs__split_action_bar);
+
+ if (mActionView == null || mContextView == null || mContainerView == null) {
+ throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
+ "with a compatible window decor layout");
+ }
+
+ mActionView.setContextView(mContextView);
+ mContextDisplayMode = mActionView.isSplitActionBar() ?
+ CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL;
+
+ // Older apps get the home button interaction enabled by default.
+ // Newer apps need to enable it explicitly.
+ setHomeButtonEnabled(mContext.getApplicationInfo().targetSdkVersion < 14);
+
+ setHasEmbeddedTabs(getResources_getBoolean(mContext,
+ R.bool.abs__action_bar_embed_tabs));
+ }
+
+ public void onConfigurationChanged(Configuration newConfig) {
+ setHasEmbeddedTabs(getResources_getBoolean(mContext,
+ R.bool.abs__action_bar_embed_tabs));
+
+ //Manually dispatch a configuration change to the action bar view on pre-2.2
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
+ mActionView.onConfigurationChanged(newConfig);
+ if (mContextView != null) {
+ mContextView.onConfigurationChanged(newConfig);
+ }
+ }
+ }
+
+ private void setHasEmbeddedTabs(boolean hasEmbeddedTabs) {
+ mHasEmbeddedTabs = hasEmbeddedTabs;
+ // Switch tab layout configuration if needed
+ if (!mHasEmbeddedTabs) {
+ mActionView.setEmbeddedTabView(null);
+ mContainerView.setTabContainer(mTabScrollView);
+ } else {
+ mContainerView.setTabContainer(null);
+ mActionView.setEmbeddedTabView(mTabScrollView);
+ }
+ final boolean isInTabMode = getNavigationMode() == NAVIGATION_MODE_TABS;
+ if (mTabScrollView != null) {
+ mTabScrollView.setVisibility(isInTabMode ? View.VISIBLE : View.GONE);
+ }
+ mActionView.setCollapsable(!mHasEmbeddedTabs && isInTabMode);
+ }
+
+ private void ensureTabsExist() {
+ if (mTabScrollView != null) {
+ return;
+ }
+
+ ScrollingTabContainerView tabScroller = new ScrollingTabContainerView(mContext);
+
+ if (mHasEmbeddedTabs) {
+ tabScroller.setVisibility(View.VISIBLE);
+ mActionView.setEmbeddedTabView(tabScroller);
+ } else {
+ tabScroller.setVisibility(getNavigationMode() == NAVIGATION_MODE_TABS ?
+ View.VISIBLE : View.GONE);
+ mContainerView.setTabContainer(tabScroller);
+ }
+ mTabScrollView = tabScroller;
+ }
+
+ void completeDeferredDestroyActionMode() {
+ if (mDeferredModeDestroyCallback != null) {
+ mDeferredModeDestroyCallback.onDestroyActionMode(mDeferredDestroyActionMode);
+ mDeferredDestroyActionMode = null;
+ mDeferredModeDestroyCallback = null;
+ }
+ }
+
+ /**
+ * Enables or disables animation between show/hide states.
+ * If animation is disabled using this method, animations in progress
+ * will be finished.
+ *
+ * @param enabled true to animate, false to not animate.
+ */
+ public void setShowHideAnimationEnabled(boolean enabled) {
+ mShowHideAnimationEnabled = enabled;
+ if (!enabled && mCurrentShowAnim != null) {
+ mCurrentShowAnim.end();
+ }
+ }
+
+ public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
+ mMenuVisibilityListeners.add(listener);
+ }
+
+ public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
+ mMenuVisibilityListeners.remove(listener);
+ }
+
+ public void dispatchMenuVisibilityChanged(boolean isVisible) {
+ if (isVisible == mLastMenuVisibility) {
+ return;
+ }
+ mLastMenuVisibility = isVisible;
+
+ final int count = mMenuVisibilityListeners.size();
+ for (int i = 0; i < count; i++) {
+ mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible);
+ }
+ }
+
+ @Override
+ public void setCustomView(int resId) {
+ setCustomView(LayoutInflater.from(getThemedContext()).inflate(resId, mActionView, false));
+ }
+
+ @Override
+ public void setDisplayUseLogoEnabled(boolean useLogo) {
+ setDisplayOptions(useLogo ? DISPLAY_USE_LOGO : 0, DISPLAY_USE_LOGO);
+ }
+
+ @Override
+ public void setDisplayShowHomeEnabled(boolean showHome) {
+ setDisplayOptions(showHome ? DISPLAY_SHOW_HOME : 0, DISPLAY_SHOW_HOME);
+ }
+
+ @Override
+ public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) {
+ setDisplayOptions(showHomeAsUp ? DISPLAY_HOME_AS_UP : 0, DISPLAY_HOME_AS_UP);
+ }
+
+ @Override
+ public void setDisplayShowTitleEnabled(boolean showTitle) {
+ setDisplayOptions(showTitle ? DISPLAY_SHOW_TITLE : 0, DISPLAY_SHOW_TITLE);
+ }
+
+ @Override
+ public void setDisplayShowCustomEnabled(boolean showCustom) {
+ setDisplayOptions(showCustom ? DISPLAY_SHOW_CUSTOM : 0, DISPLAY_SHOW_CUSTOM);
+ }
+
+ @Override
+ public void setHomeButtonEnabled(boolean enable) {
+ mActionView.setHomeButtonEnabled(enable);
+ }
+
+ @Override
+ public void setTitle(int resId) {
+ setTitle(mContext.getString(resId));
+ }
+
+ @Override
+ public void setSubtitle(int resId) {
+ setSubtitle(mContext.getString(resId));
+ }
+
+ public void setSelectedNavigationItem(int position) {
+ switch (mActionView.getNavigationMode()) {
+ case NAVIGATION_MODE_TABS:
+ selectTab(mTabs.get(position));
+ break;
+ case NAVIGATION_MODE_LIST:
+ mActionView.setDropdownSelectedPosition(position);
+ break;
+ default:
+ throw new IllegalStateException(
+ "setSelectedNavigationIndex not valid for current navigation mode");
+ }
+ }
+
+ public void removeAllTabs() {
+ cleanupTabs();
+ }
+
+ private void cleanupTabs() {
+ if (mSelectedTab != null) {
+ selectTab(null);
+ }
+ mTabs.clear();
+ if (mTabScrollView != null) {
+ mTabScrollView.removeAllTabs();
+ }
+ mSavedTabPosition = INVALID_POSITION;
+ }
+
+ public void setTitle(CharSequence title) {
+ mActionView.setTitle(title);
+ }
+
+ public void setSubtitle(CharSequence subtitle) {
+ mActionView.setSubtitle(subtitle);
+ }
+
+ public void setDisplayOptions(int options) {
+ mActionView.setDisplayOptions(options);
+ }
+
+ public void setDisplayOptions(int options, int mask) {
+ final int current = mActionView.getDisplayOptions();
+ mActionView.setDisplayOptions((options & mask) | (current & ~mask));
+ }
+
+ public void setBackgroundDrawable(Drawable d) {
+ mContainerView.setPrimaryBackground(d);
+ }
+
+ public void setStackedBackgroundDrawable(Drawable d) {
+ mContainerView.setStackedBackground(d);
+ }
+
+ public void setSplitBackgroundDrawable(Drawable d) {
+ if (mSplitView != null) {
+ mSplitView.setSplitBackground(d);
+ }
+ }
+
+ public View getCustomView() {
+ return mActionView.getCustomNavigationView();
+ }
+
+ public CharSequence getTitle() {
+ return mActionView.getTitle();
+ }
+
+ public CharSequence getSubtitle() {
+ return mActionView.getSubtitle();
+ }
+
+ public int getNavigationMode() {
+ return mActionView.getNavigationMode();
+ }
+
+ public int getDisplayOptions() {
+ return mActionView.getDisplayOptions();
+ }
+
+ public ActionMode startActionMode(ActionMode.Callback callback) {
+ boolean wasHidden = false;
+ if (mActionMode != null) {
+ wasHidden = mWasHiddenBeforeMode;
+ mActionMode.finish();
+ }
+
+ mContextView.killMode();
+ ActionModeImpl mode = new ActionModeImpl(callback);
+ if (mode.dispatchOnCreate()) {
+ mWasHiddenBeforeMode = !isShowing() || wasHidden;
+ mode.invalidate();
+ mContextView.initForMode(mode);
+ animateToMode(true);
+ if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) {
+ // TODO animate this
+ mSplitView.setVisibility(View.VISIBLE);
+ }
+ mContextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ mActionMode = mode;
+ return mode;
+ }
+ return null;
+ }
+
+ private void configureTab(Tab tab, int position) {
+ final TabImpl tabi = (TabImpl) tab;
+ final ActionBar.TabListener callback = tabi.getCallback();
+
+ if (callback == null) {
+ throw new IllegalStateException("Action Bar Tab must have a Callback");
+ }
+
+ tabi.setPosition(position);
+ mTabs.add(position, tabi);
+
+ final int count = mTabs.size();
+ for (int i = position + 1; i < count; i++) {
+ mTabs.get(i).setPosition(i);
+ }
+ }
+
+ @Override
+ public void addTab(Tab tab) {
+ addTab(tab, mTabs.isEmpty());
+ }
+
+ @Override
+ public void addTab(Tab tab, int position) {
+ addTab(tab, position, mTabs.isEmpty());
+ }
+
+ @Override
+ public void addTab(Tab tab, boolean setSelected) {
+ ensureTabsExist();
+ mTabScrollView.addTab(tab, setSelected);
+ configureTab(tab, mTabs.size());
+ if (setSelected) {
+ selectTab(tab);
+ }
+ }
+
+ @Override
+ public void addTab(Tab tab, int position, boolean setSelected) {
+ ensureTabsExist();
+ mTabScrollView.addTab(tab, position, setSelected);
+ configureTab(tab, position);
+ if (setSelected) {
+ selectTab(tab);
+ }
+ }
+
+ @Override
+ public Tab newTab() {
+ return new TabImpl();
+ }
+
+ @Override
+ public void removeTab(Tab tab) {
+ removeTabAt(tab.getPosition());
+ }
+
+ @Override
+ public void removeTabAt(int position) {
+ if (mTabScrollView == null) {
+ // No tabs around to remove
+ return;
+ }
+
+ int selectedTabPosition = mSelectedTab != null
+ ? mSelectedTab.getPosition() : mSavedTabPosition;
+ mTabScrollView.removeTabAt(position);
+ TabImpl removedTab = mTabs.remove(position);
+ if (removedTab != null) {
+ removedTab.setPosition(-1);
+ }
+
+ final int newTabCount = mTabs.size();
+ for (int i = position; i < newTabCount; i++) {
+ mTabs.get(i).setPosition(i);
+ }
+
+ if (selectedTabPosition == position) {
+ selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
+ }
+ }
+
+ @Override
+ public void selectTab(Tab tab) {
+ if (getNavigationMode() != NAVIGATION_MODE_TABS) {
+ mSavedTabPosition = tab != null ? tab.getPosition() : INVALID_POSITION;
+ return;
+ }
+
+ FragmentTransaction trans = null;
+ if (mActivity instanceof FragmentActivity) {
+ trans = ((FragmentActivity)mActivity).getSupportFragmentManager().beginTransaction()
+ .disallowAddToBackStack();
+ }
+
+ if (mSelectedTab == tab) {
+ if (mSelectedTab != null) {
+ mSelectedTab.getCallback().onTabReselected(mSelectedTab, trans);
+ mTabScrollView.animateToTab(tab.getPosition());
+ }
+ } else {
+ mTabScrollView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
+ if (mSelectedTab != null) {
+ mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans);
+ }
+ mSelectedTab = (TabImpl) tab;
+ if (mSelectedTab != null) {
+ mSelectedTab.getCallback().onTabSelected(mSelectedTab, trans);
+ }
+ }
+
+ if (trans != null && !trans.isEmpty()) {
+ trans.commit();
+ }
+ }
+
+ @Override
+ public Tab getSelectedTab() {
+ return mSelectedTab;
+ }
+
+ @Override
+ public int getHeight() {
+ return mContainerView.getHeight();
+ }
+
+ @Override
+ public void show() {
+ show(true);
+ }
+
+ void show(boolean markHiddenBeforeMode) {
+ if (mCurrentShowAnim != null) {
+ mCurrentShowAnim.end();
+ }
+ if (mContainerView.getVisibility() == View.VISIBLE) {
+ if (markHiddenBeforeMode) mWasHiddenBeforeMode = false;
+ return;
+ }
+ mContainerView.setVisibility(View.VISIBLE);
+
+ if (mShowHideAnimationEnabled) {
+ mContainerView.setAlpha(0);
+ AnimatorSet anim = new AnimatorSet();
+ AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 1));
+ if (mContentView != null) {
+ b.with(ObjectAnimator.ofFloat(mContentView, "translationY",
+ -mContainerView.getHeight(), 0));
+ mContainerView.setTranslationY(-mContainerView.getHeight());
+ b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", 0));
+ }
+ if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) {
+ mSplitView.setAlpha(0);
+ mSplitView.setVisibility(View.VISIBLE);
+ b.with(ObjectAnimator.ofFloat(mSplitView, "alpha", 1));
+ }
+ anim.addListener(mShowListener);
+ mCurrentShowAnim = anim;
+ anim.start();
+ } else {
+ mContainerView.setAlpha(1);
+ mContainerView.setTranslationY(0);
+ mShowListener.onAnimationEnd(null);
+ }
+ }
+
+ @Override
+ public void hide() {
+ if (mCurrentShowAnim != null) {
+ mCurrentShowAnim.end();
+ }
+ if (mContainerView.getVisibility() == View.GONE) {
+ return;
+ }
+
+ if (mShowHideAnimationEnabled) {
+ mContainerView.setAlpha(1);
+ mContainerView.setTransitioning(true);
+ AnimatorSet anim = new AnimatorSet();
+ AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 0));
+ if (mContentView != null) {
+ b.with(ObjectAnimator.ofFloat(mContentView, "translationY",
+ 0, -mContainerView.getHeight()));
+ b.with(ObjectAnimator.ofFloat(mContainerView, "translationY",
+ -mContainerView.getHeight()));
+ }
+ if (mSplitView != null && mSplitView.getVisibility() == View.VISIBLE) {
+ mSplitView.setAlpha(1);
+ b.with(ObjectAnimator.ofFloat(mSplitView, "alpha", 0));
+ }
+ anim.addListener(mHideListener);
+ mCurrentShowAnim = anim;
+ anim.start();
+ } else {
+ mHideListener.onAnimationEnd(null);
+ }
+ }
+
+ public boolean isShowing() {
+ return mContainerView.getVisibility() == View.VISIBLE;
+ }
+
+ void animateToMode(boolean toActionMode) {
+ if (toActionMode) {
+ show(false);
+ }
+ if (mCurrentModeAnim != null) {
+ mCurrentModeAnim.end();
+ }
+
+ mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
+ mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE);
+ if (mTabScrollView != null && !mActionView.hasEmbeddedTabs() && mActionView.isCollapsed()) {
+ mTabScrollView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
+ }
+ }
+
+ public Context getThemedContext() {
+ if (mThemedContext == null) {
+ TypedValue outValue = new TypedValue();
+ Resources.Theme currentTheme = mContext.getTheme();
+ currentTheme.resolveAttribute(R.attr.actionBarWidgetTheme,
+ outValue, true);
+ final int targetThemeRes = outValue.resourceId;
+
+ if (targetThemeRes != 0) { //XXX && mContext.getThemeResId() != targetThemeRes) {
+ mThemedContext = new ContextThemeWrapper(mContext, targetThemeRes);
+ } else {
+ mThemedContext = mContext;
+ }
+ }
+ return mThemedContext;
+ }
+
+ /**
+ * @hide
+ */
+ public class ActionModeImpl extends ActionMode implements MenuBuilder.Callback {
+ private ActionMode.Callback mCallback;
+ private MenuBuilder mMenu;
+ private WeakReference mCustomView;
+
+ public ActionModeImpl(ActionMode.Callback callback) {
+ mCallback = callback;
+ mMenu = new MenuBuilder(getThemedContext())
+ .setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ mMenu.setCallback(this);
+ }
+
+ @Override
+ public MenuInflater getMenuInflater() {
+ return new MenuInflater(getThemedContext());
+ }
+
+ @Override
+ public Menu getMenu() {
+ return mMenu;
+ }
+
+ @Override
+ public void finish() {
+ if (mActionMode != this) {
+ // Not the active action mode - no-op
+ return;
+ }
+
+ // If we were hidden before the mode was shown, defer the onDestroy
+ // callback until the animation is finished and associated relayout
+ // is about to happen. This lets apps better anticipate visibility
+ // and layout behavior.
+ if (mWasHiddenBeforeMode) {
+ mDeferredDestroyActionMode = this;
+ mDeferredModeDestroyCallback = mCallback;
+ } else {
+ mCallback.onDestroyActionMode(this);
+ }
+ mCallback = null;
+ animateToMode(false);
+
+ // Clear out the context mode views after the animation finishes
+ mContextView.closeMode();
+ mActionView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+
+ mActionMode = null;
+
+ if (mWasHiddenBeforeMode) {
+ hide();
+ }
+ }
+
+ @Override
+ public void invalidate() {
+ mMenu.stopDispatchingItemsChanged();
+ try {
+ mCallback.onPrepareActionMode(this, mMenu);
+ } finally {
+ mMenu.startDispatchingItemsChanged();
+ }
+ }
+
+ public boolean dispatchOnCreate() {
+ mMenu.stopDispatchingItemsChanged();
+ try {
+ return mCallback.onCreateActionMode(this, mMenu);
+ } finally {
+ mMenu.startDispatchingItemsChanged();
+ }
+ }
+
+ @Override
+ public void setCustomView(View view) {
+ mContextView.setCustomView(view);
+ mCustomView = new WeakReference(view);
+ }
+
+ @Override
+ public void setSubtitle(CharSequence subtitle) {
+ mContextView.setSubtitle(subtitle);
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ mContextView.setTitle(title);
+ }
+
+ @Override
+ public void setTitle(int resId) {
+ setTitle(mContext.getResources().getString(resId));
+ }
+
+ @Override
+ public void setSubtitle(int resId) {
+ setSubtitle(mContext.getResources().getString(resId));
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mContextView.getTitle();
+ }
+
+ @Override
+ public CharSequence getSubtitle() {
+ return mContextView.getSubtitle();
+ }
+
+ @Override
+ public View getCustomView() {
+ return mCustomView != null ? mCustomView.get() : null;
+ }
+
+ public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
+ if (mCallback != null) {
+ return mCallback.onActionItemClicked(this, item);
+ } else {
+ return false;
+ }
+ }
+
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ }
+
+ public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+ if (mCallback == null) {
+ return false;
+ }
+
+ if (!subMenu.hasVisibleItems()) {
+ return true;
+ }
+
+ new MenuPopupHelper(getThemedContext(), subMenu).show();
+ return true;
+ }
+
+ public void onCloseSubMenu(SubMenuBuilder menu) {
+ }
+
+ public void onMenuModeChange(MenuBuilder menu) {
+ if (mCallback == null) {
+ return;
+ }
+ invalidate();
+ mContextView.showOverflowMenu();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public class TabImpl extends ActionBar.Tab {
+ private ActionBar.TabListener mCallback;
+ private Object mTag;
+ private Drawable mIcon;
+ private CharSequence mText;
+ private CharSequence mContentDesc;
+ private int mPosition = -1;
+ private View mCustomView;
+
+ @Override
+ public Object getTag() {
+ return mTag;
+ }
+
+ @Override
+ public Tab setTag(Object tag) {
+ mTag = tag;
+ return this;
+ }
+
+ public ActionBar.TabListener getCallback() {
+ return mCallback;
+ }
+
+ @Override
+ public Tab setTabListener(ActionBar.TabListener callback) {
+ mCallback = callback;
+ return this;
+ }
+
+ @Override
+ public View getCustomView() {
+ return mCustomView;
+ }
+
+ @Override
+ public Tab setCustomView(View view) {
+ mCustomView = view;
+ if (mPosition >= 0) {
+ mTabScrollView.updateTab(mPosition);
+ }
+ return this;
+ }
+
+ @Override
+ public Tab setCustomView(int layoutResId) {
+ return setCustomView(LayoutInflater.from(getThemedContext())
+ .inflate(layoutResId, null));
+ }
+
+ @Override
+ public Drawable getIcon() {
+ return mIcon;
+ }
+
+ @Override
+ public int getPosition() {
+ return mPosition;
+ }
+
+ public void setPosition(int position) {
+ mPosition = position;
+ }
+
+ @Override
+ public CharSequence getText() {
+ return mText;
+ }
+
+ @Override
+ public Tab setIcon(Drawable icon) {
+ mIcon = icon;
+ if (mPosition >= 0) {
+ mTabScrollView.updateTab(mPosition);
+ }
+ return this;
+ }
+
+ @Override
+ public Tab setIcon(int resId) {
+ return setIcon(mContext.getResources().getDrawable(resId));
+ }
+
+ @Override
+ public Tab setText(CharSequence text) {
+ mText = text;
+ if (mPosition >= 0) {
+ mTabScrollView.updateTab(mPosition);
+ }
+ return this;
+ }
+
+ @Override
+ public Tab setText(int resId) {
+ return setText(mContext.getResources().getText(resId));
+ }
+
+ @Override
+ public void select() {
+ selectTab(this);
+ }
+
+ @Override
+ public Tab setContentDescription(int resId) {
+ return setContentDescription(mContext.getResources().getText(resId));
+ }
+
+ @Override
+ public Tab setContentDescription(CharSequence contentDesc) {
+ mContentDesc = contentDesc;
+ if (mPosition >= 0) {
+ mTabScrollView.updateTab(mPosition);
+ }
+ return this;
+ }
+
+ @Override
+ public CharSequence getContentDescription() {
+ return mContentDesc;
+ }
+ }
+
+ @Override
+ public void setCustomView(View view) {
+ mActionView.setCustomNavigationView(view);
+ }
+
+ @Override
+ public void setCustomView(View view, LayoutParams layoutParams) {
+ view.setLayoutParams(layoutParams);
+ mActionView.setCustomNavigationView(view);
+ }
+
+ @Override
+ public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
+ mActionView.setDropdownAdapter(adapter);
+ mActionView.setCallback(callback);
+ }
+
+ @Override
+ public int getSelectedNavigationIndex() {
+ switch (mActionView.getNavigationMode()) {
+ case NAVIGATION_MODE_TABS:
+ return mSelectedTab != null ? mSelectedTab.getPosition() : -1;
+ case NAVIGATION_MODE_LIST:
+ return mActionView.getDropdownSelectedPosition();
+ default:
+ return -1;
+ }
+ }
+
+ @Override
+ public int getNavigationItemCount() {
+ switch (mActionView.getNavigationMode()) {
+ case NAVIGATION_MODE_TABS:
+ return mTabs.size();
+ case NAVIGATION_MODE_LIST:
+ SpinnerAdapter adapter = mActionView.getDropdownAdapter();
+ return adapter != null ? adapter.getCount() : 0;
+ default:
+ return 0;
+ }
+ }
+
+ @Override
+ public int getTabCount() {
+ return mTabs.size();
+ }
+
+ @Override
+ public void setNavigationMode(int mode) {
+ final int oldMode = mActionView.getNavigationMode();
+ switch (oldMode) {
+ case NAVIGATION_MODE_TABS:
+ mSavedTabPosition = getSelectedNavigationIndex();
+ selectTab(null);
+ mTabScrollView.setVisibility(View.GONE);
+ break;
+ }
+ mActionView.setNavigationMode(mode);
+ switch (mode) {
+ case NAVIGATION_MODE_TABS:
+ ensureTabsExist();
+ mTabScrollView.setVisibility(View.VISIBLE);
+ if (mSavedTabPosition != INVALID_POSITION) {
+ setSelectedNavigationItem(mSavedTabPosition);
+ mSavedTabPosition = INVALID_POSITION;
+ }
+ break;
+ }
+ mActionView.setCollapsable(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs);
+ }
+
+ @Override
+ public Tab getTabAt(int index) {
+ return mTabs.get(index);
+ }
+
+
+ @Override
+ public void setIcon(int resId) {
+ mActionView.setIcon(resId);
+ }
+
+ @Override
+ public void setIcon(Drawable icon) {
+ mActionView.setIcon(icon);
+ }
+
+ @Override
+ public void setLogo(int resId) {
+ mActionView.setLogo(resId);
+ }
+
+ @Override
+ public void setLogo(Drawable logo) {
+ mActionView.setLogo(logo);
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/app/ActionBarWrapper.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/app/ActionBarWrapper.java
new file mode 100644
index 0000000000..840cb3d27c
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/app/ActionBarWrapper.java
@@ -0,0 +1,468 @@
+package com.actionbarsherlock.internal.app;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentTransaction;
+import android.view.View;
+import android.widget.SpinnerAdapter;
+
+import com.actionbarsherlock.app.ActionBar;
+
+public class ActionBarWrapper extends ActionBar implements android.app.ActionBar.OnNavigationListener, android.app.ActionBar.OnMenuVisibilityListener {
+ private final Activity mActivity;
+ private final android.app.ActionBar mActionBar;
+ private ActionBar.OnNavigationListener mNavigationListener;
+ private Set mMenuVisibilityListeners = new HashSet(1);
+ private FragmentTransaction mFragmentTransaction;
+
+
+ public ActionBarWrapper(Activity activity) {
+ mActivity = activity;
+ mActionBar = activity.getActionBar();
+ if (mActionBar != null) {
+ mActionBar.addOnMenuVisibilityListener(this);
+ }
+ }
+
+
+ @Override
+ public void setHomeButtonEnabled(boolean enabled) {
+ mActionBar.setHomeButtonEnabled(enabled);
+ }
+
+ @Override
+ public Context getThemedContext() {
+ return mActionBar.getThemedContext();
+ }
+
+ @Override
+ public void setCustomView(View view) {
+ mActionBar.setCustomView(view);
+ }
+
+ @Override
+ public void setCustomView(View view, LayoutParams layoutParams) {
+ android.app.ActionBar.LayoutParams lp = new android.app.ActionBar.LayoutParams(layoutParams);
+ lp.gravity = layoutParams.gravity;
+ lp.bottomMargin = layoutParams.bottomMargin;
+ lp.topMargin = layoutParams.topMargin;
+ lp.leftMargin = layoutParams.leftMargin;
+ lp.rightMargin = layoutParams.rightMargin;
+ mActionBar.setCustomView(view, lp);
+ }
+
+ @Override
+ public void setCustomView(int resId) {
+ mActionBar.setCustomView(resId);
+ }
+
+ @Override
+ public void setIcon(int resId) {
+ mActionBar.setIcon(resId);
+ }
+
+ @Override
+ public void setIcon(Drawable icon) {
+ mActionBar.setIcon(icon);
+ }
+
+ @Override
+ public void setLogo(int resId) {
+ mActionBar.setLogo(resId);
+ }
+
+ @Override
+ public void setLogo(Drawable logo) {
+ mActionBar.setLogo(logo);
+ }
+
+ @Override
+ public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
+ mNavigationListener = callback;
+ mActionBar.setListNavigationCallbacks(adapter, (callback != null) ? this : null);
+ }
+
+ @Override
+ public boolean onNavigationItemSelected(int itemPosition, long itemId) {
+ //This should never be a NullPointerException since we only set
+ //ourselves as the listener when the callback is not null.
+ return mNavigationListener.onNavigationItemSelected(itemPosition, itemId);
+ }
+
+ @Override
+ public void setSelectedNavigationItem(int position) {
+ mActionBar.setSelectedNavigationItem(position);
+ }
+
+ @Override
+ public int getSelectedNavigationIndex() {
+ return mActionBar.getSelectedNavigationIndex();
+ }
+
+ @Override
+ public int getNavigationItemCount() {
+ return mActionBar.getNavigationItemCount();
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ mActionBar.setTitle(title);
+ }
+
+ @Override
+ public void setTitle(int resId) {
+ mActionBar.setTitle(resId);
+ }
+
+ @Override
+ public void setSubtitle(CharSequence subtitle) {
+ mActionBar.setSubtitle(subtitle);
+ }
+
+ @Override
+ public void setSubtitle(int resId) {
+ mActionBar.setSubtitle(resId);
+ }
+
+ @Override
+ public void setDisplayOptions(int options) {
+ mActionBar.setDisplayOptions(options);
+ }
+
+ @Override
+ public void setDisplayOptions(int options, int mask) {
+ mActionBar.setDisplayOptions(options, mask);
+ }
+
+ @Override
+ public void setDisplayUseLogoEnabled(boolean useLogo) {
+ mActionBar.setDisplayUseLogoEnabled(useLogo);
+ }
+
+ @Override
+ public void setDisplayShowHomeEnabled(boolean showHome) {
+ mActionBar.setDisplayShowHomeEnabled(showHome);
+ }
+
+ @Override
+ public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) {
+ mActionBar.setDisplayHomeAsUpEnabled(showHomeAsUp);
+ }
+
+ @Override
+ public void setDisplayShowTitleEnabled(boolean showTitle) {
+ mActionBar.setDisplayShowTitleEnabled(showTitle);
+ }
+
+ @Override
+ public void setDisplayShowCustomEnabled(boolean showCustom) {
+ mActionBar.setDisplayShowCustomEnabled(showCustom);
+ }
+
+ @Override
+ public void setBackgroundDrawable(Drawable d) {
+ mActionBar.setBackgroundDrawable(d);
+ }
+
+ @Override
+ public void setStackedBackgroundDrawable(Drawable d) {
+ mActionBar.setStackedBackgroundDrawable(d);
+ }
+
+ @Override
+ public void setSplitBackgroundDrawable(Drawable d) {
+ mActionBar.setSplitBackgroundDrawable(d);
+ }
+
+ @Override
+ public View getCustomView() {
+ return mActionBar.getCustomView();
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mActionBar.getTitle();
+ }
+
+ @Override
+ public CharSequence getSubtitle() {
+ return mActionBar.getSubtitle();
+ }
+
+ @Override
+ public int getNavigationMode() {
+ return mActionBar.getNavigationMode();
+ }
+
+ @Override
+ public void setNavigationMode(int mode) {
+ mActionBar.setNavigationMode(mode);
+ }
+
+ @Override
+ public int getDisplayOptions() {
+ return mActionBar.getDisplayOptions();
+ }
+
+ public class TabWrapper extends ActionBar.Tab implements android.app.ActionBar.TabListener {
+ final android.app.ActionBar.Tab mNativeTab;
+ private Object mTag;
+ private TabListener mListener;
+
+ public TabWrapper(android.app.ActionBar.Tab nativeTab) {
+ mNativeTab = nativeTab;
+ mNativeTab.setTag(this);
+ }
+
+ @Override
+ public int getPosition() {
+ return mNativeTab.getPosition();
+ }
+
+ @Override
+ public Drawable getIcon() {
+ return mNativeTab.getIcon();
+ }
+
+ @Override
+ public CharSequence getText() {
+ return mNativeTab.getText();
+ }
+
+ @Override
+ public Tab setIcon(Drawable icon) {
+ mNativeTab.setIcon(icon);
+ return this;
+ }
+
+ @Override
+ public Tab setIcon(int resId) {
+ mNativeTab.setIcon(resId);
+ return this;
+ }
+
+ @Override
+ public Tab setText(CharSequence text) {
+ mNativeTab.setText(text);
+ return this;
+ }
+
+ @Override
+ public Tab setText(int resId) {
+ mNativeTab.setText(resId);
+ return this;
+ }
+
+ @Override
+ public Tab setCustomView(View view) {
+ mNativeTab.setCustomView(view);
+ return this;
+ }
+
+ @Override
+ public Tab setCustomView(int layoutResId) {
+ mNativeTab.setCustomView(layoutResId);
+ return this;
+ }
+
+ @Override
+ public View getCustomView() {
+ return mNativeTab.getCustomView();
+ }
+
+ @Override
+ public Tab setTag(Object obj) {
+ mTag = obj;
+ return this;
+ }
+
+ @Override
+ public Object getTag() {
+ return mTag;
+ }
+
+ @Override
+ public Tab setTabListener(TabListener listener) {
+ mNativeTab.setTabListener(listener != null ? this : null);
+ mListener = listener;
+ return this;
+ }
+
+ @Override
+ public void select() {
+ mNativeTab.select();
+ }
+
+ @Override
+ public Tab setContentDescription(int resId) {
+ mNativeTab.setContentDescription(resId);
+ return this;
+ }
+
+ @Override
+ public Tab setContentDescription(CharSequence contentDesc) {
+ mNativeTab.setContentDescription(contentDesc);
+ return this;
+ }
+
+ @Override
+ public CharSequence getContentDescription() {
+ return mNativeTab.getContentDescription();
+ }
+
+ @Override
+ public void onTabReselected(android.app.ActionBar.Tab tab, android.app.FragmentTransaction ft) {
+ if (mListener != null) {
+ FragmentTransaction trans = null;
+ if (mActivity instanceof FragmentActivity) {
+ trans = ((FragmentActivity)mActivity).getSupportFragmentManager().beginTransaction()
+ .disallowAddToBackStack();
+ }
+
+ mListener.onTabReselected(this, trans);
+
+ if (trans != null && !trans.isEmpty()) {
+ trans.commit();
+ }
+ }
+ }
+
+ @Override
+ public void onTabSelected(android.app.ActionBar.Tab tab, android.app.FragmentTransaction ft) {
+ if (mListener != null) {
+
+ if (mFragmentTransaction == null && mActivity instanceof FragmentActivity) {
+ mFragmentTransaction = ((FragmentActivity)mActivity).getSupportFragmentManager().beginTransaction()
+ .disallowAddToBackStack();
+ }
+
+ mListener.onTabSelected(this, mFragmentTransaction);
+
+ if (mFragmentTransaction != null) {
+ if (!mFragmentTransaction.isEmpty()) {
+ mFragmentTransaction.commit();
+ }
+ mFragmentTransaction = null;
+ }
+ }
+ }
+
+ @Override
+ public void onTabUnselected(android.app.ActionBar.Tab tab, android.app.FragmentTransaction ft) {
+ if (mListener != null) {
+ FragmentTransaction trans = null;
+ if (mActivity instanceof FragmentActivity) {
+ trans = ((FragmentActivity)mActivity).getSupportFragmentManager().beginTransaction()
+ .disallowAddToBackStack();
+ mFragmentTransaction = trans;
+ }
+
+ mListener.onTabUnselected(this, trans);
+ }
+ }
+ }
+
+ @Override
+ public Tab newTab() {
+ return new TabWrapper(mActionBar.newTab());
+ }
+
+ @Override
+ public void addTab(Tab tab) {
+ mActionBar.addTab(((TabWrapper)tab).mNativeTab);
+ }
+
+ @Override
+ public void addTab(Tab tab, boolean setSelected) {
+ mActionBar.addTab(((TabWrapper)tab).mNativeTab, setSelected);
+ }
+
+ @Override
+ public void addTab(Tab tab, int position) {
+ mActionBar.addTab(((TabWrapper)tab).mNativeTab, position);
+ }
+
+ @Override
+ public void addTab(Tab tab, int position, boolean setSelected) {
+ mActionBar.addTab(((TabWrapper)tab).mNativeTab, position, setSelected);
+ }
+
+ @Override
+ public void removeTab(Tab tab) {
+ mActionBar.removeTab(((TabWrapper)tab).mNativeTab);
+ }
+
+ @Override
+ public void removeTabAt(int position) {
+ mActionBar.removeTabAt(position);
+ }
+
+ @Override
+ public void removeAllTabs() {
+ mActionBar.removeAllTabs();
+ }
+
+ @Override
+ public void selectTab(Tab tab) {
+ mActionBar.selectTab(((TabWrapper)tab).mNativeTab);
+ }
+
+ @Override
+ public Tab getSelectedTab() {
+ android.app.ActionBar.Tab selected = mActionBar.getSelectedTab();
+ return (selected != null) ? (Tab)selected.getTag() : null;
+ }
+
+ @Override
+ public Tab getTabAt(int index) {
+ android.app.ActionBar.Tab selected = mActionBar.getTabAt(index);
+ return (selected != null) ? (Tab)selected.getTag() : null;
+ }
+
+ @Override
+ public int getTabCount() {
+ return mActionBar.getTabCount();
+ }
+
+ @Override
+ public int getHeight() {
+ return mActionBar.getHeight();
+ }
+
+ @Override
+ public void show() {
+ mActionBar.show();
+ }
+
+ @Override
+ public void hide() {
+ mActionBar.hide();
+ }
+
+ @Override
+ public boolean isShowing() {
+ return mActionBar.isShowing();
+ }
+
+ @Override
+ public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
+ mMenuVisibilityListeners.add(listener);
+ }
+
+ @Override
+ public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
+ mMenuVisibilityListeners.remove(listener);
+ }
+
+ @Override
+ public void onMenuVisibilityChanged(boolean isVisible) {
+ for (OnMenuVisibilityListener listener : mMenuVisibilityListeners) {
+ listener.onMenuVisibilityChanged(isVisible);
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/Animator.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/Animator.java
new file mode 100644
index 0000000000..2caf5b4a96
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/Animator.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.nineoldandroids.animation;
+
+import java.util.ArrayList;
+
+import android.view.animation.Interpolator;
+
+/**
+ * This is the superclass for classes which provide basic support for animations which can be
+ * started, ended, and have AnimatorListeners added to them.
+ */
+public abstract class Animator implements Cloneable {
+
+
+ /**
+ * The set of listeners to be sent events through the life of an animation.
+ */
+ ArrayList mListeners = null;
+
+ /**
+ * Starts this animation. If the animation has a nonzero startDelay, the animation will start
+ * running after that delay elapses. A non-delayed animation will have its initial
+ * value(s) set immediately, followed by calls to
+ * {@link AnimatorListener#onAnimationStart(Animator)} for any listeners of this animator.
+ *
+ *
The animation started by calling this method will be run on the thread that called
+ * this method. This thread should have a Looper on it (a runtime exception will be thrown if
+ * this is not the case). Also, if the animation will animate
+ * properties of objects in the view hierarchy, then the calling thread should be the UI
+ * thread for that view hierarchy.
+ *
+ */
+ public void start() {
+ }
+
+ /**
+ * Cancels the animation. Unlike {@link #end()}, cancel() causes the animation to
+ * stop in its tracks, sending an
+ * {@link android.animation.Animator.AnimatorListener#onAnimationCancel(Animator)} to
+ * its listeners, followed by an
+ * {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} message.
+ *
+ *
This method must be called on the thread that is running the animation.
+ */
+ public void cancel() {
+ }
+
+ /**
+ * Ends the animation. This causes the animation to assign the end value of the property being
+ * animated, then calling the
+ * {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} method on
+ * its listeners.
+ *
+ *
This method must be called on the thread that is running the animation.
+ */
+ public void end() {
+ }
+
+ /**
+ * The amount of time, in milliseconds, to delay starting the animation after
+ * {@link #start()} is called.
+ *
+ * @return the number of milliseconds to delay running the animation
+ */
+ public abstract long getStartDelay();
+
+ /**
+ * The amount of time, in milliseconds, to delay starting the animation after
+ * {@link #start()} is called.
+
+ * @param startDelay The amount of the delay, in milliseconds
+ */
+ public abstract void setStartDelay(long startDelay);
+
+
+ /**
+ * Sets the length of the animation.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ */
+ public abstract Animator setDuration(long duration);
+
+ /**
+ * Gets the length of the animation.
+ *
+ * @return The length of the animation, in milliseconds.
+ */
+ public abstract long getDuration();
+
+ /**
+ * The time interpolator used in calculating the elapsed fraction of this animation. The
+ * interpolator determines whether the animation runs with linear or non-linear motion,
+ * such as acceleration and deceleration. The default value is
+ * {@link android.view.animation.AccelerateDecelerateInterpolator}
+ *
+ * @param value the interpolator to be used by this animation
+ */
+ public abstract void setInterpolator(/*Time*/Interpolator value);
+
+ /**
+ * Returns whether this Animator is currently running (having been started and gone past any
+ * initial startDelay period and not yet ended).
+ *
+ * @return Whether the Animator is running.
+ */
+ public abstract boolean isRunning();
+
+ /**
+ * Returns whether this Animator has been started and not yet ended. This state is a superset
+ * of the state of {@link #isRunning()}, because an Animator with a nonzero
+ * {@link #getStartDelay() startDelay} will return true for {@link #isStarted()} during the
+ * delay phase, whereas {@link #isRunning()} will return true only after the delay phase
+ * is complete.
+ *
+ * @return Whether the Animator has been started and not yet ended.
+ */
+ public boolean isStarted() {
+ // Default method returns value for isRunning(). Subclasses should override to return a
+ // real value.
+ return isRunning();
+ }
+
+ /**
+ * Adds a listener to the set of listeners that are sent events through the life of an
+ * animation, such as start, repeat, and end.
+ *
+ * @param listener the listener to be added to the current set of listeners for this animation.
+ */
+ public void addListener(AnimatorListener listener) {
+ if (mListeners == null) {
+ mListeners = new ArrayList();
+ }
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener from the set listening to this animation.
+ *
+ * @param listener the listener to be removed from the current set of listeners for this
+ * animation.
+ */
+ public void removeListener(AnimatorListener listener) {
+ if (mListeners == null) {
+ return;
+ }
+ mListeners.remove(listener);
+ if (mListeners.size() == 0) {
+ mListeners = null;
+ }
+ }
+
+ /**
+ * Gets the set of {@link android.animation.Animator.AnimatorListener} objects that are currently
+ * listening for events on this Animator object.
+ *
+ * @return ArrayList The set of listeners.
+ */
+ public ArrayList getListeners() {
+ return mListeners;
+ }
+
+ /**
+ * Removes all listeners from this object. This is equivalent to calling
+ * getListeners() followed by calling clear() on the
+ * returned list of listeners.
+ */
+ public void removeAllListeners() {
+ if (mListeners != null) {
+ mListeners.clear();
+ mListeners = null;
+ }
+ }
+
+ @Override
+ public Animator clone() {
+ try {
+ final Animator anim = (Animator) super.clone();
+ if (mListeners != null) {
+ ArrayList oldListeners = mListeners;
+ anim.mListeners = new ArrayList();
+ int numListeners = oldListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ anim.mListeners.add(oldListeners.get(i));
+ }
+ }
+ return anim;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * This method tells the object to use appropriate information to extract
+ * starting values for the animation. For example, a AnimatorSet object will pass
+ * this call to its child objects to tell them to set up the values. A
+ * ObjectAnimator object will use the information it has about its target object
+ * and PropertyValuesHolder objects to get the start values for its properties.
+ * An ValueAnimator object will ignore the request since it does not have enough
+ * information (such as a target object) to gather these values.
+ */
+ public void setupStartValues() {
+ }
+
+ /**
+ * This method tells the object to use appropriate information to extract
+ * ending values for the animation. For example, a AnimatorSet object will pass
+ * this call to its child objects to tell them to set up the values. A
+ * ObjectAnimator object will use the information it has about its target object
+ * and PropertyValuesHolder objects to get the start values for its properties.
+ * An ValueAnimator object will ignore the request since it does not have enough
+ * information (such as a target object) to gather these values.
+ */
+ public void setupEndValues() {
+ }
+
+ /**
+ * Sets the target object whose property will be animated by this animation. Not all subclasses
+ * operate on target objects (for example, {@link ValueAnimator}, but this method
+ * is on the superclass for the convenience of dealing generically with those subclasses
+ * that do handle targets.
+ *
+ * @param target The object being animated
+ */
+ public void setTarget(Object target) {
+ }
+
+ /**
+ *
An animation listener receives notifications from an animation.
+ * Notifications indicate animation related events, such as the end or the
+ * repetition of the animation.
+ *
+ * @param animation The started animation.
+ */
+ void onAnimationStart(Animator animation);
+
+ /**
+ *
Notifies the end of the animation. This callback is not invoked
+ * for animations with repeat count set to INFINITE.
+ *
+ * @param animation The animation which reached its end.
+ */
+ void onAnimationEnd(Animator animation);
+
+ /**
+ *
Notifies the cancellation of the animation. This callback is not invoked
+ * for animations with repeat count set to INFINITE.
+ *
+ * @param animation The animation which was canceled.
+ */
+ void onAnimationCancel(Animator animation);
+
+ /**
+ *
Notifies the repetition of the animation.
+ *
+ * @param animation The animation which was repeated.
+ */
+ void onAnimationRepeat(Animator animation);
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorListenerAdapter.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorListenerAdapter.java
new file mode 100644
index 0000000000..02ddff48d2
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorListenerAdapter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.nineoldandroids.animation;
+
+/**
+ * This adapter class provides empty implementations of the methods from {@link android.animation.Animator.AnimatorListener}.
+ * Any custom listener that cares only about a subset of the methods of this listener can
+ * simply subclass this adapter class instead of implementing the interface directly.
+ */
+public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorSet.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorSet.java
new file mode 100644
index 0000000000..3231080c44
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorSet.java
@@ -0,0 +1,1111 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.nineoldandroids.animation;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+
+import android.view.animation.Interpolator;
+
+/**
+ * This class plays a set of {@link Animator} objects in the specified order. Animations
+ * can be set up to play together, in sequence, or after a specified delay.
+ *
+ *
There are two different approaches to adding animations to a AnimatorSet:
+ * either the {@link AnimatorSet#playTogether(Animator[]) playTogether()} or
+ * {@link AnimatorSet#playSequentially(Animator[]) playSequentially()} methods can be called to add
+ * a set of animations all at once, or the {@link AnimatorSet#play(Animator)} can be
+ * used in conjunction with methods in the {@link AnimatorSet.Builder Builder}
+ * class to add animations
+ * one by one.
+ *
+ *
It is possible to set up a AnimatorSet with circular dependencies between
+ * its animations. For example, an animation a1 could be set up to start before animation a2, a2
+ * before a3, and a3 before a1. The results of this configuration are undefined, but will typically
+ * result in none of the affected animations being played. Because of this (and because
+ * circular dependencies do not make logical sense anyway), circular dependencies
+ * should be avoided, and the dependency flow of animations should only be in one direction.
+ */
+@SuppressWarnings("unchecked")
+public final class AnimatorSet extends Animator {
+
+ /**
+ * Internal variables
+ * NOTE: This object implements the clone() method, making a deep copy of any referenced
+ * objects. As other non-trivial fields are added to this class, make sure to add logic
+ * to clone() to make deep copies of them.
+ */
+
+ /**
+ * Tracks animations currently being played, so that we know what to
+ * cancel or end when cancel() or end() is called on this AnimatorSet
+ */
+ private ArrayList mPlayingSet = new ArrayList();
+
+ /**
+ * Contains all nodes, mapped to their respective Animators. When new
+ * dependency information is added for an Animator, we want to add it
+ * to a single node representing that Animator, not create a new Node
+ * if one already exists.
+ */
+ private HashMap mNodeMap = new HashMap();
+
+ /**
+ * Set of all nodes created for this AnimatorSet. This list is used upon
+ * starting the set, and the nodes are placed in sorted order into the
+ * sortedNodes collection.
+ */
+ private ArrayList mNodes = new ArrayList();
+
+ /**
+ * The sorted list of nodes. This is the order in which the animations will
+ * be played. The details about when exactly they will be played depend
+ * on the dependency relationships of the nodes.
+ */
+ private ArrayList mSortedNodes = new ArrayList();
+
+ /**
+ * Flag indicating whether the nodes should be sorted prior to playing. This
+ * flag allows us to cache the previous sorted nodes so that if the sequence
+ * is replayed with no changes, it does not have to re-sort the nodes again.
+ */
+ private boolean mNeedsSort = true;
+
+ private AnimatorSetListener mSetListener = null;
+
+ /**
+ * Flag indicating that the AnimatorSet has been manually
+ * terminated (by calling cancel() or end()).
+ * This flag is used to avoid starting other animations when currently-playing
+ * child animations of this AnimatorSet end. It also determines whether cancel/end
+ * notifications are sent out via the normal AnimatorSetListener mechanism.
+ */
+ boolean mTerminated = false;
+
+ /**
+ * Indicates whether an AnimatorSet has been start()'d, whether or
+ * not there is a nonzero startDelay.
+ */
+ private boolean mStarted = false;
+
+ // The amount of time in ms to delay starting the animation after start() is called
+ private long mStartDelay = 0;
+
+ // Animator used for a nonzero startDelay
+ private ValueAnimator mDelayAnim = null;
+
+
+ // How long the child animations should last in ms. The default value is negative, which
+ // simply means that there is no duration set on the AnimatorSet. When a real duration is
+ // set, it is passed along to the child animations.
+ private long mDuration = -1;
+
+
+ /**
+ * Sets up this AnimatorSet to play all of the supplied animations at the same time.
+ *
+ * @param items The animations that will be started simultaneously.
+ */
+ public void playTogether(Animator... items) {
+ if (items != null) {
+ mNeedsSort = true;
+ Builder builder = play(items[0]);
+ for (int i = 1; i < items.length; ++i) {
+ builder.with(items[i]);
+ }
+ }
+ }
+
+ /**
+ * Sets up this AnimatorSet to play all of the supplied animations at the same time.
+ *
+ * @param items The animations that will be started simultaneously.
+ */
+ public void playTogether(Collection items) {
+ if (items != null && items.size() > 0) {
+ mNeedsSort = true;
+ Builder builder = null;
+ for (Animator anim : items) {
+ if (builder == null) {
+ builder = play(anim);
+ } else {
+ builder.with(anim);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets up this AnimatorSet to play each of the supplied animations when the
+ * previous animation ends.
+ *
+ * @param items The animations that will be started one after another.
+ */
+ public void playSequentially(Animator... items) {
+ if (items != null) {
+ mNeedsSort = true;
+ if (items.length == 1) {
+ play(items[0]);
+ } else {
+ for (int i = 0; i < items.length - 1; ++i) {
+ play(items[i]).before(items[i+1]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets up this AnimatorSet to play each of the supplied animations when the
+ * previous animation ends.
+ *
+ * @param items The animations that will be started one after another.
+ */
+ public void playSequentially(List items) {
+ if (items != null && items.size() > 0) {
+ mNeedsSort = true;
+ if (items.size() == 1) {
+ play(items.get(0));
+ } else {
+ for (int i = 0; i < items.size() - 1; ++i) {
+ play(items.get(i)).before(items.get(i+1));
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the current list of child Animator objects controlled by this
+ * AnimatorSet. This is a copy of the internal list; modifications to the returned list
+ * will not affect the AnimatorSet, although changes to the underlying Animator objects
+ * will affect those objects being managed by the AnimatorSet.
+ *
+ * @return ArrayList The list of child animations of this AnimatorSet.
+ */
+ public ArrayList getChildAnimations() {
+ ArrayList childList = new ArrayList();
+ for (Node node : mNodes) {
+ childList.add(node.animation);
+ }
+ return childList;
+ }
+
+ /**
+ * Sets the target object for all current {@link #getChildAnimations() child animations}
+ * of this AnimatorSet that take targets ({@link ObjectAnimator} and
+ * AnimatorSet).
+ *
+ * @param target The object being animated
+ */
+ @Override
+ public void setTarget(Object target) {
+ for (Node node : mNodes) {
+ Animator animation = node.animation;
+ if (animation instanceof AnimatorSet) {
+ ((AnimatorSet)animation).setTarget(target);
+ } else if (animation instanceof ObjectAnimator) {
+ ((ObjectAnimator)animation).setTarget(target);
+ }
+ }
+ }
+
+ /**
+ * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations}
+ * of this AnimatorSet.
+ *
+ * @param interpolator the interpolator to be used by each child animation of this AnimatorSet
+ */
+ @Override
+ public void setInterpolator(/*Time*/Interpolator interpolator) {
+ for (Node node : mNodes) {
+ node.animation.setInterpolator(interpolator);
+ }
+ }
+
+ /**
+ * This method creates a Builder object, which is used to
+ * set up playing constraints. This initial play() method
+ * tells the Builder the animation that is the dependency for
+ * the succeeding commands to the Builder. For example,
+ * calling play(a1).with(a2) sets up the AnimatorSet to play
+ * a1 and a2 at the same time,
+ * play(a1).before(a2) sets up the AnimatorSet to play
+ * a1 first, followed by a2, and
+ * play(a1).after(a2) sets up the AnimatorSet to play
+ * a2 first, followed by a1.
+ *
+ *
Note that play() is the only way to tell the
+ * Builder the animation upon which the dependency is created,
+ * so successive calls to the various functions in Builder
+ * will all refer to the initial parameter supplied in play()
+ * as the dependency of the other animations. For example, calling
+ * play(a1).before(a2).before(a3) will play both a2
+ * and a3 when a1 ends; it does not set up a dependency between
+ * a2 and a3.
+ *
+ * @param anim The animation that is the dependency used in later calls to the
+ * methods in the returned Builder object. A null parameter will result
+ * in a null Builder return value.
+ * @return Builder The object that constructs the AnimatorSet based on the dependencies
+ * outlined in the calls to play and the other methods in the
+ * BuilderNote that canceling a AnimatorSet also cancels all of the animations that it
+ * is responsible for.
+ */
+ @Override
+ public void cancel() {
+ mTerminated = true;
+ if (isStarted()) {
+ ArrayList tmpListeners = null;
+ if (mListeners != null) {
+ tmpListeners = (ArrayList) mListeners.clone();
+ for (AnimatorListener listener : tmpListeners) {
+ listener.onAnimationCancel(this);
+ }
+ }
+ if (mDelayAnim != null && mDelayAnim.isRunning()) {
+ // If we're currently in the startDelay period, just cancel that animator and
+ // send out the end event to all listeners
+ mDelayAnim.cancel();
+ } else if (mSortedNodes.size() > 0) {
+ for (Node node : mSortedNodes) {
+ node.animation.cancel();
+ }
+ }
+ if (tmpListeners != null) {
+ for (AnimatorListener listener : tmpListeners) {
+ listener.onAnimationEnd(this);
+ }
+ }
+ mStarted = false;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ *
Note that ending a AnimatorSet also ends all of the animations that it is
+ * responsible for.
+ */
+ @Override
+ public void end() {
+ mTerminated = true;
+ if (isStarted()) {
+ if (mSortedNodes.size() != mNodes.size()) {
+ // hasn't been started yet - sort the nodes now, then end them
+ sortNodes();
+ for (Node node : mSortedNodes) {
+ if (mSetListener == null) {
+ mSetListener = new AnimatorSetListener(this);
+ }
+ node.animation.addListener(mSetListener);
+ }
+ }
+ if (mDelayAnim != null) {
+ mDelayAnim.cancel();
+ }
+ if (mSortedNodes.size() > 0) {
+ for (Node node : mSortedNodes) {
+ node.animation.end();
+ }
+ }
+ if (mListeners != null) {
+ ArrayList tmpListeners =
+ (ArrayList) mListeners.clone();
+ for (AnimatorListener listener : tmpListeners) {
+ listener.onAnimationEnd(this);
+ }
+ }
+ mStarted = false;
+ }
+ }
+
+ /**
+ * Returns true if any of the child animations of this AnimatorSet have been started and have
+ * not yet ended.
+ * @return Whether this AnimatorSet has been started and has not yet ended.
+ */
+ @Override
+ public boolean isRunning() {
+ for (Node node : mNodes) {
+ if (node.animation.isRunning()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isStarted() {
+ return mStarted;
+ }
+
+ /**
+ * The amount of time, in milliseconds, to delay starting the animation after
+ * {@link #start()} is called.
+ *
+ * @return the number of milliseconds to delay running the animation
+ */
+ @Override
+ public long getStartDelay() {
+ return mStartDelay;
+ }
+
+ /**
+ * The amount of time, in milliseconds, to delay starting the animation after
+ * {@link #start()} is called.
+
+ * @param startDelay The amount of the delay, in milliseconds
+ */
+ @Override
+ public void setStartDelay(long startDelay) {
+ mStartDelay = startDelay;
+ }
+
+ /**
+ * Gets the length of each of the child animations of this AnimatorSet. This value may
+ * be less than 0, which indicates that no duration has been set on this AnimatorSet
+ * and each of the child animations will use their own duration.
+ *
+ * @return The length of the animation, in milliseconds, of each of the child
+ * animations of this AnimatorSet.
+ */
+ @Override
+ public long getDuration() {
+ return mDuration;
+ }
+
+ /**
+ * Sets the length of each of the current child animations of this AnimatorSet. By default,
+ * each child animation will use its own duration. If the duration is set on the AnimatorSet,
+ * then each child animation inherits this duration.
+ *
+ * @param duration The length of the animation, in milliseconds, of each of the child
+ * animations of this AnimatorSet.
+ */
+ @Override
+ public AnimatorSet setDuration(long duration) {
+ if (duration < 0) {
+ throw new IllegalArgumentException("duration must be a value of zero or greater");
+ }
+ for (Node node : mNodes) {
+ // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
+ // insert "play-after" delays
+ node.animation.setDuration(duration);
+ }
+ mDuration = duration;
+ return this;
+ }
+
+ @Override
+ public void setupStartValues() {
+ for (Node node : mNodes) {
+ node.animation.setupStartValues();
+ }
+ }
+
+ @Override
+ public void setupEndValues() {
+ for (Node node : mNodes) {
+ node.animation.setupEndValues();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ *
Starting this AnimatorSet will, in turn, start the animations for which
+ * it is responsible. The details of when exactly those animations are started depends on
+ * the dependency relationships that have been set up between the animations.
+ */
+ @Override
+ public void start() {
+ mTerminated = false;
+ mStarted = true;
+
+ // First, sort the nodes (if necessary). This will ensure that sortedNodes
+ // contains the animation nodes in the correct order.
+ sortNodes();
+
+ int numSortedNodes = mSortedNodes.size();
+ for (int i = 0; i < numSortedNodes; ++i) {
+ Node node = mSortedNodes.get(i);
+ // First, clear out the old listeners
+ ArrayList oldListeners = node.animation.getListeners();
+ if (oldListeners != null && oldListeners.size() > 0) {
+ final ArrayList clonedListeners = new
+ ArrayList(oldListeners);
+
+ for (AnimatorListener listener : clonedListeners) {
+ if (listener instanceof DependencyListener ||
+ listener instanceof AnimatorSetListener) {
+ node.animation.removeListener(listener);
+ }
+ }
+ }
+ }
+
+ // nodesToStart holds the list of nodes to be started immediately. We don't want to
+ // start the animations in the loop directly because we first need to set up
+ // dependencies on all of the nodes. For example, we don't want to start an animation
+ // when some other animation also wants to start when the first animation begins.
+ final ArrayList nodesToStart = new ArrayList();
+ for (int i = 0; i < numSortedNodes; ++i) {
+ Node node = mSortedNodes.get(i);
+ if (mSetListener == null) {
+ mSetListener = new AnimatorSetListener(this);
+ }
+ if (node.dependencies == null || node.dependencies.size() == 0) {
+ nodesToStart.add(node);
+ } else {
+ int numDependencies = node.dependencies.size();
+ for (int j = 0; j < numDependencies; ++j) {
+ Dependency dependency = node.dependencies.get(j);
+ dependency.node.animation.addListener(
+ new DependencyListener(this, node, dependency.rule));
+ }
+ node.tmpDependencies = (ArrayList) node.dependencies.clone();
+ }
+ node.animation.addListener(mSetListener);
+ }
+ // Now that all dependencies are set up, start the animations that should be started.
+ if (mStartDelay <= 0) {
+ for (Node node : nodesToStart) {
+ node.animation.start();
+ mPlayingSet.add(node.animation);
+ }
+ } else {
+ mDelayAnim = ValueAnimator.ofFloat(0f, 1f);
+ mDelayAnim.setDuration(mStartDelay);
+ mDelayAnim.addListener(new AnimatorListenerAdapter() {
+ boolean canceled = false;
+ public void onAnimationCancel(Animator anim) {
+ canceled = true;
+ }
+ public void onAnimationEnd(Animator anim) {
+ if (!canceled) {
+ int numNodes = nodesToStart.size();
+ for (int i = 0; i < numNodes; ++i) {
+ Node node = nodesToStart.get(i);
+ node.animation.start();
+ mPlayingSet.add(node.animation);
+ }
+ }
+ }
+ });
+ mDelayAnim.start();
+ }
+ if (mListeners != null) {
+ ArrayList tmpListeners =
+ (ArrayList) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationStart(this);
+ }
+ }
+ if (mNodes.size() == 0 && mStartDelay == 0) {
+ // Handle unusual case where empty AnimatorSet is started - should send out
+ // end event immediately since the event will not be sent out at all otherwise
+ mStarted = false;
+ if (mListeners != null) {
+ ArrayList tmpListeners =
+ (ArrayList) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationEnd(this);
+ }
+ }
+ }
+ }
+
+ @Override
+ public AnimatorSet clone() {
+ final AnimatorSet anim = (AnimatorSet) super.clone();
+ /*
+ * The basic clone() operation copies all items. This doesn't work very well for
+ * AnimatorSet, because it will copy references that need to be recreated and state
+ * that may not apply. What we need to do now is put the clone in an uninitialized
+ * state, with fresh, empty data structures. Then we will build up the nodes list
+ * manually, as we clone each Node (and its animation). The clone will then be sorted,
+ * and will populate any appropriate lists, when it is started.
+ */
+ anim.mNeedsSort = true;
+ anim.mTerminated = false;
+ anim.mStarted = false;
+ anim.mPlayingSet = new ArrayList();
+ anim.mNodeMap = new HashMap();
+ anim.mNodes = new ArrayList();
+ anim.mSortedNodes = new ArrayList();
+
+ // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
+ // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
+ // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
+ HashMap nodeCloneMap = new HashMap(); //
+ for (Node node : mNodes) {
+ Node nodeClone = node.clone();
+ nodeCloneMap.put(node, nodeClone);
+ anim.mNodes.add(nodeClone);
+ anim.mNodeMap.put(nodeClone.animation, nodeClone);
+ // Clear out the dependencies in the clone; we'll set these up manually later
+ nodeClone.dependencies = null;
+ nodeClone.tmpDependencies = null;
+ nodeClone.nodeDependents = null;
+ nodeClone.nodeDependencies = null;
+ // clear out any listeners that were set up by the AnimatorSet; these will
+ // be set up when the clone's nodes are sorted
+ ArrayList cloneListeners = nodeClone.animation.getListeners();
+ if (cloneListeners != null) {
+ ArrayList listenersToRemove = null;
+ for (AnimatorListener listener : cloneListeners) {
+ if (listener instanceof AnimatorSetListener) {
+ if (listenersToRemove == null) {
+ listenersToRemove = new ArrayList();
+ }
+ listenersToRemove.add(listener);
+ }
+ }
+ if (listenersToRemove != null) {
+ for (AnimatorListener listener : listenersToRemove) {
+ cloneListeners.remove(listener);
+ }
+ }
+ }
+ }
+ // Now that we've cloned all of the nodes, we're ready to walk through their
+ // dependencies, mapping the old dependencies to the new nodes
+ for (Node node : mNodes) {
+ Node nodeClone = nodeCloneMap.get(node);
+ if (node.dependencies != null) {
+ for (Dependency dependency : node.dependencies) {
+ Node clonedDependencyNode = nodeCloneMap.get(dependency.node);
+ Dependency cloneDependency = new Dependency(clonedDependencyNode,
+ dependency.rule);
+ nodeClone.addDependency(cloneDependency);
+ }
+ }
+ }
+
+ return anim;
+ }
+
+ /**
+ * This class is the mechanism by which animations are started based on events in other
+ * animations. If an animation has multiple dependencies on other animations, then
+ * all dependencies must be satisfied before the animation is started.
+ */
+ private static class DependencyListener implements AnimatorListener {
+
+ private AnimatorSet mAnimatorSet;
+
+ // The node upon which the dependency is based.
+ private Node mNode;
+
+ // The Dependency rule (WITH or AFTER) that the listener should wait for on
+ // the node
+ private int mRule;
+
+ public DependencyListener(AnimatorSet animatorSet, Node node, int rule) {
+ this.mAnimatorSet = animatorSet;
+ this.mNode = node;
+ this.mRule = rule;
+ }
+
+ /**
+ * Ignore cancel events for now. We may want to handle this eventually,
+ * to prevent follow-on animations from running when some dependency
+ * animation is canceled.
+ */
+ public void onAnimationCancel(Animator animation) {
+ }
+
+ /**
+ * An end event is received - see if this is an event we are listening for
+ */
+ public void onAnimationEnd(Animator animation) {
+ if (mRule == Dependency.AFTER) {
+ startIfReady(animation);
+ }
+ }
+
+ /**
+ * Ignore repeat events for now
+ */
+ public void onAnimationRepeat(Animator animation) {
+ }
+
+ /**
+ * A start event is received - see if this is an event we are listening for
+ */
+ public void onAnimationStart(Animator animation) {
+ if (mRule == Dependency.WITH) {
+ startIfReady(animation);
+ }
+ }
+
+ /**
+ * Check whether the event received is one that the node was waiting for.
+ * If so, mark it as complete and see whether it's time to start
+ * the animation.
+ * @param dependencyAnimation the animation that sent the event.
+ */
+ private void startIfReady(Animator dependencyAnimation) {
+ if (mAnimatorSet.mTerminated) {
+ // if the parent AnimatorSet was canceled, then don't start any dependent anims
+ return;
+ }
+ Dependency dependencyToRemove = null;
+ int numDependencies = mNode.tmpDependencies.size();
+ for (int i = 0; i < numDependencies; ++i) {
+ Dependency dependency = mNode.tmpDependencies.get(i);
+ if (dependency.rule == mRule &&
+ dependency.node.animation == dependencyAnimation) {
+ // rule fired - remove the dependency and listener and check to
+ // see whether it's time to start the animation
+ dependencyToRemove = dependency;
+ dependencyAnimation.removeListener(this);
+ break;
+ }
+ }
+ mNode.tmpDependencies.remove(dependencyToRemove);
+ if (mNode.tmpDependencies.size() == 0) {
+ // all dependencies satisfied: start the animation
+ mNode.animation.start();
+ mAnimatorSet.mPlayingSet.add(mNode.animation);
+ }
+ }
+
+ }
+
+ private class AnimatorSetListener implements AnimatorListener {
+
+ private AnimatorSet mAnimatorSet;
+
+ AnimatorSetListener(AnimatorSet animatorSet) {
+ mAnimatorSet = animatorSet;
+ }
+
+ public void onAnimationCancel(Animator animation) {
+ if (!mTerminated) {
+ // Listeners are already notified of the AnimatorSet canceling in cancel().
+ // The logic below only kicks in when animations end normally
+ if (mPlayingSet.size() == 0) {
+ if (mListeners != null) {
+ int numListeners = mListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ mListeners.get(i).onAnimationCancel(mAnimatorSet);
+ }
+ }
+ }
+ }
+ }
+
+ public void onAnimationEnd(Animator animation) {
+ animation.removeListener(this);
+ mPlayingSet.remove(animation);
+ Node animNode = mAnimatorSet.mNodeMap.get(animation);
+ animNode.done = true;
+ if (!mTerminated) {
+ // Listeners are already notified of the AnimatorSet ending in cancel() or
+ // end(); the logic below only kicks in when animations end normally
+ ArrayList sortedNodes = mAnimatorSet.mSortedNodes;
+ boolean allDone = true;
+ int numSortedNodes = sortedNodes.size();
+ for (int i = 0; i < numSortedNodes; ++i) {
+ if (!sortedNodes.get(i).done) {
+ allDone = false;
+ break;
+ }
+ }
+ if (allDone) {
+ // If this was the last child animation to end, then notify listeners that this
+ // AnimatorSet has ended
+ if (mListeners != null) {
+ ArrayList tmpListeners =
+ (ArrayList) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationEnd(mAnimatorSet);
+ }
+ }
+ mAnimatorSet.mStarted = false;
+ }
+ }
+ }
+
+ // Nothing to do
+ public void onAnimationRepeat(Animator animation) {
+ }
+
+ // Nothing to do
+ public void onAnimationStart(Animator animation) {
+ }
+
+ }
+
+ /**
+ * This method sorts the current set of nodes, if needed. The sort is a simple
+ * DependencyGraph sort, which goes like this:
+ * - All nodes without dependencies become 'roots'
+ * - while roots list is not null
+ * - for each root r
+ * - add r to sorted list
+ * - remove r as a dependency from any other node
+ * - any nodes with no dependencies are added to the roots list
+ */
+ private void sortNodes() {
+ if (mNeedsSort) {
+ mSortedNodes.clear();
+ ArrayList roots = new ArrayList();
+ int numNodes = mNodes.size();
+ for (int i = 0; i < numNodes; ++i) {
+ Node node = mNodes.get(i);
+ if (node.dependencies == null || node.dependencies.size() == 0) {
+ roots.add(node);
+ }
+ }
+ ArrayList tmpRoots = new ArrayList();
+ while (roots.size() > 0) {
+ int numRoots = roots.size();
+ for (int i = 0; i < numRoots; ++i) {
+ Node root = roots.get(i);
+ mSortedNodes.add(root);
+ if (root.nodeDependents != null) {
+ int numDependents = root.nodeDependents.size();
+ for (int j = 0; j < numDependents; ++j) {
+ Node node = root.nodeDependents.get(j);
+ node.nodeDependencies.remove(root);
+ if (node.nodeDependencies.size() == 0) {
+ tmpRoots.add(node);
+ }
+ }
+ }
+ }
+ roots.clear();
+ roots.addAll(tmpRoots);
+ tmpRoots.clear();
+ }
+ mNeedsSort = false;
+ if (mSortedNodes.size() != mNodes.size()) {
+ throw new IllegalStateException("Circular dependencies cannot exist"
+ + " in AnimatorSet");
+ }
+ } else {
+ // Doesn't need sorting, but still need to add in the nodeDependencies list
+ // because these get removed as the event listeners fire and the dependencies
+ // are satisfied
+ int numNodes = mNodes.size();
+ for (int i = 0; i < numNodes; ++i) {
+ Node node = mNodes.get(i);
+ if (node.dependencies != null && node.dependencies.size() > 0) {
+ int numDependencies = node.dependencies.size();
+ for (int j = 0; j < numDependencies; ++j) {
+ Dependency dependency = node.dependencies.get(j);
+ if (node.nodeDependencies == null) {
+ node.nodeDependencies = new ArrayList();
+ }
+ if (!node.nodeDependencies.contains(dependency.node)) {
+ node.nodeDependencies.add(dependency.node);
+ }
+ }
+ }
+ // nodes are 'done' by default; they become un-done when started, and done
+ // again when ended
+ node.done = false;
+ }
+ }
+ }
+
+ /**
+ * Dependency holds information about the node that some other node is
+ * dependent upon and the nature of that dependency.
+ *
+ */
+ private static class Dependency {
+ static final int WITH = 0; // dependent node must start with this dependency node
+ static final int AFTER = 1; // dependent node must start when this dependency node finishes
+
+ // The node that the other node with this Dependency is dependent upon
+ public Node node;
+
+ // The nature of the dependency (WITH or AFTER)
+ public int rule;
+
+ public Dependency(Node node, int rule) {
+ this.node = node;
+ this.rule = rule;
+ }
+ }
+
+ /**
+ * A Node is an embodiment of both the Animator that it wraps as well as
+ * any dependencies that are associated with that Animation. This includes
+ * both dependencies upon other nodes (in the dependencies list) as
+ * well as dependencies of other nodes upon this (in the nodeDependents list).
+ */
+ private static class Node implements Cloneable {
+ public Animator animation;
+
+ /**
+ * These are the dependencies that this node's animation has on other
+ * nodes. For example, if this node's animation should begin with some
+ * other animation ends, then there will be an item in this node's
+ * dependencies list for that other animation's node.
+ */
+ public ArrayList dependencies = null;
+
+ /**
+ * tmpDependencies is a runtime detail. We use the dependencies list for sorting.
+ * But we also use the list to keep track of when multiple dependencies are satisfied,
+ * but removing each dependency as it is satisfied. We do not want to remove
+ * the dependency itself from the list, because we need to retain that information
+ * if the AnimatorSet is launched in the future. So we create a copy of the dependency
+ * list when the AnimatorSet starts and use this tmpDependencies list to track the
+ * list of satisfied dependencies.
+ */
+ public ArrayList tmpDependencies = null;
+
+ /**
+ * nodeDependencies is just a list of the nodes that this Node is dependent upon.
+ * This information is used in sortNodes(), to determine when a node is a root.
+ */
+ public ArrayList nodeDependencies = null;
+
+ /**
+ * nodeDepdendents is the list of nodes that have this node as a dependency. This
+ * is a utility field used in sortNodes to facilitate removing this node as a
+ * dependency when it is a root node.
+ */
+ public ArrayList nodeDependents = null;
+
+ /**
+ * Flag indicating whether the animation in this node is finished. This flag
+ * is used by AnimatorSet to check, as each animation ends, whether all child animations
+ * are done and it's time to send out an end event for the entire AnimatorSet.
+ */
+ public boolean done = false;
+
+ /**
+ * Constructs the Node with the animation that it encapsulates. A Node has no
+ * dependencies by default; dependencies are added via the addDependency()
+ * method.
+ *
+ * @param animation The animation that the Node encapsulates.
+ */
+ public Node(Animator animation) {
+ this.animation = animation;
+ }
+
+ /**
+ * Add a dependency to this Node. The dependency includes information about the
+ * node that this node is dependency upon and the nature of the dependency.
+ * @param dependency
+ */
+ public void addDependency(Dependency dependency) {
+ if (dependencies == null) {
+ dependencies = new ArrayList();
+ nodeDependencies = new ArrayList();
+ }
+ dependencies.add(dependency);
+ if (!nodeDependencies.contains(dependency.node)) {
+ nodeDependencies.add(dependency.node);
+ }
+ Node dependencyNode = dependency.node;
+ if (dependencyNode.nodeDependents == null) {
+ dependencyNode.nodeDependents = new ArrayList();
+ }
+ dependencyNode.nodeDependents.add(this);
+ }
+
+ @Override
+ public Node clone() {
+ try {
+ Node node = (Node) super.clone();
+ node.animation = animation.clone();
+ return node;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+ }
+
+ /**
+ * The Builder object is a utility class to facilitate adding animations to a
+ * AnimatorSet along with the relationships between the various animations. The
+ * intention of the Builder methods, along with the {@link
+ * AnimatorSet#play(Animator) play()} method of AnimatorSet is to make it possible
+ * to express the dependency relationships of animations in a natural way. Developers can also
+ * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
+ * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
+ * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
+ *
+ *
The Builder object cannot be constructed directly, but is rather constructed
+ * internally via a call to {@link AnimatorSet#play(Animator)}.
+ *
+ *
For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to
+ * play when anim2 finishes, and anim4 to play when anim3 finishes:
+ *
+ * AnimatorSet s = new AnimatorSet();
+ * s.play(anim1).with(anim2);
+ * s.play(anim2).before(anim3);
+ * s.play(anim4).after(anim3);
+ *
+ *
+ *
Note in the example that both {@link Builder#before(Animator)} and {@link
+ * Builder#after(Animator)} are used. These are just different ways of expressing the same
+ * relationship and are provided to make it easier to say things in a way that is more natural,
+ * depending on the situation.
+ *
+ *
It is possible to make several calls into the same Builder object to express
+ * multiple relationships. However, note that it is only the animation passed into the initial
+ * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
+ * calls to the Builder object. For example, the following code starts both anim2
+ * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
+ * anim3:
+ *
+ * AnimatorSet s = new AnimatorSet();
+ * s.play(anim1).before(anim2).before(anim3);
+ *
+ * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
+ * relationship correctly:
+ *
+ * AnimatorSet s = new AnimatorSet();
+ * s.play(anim1).before(anim2);
+ * s.play(anim2).before(anim3);
+ *
+ *
+ *
Note that it is possible to express relationships that cannot be resolved and will not
+ * result in sensible results. For example, play(anim1).after(anim1) makes no
+ * sense. In general, circular dependencies like this one (or more indirect ones where a depends
+ * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
+ * that can boil down to a simple, one-way relationship of animations starting with, before, and
+ * after other, different, animations.
+ */
+ public class Builder {
+
+ /**
+ * This tracks the current node being processed. It is supplied to the play() method
+ * of AnimatorSet and passed into the constructor of Builder.
+ */
+ private Node mCurrentNode;
+
+ /**
+ * package-private constructor. Builders are only constructed by AnimatorSet, when the
+ * play() method is called.
+ *
+ * @param anim The animation that is the dependency for the other animations passed into
+ * the other methods of this Builder object.
+ */
+ Builder(Animator anim) {
+ mCurrentNode = mNodeMap.get(anim);
+ if (mCurrentNode == null) {
+ mCurrentNode = new Node(anim);
+ mNodeMap.put(anim, mCurrentNode);
+ mNodes.add(mCurrentNode);
+ }
+ }
+
+ /**
+ * Sets up the given animation to play at the same time as the animation supplied in the
+ * {@link AnimatorSet#play(Animator)} call that created this Builder object.
+ *
+ * @param anim The animation that will play when the animation supplied to the
+ * {@link AnimatorSet#play(Animator)} method starts.
+ */
+ public Builder with(Animator anim) {
+ Node node = mNodeMap.get(anim);
+ if (node == null) {
+ node = new Node(anim);
+ mNodeMap.put(anim, node);
+ mNodes.add(node);
+ }
+ Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH);
+ node.addDependency(dependency);
+ return this;
+ }
+
+ /**
+ * Sets up the given animation to play when the animation supplied in the
+ * {@link AnimatorSet#play(Animator)} call that created this Builder object
+ * ends.
+ *
+ * @param anim The animation that will play when the animation supplied to the
+ * {@link AnimatorSet#play(Animator)} method ends.
+ */
+ public Builder before(Animator anim) {
+ Node node = mNodeMap.get(anim);
+ if (node == null) {
+ node = new Node(anim);
+ mNodeMap.put(anim, node);
+ mNodes.add(node);
+ }
+ Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER);
+ node.addDependency(dependency);
+ return this;
+ }
+
+ /**
+ * Sets up the given animation to play when the animation supplied in the
+ * {@link AnimatorSet#play(Animator)} call that created this Builder object
+ * to start when the animation supplied in this method call ends.
+ *
+ * @param anim The animation whose end will cause the animation supplied to the
+ * {@link AnimatorSet#play(Animator)} method to play.
+ */
+ public Builder after(Animator anim) {
+ Node node = mNodeMap.get(anim);
+ if (node == null) {
+ node = new Node(anim);
+ mNodeMap.put(anim, node);
+ mNodes.add(node);
+ }
+ Dependency dependency = new Dependency(node, Dependency.AFTER);
+ mCurrentNode.addDependency(dependency);
+ return this;
+ }
+
+ /**
+ * Sets up the animation supplied in the
+ * {@link AnimatorSet#play(Animator)} call that created this Builder object
+ * to play when the given amount of time elapses.
+ *
+ * @param delay The number of milliseconds that should elapse before the
+ * animation starts.
+ */
+ public Builder after(long delay) {
+ // setup dummy ValueAnimator just to run the clock
+ ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+ anim.setDuration(delay);
+ after(anim);
+ return this;
+ }
+
+ }
+
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatEvaluator.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatEvaluator.java
new file mode 100644
index 0000000000..e41019364d
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatEvaluator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.nineoldandroids.animation;
+
+/**
+ * This evaluator can be used to perform type interpolation between float values.
+ */
+public class FloatEvaluator implements TypeEvaluator {
+
+ /**
+ * This function returns the result of linearly interpolating the start and end values, with
+ * fraction representing the proportion between the start and end values. The
+ * calculation is a simple parametric calculation: result = x0 + t * (v1 - v0),
+ * where x0 is startValue, x1 is endValue,
+ * and t is fraction.
+ *
+ * @param fraction The fraction from the starting to the ending values
+ * @param startValue The start value; should be of type float or
+ * Float
+ * @param endValue The end value; should be of type float or Float
+ * @return A linear interpolation between the start and end values, given the
+ * fraction parameter.
+ */
+ public Float evaluate(float fraction, Number startValue, Number endValue) {
+ float startFloat = startValue.floatValue();
+ return startFloat + fraction * (endValue.floatValue() - startFloat);
+ }
+}
\ No newline at end of file
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatKeyframeSet.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatKeyframeSet.java
new file mode 100644
index 0000000000..6d9dafa7a4
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatKeyframeSet.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.nineoldandroids.animation;
+
+import java.util.ArrayList;
+import android.view.animation.Interpolator;
+
+import com.actionbarsherlock.internal.nineoldandroids.animation.Keyframe.FloatKeyframe;
+
+/**
+ * This class holds a collection of FloatKeyframe objects and is called by ValueAnimator to calculate
+ * values between those keyframes for a given animation. The class internal to the animation
+ * package because it is an implementation detail of how Keyframes are stored and used.
+ *
+ *
This type-specific subclass of KeyframeSet, along with the other type-specific subclass for
+ * int, exists to speed up the getValue() method when there is no custom
+ * TypeEvaluator set for the animation, so that values can be calculated without autoboxing to the
+ * Object equivalents of these primitive types.
+ */
+@SuppressWarnings("unchecked")
+class FloatKeyframeSet extends KeyframeSet {
+ private float firstValue;
+ private float lastValue;
+ private float deltaValue;
+ private boolean firstTime = true;
+
+ public FloatKeyframeSet(FloatKeyframe... keyframes) {
+ super(keyframes);
+ }
+
+ @Override
+ public Object getValue(float fraction) {
+ return getFloatValue(fraction);
+ }
+
+ @Override
+ public FloatKeyframeSet clone() {
+ ArrayList keyframes = mKeyframes;
+ int numKeyframes = mKeyframes.size();
+ FloatKeyframe[] newKeyframes = new FloatKeyframe[numKeyframes];
+ for (int i = 0; i < numKeyframes; ++i) {
+ newKeyframes[i] = (FloatKeyframe) keyframes.get(i).clone();
+ }
+ FloatKeyframeSet newSet = new FloatKeyframeSet(newKeyframes);
+ return newSet;
+ }
+
+ public float getFloatValue(float fraction) {
+ if (mNumKeyframes == 2) {
+ if (firstTime) {
+ firstTime = false;
+ firstValue = ((FloatKeyframe) mKeyframes.get(0)).getFloatValue();
+ lastValue = ((FloatKeyframe) mKeyframes.get(1)).getFloatValue();
+ deltaValue = lastValue - firstValue;
+ }
+ if (mInterpolator != null) {
+ fraction = mInterpolator.getInterpolation(fraction);
+ }
+ if (mEvaluator == null) {
+ return firstValue + fraction * deltaValue;
+ } else {
+ return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).floatValue();
+ }
+ }
+ if (fraction <= 0f) {
+ final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
+ final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(1);
+ float prevValue = prevKeyframe.getFloatValue();
+ float nextValue = nextKeyframe.getFloatValue();
+ float prevFraction = prevKeyframe.getFraction();
+ float nextFraction = nextKeyframe.getFraction();
+ final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
+ if (interpolator != null) {
+ fraction = interpolator.getInterpolation(fraction);
+ }
+ float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
+ return mEvaluator == null ?
+ prevValue + intervalFraction * (nextValue - prevValue) :
+ ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
+ floatValue();
+ } else if (fraction >= 1f) {
+ final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 2);
+ final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 1);
+ float prevValue = prevKeyframe.getFloatValue();
+ float nextValue = nextKeyframe.getFloatValue();
+ float prevFraction = prevKeyframe.getFraction();
+ float nextFraction = nextKeyframe.getFraction();
+ final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
+ if (interpolator != null) {
+ fraction = interpolator.getInterpolation(fraction);
+ }
+ float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
+ return mEvaluator == null ?
+ prevValue + intervalFraction * (nextValue - prevValue) :
+ ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
+ floatValue();
+ }
+ FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
+ for (int i = 1; i < mNumKeyframes; ++i) {
+ FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i);
+ if (fraction < nextKeyframe.getFraction()) {
+ final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
+ if (interpolator != null) {
+ fraction = interpolator.getInterpolation(fraction);
+ }
+ float intervalFraction = (fraction - prevKeyframe.getFraction()) /
+ (nextKeyframe.getFraction() - prevKeyframe.getFraction());
+ float prevValue = prevKeyframe.getFloatValue();
+ float nextValue = nextKeyframe.getFloatValue();
+ return mEvaluator == null ?
+ prevValue + intervalFraction * (nextValue - prevValue) :
+ ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
+ floatValue();
+ }
+ prevKeyframe = nextKeyframe;
+ }
+ // shouldn't get here
+ return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
+ }
+
+}
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntEvaluator.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntEvaluator.java
new file mode 100644
index 0000000000..ed5e79ec64
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntEvaluator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.nineoldandroids.animation;
+
+/**
+ * This evaluator can be used to perform type interpolation between int values.
+ */
+public class IntEvaluator implements TypeEvaluator {
+
+ /**
+ * This function returns the result of linearly interpolating the start and end values, with
+ * fraction representing the proportion between the start and end values. The
+ * calculation is a simple parametric calculation: result = x0 + t * (v1 - v0),
+ * where x0 is startValue, x1 is endValue,
+ * and t is fraction.
+ *
+ * @param fraction The fraction from the starting to the ending values
+ * @param startValue The start value; should be of type int or
+ * Integer
+ * @param endValue The end value; should be of type int or Integer
+ * @return A linear interpolation between the start and end values, given the
+ * fraction parameter.
+ */
+ public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
+ int startInt = startValue;
+ return (int)(startInt + fraction * (endValue - startInt));
+ }
+}
\ No newline at end of file
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntKeyframeSet.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntKeyframeSet.java
new file mode 100644
index 0000000000..e9215e7f8c
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntKeyframeSet.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.nineoldandroids.animation;
+
+import java.util.ArrayList;
+import android.view.animation.Interpolator;
+
+import com.actionbarsherlock.internal.nineoldandroids.animation.Keyframe.IntKeyframe;
+
+/**
+ * This class holds a collection of IntKeyframe objects and is called by ValueAnimator to calculate
+ * values between those keyframes for a given animation. The class internal to the animation
+ * package because it is an implementation detail of how Keyframes are stored and used.
+ *
+ *
This type-specific subclass of KeyframeSet, along with the other type-specific subclass for
+ * float, exists to speed up the getValue() method when there is no custom
+ * TypeEvaluator set for the animation, so that values can be calculated without autoboxing to the
+ * Object equivalents of these primitive types.
+ */
+@SuppressWarnings("unchecked")
+class IntKeyframeSet extends KeyframeSet {
+ private int firstValue;
+ private int lastValue;
+ private int deltaValue;
+ private boolean firstTime = true;
+
+ public IntKeyframeSet(IntKeyframe... keyframes) {
+ super(keyframes);
+ }
+
+ @Override
+ public Object getValue(float fraction) {
+ return getIntValue(fraction);
+ }
+
+ @Override
+ public IntKeyframeSet clone() {
+ ArrayList keyframes = mKeyframes;
+ int numKeyframes = mKeyframes.size();
+ IntKeyframe[] newKeyframes = new IntKeyframe[numKeyframes];
+ for (int i = 0; i < numKeyframes; ++i) {
+ newKeyframes[i] = (IntKeyframe) keyframes.get(i).clone();
+ }
+ IntKeyframeSet newSet = new IntKeyframeSet(newKeyframes);
+ return newSet;
+ }
+
+ public int getIntValue(float fraction) {
+ if (mNumKeyframes == 2) {
+ if (firstTime) {
+ firstTime = false;
+ firstValue = ((IntKeyframe) mKeyframes.get(0)).getIntValue();
+ lastValue = ((IntKeyframe) mKeyframes.get(1)).getIntValue();
+ deltaValue = lastValue - firstValue;
+ }
+ if (mInterpolator != null) {
+ fraction = mInterpolator.getInterpolation(fraction);
+ }
+ if (mEvaluator == null) {
+ return firstValue + (int)(fraction * deltaValue);
+ } else {
+ return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue();
+ }
+ }
+ if (fraction <= 0f) {
+ final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);
+ final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(1);
+ int prevValue = prevKeyframe.getIntValue();
+ int nextValue = nextKeyframe.getIntValue();
+ float prevFraction = prevKeyframe.getFraction();
+ float nextFraction = nextKeyframe.getFraction();
+ final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
+ if (interpolator != null) {
+ fraction = interpolator.getInterpolation(fraction);
+ }
+ float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
+ return mEvaluator == null ?
+ prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
+ ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
+ intValue();
+ } else if (fraction >= 1f) {
+ final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 2);
+ final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 1);
+ int prevValue = prevKeyframe.getIntValue();
+ int nextValue = nextKeyframe.getIntValue();
+ float prevFraction = prevKeyframe.getFraction();
+ float nextFraction = nextKeyframe.getFraction();
+ final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
+ if (interpolator != null) {
+ fraction = interpolator.getInterpolation(fraction);
+ }
+ float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
+ return mEvaluator == null ?
+ prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
+ ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).intValue();
+ }
+ IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);
+ for (int i = 1; i < mNumKeyframes; ++i) {
+ IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(i);
+ if (fraction < nextKeyframe.getFraction()) {
+ final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
+ if (interpolator != null) {
+ fraction = interpolator.getInterpolation(fraction);
+ }
+ float intervalFraction = (fraction - prevKeyframe.getFraction()) /
+ (nextKeyframe.getFraction() - prevKeyframe.getFraction());
+ int prevValue = prevKeyframe.getIntValue();
+ int nextValue = nextKeyframe.getIntValue();
+ return mEvaluator == null ?
+ prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
+ ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
+ intValue();
+ }
+ prevKeyframe = nextKeyframe;
+ }
+ // shouldn't get here
+ return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).intValue();
+ }
+
+}
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/Keyframe.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/Keyframe.java
new file mode 100644
index 0000000000..ab76fa7f68
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/Keyframe.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.nineoldandroids.animation;
+
+import android.view.animation.Interpolator;
+
+/**
+ * This class holds a time/value pair for an animation. The Keyframe class is used
+ * by {@link ValueAnimator} to define the values that the animation target will have over the course
+ * of the animation. As the time proceeds from one keyframe to the other, the value of the
+ * target object will animate between the value at the previous keyframe and the value at the
+ * next keyframe. Each keyframe also holds an optional {@link TimeInterpolator}
+ * object, which defines the time interpolation over the intervalue preceding the keyframe.
+ *
+ *
The Keyframe class itself is abstract. The type-specific factory methods will return
+ * a subclass of Keyframe specific to the type of value being stored. This is done to improve
+ * performance when dealing with the most common cases (e.g., float and
+ * int values). Other types will fall into a more general Keyframe class that
+ * treats its values as Objects. Unless your animation requires dealing with a custom type
+ * or a data structure that needs to be animated directly (and evaluated using an implementation
+ * of {@link TypeEvaluator}), you should stick to using float and int as animations using those
+ * types have lower runtime overhead than other types.
+ */
+@SuppressWarnings("rawtypes")
+public abstract class Keyframe implements Cloneable {
+ /**
+ * The time at which mValue will hold true.
+ */
+ float mFraction;
+
+ /**
+ * The type of the value in this Keyframe. This type is determined at construction time,
+ * based on the type of the value object passed into the constructor.
+ */
+ Class mValueType;
+
+ /**
+ * The optional time interpolator for the interval preceding this keyframe. A null interpolator
+ * (the default) results in linear interpolation over the interval.
+ */
+ private /*Time*/Interpolator mInterpolator = null;
+
+ /**
+ * Flag to indicate whether this keyframe has a valid value. This flag is used when an
+ * animation first starts, to populate placeholder keyframes with real values derived
+ * from the target object.
+ */
+ boolean mHasValue = false;
+
+ /**
+ * Constructs a Keyframe object with the given time and value. The time defines the
+ * time, as a proportion of an overall animation's duration, at which the value will hold true
+ * for the animation. The value for the animation between keyframes will be calculated as
+ * an interpolation between the values at those keyframes.
+ *
+ * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+ * of time elapsed of the overall animation duration.
+ * @param value The value that the object will animate to as the animation time approaches
+ * the time in this keyframe, and the the value animated from as the time passes the time in
+ * this keyframe.
+ */
+ public static Keyframe ofInt(float fraction, int value) {
+ return new IntKeyframe(fraction, value);
+ }
+
+ /**
+ * Constructs a Keyframe object with the given time. The value at this time will be derived
+ * from the target object when the animation first starts (note that this implies that keyframes
+ * with no initial value must be used as part of an {@link ObjectAnimator}).
+ * The time defines the
+ * time, as a proportion of an overall animation's duration, at which the value will hold true
+ * for the animation. The value for the animation between keyframes will be calculated as
+ * an interpolation between the values at those keyframes.
+ *
+ * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+ * of time elapsed of the overall animation duration.
+ */
+ public static Keyframe ofInt(float fraction) {
+ return new IntKeyframe(fraction);
+ }
+
+ /**
+ * Constructs a Keyframe object with the given time and value. The time defines the
+ * time, as a proportion of an overall animation's duration, at which the value will hold true
+ * for the animation. The value for the animation between keyframes will be calculated as
+ * an interpolation between the values at those keyframes.
+ *
+ * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+ * of time elapsed of the overall animation duration.
+ * @param value The value that the object will animate to as the animation time approaches
+ * the time in this keyframe, and the the value animated from as the time passes the time in
+ * this keyframe.
+ */
+ public static Keyframe ofFloat(float fraction, float value) {
+ return new FloatKeyframe(fraction, value);
+ }
+
+ /**
+ * Constructs a Keyframe object with the given time. The value at this time will be derived
+ * from the target object when the animation first starts (note that this implies that keyframes
+ * with no initial value must be used as part of an {@link ObjectAnimator}).
+ * The time defines the
+ * time, as a proportion of an overall animation's duration, at which the value will hold true
+ * for the animation. The value for the animation between keyframes will be calculated as
+ * an interpolation between the values at those keyframes.
+ *
+ * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+ * of time elapsed of the overall animation duration.
+ */
+ public static Keyframe ofFloat(float fraction) {
+ return new FloatKeyframe(fraction);
+ }
+
+ /**
+ * Constructs a Keyframe object with the given time and value. The time defines the
+ * time, as a proportion of an overall animation's duration, at which the value will hold true
+ * for the animation. The value for the animation between keyframes will be calculated as
+ * an interpolation between the values at those keyframes.
+ *
+ * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+ * of time elapsed of the overall animation duration.
+ * @param value The value that the object will animate to as the animation time approaches
+ * the time in this keyframe, and the the value animated from as the time passes the time in
+ * this keyframe.
+ */
+ public static Keyframe ofObject(float fraction, Object value) {
+ return new ObjectKeyframe(fraction, value);
+ }
+
+ /**
+ * Constructs a Keyframe object with the given time. The value at this time will be derived
+ * from the target object when the animation first starts (note that this implies that keyframes
+ * with no initial value must be used as part of an {@link ObjectAnimator}).
+ * The time defines the
+ * time, as a proportion of an overall animation's duration, at which the value will hold true
+ * for the animation. The value for the animation between keyframes will be calculated as
+ * an interpolation between the values at those keyframes.
+ *
+ * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+ * of time elapsed of the overall animation duration.
+ */
+ public static Keyframe ofObject(float fraction) {
+ return new ObjectKeyframe(fraction, null);
+ }
+
+ /**
+ * Indicates whether this keyframe has a valid value. This method is called internally when
+ * an {@link ObjectAnimator} first starts; keyframes without values are assigned values at
+ * that time by deriving the value for the property from the target object.
+ *
+ * @return boolean Whether this object has a value assigned.
+ */
+ public boolean hasValue() {
+ return mHasValue;
+ }
+
+ /**
+ * Gets the value for this Keyframe.
+ *
+ * @return The value for this Keyframe.
+ */
+ public abstract Object getValue();
+
+ /**
+ * Sets the value for this Keyframe.
+ *
+ * @param value value for this Keyframe.
+ */
+ public abstract void setValue(Object value);
+
+ /**
+ * Gets the time for this keyframe, as a fraction of the overall animation duration.
+ *
+ * @return The time associated with this keyframe, as a fraction of the overall animation
+ * duration. This should be a value between 0 and 1.
+ */
+ public float getFraction() {
+ return mFraction;
+ }
+
+ /**
+ * Sets the time for this keyframe, as a fraction of the overall animation duration.
+ *
+ * @param fraction time associated with this keyframe, as a fraction of the overall animation
+ * duration. This should be a value between 0 and 1.
+ */
+ public void setFraction(float fraction) {
+ mFraction = fraction;
+ }
+
+ /**
+ * Gets the optional interpolator for this Keyframe. A value of null indicates
+ * that there is no interpolation, which is the same as linear interpolation.
+ *
+ * @return The optional interpolator for this Keyframe.
+ */
+ public /*Time*/Interpolator getInterpolator() {
+ return mInterpolator;
+ }
+
+ /**
+ * Sets the optional interpolator for this Keyframe. A value of null indicates
+ * that there is no interpolation, which is the same as linear interpolation.
+ *
+ * @return The optional interpolator for this Keyframe.
+ */
+ public void setInterpolator(/*Time*/Interpolator interpolator) {
+ mInterpolator = interpolator;
+ }
+
+ /**
+ * Gets the type of keyframe. This information is used by ValueAnimator to determine the type of
+ * {@link TypeEvaluator} to use when calculating values between keyframes. The type is based
+ * on the type of Keyframe created.
+ *
+ * @return The type of the value stored in the Keyframe.
+ */
+ public Class getType() {
+ return mValueType;
+ }
+
+ @Override
+ public abstract Keyframe clone();
+
+ /**
+ * This internal subclass is used for all types which are not int or float.
+ */
+ static class ObjectKeyframe extends Keyframe {
+
+ /**
+ * The value of the animation at the time mFraction.
+ */
+ Object mValue;
+
+ ObjectKeyframe(float fraction, Object value) {
+ mFraction = fraction;
+ mValue = value;
+ mHasValue = (value != null);
+ mValueType = mHasValue ? value.getClass() : Object.class;
+ }
+
+ public Object getValue() {
+ return mValue;
+ }
+
+ public void setValue(Object value) {
+ mValue = value;
+ mHasValue = (value != null);
+ }
+
+ @Override
+ public ObjectKeyframe clone() {
+ ObjectKeyframe kfClone = new ObjectKeyframe(getFraction(), mValue);
+ kfClone.setInterpolator(getInterpolator());
+ return kfClone;
+ }
+ }
+
+ /**
+ * Internal subclass used when the keyframe value is of type int.
+ */
+ static class IntKeyframe extends Keyframe {
+
+ /**
+ * The value of the animation at the time mFraction.
+ */
+ int mValue;
+
+ IntKeyframe(float fraction, int value) {
+ mFraction = fraction;
+ mValue = value;
+ mValueType = int.class;
+ mHasValue = true;
+ }
+
+ IntKeyframe(float fraction) {
+ mFraction = fraction;
+ mValueType = int.class;
+ }
+
+ public int getIntValue() {
+ return mValue;
+ }
+
+ public Object getValue() {
+ return mValue;
+ }
+
+ public void setValue(Object value) {
+ if (value != null && value.getClass() == Integer.class) {
+ mValue = ((Integer)value).intValue();
+ mHasValue = true;
+ }
+ }
+
+ @Override
+ public IntKeyframe clone() {
+ IntKeyframe kfClone = new IntKeyframe(getFraction(), mValue);
+ kfClone.setInterpolator(getInterpolator());
+ return kfClone;
+ }
+ }
+
+ /**
+ * Internal subclass used when the keyframe value is of type float.
+ */
+ static class FloatKeyframe extends Keyframe {
+ /**
+ * The value of the animation at the time mFraction.
+ */
+ float mValue;
+
+ FloatKeyframe(float fraction, float value) {
+ mFraction = fraction;
+ mValue = value;
+ mValueType = float.class;
+ mHasValue = true;
+ }
+
+ FloatKeyframe(float fraction) {
+ mFraction = fraction;
+ mValueType = float.class;
+ }
+
+ public float getFloatValue() {
+ return mValue;
+ }
+
+ public Object getValue() {
+ return mValue;
+ }
+
+ public void setValue(Object value) {
+ if (value != null && value.getClass() == Float.class) {
+ mValue = ((Float)value).floatValue();
+ mHasValue = true;
+ }
+ }
+
+ @Override
+ public FloatKeyframe clone() {
+ FloatKeyframe kfClone = new FloatKeyframe(getFraction(), mValue);
+ kfClone.setInterpolator(getInterpolator());
+ return kfClone;
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/KeyframeSet.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/KeyframeSet.java
new file mode 100644
index 0000000000..a71e1ad3cf
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/KeyframeSet.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.nineoldandroids.animation;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import android.view.animation.Interpolator;
+
+import com.actionbarsherlock.internal.nineoldandroids.animation.Keyframe.FloatKeyframe;
+import com.actionbarsherlock.internal.nineoldandroids.animation.Keyframe.IntKeyframe;
+import com.actionbarsherlock.internal.nineoldandroids.animation.Keyframe.ObjectKeyframe;
+
+/**
+ * This class holds a collection of Keyframe objects and is called by ValueAnimator to calculate
+ * values between those keyframes for a given animation. The class internal to the animation
+ * package because it is an implementation detail of how Keyframes are stored and used.
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+class KeyframeSet {
+
+ int mNumKeyframes;
+
+ Keyframe mFirstKeyframe;
+ Keyframe mLastKeyframe;
+ /*Time*/Interpolator mInterpolator; // only used in the 2-keyframe case
+ ArrayList mKeyframes; // only used when there are not 2 keyframes
+ TypeEvaluator mEvaluator;
+
+
+ public KeyframeSet(Keyframe... keyframes) {
+ mNumKeyframes = keyframes.length;
+ mKeyframes = new ArrayList();
+ mKeyframes.addAll(Arrays.asList(keyframes));
+ mFirstKeyframe = mKeyframes.get(0);
+ mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);
+ mInterpolator = mLastKeyframe.getInterpolator();
+ }
+
+ public static KeyframeSet ofInt(int... values) {
+ int numKeyframes = values.length;
+ IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
+ if (numKeyframes == 1) {
+ keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
+ keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
+ } else {
+ keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
+ for (int i = 1; i < numKeyframes; ++i) {
+ keyframes[i] = (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
+ }
+ }
+ return new IntKeyframeSet(keyframes);
+ }
+
+ public static KeyframeSet ofFloat(float... values) {
+ int numKeyframes = values.length;
+ FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
+ if (numKeyframes == 1) {
+ keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
+ keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
+ } else {
+ keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
+ for (int i = 1; i < numKeyframes; ++i) {
+ keyframes[i] = (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
+ }
+ }
+ return new FloatKeyframeSet(keyframes);
+ }
+
+ public static KeyframeSet ofKeyframe(Keyframe... keyframes) {
+ // if all keyframes of same primitive type, create the appropriate KeyframeSet
+ int numKeyframes = keyframes.length;
+ boolean hasFloat = false;
+ boolean hasInt = false;
+ boolean hasOther = false;
+ for (int i = 0; i < numKeyframes; ++i) {
+ if (keyframes[i] instanceof FloatKeyframe) {
+ hasFloat = true;
+ } else if (keyframes[i] instanceof IntKeyframe) {
+ hasInt = true;
+ } else {
+ hasOther = true;
+ }
+ }
+ if (hasFloat && !hasInt && !hasOther) {
+ FloatKeyframe floatKeyframes[] = new FloatKeyframe[numKeyframes];
+ for (int i = 0; i < numKeyframes; ++i) {
+ floatKeyframes[i] = (FloatKeyframe) keyframes[i];
+ }
+ return new FloatKeyframeSet(floatKeyframes);
+ } else if (hasInt && !hasFloat && !hasOther) {
+ IntKeyframe intKeyframes[] = new IntKeyframe[numKeyframes];
+ for (int i = 0; i < numKeyframes; ++i) {
+ intKeyframes[i] = (IntKeyframe) keyframes[i];
+ }
+ return new IntKeyframeSet(intKeyframes);
+ } else {
+ return new KeyframeSet(keyframes);
+ }
+ }
+
+ public static KeyframeSet ofObject(Object... values) {
+ int numKeyframes = values.length;
+ ObjectKeyframe keyframes[] = new ObjectKeyframe[Math.max(numKeyframes,2)];
+ if (numKeyframes == 1) {
+ keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f);
+ keyframes[1] = (ObjectKeyframe) Keyframe.ofObject(1f, values[0]);
+ } else {
+ keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f, values[0]);
+ for (int i = 1; i < numKeyframes; ++i) {
+ keyframes[i] = (ObjectKeyframe) Keyframe.ofObject((float) i / (numKeyframes - 1), values[i]);
+ }
+ }
+ return new KeyframeSet(keyframes);
+ }
+
+ /**
+ * Sets the TypeEvaluator to be used when calculating animated values. This object
+ * is required only for KeyframeSets that are not either IntKeyframeSet or FloatKeyframeSet,
+ * both of which assume their own evaluator to speed up calculations with those primitive
+ * types.
+ *
+ * @param evaluator The TypeEvaluator to be used to calculate animated values.
+ */
+ public void setEvaluator(TypeEvaluator evaluator) {
+ mEvaluator = evaluator;
+ }
+
+ @Override
+ public KeyframeSet clone() {
+ ArrayList keyframes = mKeyframes;
+ int numKeyframes = mKeyframes.size();
+ Keyframe[] newKeyframes = new Keyframe[numKeyframes];
+ for (int i = 0; i < numKeyframes; ++i) {
+ newKeyframes[i] = keyframes.get(i).clone();
+ }
+ KeyframeSet newSet = new KeyframeSet(newKeyframes);
+ return newSet;
+ }
+
+ /**
+ * Gets the animated value, given the elapsed fraction of the animation (interpolated by the
+ * animation's interpolator) and the evaluator used to calculate in-between values. This
+ * function maps the input fraction to the appropriate keyframe interval and a fraction
+ * between them and returns the interpolated value. Note that the input fraction may fall
+ * outside the [0-1] bounds, if the animation's interpolator made that happen (e.g., a
+ * spring interpolation that might send the fraction past 1.0). We handle this situation by
+ * just using the two keyframes at the appropriate end when the value is outside those bounds.
+ *
+ * @param fraction The elapsed fraction of the animation
+ * @return The animated value.
+ */
+ public Object getValue(float fraction) {
+
+ // Special-case optimization for the common case of only two keyframes
+ if (mNumKeyframes == 2) {
+ if (mInterpolator != null) {
+ fraction = mInterpolator.getInterpolation(fraction);
+ }
+ return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
+ mLastKeyframe.getValue());
+ }
+ if (fraction <= 0f) {
+ final Keyframe nextKeyframe = mKeyframes.get(1);
+ final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
+ if (interpolator != null) {
+ fraction = interpolator.getInterpolation(fraction);
+ }
+ final float prevFraction = mFirstKeyframe.getFraction();
+ float intervalFraction = (fraction - prevFraction) /
+ (nextKeyframe.getFraction() - prevFraction);
+ return mEvaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),
+ nextKeyframe.getValue());
+ } else if (fraction >= 1f) {
+ final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
+ final /*Time*/Interpolator interpolator = mLastKeyframe.getInterpolator();
+ if (interpolator != null) {
+ fraction = interpolator.getInterpolation(fraction);
+ }
+ final float prevFraction = prevKeyframe.getFraction();
+ float intervalFraction = (fraction - prevFraction) /
+ (mLastKeyframe.getFraction() - prevFraction);
+ return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
+ mLastKeyframe.getValue());
+ }
+ Keyframe prevKeyframe = mFirstKeyframe;
+ for (int i = 1; i < mNumKeyframes; ++i) {
+ Keyframe nextKeyframe = mKeyframes.get(i);
+ if (fraction < nextKeyframe.getFraction()) {
+ final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
+ if (interpolator != null) {
+ fraction = interpolator.getInterpolation(fraction);
+ }
+ final float prevFraction = prevKeyframe.getFraction();
+ float intervalFraction = (fraction - prevFraction) /
+ (nextKeyframe.getFraction() - prevFraction);
+ return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
+ nextKeyframe.getValue());
+ }
+ prevKeyframe = nextKeyframe;
+ }
+ // shouldn't reach here
+ return mLastKeyframe.getValue();
+ }
+
+ @Override
+ public String toString() {
+ String returnVal = " ";
+ for (int i = 0; i < mNumKeyframes; ++i) {
+ returnVal += mKeyframes.get(i).getValue() + " ";
+ }
+ return returnVal;
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/ObjectAnimator.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/ObjectAnimator.java
new file mode 100644
index 0000000000..21d15c02ac
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/ObjectAnimator.java
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.nineoldandroids.animation;
+
+import android.util.Log;
+//import android.util.Property;
+
+//import java.lang.reflect.Method;
+import java.util.ArrayList;
+
+/**
+ * This subclass of {@link ValueAnimator} provides support for animating properties on target objects.
+ * The constructors of this class take parameters to define the target object that will be animated
+ * as well as the name of the property that will be animated. Appropriate set/get functions
+ * are then determined internally and the animation will call these functions as necessary to
+ * animate the property.
+ *
+ * @see #setPropertyName(String)
+ *
+ */
+@SuppressWarnings("rawtypes")
+public final class ObjectAnimator extends ValueAnimator {
+ private static final boolean DBG = false;
+
+ // The target object on which the property exists, set in the constructor
+ private Object mTarget;
+
+ private String mPropertyName;
+
+ //private Property mProperty;
+
+ /**
+ * Sets the name of the property that will be animated. This name is used to derive
+ * a setter function that will be called to set animated values.
+ * For example, a property name of foo will result
+ * in a call to the function setFoo() on the target object. If either
+ * valueFrom or valueTo is null, then a getter function will
+ * also be derived and called.
+ *
+ *
For best performance of the mechanism that calls the setter function determined by the
+ * name of the property being animated, use float or int typed values,
+ * and make the setter function for those properties have a void return value. This
+ * will cause the code to take an optimized path for these constrained circumstances. Other
+ * property types and return types will work, but will have more overhead in processing
+ * the requests due to normal reflection mechanisms.
+ *
+ *
Note that the setter function derived from this property name
+ * must take the same parameter type as the
+ * valueFrom and valueTo properties, otherwise the call to
+ * the setter function will fail.
+ *
+ *
If this ObjectAnimator has been set up to animate several properties together,
+ * using more than one PropertyValuesHolder objects, then setting the propertyName simply
+ * sets the propertyName in the first of those PropertyValuesHolder objects.
+ *
+ * @param propertyName The name of the property being animated. Should not be null.
+ */
+ public void setPropertyName(String propertyName) {
+ // mValues could be null if this is being constructed piecemeal. Just record the
+ // propertyName to be used later when setValues() is called if so.
+ if (mValues != null) {
+ PropertyValuesHolder valuesHolder = mValues[0];
+ String oldName = valuesHolder.getPropertyName();
+ valuesHolder.setPropertyName(propertyName);
+ mValuesMap.remove(oldName);
+ mValuesMap.put(propertyName, valuesHolder);
+ }
+ mPropertyName = propertyName;
+ // New property/values/target should cause re-initialization prior to starting
+ mInitialized = false;
+ }
+
+ /**
+ * Sets the property that will be animated. Property objects will take precedence over
+ * properties specified by the {@link #setPropertyName(String)} method. Animations should
+ * be set up to use one or the other, not both.
+ *
+ * @param property The property being animated. Should not be null.
+ */
+ //public void setProperty(Property property) {
+ // // mValues could be null if this is being constructed piecemeal. Just record the
+ // // propertyName to be used later when setValues() is called if so.
+ // if (mValues != null) {
+ // PropertyValuesHolder valuesHolder = mValues[0];
+ // String oldName = valuesHolder.getPropertyName();
+ // valuesHolder.setProperty(property);
+ // mValuesMap.remove(oldName);
+ // mValuesMap.put(mPropertyName, valuesHolder);
+ // }
+ // if (mProperty != null) {
+ // mPropertyName = property.getName();
+ // }
+ // mProperty = property;
+ // // New property/values/target should cause re-initialization prior to starting
+ // mInitialized = false;
+ //}
+
+ /**
+ * Gets the name of the property that will be animated. This name will be used to derive
+ * a setter function that will be called to set animated values.
+ * For example, a property name of foo will result
+ * in a call to the function setFoo() on the target object. If either
+ * valueFrom or valueTo is null, then a getter function will
+ * also be derived and called.
+ */
+ public String getPropertyName() {
+ return mPropertyName;
+ }
+
+ /**
+ * Creates a new ObjectAnimator object. This default constructor is primarily for
+ * use internally; the other constructors which take parameters are more generally
+ * useful.
+ */
+ public ObjectAnimator() {
+ }
+
+ /**
+ * Private utility constructor that initializes the target object and name of the
+ * property being animated.
+ *
+ * @param target The object whose property is to be animated. This object should
+ * have a public method on it called setName(), where name is
+ * the value of the propertyName parameter.
+ * @param propertyName The name of the property being animated.
+ */
+ private ObjectAnimator(Object target, String propertyName) {
+ mTarget = target;
+ setPropertyName(propertyName);
+ }
+
+ /**
+ * Private utility constructor that initializes the target object and property being animated.
+ *
+ * @param target The object whose property is to be animated.
+ * @param property The property being animated.
+ */
+ //private ObjectAnimator(T target, Property property) {
+ // mTarget = target;
+ // setProperty(property);
+ //}
+
+ /**
+ * Constructs and returns an ObjectAnimator that animates between int values. A single
+ * value implies that that value is the one being animated to. Two values imply a starting
+ * and ending values. More than two values imply a starting value, values to animate through
+ * along the way, and an ending value (these values will be distributed evenly across
+ * the duration of the animation).
+ *
+ * @param target The object whose property is to be animated. This object should
+ * have a public method on it called setName(), where name is
+ * the value of the propertyName parameter.
+ * @param propertyName The name of the property being animated.
+ * @param values A set of values that the animation will animate between over time.
+ * @return An ObjectAnimator object that is set up to animate between the given values.
+ */
+ public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
+ ObjectAnimator anim = new ObjectAnimator(target, propertyName);
+ anim.setIntValues(values);
+ return anim;
+ }
+
+ /**
+ * Constructs and returns an ObjectAnimator that animates between int values. A single
+ * value implies that that value is the one being animated to. Two values imply a starting
+ * and ending values. More than two values imply a starting value, values to animate through
+ * along the way, and an ending value (these values will be distributed evenly across
+ * the duration of the animation).
+ *
+ * @param target The object whose property is to be animated.
+ * @param property The property being animated.
+ * @param values A set of values that the animation will animate between over time.
+ * @return An ObjectAnimator object that is set up to animate between the given values.
+ */
+ //public static ObjectAnimator ofInt(T target, Property property, int... values) {
+ // ObjectAnimator anim = new ObjectAnimator(target, property);
+ // anim.setIntValues(values);
+ // return anim;
+ //}
+
+ /**
+ * Constructs and returns an ObjectAnimator that animates between float values. A single
+ * value implies that that value is the one being animated to. Two values imply a starting
+ * and ending values. More than two values imply a starting value, values to animate through
+ * along the way, and an ending value (these values will be distributed evenly across
+ * the duration of the animation).
+ *
+ * @param target The object whose property is to be animated. This object should
+ * have a public method on it called setName(), where name is
+ * the value of the propertyName parameter.
+ * @param propertyName The name of the property being animated.
+ * @param values A set of values that the animation will animate between over time.
+ * @return An ObjectAnimator object that is set up to animate between the given values.
+ */
+ public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
+ ObjectAnimator anim = new ObjectAnimator(target, propertyName);
+ anim.setFloatValues(values);
+ return anim;
+ }
+
+ /**
+ * Constructs and returns an ObjectAnimator that animates between float values. A single
+ * value implies that that value is the one being animated to. Two values imply a starting
+ * and ending values. More than two values imply a starting value, values to animate through
+ * along the way, and an ending value (these values will be distributed evenly across
+ * the duration of the animation).
+ *
+ * @param target The object whose property is to be animated.
+ * @param property The property being animated.
+ * @param values A set of values that the animation will animate between over time.
+ * @return An ObjectAnimator object that is set up to animate between the given values.
+ */
+ //public static ObjectAnimator ofFloat(T target, Property property,
+ // float... values) {
+ // ObjectAnimator anim = new ObjectAnimator(target, property);
+ // anim.setFloatValues(values);
+ // return anim;
+ //}
+
+ /**
+ * Constructs and returns an ObjectAnimator that animates between Object values. A single
+ * value implies that that value is the one being animated to. Two values imply a starting
+ * and ending values. More than two values imply a starting value, values to animate through
+ * along the way, and an ending value (these values will be distributed evenly across
+ * the duration of the animation).
+ *
+ * @param target The object whose property is to be animated. This object should
+ * have a public method on it called setName(), where name is
+ * the value of the propertyName parameter.
+ * @param propertyName The name of the property being animated.
+ * @param evaluator A TypeEvaluator that will be called on each animation frame to
+ * provide the necessary interpolation between the Object values to derive the animated
+ * value.
+ * @param values A set of values that the animation will animate between over time.
+ * @return An ObjectAnimator object that is set up to animate between the given values.
+ */
+ public static ObjectAnimator ofObject(Object target, String propertyName,
+ TypeEvaluator evaluator, Object... values) {
+ ObjectAnimator anim = new ObjectAnimator(target, propertyName);
+ anim.setObjectValues(values);
+ anim.setEvaluator(evaluator);
+ return anim;
+ }
+
+ /**
+ * Constructs and returns an ObjectAnimator that animates between Object values. A single
+ * value implies that that value is the one being animated to. Two values imply a starting
+ * and ending values. More than two values imply a starting value, values to animate through
+ * along the way, and an ending value (these values will be distributed evenly across
+ * the duration of the animation).
+ *
+ * @param target The object whose property is to be animated.
+ * @param property The property being animated.
+ * @param evaluator A TypeEvaluator that will be called on each animation frame to
+ * provide the necessary interpolation between the Object values to derive the animated
+ * value.
+ * @param values A set of values that the animation will animate between over time.
+ * @return An ObjectAnimator object that is set up to animate between the given values.
+ */
+ //public static ObjectAnimator ofObject(T target, Property property,
+ // TypeEvaluator evaluator, V... values) {
+ // ObjectAnimator anim = new ObjectAnimator(target, property);
+ // anim.setObjectValues(values);
+ // anim.setEvaluator(evaluator);
+ // return anim;
+ //}
+
+ /**
+ * Constructs and returns an ObjectAnimator that animates between the sets of values specified
+ * in PropertyValueHolder objects. This variant should be used when animating
+ * several properties at once with the same ObjectAnimator, since PropertyValuesHolder allows
+ * you to associate a set of animation values with a property name.
+ *
+ * @param target The object whose property is to be animated. Depending on how the
+ * PropertyValuesObjects were constructed, the target object should either have the {@link
+ * android.util.Property} objects used to construct the PropertyValuesHolder objects or (if the
+ * PropertyValuesHOlder objects were created with property names) the target object should have
+ * public methods on it called setName(), where name is the name of
+ * the property passed in as the propertyName parameter for each of the
+ * PropertyValuesHolder objects.
+ * @param values A set of PropertyValuesHolder objects whose values will be animated between
+ * over time.
+ * @return An ObjectAnimator object that is set up to animate between the given values.
+ */
+ public static ObjectAnimator ofPropertyValuesHolder(Object target,
+ PropertyValuesHolder... values) {
+ ObjectAnimator anim = new ObjectAnimator();
+ anim.mTarget = target;
+ anim.setValues(values);
+ return anim;
+ }
+
+ @Override
+ public void setIntValues(int... values) {
+ if (mValues == null || mValues.length == 0) {
+ // No values yet - this animator is being constructed piecemeal. Init the values with
+ // whatever the current propertyName is
+ //if (mProperty != null) {
+ // setValues(PropertyValuesHolder.ofInt(mProperty, values));
+ //} else {
+ setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
+ //}
+ } else {
+ super.setIntValues(values);
+ }
+ }
+
+ @Override
+ public void setFloatValues(float... values) {
+ if (mValues == null || mValues.length == 0) {
+ // No values yet - this animator is being constructed piecemeal. Init the values with
+ // whatever the current propertyName is
+ //if (mProperty != null) {
+ // setValues(PropertyValuesHolder.ofFloat(mProperty, values));
+ //} else {
+ setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
+ //}
+ } else {
+ super.setFloatValues(values);
+ }
+ }
+
+ @Override
+ public void setObjectValues(Object... values) {
+ if (mValues == null || mValues.length == 0) {
+ // No values yet - this animator is being constructed piecemeal. Init the values with
+ // whatever the current propertyName is
+ //if (mProperty != null) {
+ // setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator)null, values));
+ //} else {
+ setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator)null, values));
+ //}
+ } else {
+ super.setObjectValues(values);
+ }
+ }
+
+ @Override
+ public void start() {
+ if (DBG) {
+ Log.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration());
+ for (int i = 0; i < mValues.length; ++i) {
+ PropertyValuesHolder pvh = mValues[i];
+ ArrayList keyframes = pvh.mKeyframeSet.mKeyframes;
+ Log.d("ObjectAnimator", " Values[" + i + "]: " +
+ pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " +
+ keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue());
+ }
+ }
+ super.start();
+ }
+
+ /**
+ * This function is called immediately before processing the first animation
+ * frame of an animation. If there is a nonzero startDelay, the
+ * function is called after that delay ends.
+ * It takes care of the final initialization steps for the
+ * animation. This includes setting mEvaluator, if the user has not yet
+ * set it up, and the setter/getter methods, if the user did not supply
+ * them.
+ *
+ *
Overriders of this method should call the superclass method to cause
+ * internal mechanisms to be set up correctly.
+ */
+ @Override
+ void initAnimation() {
+ if (!mInitialized) {
+ // mValueType may change due to setter/getter setup; do this before calling super.init(),
+ // which uses mValueType to set up the default type evaluator.
+ int numValues = mValues.length;
+ for (int i = 0; i < numValues; ++i) {
+ mValues[i].setupSetterAndGetter(mTarget);
+ }
+ super.initAnimation();
+ }
+ }
+
+ /**
+ * Sets the length of the animation. The default duration is 300 milliseconds.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ * @return ObjectAnimator The object called with setDuration(). This return
+ * value makes it easier to compose statements together that construct and then set the
+ * duration, as in
+ * ObjectAnimator.ofInt(target, propertyName, 0, 10).setDuration(500).start().
+ */
+ @Override
+ public ObjectAnimator setDuration(long duration) {
+ super.setDuration(duration);
+ return this;
+ }
+
+
+ /**
+ * The target object whose property will be animated by this animation
+ *
+ * @return The object being animated
+ */
+ public Object getTarget() {
+ return mTarget;
+ }
+
+ /**
+ * Sets the target object whose property will be animated by this animation
+ *
+ * @param target The object being animated
+ */
+ @Override
+ public void setTarget(Object target) {
+ if (mTarget != target) {
+ final Object oldTarget = mTarget;
+ mTarget = target;
+ if (oldTarget != null && target != null && oldTarget.getClass() == target.getClass()) {
+ return;
+ }
+ // New target type should cause re-initialization prior to starting
+ mInitialized = false;
+ }
+ }
+
+ @Override
+ public void setupStartValues() {
+ initAnimation();
+ int numValues = mValues.length;
+ for (int i = 0; i < numValues; ++i) {
+ mValues[i].setupStartValue(mTarget);
+ }
+ }
+
+ @Override
+ public void setupEndValues() {
+ initAnimation();
+ int numValues = mValues.length;
+ for (int i = 0; i < numValues; ++i) {
+ mValues[i].setupEndValue(mTarget);
+ }
+ }
+
+ /**
+ * This method is called with the elapsed fraction of the animation during every
+ * animation frame. This function turns the elapsed fraction into an interpolated fraction
+ * and then into an animated value (from the evaluator. The function is called mostly during
+ * animation updates, but it is also called when the end()
+ * function is called, to set the final value on the property.
+ *
+ *
Overrides of this method must call the superclass to perform the calculation
+ * of the animated value.
+ *
+ * @param fraction The elapsed fraction of the animation.
+ */
+ @Override
+ void animateValue(float fraction) {
+ super.animateValue(fraction);
+ int numValues = mValues.length;
+ for (int i = 0; i < numValues; ++i) {
+ mValues[i].setAnimatedValue(mTarget);
+ }
+ }
+
+ @Override
+ public ObjectAnimator clone() {
+ final ObjectAnimator anim = (ObjectAnimator) super.clone();
+ return anim;
+ }
+
+ @Override
+ public String toString() {
+ String returnVal = "ObjectAnimator@" + Integer.toHexString(hashCode()) + ", target " +
+ mTarget;
+ if (mValues != null) {
+ for (int i = 0; i < mValues.length; ++i) {
+ returnVal += "\n " + mValues[i].toString();
+ }
+ }
+ return returnVal;
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/PropertyValuesHolder.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/PropertyValuesHolder.java
new file mode 100644
index 0000000000..84f7504abc
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/PropertyValuesHolder.java
@@ -0,0 +1,1012 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.nineoldandroids.animation;
+
+//import android.util.FloatProperty;
+//import android.util.IntProperty;
+import android.util.Log;
+//import android.util.Property;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * This class holds information about a property and the values that that property
+ * should take on during an animation. PropertyValuesHolder objects can be used to create
+ * animations with ValueAnimator or ObjectAnimator that operate on several different properties
+ * in parallel.
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class PropertyValuesHolder implements Cloneable {
+
+ /**
+ * The name of the property associated with the values. This need not be a real property,
+ * unless this object is being used with ObjectAnimator. But this is the name by which
+ * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator.
+ */
+ String mPropertyName;
+
+ /**
+ * @hide
+ */
+ //protected Property mProperty;
+
+ /**
+ * The setter function, if needed. ObjectAnimator hands off this functionality to
+ * PropertyValuesHolder, since it holds all of the per-property information. This
+ * property is automatically
+ * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
+ */
+ Method mSetter = null;
+
+ /**
+ * The getter function, if needed. ObjectAnimator hands off this functionality to
+ * PropertyValuesHolder, since it holds all of the per-property information. This
+ * property is automatically
+ * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
+ * The getter is only derived and used if one of the values is null.
+ */
+ private Method mGetter = null;
+
+ /**
+ * The type of values supplied. This information is used both in deriving the setter/getter
+ * functions and in deriving the type of TypeEvaluator.
+ */
+ Class mValueType;
+
+ /**
+ * The set of keyframes (time/value pairs) that define this animation.
+ */
+ KeyframeSet mKeyframeSet = null;
+
+
+ // type evaluators for the primitive types handled by this implementation
+ private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
+ private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
+
+ // We try several different types when searching for appropriate setter/getter functions.
+ // The caller may have supplied values in a type that does not match the setter/getter
+ // functions (such as the integers 0 and 1 to represent floating point values for alpha).
+ // Also, the use of generics in constructors means that we end up with the Object versions
+ // of primitive types (Float vs. float). But most likely, the setter/getter functions
+ // will take primitive types instead.
+ // So we supply an ordered array of other types to try before giving up.
+ private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class,
+ Double.class, Integer.class};
+ private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class,
+ Float.class, Double.class};
+ private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class,
+ Float.class, Integer.class};
+
+ // These maps hold all property entries for a particular class. This map
+ // is used to speed up property/setter/getter lookups for a given class/property
+ // combination. No need to use reflection on the combination more than once.
+ private static final HashMap> sSetterPropertyMap =
+ new HashMap>();
+ private static final HashMap> sGetterPropertyMap =
+ new HashMap>();
+
+ // This lock is used to ensure that only one thread is accessing the property maps
+ // at a time.
+ final ReentrantReadWriteLock mPropertyMapLock = new ReentrantReadWriteLock();
+
+ // Used to pass single value to varargs parameter in setter invocation
+ final Object[] mTmpValueArray = new Object[1];
+
+ /**
+ * The type evaluator used to calculate the animated values. This evaluator is determined
+ * automatically based on the type of the start/end objects passed into the constructor,
+ * but the system only knows about the primitive types int and float. Any other
+ * type will need to set the evaluator to a custom evaluator for that type.
+ */
+ private TypeEvaluator mEvaluator;
+
+ /**
+ * The value most recently calculated by calculateValue(). This is set during
+ * that function and might be retrieved later either by ValueAnimator.animatedValue() or
+ * by the property-setting logic in ObjectAnimator.animatedValue().
+ */
+ private Object mAnimatedValue;
+
+ /**
+ * Internal utility constructor, used by the factory methods to set the property name.
+ * @param propertyName The name of the property for this holder.
+ */
+ private PropertyValuesHolder(String propertyName) {
+ mPropertyName = propertyName;
+ }
+
+ /**
+ * Internal utility constructor, used by the factory methods to set the property.
+ * @param property The property for this holder.
+ */
+ //private PropertyValuesHolder(Property property) {
+ // mProperty = property;
+ // if (property != null) {
+ // mPropertyName = property.getName();
+ // }
+ //}
+
+ /**
+ * Constructs and returns a PropertyValuesHolder with a given property name and
+ * set of int values.
+ * @param propertyName The name of the property being animated.
+ * @param values The values that the named property will animate between.
+ * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
+ */
+ public static PropertyValuesHolder ofInt(String propertyName, int... values) {
+ return new IntPropertyValuesHolder(propertyName, values);
+ }
+
+ /**
+ * Constructs and returns a PropertyValuesHolder with a given property and
+ * set of int values.
+ * @param property The property being animated. Should not be null.
+ * @param values The values that the property will animate between.
+ * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
+ */
+ //public static PropertyValuesHolder ofInt(Property, Integer> property, int... values) {
+ // return new IntPropertyValuesHolder(property, values);
+ //}
+
+ /**
+ * Constructs and returns a PropertyValuesHolder with a given property name and
+ * set of float values.
+ * @param propertyName The name of the property being animated.
+ * @param values The values that the named property will animate between.
+ * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
+ */
+ public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
+ return new FloatPropertyValuesHolder(propertyName, values);
+ }
+
+ /**
+ * Constructs and returns a PropertyValuesHolder with a given property and
+ * set of float values.
+ * @param property The property being animated. Should not be null.
+ * @param values The values that the property will animate between.
+ * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
+ */
+ //public static PropertyValuesHolder ofFloat(Property, Float> property, float... values) {
+ // return new FloatPropertyValuesHolder(property, values);
+ //}
+
+ /**
+ * Constructs and returns a PropertyValuesHolder with a given property name and
+ * set of Object values. This variant also takes a TypeEvaluator because the system
+ * cannot automatically interpolate between objects of unknown type.
+ *
+ * @param propertyName The name of the property being animated.
+ * @param evaluator A TypeEvaluator that will be called on each animation frame to
+ * provide the necessary interpolation between the Object values to derive the animated
+ * value.
+ * @param values The values that the named property will animate between.
+ * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
+ */
+ public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
+ Object... values) {
+ PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
+ pvh.setObjectValues(values);
+ pvh.setEvaluator(evaluator);
+ return pvh;
+ }
+
+ /**
+ * Constructs and returns a PropertyValuesHolder with a given property and
+ * set of Object values. This variant also takes a TypeEvaluator because the system
+ * cannot automatically interpolate between objects of unknown type.
+ *
+ * @param property The property being animated. Should not be null.
+ * @param evaluator A TypeEvaluator that will be called on each animation frame to
+ * provide the necessary interpolation between the Object values to derive the animated
+ * value.
+ * @param values The values that the property will animate between.
+ * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
+ */
+ //public static PropertyValuesHolder ofObject(Property property,
+ // TypeEvaluator evaluator, V... values) {
+ // PropertyValuesHolder pvh = new PropertyValuesHolder(property);
+ // pvh.setObjectValues(values);
+ // pvh.setEvaluator(evaluator);
+ // return pvh;
+ //}
+
+ /**
+ * Constructs and returns a PropertyValuesHolder object with the specified property name and set
+ * of values. These values can be of any type, but the type should be consistent so that
+ * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
+ * the common type.
+ *
If there is only one value, it is assumed to be the end value of an animation,
+ * and an initial value will be derived, if possible, by calling a getter function
+ * on the object. Also, if any value is null, the value will be filled in when the animation
+ * starts in the same way. This mechanism of automatically getting null values only works
+ * if the PropertyValuesHolder object is used in conjunction
+ * {@link ObjectAnimator}, and with a getter function
+ * derived automatically from propertyName, since otherwise PropertyValuesHolder has
+ * no way of determining what the value should be.
+ * @param propertyName The name of the property associated with this set of values. This
+ * can be the actual property name to be used when using a ObjectAnimator object, or
+ * just a name used to get animated values, such as if this object is used with an
+ * ValueAnimator object.
+ * @param values The set of values to animate between.
+ */
+ public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
+ KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
+ if (keyframeSet instanceof IntKeyframeSet) {
+ return new IntPropertyValuesHolder(propertyName, (IntKeyframeSet) keyframeSet);
+ } else if (keyframeSet instanceof FloatKeyframeSet) {
+ return new FloatPropertyValuesHolder(propertyName, (FloatKeyframeSet) keyframeSet);
+ }
+ else {
+ PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
+ pvh.mKeyframeSet = keyframeSet;
+ pvh.mValueType = values[0].getType();
+ return pvh;
+ }
+ }
+
+ /**
+ * Constructs and returns a PropertyValuesHolder object with the specified property and set
+ * of values. These values can be of any type, but the type should be consistent so that
+ * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
+ * the common type.
+ *
If there is only one value, it is assumed to be the end value of an animation,
+ * and an initial value will be derived, if possible, by calling the property's
+ * {@link android.util.Property#get(Object)} function.
+ * Also, if any value is null, the value will be filled in when the animation
+ * starts in the same way. This mechanism of automatically getting null values only works
+ * if the PropertyValuesHolder object is used in conjunction with
+ * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has
+ * no way of determining what the value should be.
+ * @param property The property associated with this set of values. Should not be null.
+ * @param values The set of values to animate between.
+ */
+ //public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) {
+ // KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
+ // if (keyframeSet instanceof IntKeyframeSet) {
+ // return new IntPropertyValuesHolder(property, (IntKeyframeSet) keyframeSet);
+ // } else if (keyframeSet instanceof FloatKeyframeSet) {
+ // return new FloatPropertyValuesHolder(property, (FloatKeyframeSet) keyframeSet);
+ // }
+ // else {
+ // PropertyValuesHolder pvh = new PropertyValuesHolder(property);
+ // pvh.mKeyframeSet = keyframeSet;
+ // pvh.mValueType = ((Keyframe)values[0]).getType();
+ // return pvh;
+ // }
+ //}
+
+ /**
+ * Set the animated values for this object to this set of ints.
+ * If there is only one value, it is assumed to be the end value of an animation,
+ * and an initial value will be derived, if possible, by calling a getter function
+ * on the object. Also, if any value is null, the value will be filled in when the animation
+ * starts in the same way. This mechanism of automatically getting null values only works
+ * if the PropertyValuesHolder object is used in conjunction
+ * {@link ObjectAnimator}, and with a getter function
+ * derived automatically from propertyName, since otherwise PropertyValuesHolder has
+ * no way of determining what the value should be.
+ *
+ * @param values One or more values that the animation will animate between.
+ */
+ public void setIntValues(int... values) {
+ mValueType = int.class;
+ mKeyframeSet = KeyframeSet.ofInt(values);
+ }
+
+ /**
+ * Set the animated values for this object to this set of floats.
+ * If there is only one value, it is assumed to be the end value of an animation,
+ * and an initial value will be derived, if possible, by calling a getter function
+ * on the object. Also, if any value is null, the value will be filled in when the animation
+ * starts in the same way. This mechanism of automatically getting null values only works
+ * if the PropertyValuesHolder object is used in conjunction
+ * {@link ObjectAnimator}, and with a getter function
+ * derived automatically from propertyName, since otherwise PropertyValuesHolder has
+ * no way of determining what the value should be.
+ *
+ * @param values One or more values that the animation will animate between.
+ */
+ public void setFloatValues(float... values) {
+ mValueType = float.class;
+ mKeyframeSet = KeyframeSet.ofFloat(values);
+ }
+
+ /**
+ * Set the animated values for this object to this set of Keyframes.
+ *
+ * @param values One or more values that the animation will animate between.
+ */
+ public void setKeyframes(Keyframe... values) {
+ int numKeyframes = values.length;
+ Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
+ mValueType = values[0].getType();
+ for (int i = 0; i < numKeyframes; ++i) {
+ keyframes[i] = values[i];
+ }
+ mKeyframeSet = new KeyframeSet(keyframes);
+ }
+
+ /**
+ * Set the animated values for this object to this set of Objects.
+ * If there is only one value, it is assumed to be the end value of an animation,
+ * and an initial value will be derived, if possible, by calling a getter function
+ * on the object. Also, if any value is null, the value will be filled in when the animation
+ * starts in the same way. This mechanism of automatically getting null values only works
+ * if the PropertyValuesHolder object is used in conjunction
+ * {@link ObjectAnimator}, and with a getter function
+ * derived automatically from propertyName, since otherwise PropertyValuesHolder has
+ * no way of determining what the value should be.
+ *
+ * @param values One or more values that the animation will animate between.
+ */
+ public void setObjectValues(Object... values) {
+ mValueType = values[0].getClass();
+ mKeyframeSet = KeyframeSet.ofObject(values);
+ }
+
+ /**
+ * Determine the setter or getter function using the JavaBeans convention of setFoo or
+ * getFoo for a property named 'foo'. This function figures out what the name of the
+ * function should be and uses reflection to find the Method with that name on the
+ * target object.
+ *
+ * @param targetClass The class to search for the method
+ * @param prefix "set" or "get", depending on whether we need a setter or getter.
+ * @param valueType The type of the parameter (in the case of a setter). This type
+ * is derived from the values set on this PropertyValuesHolder. This type is used as
+ * a first guess at the parameter type, but we check for methods with several different
+ * types to avoid problems with slight mis-matches between supplied values and actual
+ * value types used on the setter.
+ * @return Method the method associated with mPropertyName.
+ */
+ private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
+ // TODO: faster implementation...
+ Method returnVal = null;
+ String methodName = getMethodName(prefix, mPropertyName);
+ Class args[] = null;
+ if (valueType == null) {
+ try {
+ returnVal = targetClass.getMethod(methodName, args);
+ } catch (NoSuchMethodException e) {
+ Log.e("PropertyValuesHolder", targetClass.getSimpleName() + " - " +
+ "Couldn't find no-arg method for property " + mPropertyName + ": " + e);
+ }
+ } else {
+ args = new Class[1];
+ Class typeVariants[];
+ if (mValueType.equals(Float.class)) {
+ typeVariants = FLOAT_VARIANTS;
+ } else if (mValueType.equals(Integer.class)) {
+ typeVariants = INTEGER_VARIANTS;
+ } else if (mValueType.equals(Double.class)) {
+ typeVariants = DOUBLE_VARIANTS;
+ } else {
+ typeVariants = new Class[1];
+ typeVariants[0] = mValueType;
+ }
+ for (Class typeVariant : typeVariants) {
+ args[0] = typeVariant;
+ try {
+ returnVal = targetClass.getMethod(methodName, args);
+ // change the value type to suit
+ mValueType = typeVariant;
+ return returnVal;
+ } catch (NoSuchMethodException e) {
+ // Swallow the error and keep trying other variants
+ }
+ }
+ // If we got here, then no appropriate function was found
+ Log.e("PropertyValuesHolder",
+ "Couldn't find " + prefix + "ter property " + mPropertyName +
+ " for " + targetClass.getSimpleName() +
+ " with value type "+ mValueType);
+ }
+
+ return returnVal;
+ }
+
+
+ /**
+ * Returns the setter or getter requested. This utility function checks whether the
+ * requested method exists in the propertyMapMap cache. If not, it calls another
+ * utility function to request the Method from the targetClass directly.
+ * @param targetClass The Class on which the requested method should exist.
+ * @param propertyMapMap The cache of setters/getters derived so far.
+ * @param prefix "set" or "get", for the setter or getter.
+ * @param valueType The type of parameter passed into the method (null for getter).
+ * @return Method the method associated with mPropertyName.
+ */
+ private Method setupSetterOrGetter(Class targetClass,
+ HashMap> propertyMapMap,
+ String prefix, Class valueType) {
+ Method setterOrGetter = null;
+ try {
+ // Have to lock property map prior to reading it, to guard against
+ // another thread putting something in there after we've checked it
+ // but before we've added an entry to it
+ mPropertyMapLock.writeLock().lock();
+ HashMap propertyMap = propertyMapMap.get(targetClass);
+ if (propertyMap != null) {
+ setterOrGetter = propertyMap.get(mPropertyName);
+ }
+ if (setterOrGetter == null) {
+ setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
+ if (propertyMap == null) {
+ propertyMap = new HashMap();
+ propertyMapMap.put(targetClass, propertyMap);
+ }
+ propertyMap.put(mPropertyName, setterOrGetter);
+ }
+ } finally {
+ mPropertyMapLock.writeLock().unlock();
+ }
+ return setterOrGetter;
+ }
+
+ /**
+ * Utility function to get the setter from targetClass
+ * @param targetClass The Class on which the requested method should exist.
+ */
+ void setupSetter(Class targetClass) {
+ mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType);
+ }
+
+ /**
+ * Utility function to get the getter from targetClass
+ */
+ private void setupGetter(Class targetClass) {
+ mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
+ }
+
+ /**
+ * Internal function (called from ObjectAnimator) to set up the setter and getter
+ * prior to running the animation. If the setter has not been manually set for this
+ * object, it will be derived automatically given the property name, target object, and
+ * types of values supplied. If no getter has been set, it will be supplied iff any of the
+ * supplied values was null. If there is a null value, then the getter (supplied or derived)
+ * will be called to set those null values to the current value of the property
+ * on the target object.
+ * @param target The object on which the setter (and possibly getter) exist.
+ */
+ void setupSetterAndGetter(Object target) {
+ //if (mProperty != null) {
+ // // check to make sure that mProperty is on the class of target
+ // try {
+ // Object testValue = mProperty.get(target);
+ // for (Keyframe kf : mKeyframeSet.mKeyframes) {
+ // if (!kf.hasValue()) {
+ // kf.setValue(mProperty.get(target));
+ // }
+ // }
+ // return;
+ // } catch (ClassCastException e) {
+ // Log.e("PropertyValuesHolder","No such property (" + mProperty.getName() +
+ // ") on target object " + target + ". Trying reflection instead");
+ // mProperty = null;
+ // }
+ //}
+ Class targetClass = target.getClass();
+ if (mSetter == null) {
+ setupSetter(targetClass);
+ }
+ for (Keyframe kf : mKeyframeSet.mKeyframes) {
+ if (!kf.hasValue()) {
+ if (mGetter == null) {
+ setupGetter(targetClass);
+ }
+ try {
+ kf.setValue(mGetter.invoke(target));
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ }
+ }
+ }
+ }
+
+ /**
+ * Utility function to set the value stored in a particular Keyframe. The value used is
+ * whatever the value is for the property name specified in the keyframe on the target object.
+ *
+ * @param target The target object from which the current value should be extracted.
+ * @param kf The keyframe which holds the property name and value.
+ */
+ private void setupValue(Object target, Keyframe kf) {
+ //if (mProperty != null) {
+ // kf.setValue(mProperty.get(target));
+ //}
+ try {
+ if (mGetter == null) {
+ Class targetClass = target.getClass();
+ setupGetter(targetClass);
+ }
+ kf.setValue(mGetter.invoke(target));
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ }
+ }
+
+ /**
+ * This function is called by ObjectAnimator when setting the start values for an animation.
+ * The start values are set according to the current values in the target object. The
+ * property whose value is extracted is whatever is specified by the propertyName of this
+ * PropertyValuesHolder object.
+ *
+ * @param target The object which holds the start values that should be set.
+ */
+ void setupStartValue(Object target) {
+ setupValue(target, mKeyframeSet.mKeyframes.get(0));
+ }
+
+ /**
+ * This function is called by ObjectAnimator when setting the end values for an animation.
+ * The end values are set according to the current values in the target object. The
+ * property whose value is extracted is whatever is specified by the propertyName of this
+ * PropertyValuesHolder object.
+ *
+ * @param target The object which holds the start values that should be set.
+ */
+ void setupEndValue(Object target) {
+ setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1));
+ }
+
+ @Override
+ public PropertyValuesHolder clone() {
+ try {
+ PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
+ newPVH.mPropertyName = mPropertyName;
+ //newPVH.mProperty = mProperty;
+ newPVH.mKeyframeSet = mKeyframeSet.clone();
+ newPVH.mEvaluator = mEvaluator;
+ return newPVH;
+ } catch (CloneNotSupportedException e) {
+ // won't reach here
+ return null;
+ }
+ }
+
+ /**
+ * Internal function to set the value on the target object, using the setter set up
+ * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
+ * to handle turning the value calculated by ValueAnimator into a value set on the object
+ * according to the name of the property.
+ * @param target The target object on which the value is set
+ */
+ void setAnimatedValue(Object target) {
+ //if (mProperty != null) {
+ // mProperty.set(target, getAnimatedValue());
+ //}
+ if (mSetter != null) {
+ try {
+ mTmpValueArray[0] = getAnimatedValue();
+ mSetter.invoke(target, mTmpValueArray);
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ }
+ }
+ }
+
+ /**
+ * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
+ * to calculate animated values.
+ */
+ void init() {
+ if (mEvaluator == null) {
+ // We already handle int and float automatically, but not their Object
+ // equivalents
+ mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
+ (mValueType == Float.class) ? sFloatEvaluator :
+ null;
+ }
+ if (mEvaluator != null) {
+ // KeyframeSet knows how to evaluate the common types - only give it a custom
+ // evaluator if one has been set on this class
+ mKeyframeSet.setEvaluator(mEvaluator);
+ }
+ }
+
+ /**
+ * The TypeEvaluator will the automatically determined based on the type of values
+ * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
+ * desired. This may be important in cases where either the type of the values supplied
+ * do not match the way that they should be interpolated between, or if the values
+ * are of a custom type or one not currently understood by the animation system. Currently,
+ * only values of type float and int (and their Object equivalents: Float
+ * and Integer) are correctly interpolated; all other types require setting a TypeEvaluator.
+ * @param evaluator
+ */
+ public void setEvaluator(TypeEvaluator evaluator) {
+ mEvaluator = evaluator;
+ mKeyframeSet.setEvaluator(evaluator);
+ }
+
+ /**
+ * Function used to calculate the value according to the evaluator set up for
+ * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
+ *
+ * @param fraction The elapsed, interpolated fraction of the animation.
+ */
+ void calculateValue(float fraction) {
+ mAnimatedValue = mKeyframeSet.getValue(fraction);
+ }
+
+ /**
+ * Sets the name of the property that will be animated. This name is used to derive
+ * a setter function that will be called to set animated values.
+ * For example, a property name of foo will result
+ * in a call to the function setFoo() on the target object. If either
+ * valueFrom or valueTo is null, then a getter function will
+ * also be derived and called.
+ *
+ *
Note that the setter function derived from this property name
+ * must take the same parameter type as the
+ * valueFrom and valueTo properties, otherwise the call to
+ * the setter function will fail.
+ *
+ * @param propertyName The name of the property being animated.
+ */
+ public void setPropertyName(String propertyName) {
+ mPropertyName = propertyName;
+ }
+
+ /**
+ * Sets the property that will be animated.
+ *
+ *
Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
+ * must exist on the target object specified in that ObjectAnimator.
+ *
+ * @param property The property being animated.
+ */
+ //public void setProperty(Property property) {
+ // mProperty = property;
+ //}
+
+ /**
+ * Gets the name of the property that will be animated. This name will be used to derive
+ * a setter function that will be called to set animated values.
+ * For example, a property name of foo will result
+ * in a call to the function setFoo() on the target object. If either
+ * valueFrom or valueTo is null, then a getter function will
+ * also be derived and called.
+ */
+ public String getPropertyName() {
+ return mPropertyName;
+ }
+
+ /**
+ * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
+ * most recently calculated in calculateValue().
+ * @return
+ */
+ Object getAnimatedValue() {
+ return mAnimatedValue;
+ }
+
+ @Override
+ public String toString() {
+ return mPropertyName + ": " + mKeyframeSet.toString();
+ }
+
+ /**
+ * Utility method to derive a setter/getter method name from a property name, where the
+ * prefix is typically "set" or "get" and the first letter of the property name is
+ * capitalized.
+ *
+ * @param prefix The precursor to the method name, before the property name begins, typically
+ * "set" or "get".
+ * @param propertyName The name of the property that represents the bulk of the method name
+ * after the prefix. The first letter of this word will be capitalized in the resulting
+ * method name.
+ * @return String the property name converted to a method name according to the conventions
+ * specified above.
+ */
+ static String getMethodName(String prefix, String propertyName) {
+ if (propertyName == null || propertyName.length() == 0) {
+ // shouldn't get here
+ return prefix;
+ }
+ char firstLetter = Character.toUpperCase(propertyName.charAt(0));
+ String theRest = propertyName.substring(1);
+ return prefix + firstLetter + theRest;
+ }
+
+ static class IntPropertyValuesHolder extends PropertyValuesHolder {
+
+ // Cache JNI functions to avoid looking them up twice
+ //private static final HashMap> sJNISetterPropertyMap =
+ // new HashMap>();
+ //int mJniSetter;
+ //private IntProperty mIntProperty;
+
+ IntKeyframeSet mIntKeyframeSet;
+ int mIntAnimatedValue;
+
+ public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) {
+ super(propertyName);
+ mValueType = int.class;
+ mKeyframeSet = keyframeSet;
+ mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
+ }
+
+ //public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) {
+ // super(property);
+ // mValueType = int.class;
+ // mKeyframeSet = keyframeSet;
+ // mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
+ // if (property instanceof IntProperty) {
+ // mIntProperty = (IntProperty) mProperty;
+ // }
+ //}
+
+ public IntPropertyValuesHolder(String propertyName, int... values) {
+ super(propertyName);
+ setIntValues(values);
+ }
+
+ //public IntPropertyValuesHolder(Property property, int... values) {
+ // super(property);
+ // setIntValues(values);
+ // if (property instanceof IntProperty) {
+ // mIntProperty = (IntProperty) mProperty;
+ // }
+ //}
+
+ @Override
+ public void setIntValues(int... values) {
+ super.setIntValues(values);
+ mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
+ }
+
+ @Override
+ void calculateValue(float fraction) {
+ mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction);
+ }
+
+ @Override
+ Object getAnimatedValue() {
+ return mIntAnimatedValue;
+ }
+
+ @Override
+ public IntPropertyValuesHolder clone() {
+ IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
+ newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet;
+ return newPVH;
+ }
+
+ /**
+ * Internal function to set the value on the target object, using the setter set up
+ * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
+ * to handle turning the value calculated by ValueAnimator into a value set on the object
+ * according to the name of the property.
+ * @param target The target object on which the value is set
+ */
+ @Override
+ void setAnimatedValue(Object target) {
+ //if (mIntProperty != null) {
+ // mIntProperty.setValue(target, mIntAnimatedValue);
+ // return;
+ //}
+ //if (mProperty != null) {
+ // mProperty.set(target, mIntAnimatedValue);
+ // return;
+ //}
+ //if (mJniSetter != 0) {
+ // nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
+ // return;
+ //}
+ if (mSetter != null) {
+ try {
+ mTmpValueArray[0] = mIntAnimatedValue;
+ mSetter.invoke(target, mTmpValueArray);
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ }
+ }
+ }
+
+ @Override
+ void setupSetter(Class targetClass) {
+ //if (mProperty != null) {
+ // return;
+ //}
+ // Check new static hashmap for setter method
+ //try {
+ // mPropertyMapLock.writeLock().lock();
+ // HashMap propertyMap = sJNISetterPropertyMap.get(targetClass);
+ // if (propertyMap != null) {
+ // Integer mJniSetterInteger = propertyMap.get(mPropertyName);
+ // if (mJniSetterInteger != null) {
+ // mJniSetter = mJniSetterInteger;
+ // }
+ // }
+ // if (mJniSetter == 0) {
+ // String methodName = getMethodName("set", mPropertyName);
+ // mJniSetter = nGetIntMethod(targetClass, methodName);
+ // if (mJniSetter != 0) {
+ // if (propertyMap == null) {
+ // propertyMap = new HashMap();
+ // sJNISetterPropertyMap.put(targetClass, propertyMap);
+ // }
+ // propertyMap.put(mPropertyName, mJniSetter);
+ // }
+ // }
+ //} catch (NoSuchMethodError e) {
+ // Log.d("PropertyValuesHolder",
+ // "Can't find native method using JNI, use reflection" + e);
+ //} finally {
+ // mPropertyMapLock.writeLock().unlock();
+ //}
+ //if (mJniSetter == 0) {
+ // Couldn't find method through fast JNI approach - just use reflection
+ super.setupSetter(targetClass);
+ //}
+ }
+ }
+
+ static class FloatPropertyValuesHolder extends PropertyValuesHolder {
+
+ // Cache JNI functions to avoid looking them up twice
+ //private static final HashMap> sJNISetterPropertyMap =
+ // new HashMap>();
+ //int mJniSetter;
+ //private FloatProperty mFloatProperty;
+
+ FloatKeyframeSet mFloatKeyframeSet;
+ float mFloatAnimatedValue;
+
+ public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) {
+ super(propertyName);
+ mValueType = float.class;
+ mKeyframeSet = keyframeSet;
+ mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
+ }
+
+ //public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) {
+ // super(property);
+ // mValueType = float.class;
+ // mKeyframeSet = keyframeSet;
+ // mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
+ // if (property instanceof FloatProperty) {
+ // mFloatProperty = (FloatProperty) mProperty;
+ // }
+ //}
+
+ public FloatPropertyValuesHolder(String propertyName, float... values) {
+ super(propertyName);
+ setFloatValues(values);
+ }
+
+ //public FloatPropertyValuesHolder(Property property, float... values) {
+ // super(property);
+ // setFloatValues(values);
+ // if (property instanceof FloatProperty) {
+ // mFloatProperty = (FloatProperty) mProperty;
+ // }
+ //}
+
+ @Override
+ public void setFloatValues(float... values) {
+ super.setFloatValues(values);
+ mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
+ }
+
+ @Override
+ void calculateValue(float fraction) {
+ mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
+ }
+
+ @Override
+ Object getAnimatedValue() {
+ return mFloatAnimatedValue;
+ }
+
+ @Override
+ public FloatPropertyValuesHolder clone() {
+ FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
+ newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet;
+ return newPVH;
+ }
+
+ /**
+ * Internal function to set the value on the target object, using the setter set up
+ * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
+ * to handle turning the value calculated by ValueAnimator into a value set on the object
+ * according to the name of the property.
+ * @param target The target object on which the value is set
+ */
+ @Override
+ void setAnimatedValue(Object target) {
+ //if (mFloatProperty != null) {
+ // mFloatProperty.setValue(target, mFloatAnimatedValue);
+ // return;
+ //}
+ //if (mProperty != null) {
+ // mProperty.set(target, mFloatAnimatedValue);
+ // return;
+ //}
+ //if (mJniSetter != 0) {
+ // nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
+ // return;
+ //}
+ if (mSetter != null) {
+ try {
+ mTmpValueArray[0] = mFloatAnimatedValue;
+ mSetter.invoke(target, mTmpValueArray);
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ }
+ }
+ }
+
+ @Override
+ void setupSetter(Class targetClass) {
+ //if (mProperty != null) {
+ // return;
+ //}
+ // Check new static hashmap for setter method
+ //try {
+ // mPropertyMapLock.writeLock().lock();
+ // HashMap propertyMap = sJNISetterPropertyMap.get(targetClass);
+ // if (propertyMap != null) {
+ // Integer mJniSetterInteger = propertyMap.get(mPropertyName);
+ // if (mJniSetterInteger != null) {
+ // mJniSetter = mJniSetterInteger;
+ // }
+ // }
+ // if (mJniSetter == 0) {
+ // String methodName = getMethodName("set", mPropertyName);
+ // mJniSetter = nGetFloatMethod(targetClass, methodName);
+ // if (mJniSetter != 0) {
+ // if (propertyMap == null) {
+ // propertyMap = new HashMap();
+ // sJNISetterPropertyMap.put(targetClass, propertyMap);
+ // }
+ // propertyMap.put(mPropertyName, mJniSetter);
+ // }
+ // }
+ //} catch (NoSuchMethodError e) {
+ // Log.d("PropertyValuesHolder",
+ // "Can't find native method using JNI, use reflection" + e);
+ //} finally {
+ // mPropertyMapLock.writeLock().unlock();
+ //}
+ //if (mJniSetter == 0) {
+ // Couldn't find method through fast JNI approach - just use reflection
+ super.setupSetter(targetClass);
+ //}
+ }
+
+ }
+
+ //native static private int nGetIntMethod(Class targetClass, String methodName);
+ //native static private int nGetFloatMethod(Class targetClass, String methodName);
+ //native static private void nCallIntMethod(Object target, int methodID, int arg);
+ //native static private void nCallFloatMethod(Object target, int methodID, float arg);
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/TypeEvaluator.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/TypeEvaluator.java
new file mode 100644
index 0000000000..0ea3192446
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/TypeEvaluator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.nineoldandroids.animation;
+
+/**
+ * Interface for use with the {@link ValueAnimator#setEvaluator(TypeEvaluator)} function. Evaluators
+ * allow developers to create animations on arbitrary property types, by allowing them to supply
+ * custom evaulators for types that are not automatically understood and used by the animation
+ * system.
+ *
+ * @see ValueAnimator#setEvaluator(TypeEvaluator)
+ */
+public interface TypeEvaluator {
+
+ /**
+ * This function returns the result of linearly interpolating the start and end values, with
+ * fraction representing the proportion between the start and end values. The
+ * calculation is a simple parametric calculation: result = x0 + t * (v1 - v0),
+ * where x0 is startValue, x1 is endValue,
+ * and t is fraction.
+ *
+ * @param fraction The fraction from the starting to the ending values
+ * @param startValue The start value.
+ * @param endValue The end value.
+ * @return A linear interpolation between the start and end values, given the
+ * fraction parameter.
+ */
+ public T evaluate(float fraction, T startValue, T endValue);
+
+}
\ No newline at end of file
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/ValueAnimator.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/ValueAnimator.java
new file mode 100644
index 0000000000..d8a12c6882
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/animation/ValueAnimator.java
@@ -0,0 +1,1265 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.nineoldandroids.animation;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.AndroidRuntimeException;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * This class provides a simple timing engine for running animations
+ * which calculate animated values and set them on target objects.
+ *
+ *
There is a single timing pulse that all animations use. It runs in a
+ * custom handler to ensure that property changes happen on the UI thread.
+ *
+ *
By default, ValueAnimator uses non-linear time interpolation, via the
+ * {@link AccelerateDecelerateInterpolator} class, which accelerates into and decelerates
+ * out of an animation. This behavior can be changed by calling
+ * {@link ValueAnimator#setInterpolator(TimeInterpolator)}.
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class ValueAnimator extends Animator {
+
+ /**
+ * Internal constants
+ */
+
+ /*
+ * The default amount of time in ms between animation frames
+ */
+ private static final long DEFAULT_FRAME_DELAY = 10;
+
+ /**
+ * Messages sent to timing handler: START is sent when an animation first begins, FRAME is sent
+ * by the handler to itself to process the next animation frame
+ */
+ static final int ANIMATION_START = 0;
+ static final int ANIMATION_FRAME = 1;
+
+ /**
+ * Values used with internal variable mPlayingState to indicate the current state of an
+ * animation.
+ */
+ static final int STOPPED = 0; // Not yet playing
+ static final int RUNNING = 1; // Playing normally
+ static final int SEEKED = 2; // Seeked to some time value
+
+ /**
+ * Internal variables
+ * NOTE: This object implements the clone() method, making a deep copy of any referenced
+ * objects. As other non-trivial fields are added to this class, make sure to add logic
+ * to clone() to make deep copies of them.
+ */
+
+ // The first time that the animation's animateFrame() method is called. This time is used to
+ // determine elapsed time (and therefore the elapsed fraction) in subsequent calls
+ // to animateFrame()
+ long mStartTime;
+
+ /**
+ * Set when setCurrentPlayTime() is called. If negative, animation is not currently seeked
+ * to a value.
+ */
+ long mSeekTime = -1;
+
+ // TODO: We access the following ThreadLocal variables often, some of them on every update.
+ // If ThreadLocal access is significantly expensive, we may want to put all of these
+ // fields into a structure sot hat we just access ThreadLocal once to get the reference
+ // to that structure, then access the structure directly for each field.
+
+ // The static sAnimationHandler processes the internal timing loop on which all animations
+ // are based
+ private static ThreadLocal sAnimationHandler =
+ new ThreadLocal();
+
+ // The per-thread list of all active animations
+ private static final ThreadLocal> sAnimations =
+ new ThreadLocal>() {
+ @Override
+ protected ArrayList initialValue() {
+ return new ArrayList();
+ }
+ };
+
+ // The per-thread set of animations to be started on the next animation frame
+ private static final ThreadLocal> sPendingAnimations =
+ new ThreadLocal>() {
+ @Override
+ protected ArrayList initialValue() {
+ return new ArrayList();
+ }
+ };
+
+ /**
+ * Internal per-thread collections used to avoid set collisions as animations start and end
+ * while being processed.
+ */
+ private static final ThreadLocal> sDelayedAnims =
+ new ThreadLocal>() {
+ @Override
+ protected ArrayList initialValue() {
+ return new ArrayList();
+ }
+ };
+
+ private static final ThreadLocal> sEndingAnims =
+ new ThreadLocal>() {
+ @Override
+ protected ArrayList initialValue() {
+ return new ArrayList();
+ }
+ };
+
+ private static final ThreadLocal> sReadyAnims =
+ new ThreadLocal>() {
+ @Override
+ protected ArrayList initialValue() {
+ return new ArrayList();
+ }
+ };
+
+ // The time interpolator to be used if none is set on the animation
+ private static final /*Time*/Interpolator sDefaultInterpolator =
+ new AccelerateDecelerateInterpolator();
+
+ // type evaluators for the primitive types handled by this implementation
+ //private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
+ //private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
+
+ /**
+ * Used to indicate whether the animation is currently playing in reverse. This causes the
+ * elapsed fraction to be inverted to calculate the appropriate values.
+ */
+ private boolean mPlayingBackwards = false;
+
+ /**
+ * This variable tracks the current iteration that is playing. When mCurrentIteration exceeds the
+ * repeatCount (if repeatCount!=INFINITE), the animation ends
+ */
+ private int mCurrentIteration = 0;
+
+ /**
+ * Tracks current elapsed/eased fraction, for querying in getAnimatedFraction().
+ */
+ private float mCurrentFraction = 0f;
+
+ /**
+ * Tracks whether a startDelay'd animation has begun playing through the startDelay.
+ */
+ private boolean mStartedDelay = false;
+
+ /**
+ * Tracks the time at which the animation began playing through its startDelay. This is
+ * different from the mStartTime variable, which is used to track when the animation became
+ * active (which is when the startDelay expired and the animation was added to the active
+ * animations list).
+ */
+ private long mDelayStartTime;
+
+ /**
+ * Flag that represents the current state of the animation. Used to figure out when to start
+ * an animation (if state == STOPPED). Also used to end an animation that
+ * has been cancel()'d or end()'d since the last animation frame. Possible values are
+ * STOPPED, RUNNING, SEEKED.
+ */
+ int mPlayingState = STOPPED;
+
+ /**
+ * Additional playing state to indicate whether an animator has been start()'d. There is
+ * some lag between a call to start() and the first animation frame. We should still note
+ * that the animation has been started, even if it's first animation frame has not yet
+ * happened, and reflect that state in isRunning().
+ * Note that delayed animations are different: they are not started until their first
+ * animation frame, which occurs after their delay elapses.
+ */
+ private boolean mRunning = false;
+
+ /**
+ * Additional playing state to indicate whether an animator has been start()'d, whether or
+ * not there is a nonzero startDelay.
+ */
+ private boolean mStarted = false;
+
+ /**
+ * Flag that denotes whether the animation is set up and ready to go. Used to
+ * set up animation that has not yet been started.
+ */
+ boolean mInitialized = false;
+
+ //
+ // Backing variables
+ //
+
+ // How long the animation should last in ms
+ private long mDuration = 300;
+
+ // The amount of time in ms to delay starting the animation after start() is called
+ private long mStartDelay = 0;
+
+ // The number of milliseconds between animation frames
+ private static long sFrameDelay = DEFAULT_FRAME_DELAY;
+
+ // The number of times the animation will repeat. The default is 0, which means the animation
+ // will play only once
+ private int mRepeatCount = 0;
+
+ /**
+ * The type of repetition that will occur when repeatMode is nonzero. RESTART means the
+ * animation will start from the beginning on every new cycle. REVERSE means the animation
+ * will reverse directions on each iteration.
+ */
+ private int mRepeatMode = RESTART;
+
+ /**
+ * The time interpolator to be used. The elapsed fraction of the animation will be passed
+ * through this interpolator to calculate the interpolated fraction, which is then used to
+ * calculate the animated values.
+ */
+ private /*Time*/Interpolator mInterpolator = sDefaultInterpolator;
+
+ /**
+ * The set of listeners to be sent events through the life of an animation.
+ */
+ private ArrayList mUpdateListeners = null;
+
+ /**
+ * The property/value sets being animated.
+ */
+ PropertyValuesHolder[] mValues;
+
+ /**
+ * A hashmap of the PropertyValuesHolder objects. This map is used to lookup animated values
+ * by property name during calls to getAnimatedValue(String).
+ */
+ HashMap mValuesMap;
+
+ /**
+ * Public constants
+ */
+
+ /**
+ * When the animation reaches the end and repeatCount is INFINITE
+ * or a positive value, the animation restarts from the beginning.
+ */
+ public static final int RESTART = 1;
+ /**
+ * When the animation reaches the end and repeatCount is INFINITE
+ * or a positive value, the animation reverses direction on every iteration.
+ */
+ public static final int REVERSE = 2;
+ /**
+ * This value used used with the {@link #setRepeatCount(int)} property to repeat
+ * the animation indefinitely.
+ */
+ public static final int INFINITE = -1;
+
+ /**
+ * Creates a new ValueAnimator object. This default constructor is primarily for
+ * use internally; the factory methods which take parameters are more generally
+ * useful.
+ */
+ public ValueAnimator() {
+ }
+
+ /**
+ * Constructs and returns a ValueAnimator that animates between int values. A single
+ * value implies that that value is the one being animated to. However, this is not typically
+ * useful in a ValueAnimator object because there is no way for the object to determine the
+ * starting value for the animation (unlike ObjectAnimator, which can derive that value
+ * from the target object and property being animated). Therefore, there should typically
+ * be two or more values.
+ *
+ * @param values A set of values that the animation will animate between over time.
+ * @return A ValueAnimator object that is set up to animate between the given values.
+ */
+ public static ValueAnimator ofInt(int... values) {
+ ValueAnimator anim = new ValueAnimator();
+ anim.setIntValues(values);
+ return anim;
+ }
+
+ /**
+ * Constructs and returns a ValueAnimator that animates between float values. A single
+ * value implies that that value is the one being animated to. However, this is not typically
+ * useful in a ValueAnimator object because there is no way for the object to determine the
+ * starting value for the animation (unlike ObjectAnimator, which can derive that value
+ * from the target object and property being animated). Therefore, there should typically
+ * be two or more values.
+ *
+ * @param values A set of values that the animation will animate between over time.
+ * @return A ValueAnimator object that is set up to animate between the given values.
+ */
+ public static ValueAnimator ofFloat(float... values) {
+ ValueAnimator anim = new ValueAnimator();
+ anim.setFloatValues(values);
+ return anim;
+ }
+
+ /**
+ * Constructs and returns a ValueAnimator that animates between the values
+ * specified in the PropertyValuesHolder objects.
+ *
+ * @param values A set of PropertyValuesHolder objects whose values will be animated
+ * between over time.
+ * @return A ValueAnimator object that is set up to animate between the given values.
+ */
+ public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values) {
+ ValueAnimator anim = new ValueAnimator();
+ anim.setValues(values);
+ return anim;
+ }
+ /**
+ * Constructs and returns a ValueAnimator that animates between Object values. A single
+ * value implies that that value is the one being animated to. However, this is not typically
+ * useful in a ValueAnimator object because there is no way for the object to determine the
+ * starting value for the animation (unlike ObjectAnimator, which can derive that value
+ * from the target object and property being animated). Therefore, there should typically
+ * be two or more values.
+ *
+ *
Since ValueAnimator does not know how to animate between arbitrary Objects, this
+ * factory method also takes a TypeEvaluator object that the ValueAnimator will use
+ * to perform that interpolation.
+ *
+ * @param evaluator A TypeEvaluator that will be called on each animation frame to
+ * provide the ncessry interpolation between the Object values to derive the animated
+ * value.
+ * @param values A set of values that the animation will animate between over time.
+ * @return A ValueAnimator object that is set up to animate between the given values.
+ */
+ public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) {
+ ValueAnimator anim = new ValueAnimator();
+ anim.setObjectValues(values);
+ anim.setEvaluator(evaluator);
+ return anim;
+ }
+
+ /**
+ * Sets int values that will be animated between. A single
+ * value implies that that value is the one being animated to. However, this is not typically
+ * useful in a ValueAnimator object because there is no way for the object to determine the
+ * starting value for the animation (unlike ObjectAnimator, which can derive that value
+ * from the target object and property being animated). Therefore, there should typically
+ * be two or more values.
+ *
+ *
If there are already multiple sets of values defined for this ValueAnimator via more
+ * than one PropertyValuesHolder object, this method will set the values for the first
+ * of those objects.
+ *
+ * @param values A set of values that the animation will animate between over time.
+ */
+ public void setIntValues(int... values) {
+ if (values == null || values.length == 0) {
+ return;
+ }
+ if (mValues == null || mValues.length == 0) {
+ setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofInt("", values)});
+ } else {
+ PropertyValuesHolder valuesHolder = mValues[0];
+ valuesHolder.setIntValues(values);
+ }
+ // New property/values/target should cause re-initialization prior to starting
+ mInitialized = false;
+ }
+
+ /**
+ * Sets float values that will be animated between. A single
+ * value implies that that value is the one being animated to. However, this is not typically
+ * useful in a ValueAnimator object because there is no way for the object to determine the
+ * starting value for the animation (unlike ObjectAnimator, which can derive that value
+ * from the target object and property being animated). Therefore, there should typically
+ * be two or more values.
+ *
+ *
If there are already multiple sets of values defined for this ValueAnimator via more
+ * than one PropertyValuesHolder object, this method will set the values for the first
+ * of those objects.
+ *
+ * @param values A set of values that the animation will animate between over time.
+ */
+ public void setFloatValues(float... values) {
+ if (values == null || values.length == 0) {
+ return;
+ }
+ if (mValues == null || mValues.length == 0) {
+ setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofFloat("", values)});
+ } else {
+ PropertyValuesHolder valuesHolder = mValues[0];
+ valuesHolder.setFloatValues(values);
+ }
+ // New property/values/target should cause re-initialization prior to starting
+ mInitialized = false;
+ }
+
+ /**
+ * Sets the values to animate between for this animation. A single
+ * value implies that that value is the one being animated to. However, this is not typically
+ * useful in a ValueAnimator object because there is no way for the object to determine the
+ * starting value for the animation (unlike ObjectAnimator, which can derive that value
+ * from the target object and property being animated). Therefore, there should typically
+ * be two or more values.
+ *
+ *
If there are already multiple sets of values defined for this ValueAnimator via more
+ * than one PropertyValuesHolder object, this method will set the values for the first
+ * of those objects.
+ *
+ *
There should be a TypeEvaluator set on the ValueAnimator that knows how to interpolate
+ * between these value objects. ValueAnimator only knows how to interpolate between the
+ * primitive types specified in the other setValues() methods.
+ *
+ * @param values The set of values to animate between.
+ */
+ public void setObjectValues(Object... values) {
+ if (values == null || values.length == 0) {
+ return;
+ }
+ if (mValues == null || mValues.length == 0) {
+ setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofObject("",
+ (TypeEvaluator)null, values)});
+ } else {
+ PropertyValuesHolder valuesHolder = mValues[0];
+ valuesHolder.setObjectValues(values);
+ }
+ // New property/values/target should cause re-initialization prior to starting
+ mInitialized = false;
+ }
+
+ /**
+ * Sets the values, per property, being animated between. This function is called internally
+ * by the constructors of ValueAnimator that take a list of values. But an ValueAnimator can
+ * be constructed without values and this method can be called to set the values manually
+ * instead.
+ *
+ * @param values The set of values, per property, being animated between.
+ */
+ public void setValues(PropertyValuesHolder... values) {
+ int numValues = values.length;
+ mValues = values;
+ mValuesMap = new HashMap(numValues);
+ for (int i = 0; i < numValues; ++i) {
+ PropertyValuesHolder valuesHolder = values[i];
+ mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
+ }
+ // New property/values/target should cause re-initialization prior to starting
+ mInitialized = false;
+ }
+
+ /**
+ * Returns the values that this ValueAnimator animates between. These values are stored in
+ * PropertyValuesHolder objects, even if the ValueAnimator was created with a simple list
+ * of value objects instead.
+ *
+ * @return PropertyValuesHolder[] An array of PropertyValuesHolder objects which hold the
+ * values, per property, that define the animation.
+ */
+ public PropertyValuesHolder[] getValues() {
+ return mValues;
+ }
+
+ /**
+ * This function is called immediately before processing the first animation
+ * frame of an animation. If there is a nonzero startDelay, the
+ * function is called after that delay ends.
+ * It takes care of the final initialization steps for the
+ * animation.
+ *
+ *
Overrides of this method should call the superclass method to ensure
+ * that internal mechanisms for the animation are set up correctly.
+ */
+ void initAnimation() {
+ if (!mInitialized) {
+ int numValues = mValues.length;
+ for (int i = 0; i < numValues; ++i) {
+ mValues[i].init();
+ }
+ mInitialized = true;
+ }
+ }
+
+
+ /**
+ * Sets the length of the animation. The default duration is 300 milliseconds.
+ *
+ * @param duration The length of the animation, in milliseconds. This value cannot
+ * be negative.
+ * @return ValueAnimator The object called with setDuration(). This return
+ * value makes it easier to compose statements together that construct and then set the
+ * duration, as in ValueAnimator.ofInt(0, 10).setDuration(500).start().
+ */
+ public ValueAnimator setDuration(long duration) {
+ if (duration < 0) {
+ throw new IllegalArgumentException("Animators cannot have negative duration: " +
+ duration);
+ }
+ mDuration = duration;
+ return this;
+ }
+
+ /**
+ * Gets the length of the animation. The default duration is 300 milliseconds.
+ *
+ * @return The length of the animation, in milliseconds.
+ */
+ public long getDuration() {
+ return mDuration;
+ }
+
+ /**
+ * Sets the position of the animation to the specified point in time. This time should
+ * be between 0 and the total duration of the animation, including any repetition. If
+ * the animation has not yet been started, then it will not advance forward after it is
+ * set to this time; it will simply set the time to this value and perform any appropriate
+ * actions based on that time. If the animation is already running, then setCurrentPlayTime()
+ * will set the current playing time to this value and continue playing from that point.
+ *
+ * @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
+ */
+ public void setCurrentPlayTime(long playTime) {
+ initAnimation();
+ long currentTime = AnimationUtils.currentAnimationTimeMillis();
+ if (mPlayingState != RUNNING) {
+ mSeekTime = playTime;
+ mPlayingState = SEEKED;
+ }
+ mStartTime = currentTime - playTime;
+ animationFrame(currentTime);
+ }
+
+ /**
+ * Gets the current position of the animation in time, which is equal to the current
+ * time minus the time that the animation started. An animation that is not yet started will
+ * return a value of zero.
+ *
+ * @return The current position in time of the animation.
+ */
+ public long getCurrentPlayTime() {
+ if (!mInitialized || mPlayingState == STOPPED) {
+ return 0;
+ }
+ return AnimationUtils.currentAnimationTimeMillis() - mStartTime;
+ }
+
+ /**
+ * This custom, static handler handles the timing pulse that is shared by
+ * all active animations. This approach ensures that the setting of animation
+ * values will happen on the UI thread and that all animations will share
+ * the same times for calculating their values, which makes synchronizing
+ * animations possible.
+ *
+ */
+ private static class AnimationHandler extends Handler {
+ /**
+ * There are only two messages that we care about: ANIMATION_START and
+ * ANIMATION_FRAME. The START message is sent when an animation's start()
+ * method is called. It cannot start synchronously when start() is called
+ * because the call may be on the wrong thread, and it would also not be
+ * synchronized with other animations because it would not start on a common
+ * timing pulse. So each animation sends a START message to the handler, which
+ * causes the handler to place the animation on the active animations queue and
+ * start processing frames for that animation.
+ * The FRAME message is the one that is sent over and over while there are any
+ * active animations to process.
+ */
+ @Override
+ public void handleMessage(Message msg) {
+ boolean callAgain = true;
+ ArrayList animations = sAnimations.get();
+ ArrayList delayedAnims = sDelayedAnims.get();
+ switch (msg.what) {
+ // TODO: should we avoid sending frame message when starting if we
+ // were already running?
+ case ANIMATION_START:
+ ArrayList pendingAnimations = sPendingAnimations.get();
+ if (animations.size() > 0 || delayedAnims.size() > 0) {
+ callAgain = false;
+ }
+ // pendingAnims holds any animations that have requested to be started
+ // We're going to clear sPendingAnimations, but starting animation may
+ // cause more to be added to the pending list (for example, if one animation
+ // starting triggers another starting). So we loop until sPendingAnimations
+ // is empty.
+ while (pendingAnimations.size() > 0) {
+ ArrayList pendingCopy =
+ (ArrayList) pendingAnimations.clone();
+ pendingAnimations.clear();
+ int count = pendingCopy.size();
+ for (int i = 0; i < count; ++i) {
+ ValueAnimator anim = pendingCopy.get(i);
+ // If the animation has a startDelay, place it on the delayed list
+ if (anim.mStartDelay == 0) {
+ anim.startAnimation();
+ } else {
+ delayedAnims.add(anim);
+ }
+ }
+ }
+ // fall through to process first frame of new animations
+ case ANIMATION_FRAME:
+ // currentTime holds the common time for all animations processed
+ // during this frame
+ long currentTime = AnimationUtils.currentAnimationTimeMillis();
+ ArrayList readyAnims = sReadyAnims.get();
+ ArrayList endingAnims = sEndingAnims.get();
+
+ // First, process animations currently sitting on the delayed queue, adding
+ // them to the active animations if they are ready
+ int numDelayedAnims = delayedAnims.size();
+ for (int i = 0; i < numDelayedAnims; ++i) {
+ ValueAnimator anim = delayedAnims.get(i);
+ if (anim.delayedAnimationFrame(currentTime)) {
+ readyAnims.add(anim);
+ }
+ }
+ int numReadyAnims = readyAnims.size();
+ if (numReadyAnims > 0) {
+ for (int i = 0; i < numReadyAnims; ++i) {
+ ValueAnimator anim = readyAnims.get(i);
+ anim.startAnimation();
+ anim.mRunning = true;
+ delayedAnims.remove(anim);
+ }
+ readyAnims.clear();
+ }
+
+ // Now process all active animations. The return value from animationFrame()
+ // tells the handler whether it should now be ended
+ int numAnims = animations.size();
+ int i = 0;
+ while (i < numAnims) {
+ ValueAnimator anim = animations.get(i);
+ if (anim.animationFrame(currentTime)) {
+ endingAnims.add(anim);
+ }
+ if (animations.size() == numAnims) {
+ ++i;
+ } else {
+ // An animation might be canceled or ended by client code
+ // during the animation frame. Check to see if this happened by
+ // seeing whether the current index is the same as it was before
+ // calling animationFrame(). Another approach would be to copy
+ // animations to a temporary list and process that list instead,
+ // but that entails garbage and processing overhead that would
+ // be nice to avoid.
+ --numAnims;
+ endingAnims.remove(anim);
+ }
+ }
+ if (endingAnims.size() > 0) {
+ for (i = 0; i < endingAnims.size(); ++i) {
+ endingAnims.get(i).endAnimation();
+ }
+ endingAnims.clear();
+ }
+
+ // If there are still active or delayed animations, call the handler again
+ // after the frameDelay
+ if (callAgain && (!animations.isEmpty() || !delayedAnims.isEmpty())) {
+ sendEmptyMessageDelayed(ANIMATION_FRAME, Math.max(0, sFrameDelay -
+ (AnimationUtils.currentAnimationTimeMillis() - currentTime)));
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * The amount of time, in milliseconds, to delay starting the animation after
+ * {@link #start()} is called.
+ *
+ * @return the number of milliseconds to delay running the animation
+ */
+ public long getStartDelay() {
+ return mStartDelay;
+ }
+
+ /**
+ * The amount of time, in milliseconds, to delay starting the animation after
+ * {@link #start()} is called.
+
+ * @param startDelay The amount of the delay, in milliseconds
+ */
+ public void setStartDelay(long startDelay) {
+ this.mStartDelay = startDelay;
+ }
+
+ /**
+ * The amount of time, in milliseconds, between each frame of the animation. This is a
+ * requested time that the animation will attempt to honor, but the actual delay between
+ * frames may be different, depending on system load and capabilities. This is a static
+ * function because the same delay will be applied to all animations, since they are all
+ * run off of a single timing loop.
+ *
+ * @return the requested time between frames, in milliseconds
+ */
+ public static long getFrameDelay() {
+ return sFrameDelay;
+ }
+
+ /**
+ * The amount of time, in milliseconds, between each frame of the animation. This is a
+ * requested time that the animation will attempt to honor, but the actual delay between
+ * frames may be different, depending on system load and capabilities. This is a static
+ * function because the same delay will be applied to all animations, since they are all
+ * run off of a single timing loop.
+ *
+ * @param frameDelay the requested time between frames, in milliseconds
+ */
+ public static void setFrameDelay(long frameDelay) {
+ sFrameDelay = frameDelay;
+ }
+
+ /**
+ * The most recent value calculated by this ValueAnimator when there is just one
+ * property being animated. This value is only sensible while the animation is running. The main
+ * purpose for this read-only property is to retrieve the value from the ValueAnimator
+ * during a call to {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which
+ * is called during each animation frame, immediately after the value is calculated.
+ *
+ * @return animatedValue The value most recently calculated by this ValueAnimator for
+ * the single property being animated. If there are several properties being animated
+ * (specified by several PropertyValuesHolder objects in the constructor), this function
+ * returns the animated value for the first of those objects.
+ */
+ public Object getAnimatedValue() {
+ if (mValues != null && mValues.length > 0) {
+ return mValues[0].getAnimatedValue();
+ }
+ // Shouldn't get here; should always have values unless ValueAnimator was set up wrong
+ return null;
+ }
+
+ /**
+ * The most recent value calculated by this ValueAnimator for propertyName.
+ * The main purpose for this read-only property is to retrieve the value from the
+ * ValueAnimator during a call to
+ * {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which
+ * is called during each animation frame, immediately after the value is calculated.
+ *
+ * @return animatedValue The value most recently calculated for the named property
+ * by this ValueAnimator.
+ */
+ public Object getAnimatedValue(String propertyName) {
+ PropertyValuesHolder valuesHolder = mValuesMap.get(propertyName);
+ if (valuesHolder != null) {
+ return valuesHolder.getAnimatedValue();
+ } else {
+ // At least avoid crashing if called with bogus propertyName
+ return null;
+ }
+ }
+
+ /**
+ * Sets how many times the animation should be repeated. If the repeat
+ * count is 0, the animation is never repeated. If the repeat count is
+ * greater than 0 or {@link #INFINITE}, the repeat mode will be taken
+ * into account. The repeat count is 0 by default.
+ *
+ * @param value the number of times the animation should be repeated
+ */
+ public void setRepeatCount(int value) {
+ mRepeatCount = value;
+ }
+ /**
+ * Defines how many times the animation should repeat. The default value
+ * is 0.
+ *
+ * @return the number of times the animation should repeat, or {@link #INFINITE}
+ */
+ public int getRepeatCount() {
+ return mRepeatCount;
+ }
+
+ /**
+ * Defines what this animation should do when it reaches the end. This
+ * setting is applied only when the repeat count is either greater than
+ * 0 or {@link #INFINITE}. Defaults to {@link #RESTART}.
+ *
+ * @param value {@link #RESTART} or {@link #REVERSE}
+ */
+ public void setRepeatMode(int value) {
+ mRepeatMode = value;
+ }
+
+ /**
+ * Defines what this animation should do when it reaches the end.
+ *
+ * @return either one of {@link #REVERSE} or {@link #RESTART}
+ */
+ public int getRepeatMode() {
+ return mRepeatMode;
+ }
+
+ /**
+ * Adds a listener to the set of listeners that are sent update events through the life of
+ * an animation. This method is called on all listeners for every frame of the animation,
+ * after the values for the animation have been calculated.
+ *
+ * @param listener the listener to be added to the current set of listeners for this animation.
+ */
+ public void addUpdateListener(AnimatorUpdateListener listener) {
+ if (mUpdateListeners == null) {
+ mUpdateListeners = new ArrayList();
+ }
+ mUpdateListeners.add(listener);
+ }
+
+ /**
+ * Removes all listeners from the set listening to frame updates for this animation.
+ */
+ public void removeAllUpdateListeners() {
+ if (mUpdateListeners == null) {
+ return;
+ }
+ mUpdateListeners.clear();
+ mUpdateListeners = null;
+ }
+
+ /**
+ * Removes a listener from the set listening to frame updates for this animation.
+ *
+ * @param listener the listener to be removed from the current set of update listeners
+ * for this animation.
+ */
+ public void removeUpdateListener(AnimatorUpdateListener listener) {
+ if (mUpdateListeners == null) {
+ return;
+ }
+ mUpdateListeners.remove(listener);
+ if (mUpdateListeners.size() == 0) {
+ mUpdateListeners = null;
+ }
+ }
+
+
+ /**
+ * The time interpolator used in calculating the elapsed fraction of this animation. The
+ * interpolator determines whether the animation runs with linear or non-linear motion,
+ * such as acceleration and deceleration. The default value is
+ * {@link android.view.animation.AccelerateDecelerateInterpolator}
+ *
+ * @param value the interpolator to be used by this animation. A value of null
+ * will result in linear interpolation.
+ */
+ @Override
+ public void setInterpolator(/*Time*/Interpolator value) {
+ if (value != null) {
+ mInterpolator = value;
+ } else {
+ mInterpolator = new LinearInterpolator();
+ }
+ }
+
+ /**
+ * Returns the timing interpolator that this ValueAnimator uses.
+ *
+ * @return The timing interpolator for this ValueAnimator.
+ */
+ public /*Time*/Interpolator getInterpolator() {
+ return mInterpolator;
+ }
+
+ /**
+ * The type evaluator to be used when calculating the animated values of this animation.
+ * The system will automatically assign a float or int evaluator based on the type
+ * of startValue and endValue in the constructor. But if these values
+ * are not one of these primitive types, or if different evaluation is desired (such as is
+ * necessary with int values that represent colors), a custom evaluator needs to be assigned.
+ * For example, when running an animation on color values, the {@link ArgbEvaluator}
+ * should be used to get correct RGB color interpolation.
+ *
+ *
If this ValueAnimator has only one set of values being animated between, this evaluator
+ * will be used for that set. If there are several sets of values being animated, which is
+ * the case if PropertyValuesHOlder objects were set on the ValueAnimator, then the evaluator
+ * is assigned just to the first PropertyValuesHolder object.
+ *
+ * @param value the evaluator to be used this animation
+ */
+ public void setEvaluator(TypeEvaluator value) {
+ if (value != null && mValues != null && mValues.length > 0) {
+ mValues[0].setEvaluator(value);
+ }
+ }
+
+ /**
+ * Start the animation playing. This version of start() takes a boolean flag that indicates
+ * whether the animation should play in reverse. The flag is usually false, but may be set
+ * to true if called from the reverse() method.
+ *
+ *
The animation started by calling this method will be run on the thread that called
+ * this method. This thread should have a Looper on it (a runtime exception will be thrown if
+ * this is not the case). Also, if the animation will animate
+ * properties of objects in the view hierarchy, then the calling thread should be the UI
+ * thread for that view hierarchy.
+ *
+ * @param playBackwards Whether the ValueAnimator should start playing in reverse.
+ */
+ private void start(boolean playBackwards) {
+ if (Looper.myLooper() == null) {
+ throw new AndroidRuntimeException("Animators may only be run on Looper threads");
+ }
+ mPlayingBackwards = playBackwards;
+ mCurrentIteration = 0;
+ mPlayingState = STOPPED;
+ mStarted = true;
+ mStartedDelay = false;
+ sPendingAnimations.get().add(this);
+ if (mStartDelay == 0) {
+ // This sets the initial value of the animation, prior to actually starting it running
+ setCurrentPlayTime(getCurrentPlayTime());
+ mPlayingState = STOPPED;
+ mRunning = true;
+
+ if (mListeners != null) {
+ ArrayList tmpListeners =
+ (ArrayList) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationStart(this);
+ }
+ }
+ }
+ AnimationHandler animationHandler = sAnimationHandler.get();
+ if (animationHandler == null) {
+ animationHandler = new AnimationHandler();
+ sAnimationHandler.set(animationHandler);
+ }
+ animationHandler.sendEmptyMessage(ANIMATION_START);
+ }
+
+ @Override
+ public void start() {
+ start(false);
+ }
+
+ @Override
+ public void cancel() {
+ // Only cancel if the animation is actually running or has been started and is about
+ // to run
+ if (mPlayingState != STOPPED || sPendingAnimations.get().contains(this) ||
+ sDelayedAnims.get().contains(this)) {
+ // Only notify listeners if the animator has actually started
+ if (mRunning && mListeners != null) {
+ ArrayList tmpListeners =
+ (ArrayList) mListeners.clone();
+ for (AnimatorListener listener : tmpListeners) {
+ listener.onAnimationCancel(this);
+ }
+ }
+ endAnimation();
+ }
+ }
+
+ @Override
+ public void end() {
+ if (!sAnimations.get().contains(this) && !sPendingAnimations.get().contains(this)) {
+ // Special case if the animation has not yet started; get it ready for ending
+ mStartedDelay = false;
+ startAnimation();
+ } else if (!mInitialized) {
+ initAnimation();
+ }
+ // The final value set on the target varies, depending on whether the animation
+ // was supposed to repeat an odd number of times
+ if (mRepeatCount > 0 && (mRepeatCount & 0x01) == 1) {
+ animateValue(0f);
+ } else {
+ animateValue(1f);
+ }
+ endAnimation();
+ }
+
+ @Override
+ public boolean isRunning() {
+ return (mPlayingState == RUNNING || mRunning);
+ }
+
+ @Override
+ public boolean isStarted() {
+ return mStarted;
+ }
+
+ /**
+ * Plays the ValueAnimator in reverse. If the animation is already running,
+ * it will stop itself and play backwards from the point reached when reverse was called.
+ * If the animation is not currently running, then it will start from the end and
+ * play backwards. This behavior is only set for the current animation; future playing
+ * of the animation will use the default behavior of playing forward.
+ */
+ public void reverse() {
+ mPlayingBackwards = !mPlayingBackwards;
+ if (mPlayingState == RUNNING) {
+ long currentTime = AnimationUtils.currentAnimationTimeMillis();
+ long currentPlayTime = currentTime - mStartTime;
+ long timeLeft = mDuration - currentPlayTime;
+ mStartTime = currentTime - timeLeft;
+ } else {
+ start(true);
+ }
+ }
+
+ /**
+ * Called internally to end an animation by removing it from the animations list. Must be
+ * called on the UI thread.
+ */
+ private void endAnimation() {
+ sAnimations.get().remove(this);
+ sPendingAnimations.get().remove(this);
+ sDelayedAnims.get().remove(this);
+ mPlayingState = STOPPED;
+ if (mRunning && mListeners != null) {
+ ArrayList tmpListeners =
+ (ArrayList) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationEnd(this);
+ }
+ }
+ mRunning = false;
+ mStarted = false;
+ }
+
+ /**
+ * Called internally to start an animation by adding it to the active animations list. Must be
+ * called on the UI thread.
+ */
+ private void startAnimation() {
+ initAnimation();
+ sAnimations.get().add(this);
+ if (mStartDelay > 0 && mListeners != null) {
+ // Listeners were already notified in start() if startDelay is 0; this is
+ // just for delayed animations
+ ArrayList tmpListeners =
+ (ArrayList) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationStart(this);
+ }
+ }
+ }
+
+ /**
+ * Internal function called to process an animation frame on an animation that is currently
+ * sleeping through its startDelay phase. The return value indicates whether it
+ * should be woken up and put on the active animations queue.
+ *
+ * @param currentTime The current animation time, used to calculate whether the animation
+ * has exceeded its startDelay and should be started.
+ * @return True if the animation's startDelay has been exceeded and the animation
+ * should be added to the set of active animations.
+ */
+ private boolean delayedAnimationFrame(long currentTime) {
+ if (!mStartedDelay) {
+ mStartedDelay = true;
+ mDelayStartTime = currentTime;
+ } else {
+ long deltaTime = currentTime - mDelayStartTime;
+ if (deltaTime > mStartDelay) {
+ // startDelay ended - start the anim and record the
+ // mStartTime appropriately
+ mStartTime = currentTime - (deltaTime - mStartDelay);
+ mPlayingState = RUNNING;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This internal function processes a single animation frame for a given animation. The
+ * currentTime parameter is the timing pulse sent by the handler, used to calculate the
+ * elapsed duration, and therefore
+ * the elapsed fraction, of the animation. The return value indicates whether the animation
+ * should be ended (which happens when the elapsed time of the animation exceeds the
+ * animation's duration, including the repeatCount).
+ *
+ * @param currentTime The current time, as tracked by the static timing handler
+ * @return true if the animation's duration, including any repetitions due to
+ * repeatCount has been exceeded and the animation should be ended.
+ */
+ boolean animationFrame(long currentTime) {
+ boolean done = false;
+
+ if (mPlayingState == STOPPED) {
+ mPlayingState = RUNNING;
+ if (mSeekTime < 0) {
+ mStartTime = currentTime;
+ } else {
+ mStartTime = currentTime - mSeekTime;
+ // Now that we're playing, reset the seek time
+ mSeekTime = -1;
+ }
+ }
+ switch (mPlayingState) {
+ case RUNNING:
+ case SEEKED:
+ float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
+ if (fraction >= 1f) {
+ if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
+ // Time to repeat
+ if (mListeners != null) {
+ int numListeners = mListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ mListeners.get(i).onAnimationRepeat(this);
+ }
+ }
+ if (mRepeatMode == REVERSE) {
+ mPlayingBackwards = mPlayingBackwards ? false : true;
+ }
+ mCurrentIteration += (int)fraction;
+ fraction = fraction % 1f;
+ mStartTime += mDuration;
+ } else {
+ done = true;
+ fraction = Math.min(fraction, 1.0f);
+ }
+ }
+ if (mPlayingBackwards) {
+ fraction = 1f - fraction;
+ }
+ animateValue(fraction);
+ break;
+ }
+
+ return done;
+ }
+
+ /**
+ * Returns the current animation fraction, which is the elapsed/interpolated fraction used in
+ * the most recent frame update on the animation.
+ *
+ * @return Elapsed/interpolated fraction of the animation.
+ */
+ public float getAnimatedFraction() {
+ return mCurrentFraction;
+ }
+
+ /**
+ * This method is called with the elapsed fraction of the animation during every
+ * animation frame. This function turns the elapsed fraction into an interpolated fraction
+ * and then into an animated value (from the evaluator. The function is called mostly during
+ * animation updates, but it is also called when the end()
+ * function is called, to set the final value on the property.
+ *
+ *
Overrides of this method must call the superclass to perform the calculation
+ * of the animated value.
+ *
+ * @param fraction The elapsed fraction of the animation.
+ */
+ void animateValue(float fraction) {
+ fraction = mInterpolator.getInterpolation(fraction);
+ mCurrentFraction = fraction;
+ int numValues = mValues.length;
+ for (int i = 0; i < numValues; ++i) {
+ mValues[i].calculateValue(fraction);
+ }
+ if (mUpdateListeners != null) {
+ int numListeners = mUpdateListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ mUpdateListeners.get(i).onAnimationUpdate(this);
+ }
+ }
+ }
+
+ @Override
+ public ValueAnimator clone() {
+ final ValueAnimator anim = (ValueAnimator) super.clone();
+ if (mUpdateListeners != null) {
+ ArrayList oldListeners = mUpdateListeners;
+ anim.mUpdateListeners = new ArrayList();
+ int numListeners = oldListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ anim.mUpdateListeners.add(oldListeners.get(i));
+ }
+ }
+ anim.mSeekTime = -1;
+ anim.mPlayingBackwards = false;
+ anim.mCurrentIteration = 0;
+ anim.mInitialized = false;
+ anim.mPlayingState = STOPPED;
+ anim.mStartedDelay = false;
+ PropertyValuesHolder[] oldValues = mValues;
+ if (oldValues != null) {
+ int numValues = oldValues.length;
+ anim.mValues = new PropertyValuesHolder[numValues];
+ anim.mValuesMap = new HashMap(numValues);
+ for (int i = 0; i < numValues; ++i) {
+ PropertyValuesHolder newValuesHolder = oldValues[i].clone();
+ anim.mValues[i] = newValuesHolder;
+ anim.mValuesMap.put(newValuesHolder.getPropertyName(), newValuesHolder);
+ }
+ }
+ return anim;
+ }
+
+ /**
+ * Implementors of this interface can add themselves as update listeners
+ * to an ValueAnimator instance to receive callbacks on every animation
+ * frame, after the current frame's values have been calculated for that
+ * ValueAnimator.
+ */
+ public static interface AnimatorUpdateListener {
+ /**
+ *
Notifies the occurrence of another frame of the animation.
+ *
+ * @param animation The animation which was repeated.
+ */
+ void onAnimationUpdate(ValueAnimator animation);
+
+ }
+
+ /**
+ * Return the number of animations currently running.
+ *
+ * Used by StrictMode internally to annotate violations. Only
+ * called on the main thread.
+ *
+ * @hide
+ */
+ public static int getCurrentAnimationsCount() {
+ return sAnimations.get().size();
+ }
+
+ /**
+ * Clear all animations on this thread, without canceling or ending them.
+ * This should be used with caution.
+ *
+ * @hide
+ */
+ public static void clearAllAnimations() {
+ sAnimations.get().clear();
+ sPendingAnimations.get().clear();
+ sDelayedAnims.get().clear();
+ }
+
+ @Override
+ public String toString() {
+ String returnVal = "ValueAnimator@" + Integer.toHexString(hashCode());
+ if (mValues != null) {
+ for (int i = 0; i < mValues.length; ++i) {
+ returnVal += "\n " + mValues[i].toString();
+ }
+ }
+ return returnVal;
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/view/NineViewGroup.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/view/NineViewGroup.java
new file mode 100644
index 0000000000..7b830b9c05
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/view/NineViewGroup.java
@@ -0,0 +1,79 @@
+package com.actionbarsherlock.internal.nineoldandroids.view;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+
+import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy;
+
+public abstract class NineViewGroup extends ViewGroup {
+ private final AnimatorProxy mProxy;
+
+ public NineViewGroup(Context context) {
+ super(context);
+ mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
+ }
+ public NineViewGroup(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
+ }
+ public NineViewGroup(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ if (mProxy != null) {
+ if (visibility == GONE) {
+ clearAnimation();
+ } else if (visibility == VISIBLE) {
+ setAnimation(mProxy);
+ }
+ }
+ super.setVisibility(visibility);
+ }
+
+ public float getAlpha() {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ return mProxy.getAlpha();
+ } else {
+ return super.getAlpha();
+ }
+ }
+ public void setAlpha(float alpha) {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ mProxy.setAlpha(alpha);
+ } else {
+ super.setAlpha(alpha);
+ }
+ }
+ public float getTranslationX() {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ return mProxy.getTranslationX();
+ } else {
+ return super.getTranslationX();
+ }
+ }
+ public void setTranslationX(float translationX) {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ mProxy.setTranslationX(translationX);
+ } else {
+ super.setTranslationX(translationX);
+ }
+ }
+ public float getTranslationY() {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ return mProxy.getTranslationY();
+ } else {
+ return super.getTranslationY();
+ }
+ }
+ public void setTranslationY(float translationY) {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ mProxy.setTranslationY(translationY);
+ } else {
+ super.setTranslationY(translationY);
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/view/animation/AnimatorProxy.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/view/animation/AnimatorProxy.java
new file mode 100644
index 0000000000..067d0494ee
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/view/animation/AnimatorProxy.java
@@ -0,0 +1,212 @@
+package com.actionbarsherlock.internal.nineoldandroids.view.animation;
+
+import java.lang.ref.WeakReference;
+import java.util.WeakHashMap;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.os.Build;
+import android.util.FloatMath;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+public final class AnimatorProxy extends Animation {
+ public static final boolean NEEDS_PROXY = Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB;
+
+ private static final WeakHashMap PROXIES =
+ new WeakHashMap();
+
+ public static AnimatorProxy wrap(View view) {
+ AnimatorProxy proxy = PROXIES.get(view);
+ if (proxy == null) {
+ proxy = new AnimatorProxy(view);
+ PROXIES.put(view, proxy);
+ }
+ return proxy;
+ }
+
+ private final WeakReference mView;
+
+ private float mAlpha = 1;
+ private float mScaleX = 1;
+ private float mScaleY = 1;
+ private float mTranslationX;
+ private float mTranslationY;
+
+ private final RectF mBefore = new RectF();
+ private final RectF mAfter = new RectF();
+ private final Matrix mTempMatrix = new Matrix();
+
+ private AnimatorProxy(View view) {
+ setDuration(0); //perform transformation immediately
+ setFillAfter(true); //persist transformation beyond duration
+ view.setAnimation(this);
+ mView = new WeakReference(view);
+ }
+
+ public float getAlpha() {
+ return mAlpha;
+ }
+ public void setAlpha(float alpha) {
+ if (mAlpha != alpha) {
+ mAlpha = alpha;
+ View view = mView.get();
+ if (view != null) {
+ view.invalidate();
+ }
+ }
+ }
+ public float getScaleX() {
+ return mScaleX;
+ }
+ public void setScaleX(float scaleX) {
+ if (mScaleX != scaleX) {
+ prepareForUpdate();
+ mScaleX = scaleX;
+ invalidateAfterUpdate();
+ }
+ }
+ public float getScaleY() {
+ return mScaleY;
+ }
+ public void setScaleY(float scaleY) {
+ if (mScaleY != scaleY) {
+ prepareForUpdate();
+ mScaleY = scaleY;
+ invalidateAfterUpdate();
+ }
+ }
+ public int getScrollX() {
+ View view = mView.get();
+ if (view == null) {
+ return 0;
+ }
+ return view.getScrollX();
+ }
+ public void setScrollX(int value) {
+ View view = mView.get();
+ if (view != null) {
+ view.scrollTo(value, view.getScrollY());
+ }
+ }
+ public int getScrollY() {
+ View view = mView.get();
+ if (view == null) {
+ return 0;
+ }
+ return view.getScrollY();
+ }
+ public void setScrollY(int value) {
+ View view = mView.get();
+ if (view != null) {
+ view.scrollTo(view.getScrollY(), value);
+ }
+ }
+
+ public float getTranslationX() {
+ return mTranslationX;
+ }
+ public void setTranslationX(float translationX) {
+ if (mTranslationX != translationX) {
+ prepareForUpdate();
+ mTranslationX = translationX;
+ invalidateAfterUpdate();
+ }
+ }
+ public float getTranslationY() {
+ return mTranslationY;
+ }
+ public void setTranslationY(float translationY) {
+ if (mTranslationY != translationY) {
+ prepareForUpdate();
+ mTranslationY = translationY;
+ invalidateAfterUpdate();
+ }
+ }
+
+ private void prepareForUpdate() {
+ View view = mView.get();
+ if (view != null) {
+ computeRect(mBefore, view);
+ }
+ }
+ private void invalidateAfterUpdate() {
+ View view = mView.get();
+ if (view == null) {
+ return;
+ }
+ View parent = (View)view.getParent();
+ if (parent == null) {
+ return;
+ }
+
+ view.setAnimation(this);
+
+ final RectF after = mAfter;
+ computeRect(after, view);
+ after.union(mBefore);
+
+ parent.invalidate(
+ (int) FloatMath.floor(after.left),
+ (int) FloatMath.floor(after.top),
+ (int) FloatMath.ceil(after.right),
+ (int) FloatMath.ceil(after.bottom));
+ }
+
+ private void computeRect(final RectF r, View view) {
+ // compute current rectangle according to matrix transformation
+ final float w = view.getWidth();
+ final float h = view.getHeight();
+
+ // use a rectangle at 0,0 to make sure we don't run into issues with scaling
+ r.set(0, 0, w, h);
+
+ final Matrix m = mTempMatrix;
+ m.reset();
+ transformMatrix(m, view);
+ mTempMatrix.mapRect(r);
+
+ r.offset(view.getLeft(), view.getTop());
+
+ // Straighten coords if rotations flipped them
+ if (r.right < r.left) {
+ final float f = r.right;
+ r.right = r.left;
+ r.left = f;
+ }
+ if (r.bottom < r.top) {
+ final float f = r.top;
+ r.top = r.bottom;
+ r.bottom = f;
+ }
+ }
+
+ private void transformMatrix(Matrix m, View view) {
+ final float w = view.getWidth();
+ final float h = view.getHeight();
+
+ final float sX = mScaleX;
+ final float sY = mScaleY;
+ if ((sX != 1.0f) || (sY != 1.0f)) {
+ final float deltaSX = ((sX * w) - w) / 2f;
+ final float deltaSY = ((sY * h) - h) / 2f;
+ m.postScale(sX, sY);
+ m.postTranslate(-deltaSX, -deltaSY);
+ }
+ m.postTranslate(mTranslationX, mTranslationY);
+ }
+
+ @Override
+ protected void applyTransformation(float interpolatedTime, Transformation t) {
+ View view = mView.get();
+ if (view != null) {
+ t.setAlpha(mAlpha);
+ transformMatrix(t.getMatrix(), view);
+ }
+ }
+
+ @Override
+ public void reset() {
+ /* Do nothing. */
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineFrameLayout.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineFrameLayout.java
new file mode 100644
index 0000000000..953e3e8444
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineFrameLayout.java
@@ -0,0 +1,57 @@
+package com.actionbarsherlock.internal.nineoldandroids.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy;
+
+public class NineFrameLayout extends FrameLayout {
+ private final AnimatorProxy mProxy;
+
+ public NineFrameLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ if (mProxy != null) {
+ if (visibility == GONE) {
+ clearAnimation();
+ } else if (visibility == VISIBLE) {
+ setAnimation(mProxy);
+ }
+ }
+ super.setVisibility(visibility);
+ }
+
+ public float getAlpha() {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ return mProxy.getAlpha();
+ } else {
+ return super.getAlpha();
+ }
+ }
+ public void setAlpha(float alpha) {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ mProxy.setAlpha(alpha);
+ } else {
+ super.setAlpha(alpha);
+ }
+ }
+ public float getTranslationY() {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ return mProxy.getTranslationY();
+ } else {
+ return super.getTranslationY();
+ }
+ }
+ public void setTranslationY(float translationY) {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ mProxy.setTranslationY(translationY);
+ } else {
+ super.setTranslationY(translationY);
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineHorizontalScrollView.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineHorizontalScrollView.java
new file mode 100644
index 0000000000..129b5aaaa6
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineHorizontalScrollView.java
@@ -0,0 +1,41 @@
+package com.actionbarsherlock.internal.nineoldandroids.widget;
+
+import android.content.Context;
+import android.widget.HorizontalScrollView;
+import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy;
+
+public class NineHorizontalScrollView extends HorizontalScrollView {
+ private final AnimatorProxy mProxy;
+
+ public NineHorizontalScrollView(Context context) {
+ super(context);
+ mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ if (mProxy != null) {
+ if (visibility == GONE) {
+ clearAnimation();
+ } else if (visibility == VISIBLE) {
+ setAnimation(mProxy);
+ }
+ }
+ super.setVisibility(visibility);
+ }
+
+ public float getAlpha() {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ return mProxy.getAlpha();
+ } else {
+ return super.getAlpha();
+ }
+ }
+ public void setAlpha(float alpha) {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ mProxy.setAlpha(alpha);
+ } else {
+ super.setAlpha(alpha);
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineLinearLayout.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineLinearLayout.java
new file mode 100644
index 0000000000..1f381013a7
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineLinearLayout.java
@@ -0,0 +1,57 @@
+package com.actionbarsherlock.internal.nineoldandroids.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy;
+
+public class NineLinearLayout extends LinearLayout {
+ private final AnimatorProxy mProxy;
+
+ public NineLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ if (mProxy != null) {
+ if (visibility == GONE) {
+ clearAnimation();
+ } else if (visibility == VISIBLE) {
+ setAnimation(mProxy);
+ }
+ }
+ super.setVisibility(visibility);
+ }
+
+ public float getAlpha() {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ return mProxy.getAlpha();
+ } else {
+ return super.getAlpha();
+ }
+ }
+ public void setAlpha(float alpha) {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ mProxy.setAlpha(alpha);
+ } else {
+ super.setAlpha(alpha);
+ }
+ }
+ public float getTranslationX() {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ return mProxy.getTranslationX();
+ } else {
+ return super.getTranslationX();
+ }
+ }
+ public void setTranslationX(float translationX) {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ mProxy.setTranslationX(translationX);
+ } else {
+ super.setTranslationX(translationX);
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/ActionProviderWrapper.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/ActionProviderWrapper.java
new file mode 100644
index 0000000000..b136d50f07
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/ActionProviderWrapper.java
@@ -0,0 +1,40 @@
+package com.actionbarsherlock.internal.view;
+
+import com.actionbarsherlock.internal.view.menu.SubMenuWrapper;
+import com.actionbarsherlock.view.ActionProvider;
+import android.view.View;
+
+public class ActionProviderWrapper extends android.view.ActionProvider {
+ private final ActionProvider mProvider;
+
+
+ public ActionProviderWrapper(ActionProvider provider) {
+ super(null/*TODO*/); //XXX this *should* be unused
+ mProvider = provider;
+ }
+
+
+ public ActionProvider unwrap() {
+ return mProvider;
+ }
+
+ @Override
+ public View onCreateActionView() {
+ return mProvider.onCreateActionView();
+ }
+
+ @Override
+ public boolean hasSubMenu() {
+ return mProvider.hasSubMenu();
+ }
+
+ @Override
+ public boolean onPerformDefaultAction() {
+ return mProvider.onPerformDefaultAction();
+ }
+
+ @Override
+ public void onPrepareSubMenu(android.view.SubMenu subMenu) {
+ mProvider.onPrepareSubMenu(new SubMenuWrapper(subMenu));
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/StandaloneActionMode.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/StandaloneActionMode.java
new file mode 100644
index 0000000000..0a87bd3f79
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/StandaloneActionMode.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.actionbarsherlock.internal.view;
+
+import android.content.Context;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+
+import java.lang.ref.WeakReference;
+
+import com.actionbarsherlock.internal.view.menu.MenuBuilder;
+import com.actionbarsherlock.internal.view.menu.MenuPopupHelper;
+import com.actionbarsherlock.internal.view.menu.SubMenuBuilder;
+import com.actionbarsherlock.internal.widget.ActionBarContextView;
+import com.actionbarsherlock.view.ActionMode;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+
+public class StandaloneActionMode extends ActionMode implements MenuBuilder.Callback {
+ private Context mContext;
+ private ActionBarContextView mContextView;
+ private ActionMode.Callback mCallback;
+ private WeakReference mCustomView;
+ private boolean mFinished;
+ private boolean mFocusable;
+
+ private MenuBuilder mMenu;
+
+ public StandaloneActionMode(Context context, ActionBarContextView view,
+ ActionMode.Callback callback, boolean isFocusable) {
+ mContext = context;
+ mContextView = view;
+ mCallback = callback;
+
+ mMenu = new MenuBuilder(context).setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ mMenu.setCallback(this);
+ mFocusable = isFocusable;
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ mContextView.setTitle(title);
+ }
+
+ @Override
+ public void setSubtitle(CharSequence subtitle) {
+ mContextView.setSubtitle(subtitle);
+ }
+
+ @Override
+ public void setTitle(int resId) {
+ setTitle(mContext.getString(resId));
+ }
+
+ @Override
+ public void setSubtitle(int resId) {
+ setSubtitle(mContext.getString(resId));
+ }
+
+ @Override
+ public void setCustomView(View view) {
+ mContextView.setCustomView(view);
+ mCustomView = view != null ? new WeakReference(view) : null;
+ }
+
+ @Override
+ public void invalidate() {
+ mCallback.onPrepareActionMode(this, mMenu);
+ }
+
+ @Override
+ public void finish() {
+ if (mFinished) {
+ return;
+ }
+ mFinished = true;
+
+ mContextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ mCallback.onDestroyActionMode(this);
+ }
+
+ @Override
+ public Menu getMenu() {
+ return mMenu;
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mContextView.getTitle();
+ }
+
+ @Override
+ public CharSequence getSubtitle() {
+ return mContextView.getSubtitle();
+ }
+
+ @Override
+ public View getCustomView() {
+ return mCustomView != null ? mCustomView.get() : null;
+ }
+
+ @Override
+ public MenuInflater getMenuInflater() {
+ return new MenuInflater(mContext);
+ }
+
+ public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
+ return mCallback.onActionItemClicked(this, item);
+ }
+
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ }
+
+ public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+ if (!subMenu.hasVisibleItems()) {
+ return true;
+ }
+
+ new MenuPopupHelper(mContext, subMenu).show();
+ return true;
+ }
+
+ public void onCloseSubMenu(SubMenuBuilder menu) {
+ }
+
+ public void onMenuModeChange(MenuBuilder menu) {
+ invalidate();
+ mContextView.showOverflowMenu();
+ }
+
+ public boolean isUiFocusable() {
+ return mFocusable;
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/View_HasStateListenerSupport.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/View_HasStateListenerSupport.java
new file mode 100644
index 0000000000..7d45e81be7
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/View_HasStateListenerSupport.java
@@ -0,0 +1,6 @@
+package com.actionbarsherlock.internal.view;
+
+public interface View_HasStateListenerSupport {
+ void addOnAttachStateChangeListener(View_OnAttachStateChangeListener listener);
+ void removeOnAttachStateChangeListener(View_OnAttachStateChangeListener listener);
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/View_OnAttachStateChangeListener.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/View_OnAttachStateChangeListener.java
new file mode 100644
index 0000000000..3869d32907
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/View_OnAttachStateChangeListener.java
@@ -0,0 +1,8 @@
+package com.actionbarsherlock.internal.view;
+
+import android.view.View;
+
+public interface View_OnAttachStateChangeListener {
+ void onViewAttachedToWindow(View v);
+ void onViewDetachedFromWindow(View v);
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenu.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenu.java
new file mode 100644
index 0000000000..0354ad1ad1
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenu.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.view.menu;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.view.KeyEvent;
+
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.SubMenu;
+
+/**
+ * @hide
+ */
+public class ActionMenu implements Menu {
+ private Context mContext;
+
+ private boolean mIsQwerty;
+
+ private ArrayList mItems;
+
+ public ActionMenu(Context context) {
+ mContext = context;
+ mItems = new ArrayList();
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public MenuItem add(CharSequence title) {
+ return add(0, 0, 0, title);
+ }
+
+ public MenuItem add(int titleRes) {
+ return add(0, 0, 0, titleRes);
+ }
+
+ public MenuItem add(int groupId, int itemId, int order, int titleRes) {
+ return add(groupId, itemId, order, mContext.getResources().getString(titleRes));
+ }
+
+ public MenuItem add(int groupId, int itemId, int order, CharSequence title) {
+ ActionMenuItem item = new ActionMenuItem(getContext(),
+ groupId, itemId, 0, order, title);
+ mItems.add(order, item);
+ return item;
+ }
+
+ public int addIntentOptions(int groupId, int itemId, int order,
+ ComponentName caller, Intent[] specifics, Intent intent, int flags,
+ MenuItem[] outSpecificItems) {
+ PackageManager pm = mContext.getPackageManager();
+ final List lri =
+ pm.queryIntentActivityOptions(caller, specifics, intent, 0);
+ final int N = lri != null ? lri.size() : 0;
+
+ if ((flags & FLAG_APPEND_TO_GROUP) == 0) {
+ removeGroup(groupId);
+ }
+
+ for (int i=0; i= 0) {
+ outSpecificItems[ri.specificIndex] = item;
+ }
+ }
+
+ return N;
+ }
+
+ public SubMenu addSubMenu(CharSequence title) {
+ // TODO Implement submenus
+ return null;
+ }
+
+ public SubMenu addSubMenu(int titleRes) {
+ // TODO Implement submenus
+ return null;
+ }
+
+ public SubMenu addSubMenu(int groupId, int itemId, int order,
+ CharSequence title) {
+ // TODO Implement submenus
+ return null;
+ }
+
+ public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) {
+ // TODO Implement submenus
+ return null;
+ }
+
+ public void clear() {
+ mItems.clear();
+ }
+
+ public void close() {
+ }
+
+ private int findItemIndex(int id) {
+ final ArrayList items = mItems;
+ final int itemCount = items.size();
+ for (int i = 0; i < itemCount; i++) {
+ if (items.get(i).getItemId() == id) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ public MenuItem findItem(int id) {
+ return mItems.get(findItemIndex(id));
+ }
+
+ public MenuItem getItem(int index) {
+ return mItems.get(index);
+ }
+
+ public boolean hasVisibleItems() {
+ final ArrayList items = mItems;
+ final int itemCount = items.size();
+
+ for (int i = 0; i < itemCount; i++) {
+ if (items.get(i).isVisible()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private ActionMenuItem findItemWithShortcut(int keyCode, KeyEvent event) {
+ // TODO Make this smarter.
+ final boolean qwerty = mIsQwerty;
+ final ArrayList items = mItems;
+ final int itemCount = items.size();
+
+ for (int i = 0; i < itemCount; i++) {
+ ActionMenuItem item = items.get(i);
+ final char shortcut = qwerty ? item.getAlphabeticShortcut() :
+ item.getNumericShortcut();
+ if (keyCode == shortcut) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ public boolean isShortcutKey(int keyCode, KeyEvent event) {
+ return findItemWithShortcut(keyCode, event) != null;
+ }
+
+ public boolean performIdentifierAction(int id, int flags) {
+ final int index = findItemIndex(id);
+ if (index < 0) {
+ return false;
+ }
+
+ return mItems.get(index).invoke();
+ }
+
+ public boolean performShortcut(int keyCode, KeyEvent event, int flags) {
+ ActionMenuItem item = findItemWithShortcut(keyCode, event);
+ if (item == null) {
+ return false;
+ }
+
+ return item.invoke();
+ }
+
+ public void removeGroup(int groupId) {
+ final ArrayList items = mItems;
+ int itemCount = items.size();
+ int i = 0;
+ while (i < itemCount) {
+ if (items.get(i).getGroupId() == groupId) {
+ items.remove(i);
+ itemCount--;
+ } else {
+ i++;
+ }
+ }
+ }
+
+ public void removeItem(int id) {
+ mItems.remove(findItemIndex(id));
+ }
+
+ public void setGroupCheckable(int group, boolean checkable,
+ boolean exclusive) {
+ final ArrayList items = mItems;
+ final int itemCount = items.size();
+
+ for (int i = 0; i < itemCount; i++) {
+ ActionMenuItem item = items.get(i);
+ if (item.getGroupId() == group) {
+ item.setCheckable(checkable);
+ item.setExclusiveCheckable(exclusive);
+ }
+ }
+ }
+
+ public void setGroupEnabled(int group, boolean enabled) {
+ final ArrayList items = mItems;
+ final int itemCount = items.size();
+
+ for (int i = 0; i < itemCount; i++) {
+ ActionMenuItem item = items.get(i);
+ if (item.getGroupId() == group) {
+ item.setEnabled(enabled);
+ }
+ }
+ }
+
+ public void setGroupVisible(int group, boolean visible) {
+ final ArrayList items = mItems;
+ final int itemCount = items.size();
+
+ for (int i = 0; i < itemCount; i++) {
+ ActionMenuItem item = items.get(i);
+ if (item.getGroupId() == group) {
+ item.setVisible(visible);
+ }
+ }
+ }
+
+ public void setQwertyMode(boolean isQwerty) {
+ mIsQwerty = isQwerty;
+ }
+
+ public int size() {
+ return mItems.size();
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuItem.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuItem.java
new file mode 100644
index 0000000000..510b974886
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuItem.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.view.menu;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View;
+
+import com.actionbarsherlock.view.ActionProvider;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.SubMenu;
+
+/**
+ * @hide
+ */
+public class ActionMenuItem implements MenuItem {
+ private final int mId;
+ private final int mGroup;
+ //UNUSED private final int mCategoryOrder;
+ private final int mOrdering;
+
+ private CharSequence mTitle;
+ private CharSequence mTitleCondensed;
+ private Intent mIntent;
+ private char mShortcutNumericChar;
+ private char mShortcutAlphabeticChar;
+
+ private Drawable mIconDrawable;
+ //UNUSED private int mIconResId = NO_ICON;
+
+ private Context mContext;
+
+ private MenuItem.OnMenuItemClickListener mClickListener;
+
+ //UNUSED private static final int NO_ICON = 0;
+
+ private int mFlags = ENABLED;
+ private static final int CHECKABLE = 0x00000001;
+ private static final int CHECKED = 0x00000002;
+ private static final int EXCLUSIVE = 0x00000004;
+ private static final int HIDDEN = 0x00000008;
+ private static final int ENABLED = 0x00000010;
+
+ public ActionMenuItem(Context context, int group, int id, int categoryOrder, int ordering,
+ CharSequence title) {
+ mContext = context;
+ mId = id;
+ mGroup = group;
+ //UNUSED mCategoryOrder = categoryOrder;
+ mOrdering = ordering;
+ mTitle = title;
+ }
+
+ public char getAlphabeticShortcut() {
+ return mShortcutAlphabeticChar;
+ }
+
+ public int getGroupId() {
+ return mGroup;
+ }
+
+ public Drawable getIcon() {
+ return mIconDrawable;
+ }
+
+ public Intent getIntent() {
+ return mIntent;
+ }
+
+ public int getItemId() {
+ return mId;
+ }
+
+ public ContextMenuInfo getMenuInfo() {
+ return null;
+ }
+
+ public char getNumericShortcut() {
+ return mShortcutNumericChar;
+ }
+
+ public int getOrder() {
+ return mOrdering;
+ }
+
+ public SubMenu getSubMenu() {
+ return null;
+ }
+
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ public CharSequence getTitleCondensed() {
+ return mTitleCondensed;
+ }
+
+ public boolean hasSubMenu() {
+ return false;
+ }
+
+ public boolean isCheckable() {
+ return (mFlags & CHECKABLE) != 0;
+ }
+
+ public boolean isChecked() {
+ return (mFlags & CHECKED) != 0;
+ }
+
+ public boolean isEnabled() {
+ return (mFlags & ENABLED) != 0;
+ }
+
+ public boolean isVisible() {
+ return (mFlags & HIDDEN) == 0;
+ }
+
+ public MenuItem setAlphabeticShortcut(char alphaChar) {
+ mShortcutAlphabeticChar = alphaChar;
+ return this;
+ }
+
+ public MenuItem setCheckable(boolean checkable) {
+ mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0);
+ return this;
+ }
+
+ public ActionMenuItem setExclusiveCheckable(boolean exclusive) {
+ mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0);
+ return this;
+ }
+
+ public MenuItem setChecked(boolean checked) {
+ mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0);
+ return this;
+ }
+
+ public MenuItem setEnabled(boolean enabled) {
+ mFlags = (mFlags & ~ENABLED) | (enabled ? ENABLED : 0);
+ return this;
+ }
+
+ public MenuItem setIcon(Drawable icon) {
+ mIconDrawable = icon;
+ //UNUSED mIconResId = NO_ICON;
+ return this;
+ }
+
+ public MenuItem setIcon(int iconRes) {
+ //UNUSED mIconResId = iconRes;
+ mIconDrawable = mContext.getResources().getDrawable(iconRes);
+ return this;
+ }
+
+ public MenuItem setIntent(Intent intent) {
+ mIntent = intent;
+ return this;
+ }
+
+ public MenuItem setNumericShortcut(char numericChar) {
+ mShortcutNumericChar = numericChar;
+ return this;
+ }
+
+ public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) {
+ mClickListener = menuItemClickListener;
+ return this;
+ }
+
+ public MenuItem setShortcut(char numericChar, char alphaChar) {
+ mShortcutNumericChar = numericChar;
+ mShortcutAlphabeticChar = alphaChar;
+ return this;
+ }
+
+ public MenuItem setTitle(CharSequence title) {
+ mTitle = title;
+ return this;
+ }
+
+ public MenuItem setTitle(int title) {
+ mTitle = mContext.getResources().getString(title);
+ return this;
+ }
+
+ public MenuItem setTitleCondensed(CharSequence title) {
+ mTitleCondensed = title;
+ return this;
+ }
+
+ public MenuItem setVisible(boolean visible) {
+ mFlags = (mFlags & HIDDEN) | (visible ? 0 : HIDDEN);
+ return this;
+ }
+
+ public boolean invoke() {
+ if (mClickListener != null && mClickListener.onMenuItemClick(this)) {
+ return true;
+ }
+
+ if (mIntent != null) {
+ mContext.startActivity(mIntent);
+ return true;
+ }
+
+ return false;
+ }
+
+ public void setShowAsAction(int show) {
+ // Do nothing. ActionMenuItems always show as action buttons.
+ }
+
+ public MenuItem setActionView(View actionView) {
+ throw new UnsupportedOperationException();
+ }
+
+ public View getActionView() {
+ return null;
+ }
+
+ @Override
+ public MenuItem setActionView(int resId) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ActionProvider getActionProvider() {
+ return null;
+ }
+
+ @Override
+ public MenuItem setActionProvider(ActionProvider actionProvider) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public MenuItem setShowAsActionFlags(int actionEnum) {
+ setShowAsAction(actionEnum);
+ return this;
+ }
+
+ @Override
+ public boolean expandActionView() {
+ return false;
+ }
+
+ @Override
+ public boolean collapseActionView() {
+ return false;
+ }
+
+ @Override
+ public boolean isActionViewExpanded() {
+ return false;
+ }
+
+ @Override
+ public MenuItem setOnActionExpandListener(OnActionExpandListener listener) {
+ // No need to save the listener; ActionMenuItem does not support collapsing items.
+ return this;
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuItemView.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuItemView.java
new file mode 100644
index 0000000000..dcb50f3621
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuItemView.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.view.menu;
+
+import java.util.HashSet;
+import java.util.Set;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.Toast;
+
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.internal.view.View_HasStateListenerSupport;
+import com.actionbarsherlock.internal.view.View_OnAttachStateChangeListener;
+import com.actionbarsherlock.internal.widget.CapitalizingButton;
+
+import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getBoolean;
+
+/**
+ * @hide
+ */
+public class ActionMenuItemView extends LinearLayout
+ implements MenuView.ItemView, View.OnClickListener, View.OnLongClickListener,
+ ActionMenuView.ActionMenuChildView, View_HasStateListenerSupport {
+ //UNUSED private static final String TAG = "ActionMenuItemView";
+
+ private MenuItemImpl mItemData;
+ private CharSequence mTitle;
+ private MenuBuilder.ItemInvoker mItemInvoker;
+
+ private ImageButton mImageButton;
+ private CapitalizingButton mTextButton;
+ private boolean mAllowTextWithIcon;
+ private boolean mExpandedFormat;
+ private int mMinWidth;
+
+ private final Set mListeners = new HashSet();
+
+ public ActionMenuItemView(Context context) {
+ this(context, null);
+ }
+
+ public ActionMenuItemView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) {
+ //TODO super(context, attrs, defStyle);
+ super(context, attrs);
+ mAllowTextWithIcon = getResources_getBoolean(context,
+ R.bool.abs__config_allowActionMenuItemTextWithIcon);
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.SherlockActionMenuItemView, 0, 0);
+ mMinWidth = a.getDimensionPixelSize(
+ R.styleable.SherlockActionMenuItemView_android_minWidth, 0);
+ a.recycle();
+ }
+
+ @Override
+ public void addOnAttachStateChangeListener(View_OnAttachStateChangeListener listener) {
+ mListeners.add(listener);
+ }
+
+ @Override
+ public void removeOnAttachStateChangeListener(View_OnAttachStateChangeListener listener) {
+ mListeners.remove(listener);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ for (View_OnAttachStateChangeListener listener : mListeners) {
+ listener.onViewAttachedToWindow(this);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ for (View_OnAttachStateChangeListener listener : mListeners) {
+ listener.onViewDetachedFromWindow(this);
+ }
+ }
+
+ @Override
+ public void onFinishInflate() {
+
+ mImageButton = (ImageButton) findViewById(R.id.abs__imageButton);
+ mTextButton = (CapitalizingButton) findViewById(R.id.abs__textButton);
+ mImageButton.setOnClickListener(this);
+ mTextButton.setOnClickListener(this);
+ mImageButton.setOnLongClickListener(this);
+ setOnClickListener(this);
+ setOnLongClickListener(this);
+ }
+
+ public MenuItemImpl getItemData() {
+ return mItemData;
+ }
+
+ public void initialize(MenuItemImpl itemData, int menuType) {
+ mItemData = itemData;
+
+ setIcon(itemData.getIcon());
+ setTitle(itemData.getTitleForItemView(this)); // Title only takes effect if there is no icon
+ setId(itemData.getItemId());
+
+ setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
+ setEnabled(itemData.isEnabled());
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ mImageButton.setEnabled(enabled);
+ mTextButton.setEnabled(enabled);
+ }
+
+ public void onClick(View v) {
+ if (mItemInvoker != null) {
+ mItemInvoker.invokeItem(mItemData);
+ }
+ }
+
+ public void setItemInvoker(MenuBuilder.ItemInvoker invoker) {
+ mItemInvoker = invoker;
+ }
+
+ public boolean prefersCondensedTitle() {
+ return true;
+ }
+
+ public void setCheckable(boolean checkable) {
+ // TODO Support checkable action items
+ }
+
+ public void setChecked(boolean checked) {
+ // TODO Support checkable action items
+ }
+
+ public void setExpandedFormat(boolean expandedFormat) {
+ if (mExpandedFormat != expandedFormat) {
+ mExpandedFormat = expandedFormat;
+ if (mItemData != null) {
+ mItemData.actionFormatChanged();
+ }
+ }
+ }
+
+ private void updateTextButtonVisibility() {
+ boolean visible = !TextUtils.isEmpty(mTextButton.getText());
+ visible &= mImageButton.getDrawable() == null ||
+ (mItemData.showsTextAsAction() && (mAllowTextWithIcon || mExpandedFormat));
+
+ mTextButton.setVisibility(visible ? VISIBLE : GONE);
+ }
+
+ public void setIcon(Drawable icon) {
+ mImageButton.setImageDrawable(icon);
+ if (icon != null) {
+ mImageButton.setVisibility(VISIBLE);
+ } else {
+ mImageButton.setVisibility(GONE);
+ }
+
+ updateTextButtonVisibility();
+ }
+
+ public boolean hasText() {
+ return mTextButton.getVisibility() != GONE;
+ }
+
+ public void setShortcut(boolean showShortcut, char shortcutKey) {
+ // Action buttons don't show text for shortcut keys.
+ }
+
+ public void setTitle(CharSequence title) {
+ mTitle = title;
+
+ mTextButton.setTextCompat(mTitle);
+
+ setContentDescription(mTitle);
+ updateTextButtonVisibility();
+ }
+
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ onPopulateAccessibilityEvent(event);
+ return true;
+ }
+
+ @Override
+ public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ super.onPopulateAccessibilityEvent(event);
+ }
+ final CharSequence cdesc = getContentDescription();
+ if (!TextUtils.isEmpty(cdesc)) {
+ event.getText().add(cdesc);
+ }
+ }
+
+ @Override
+ public boolean dispatchHoverEvent(MotionEvent event) {
+ // Don't allow children to hover; we want this to be treated as a single component.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ return onHoverEvent(event);
+ }
+ return false;
+ }
+
+ public boolean showsIcon() {
+ return true;
+ }
+
+ public boolean needsDividerBefore() {
+ return hasText() && mItemData.getIcon() == null;
+ }
+
+ public boolean needsDividerAfter() {
+ return hasText();
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (hasText()) {
+ // Don't show the cheat sheet for items that already show text.
+ return false;
+ }
+
+ final int[] screenPos = new int[2];
+ final Rect displayFrame = new Rect();
+ getLocationOnScreen(screenPos);
+ getWindowVisibleDisplayFrame(displayFrame);
+
+ final Context context = getContext();
+ final int width = getWidth();
+ final int height = getHeight();
+ final int midy = screenPos[1] + height / 2;
+ final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
+
+ Toast cheatSheet = Toast.makeText(context, mItemData.getTitle(), Toast.LENGTH_SHORT);
+ if (midy < displayFrame.height()) {
+ // Show along the top; follow action buttons
+ cheatSheet.setGravity(Gravity.TOP | Gravity.RIGHT,
+ screenWidth - screenPos[0] - width / 2, height);
+ } else {
+ // Show along the bottom center
+ cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
+ }
+ cheatSheet.show();
+ return true;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ final int specSize = MeasureSpec.getSize(widthMeasureSpec);
+ final int oldMeasuredWidth = getMeasuredWidth();
+ final int targetWidth = widthMode == MeasureSpec.AT_MOST ? Math.min(specSize, mMinWidth)
+ : mMinWidth;
+
+ if (widthMode != MeasureSpec.EXACTLY && mMinWidth > 0 && oldMeasuredWidth < targetWidth) {
+ // Remeasure at exactly the minimum width.
+ super.onMeasure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
+ heightMeasureSpec);
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuPresenter.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuPresenter.java
new file mode 100644
index 0000000000..876a22c589
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuPresenter.java
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.view.menu;
+
+import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getInteger;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseBooleanArray;
+import android.view.SoundEffectConstants;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.internal.view.View_HasStateListenerSupport;
+import com.actionbarsherlock.internal.view.View_OnAttachStateChangeListener;
+import com.actionbarsherlock.internal.view.menu.ActionMenuView.ActionMenuChildView;
+import com.actionbarsherlock.view.ActionProvider;
+import com.actionbarsherlock.view.MenuItem;
+
+/**
+ * MenuPresenter for building action menus as seen in the action bar and action modes.
+ */
+public class ActionMenuPresenter extends BaseMenuPresenter
+ implements ActionProvider.SubUiVisibilityListener {
+ //UNUSED private static final String TAG = "ActionMenuPresenter";
+
+ private View mOverflowButton;
+ private boolean mReserveOverflow;
+ private boolean mReserveOverflowSet;
+ private int mWidthLimit;
+ private int mActionItemWidthLimit;
+ private int mMaxItems;
+ private boolean mMaxItemsSet;
+ private boolean mStrictWidthLimit;
+ private boolean mWidthLimitSet;
+ private boolean mExpandedActionViewsExclusive;
+
+ private int mMinCellSize;
+
+ // Group IDs that have been added as actions - used temporarily, allocated here for reuse.
+ private final SparseBooleanArray mActionButtonGroups = new SparseBooleanArray();
+
+ private View mScrapActionButtonView;
+
+ private OverflowPopup mOverflowPopup;
+ private ActionButtonSubmenu mActionButtonPopup;
+
+ private OpenOverflowRunnable mPostedOpenRunnable;
+
+ final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback();
+ int mOpenSubMenuId;
+
+ public ActionMenuPresenter(Context context) {
+ super(context, R.layout.abs__action_menu_layout,
+ R.layout.abs__action_menu_item_layout);
+ }
+
+ @Override
+ public void initForMenu(Context context, MenuBuilder menu) {
+ super.initForMenu(context, menu);
+
+ final Resources res = context.getResources();
+
+ if (!mReserveOverflowSet) {
+ mReserveOverflow = reserveOverflow(mContext);
+ }
+
+ if (!mWidthLimitSet) {
+ mWidthLimit = res.getDisplayMetrics().widthPixels / 2;
+ }
+
+ // Measure for initial configuration
+ if (!mMaxItemsSet) {
+ mMaxItems = getResources_getInteger(context, R.integer.abs__max_action_buttons);
+ }
+
+ int width = mWidthLimit;
+ if (mReserveOverflow) {
+ if (mOverflowButton == null) {
+ mOverflowButton = new OverflowMenuButton(mSystemContext);
+ final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ mOverflowButton.measure(spec, spec);
+ }
+ width -= mOverflowButton.getMeasuredWidth();
+ } else {
+ mOverflowButton = null;
+ }
+
+ mActionItemWidthLimit = width;
+
+ mMinCellSize = (int) (ActionMenuView.MIN_CELL_SIZE * res.getDisplayMetrics().density);
+
+ // Drop a scrap view as it may no longer reflect the proper context/config.
+ mScrapActionButtonView = null;
+ }
+
+ public static boolean reserveOverflow(Context context) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB);
+ } else {
+ return !HasPermanentMenuKey.get(context);
+ }
+ }
+
+ private static class HasPermanentMenuKey {
+ public static boolean get(Context context) {
+ return ViewConfiguration.get(context).hasPermanentMenuKey();
+ }
+ }
+
+ public void onConfigurationChanged(Configuration newConfig) {
+ if (!mMaxItemsSet) {
+ mMaxItems = getResources_getInteger(mContext,
+ R.integer.abs__max_action_buttons);
+ if (mMenu != null) {
+ mMenu.onItemsChanged(true);
+ }
+ }
+ }
+
+ public void setWidthLimit(int width, boolean strict) {
+ mWidthLimit = width;
+ mStrictWidthLimit = strict;
+ mWidthLimitSet = true;
+ }
+
+ public void setReserveOverflow(boolean reserveOverflow) {
+ mReserveOverflow = reserveOverflow;
+ mReserveOverflowSet = true;
+ }
+
+ public void setItemLimit(int itemCount) {
+ mMaxItems = itemCount;
+ mMaxItemsSet = true;
+ }
+
+ public void setExpandedActionViewsExclusive(boolean isExclusive) {
+ mExpandedActionViewsExclusive = isExclusive;
+ }
+
+ @Override
+ public MenuView getMenuView(ViewGroup root) {
+ MenuView result = super.getMenuView(root);
+ ((ActionMenuView) result).setPresenter(this);
+ return result;
+ }
+
+ @Override
+ public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) {
+ View actionView = item.getActionView();
+ if (actionView == null || item.hasCollapsibleActionView()) {
+ if (!(convertView instanceof ActionMenuItemView)) {
+ convertView = null;
+ }
+ actionView = super.getItemView(item, convertView, parent);
+ }
+ actionView.setVisibility(item.isActionViewExpanded() ? View.GONE : View.VISIBLE);
+
+ final ActionMenuView menuParent = (ActionMenuView) parent;
+ final ViewGroup.LayoutParams lp = actionView.getLayoutParams();
+ if (!menuParent.checkLayoutParams(lp)) {
+ actionView.setLayoutParams(menuParent.generateLayoutParams(lp));
+ }
+ return actionView;
+ }
+
+ @Override
+ public void bindItemView(MenuItemImpl item, MenuView.ItemView itemView) {
+ itemView.initialize(item, 0);
+
+ final ActionMenuView menuView = (ActionMenuView) mMenuView;
+ ActionMenuItemView actionItemView = (ActionMenuItemView) itemView;
+ actionItemView.setItemInvoker(menuView);
+ }
+
+ @Override
+ public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) {
+ return item.isActionButton();
+ }
+
+ @Override
+ public void updateMenuView(boolean cleared) {
+ super.updateMenuView(cleared);
+
+ if (mMenu != null) {
+ final ArrayList actionItems = mMenu.getActionItems();
+ final int count = actionItems.size();
+ for (int i = 0; i < count; i++) {
+ final ActionProvider provider = actionItems.get(i).getActionProvider();
+ if (provider != null) {
+ provider.setSubUiVisibilityListener(this);
+ }
+ }
+ }
+
+ final ArrayList nonActionItems = mMenu != null ?
+ mMenu.getNonActionItems() : null;
+
+ boolean hasOverflow = false;
+ if (mReserveOverflow && nonActionItems != null) {
+ final int count = nonActionItems.size();
+ if (count == 1) {
+ hasOverflow = !nonActionItems.get(0).isActionViewExpanded();
+ } else {
+ hasOverflow = count > 0;
+ }
+ }
+
+ if (hasOverflow) {
+ if (mOverflowButton == null) {
+ mOverflowButton = new OverflowMenuButton(mSystemContext);
+ }
+ ViewGroup parent = (ViewGroup) mOverflowButton.getParent();
+ if (parent != mMenuView) {
+ if (parent != null) {
+ parent.removeView(mOverflowButton);
+ }
+ ActionMenuView menuView = (ActionMenuView) mMenuView;
+ menuView.addView(mOverflowButton, menuView.generateOverflowButtonLayoutParams());
+ }
+ } else if (mOverflowButton != null && mOverflowButton.getParent() == mMenuView) {
+ ((ViewGroup) mMenuView).removeView(mOverflowButton);
+ }
+
+ ((ActionMenuView) mMenuView).setOverflowReserved(mReserveOverflow);
+ }
+
+ @Override
+ public boolean filterLeftoverView(ViewGroup parent, int childIndex) {
+ if (parent.getChildAt(childIndex) == mOverflowButton) return false;
+ return super.filterLeftoverView(parent, childIndex);
+ }
+
+ public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+ if (!subMenu.hasVisibleItems()) return false;
+
+ SubMenuBuilder topSubMenu = subMenu;
+ while (topSubMenu.getParentMenu() != mMenu) {
+ topSubMenu = (SubMenuBuilder) topSubMenu.getParentMenu();
+ }
+ View anchor = findViewForItem(topSubMenu.getItem());
+ if (anchor == null) {
+ if (mOverflowButton == null) return false;
+ anchor = mOverflowButton;
+ }
+
+ mOpenSubMenuId = subMenu.getItem().getItemId();
+ mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu);
+ mActionButtonPopup.setAnchorView(anchor);
+ mActionButtonPopup.show();
+ super.onSubMenuSelected(subMenu);
+ return true;
+ }
+
+ private View findViewForItem(MenuItem item) {
+ final ViewGroup parent = (ViewGroup) mMenuView;
+ if (parent == null) return null;
+
+ final int count = parent.getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = parent.getChildAt(i);
+ if (child instanceof MenuView.ItemView &&
+ ((MenuView.ItemView) child).getItemData() == item) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Display the overflow menu if one is present.
+ * @return true if the overflow menu was shown, false otherwise.
+ */
+ public boolean showOverflowMenu() {
+ if (mReserveOverflow && !isOverflowMenuShowing() && mMenu != null && mMenuView != null &&
+ mPostedOpenRunnable == null && !mMenu.getNonActionItems().isEmpty()) {
+ OverflowPopup popup = new OverflowPopup(mContext, mMenu, mOverflowButton, true);
+ mPostedOpenRunnable = new OpenOverflowRunnable(popup);
+ // Post this for later; we might still need a layout for the anchor to be right.
+ ((View) mMenuView).post(mPostedOpenRunnable);
+
+ // ActionMenuPresenter uses null as a callback argument here
+ // to indicate overflow is opening.
+ super.onSubMenuSelected(null);
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Hide the overflow menu if it is currently showing.
+ *
+ * @return true if the overflow menu was hidden, false otherwise.
+ */
+ public boolean hideOverflowMenu() {
+ if (mPostedOpenRunnable != null && mMenuView != null) {
+ ((View) mMenuView).removeCallbacks(mPostedOpenRunnable);
+ mPostedOpenRunnable = null;
+ return true;
+ }
+
+ MenuPopupHelper popup = mOverflowPopup;
+ if (popup != null) {
+ popup.dismiss();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Dismiss all popup menus - overflow and submenus.
+ * @return true if popups were dismissed, false otherwise. (This can be because none were open.)
+ */
+ public boolean dismissPopupMenus() {
+ boolean result = hideOverflowMenu();
+ result |= hideSubMenus();
+ return result;
+ }
+
+ /**
+ * Dismiss all submenu popups.
+ *
+ * @return true if popups were dismissed, false otherwise. (This can be because none were open.)
+ */
+ public boolean hideSubMenus() {
+ if (mActionButtonPopup != null) {
+ mActionButtonPopup.dismiss();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @return true if the overflow menu is currently showing
+ */
+ public boolean isOverflowMenuShowing() {
+ return mOverflowPopup != null && mOverflowPopup.isShowing();
+ }
+
+ /**
+ * @return true if space has been reserved in the action menu for an overflow item.
+ */
+ public boolean isOverflowReserved() {
+ return mReserveOverflow;
+ }
+
+ public boolean flagActionItems() {
+ final ArrayList visibleItems = mMenu.getVisibleItems();
+ final int itemsSize = visibleItems.size();
+ int maxActions = mMaxItems;
+ int widthLimit = mActionItemWidthLimit;
+ final int querySpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final ViewGroup parent = (ViewGroup) mMenuView;
+
+ int requiredItems = 0;
+ int requestedItems = 0;
+ int firstActionWidth = 0;
+ boolean hasOverflow = false;
+ for (int i = 0; i < itemsSize; i++) {
+ MenuItemImpl item = visibleItems.get(i);
+ if (item.requiresActionButton()) {
+ requiredItems++;
+ } else if (item.requestsActionButton()) {
+ requestedItems++;
+ } else {
+ hasOverflow = true;
+ }
+ if (mExpandedActionViewsExclusive && item.isActionViewExpanded()) {
+ // Overflow everything if we have an expanded action view and we're
+ // space constrained.
+ maxActions = 0;
+ }
+ }
+
+ // Reserve a spot for the overflow item if needed.
+ if (mReserveOverflow &&
+ (hasOverflow || requiredItems + requestedItems > maxActions)) {
+ maxActions--;
+ }
+ maxActions -= requiredItems;
+
+ final SparseBooleanArray seenGroups = mActionButtonGroups;
+ seenGroups.clear();
+
+ int cellSize = 0;
+ int cellsRemaining = 0;
+ if (mStrictWidthLimit) {
+ cellsRemaining = widthLimit / mMinCellSize;
+ final int cellSizeRemaining = widthLimit % mMinCellSize;
+ cellSize = mMinCellSize + cellSizeRemaining / cellsRemaining;
+ }
+
+ // Flag as many more requested items as will fit.
+ for (int i = 0; i < itemsSize; i++) {
+ MenuItemImpl item = visibleItems.get(i);
+
+ if (item.requiresActionButton()) {
+ View v = getItemView(item, mScrapActionButtonView, parent);
+ if (mScrapActionButtonView == null) {
+ mScrapActionButtonView = v;
+ }
+ if (mStrictWidthLimit) {
+ cellsRemaining -= ActionMenuView.measureChildForCells(v,
+ cellSize, cellsRemaining, querySpec, 0);
+ } else {
+ v.measure(querySpec, querySpec);
+ }
+ final int measuredWidth = v.getMeasuredWidth();
+ widthLimit -= measuredWidth;
+ if (firstActionWidth == 0) {
+ firstActionWidth = measuredWidth;
+ }
+ final int groupId = item.getGroupId();
+ if (groupId != 0) {
+ seenGroups.put(groupId, true);
+ }
+ item.setIsActionButton(true);
+ } else if (item.requestsActionButton()) {
+ // Items in a group with other items that already have an action slot
+ // can break the max actions rule, but not the width limit.
+ final int groupId = item.getGroupId();
+ final boolean inGroup = seenGroups.get(groupId);
+ boolean isAction = (maxActions > 0 || inGroup) && widthLimit > 0 &&
+ (!mStrictWidthLimit || cellsRemaining > 0);
+
+ if (isAction) {
+ View v = getItemView(item, mScrapActionButtonView, parent);
+ if (mScrapActionButtonView == null) {
+ mScrapActionButtonView = v;
+ }
+ if (mStrictWidthLimit) {
+ final int cells = ActionMenuView.measureChildForCells(v,
+ cellSize, cellsRemaining, querySpec, 0);
+ cellsRemaining -= cells;
+ if (cells == 0) {
+ isAction = false;
+ }
+ } else {
+ v.measure(querySpec, querySpec);
+ }
+ final int measuredWidth = v.getMeasuredWidth();
+ widthLimit -= measuredWidth;
+ if (firstActionWidth == 0) {
+ firstActionWidth = measuredWidth;
+ }
+
+ if (mStrictWidthLimit) {
+ isAction &= widthLimit >= 0;
+ } else {
+ // Did this push the entire first item past the limit?
+ isAction &= widthLimit + firstActionWidth > 0;
+ }
+ }
+
+ if (isAction && groupId != 0) {
+ seenGroups.put(groupId, true);
+ } else if (inGroup) {
+ // We broke the width limit. Demote the whole group, they all overflow now.
+ seenGroups.put(groupId, false);
+ for (int j = 0; j < i; j++) {
+ MenuItemImpl areYouMyGroupie = visibleItems.get(j);
+ if (areYouMyGroupie.getGroupId() == groupId) {
+ // Give back the action slot
+ if (areYouMyGroupie.isActionButton()) maxActions++;
+ areYouMyGroupie.setIsActionButton(false);
+ }
+ }
+ }
+
+ if (isAction) maxActions--;
+
+ item.setIsActionButton(isAction);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ dismissPopupMenus();
+ super.onCloseMenu(menu, allMenusAreClosing);
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ SavedState state = new SavedState();
+ state.openSubMenuId = mOpenSubMenuId;
+ return state;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ SavedState saved = (SavedState) state;
+ if (saved.openSubMenuId > 0) {
+ MenuItem item = mMenu.findItem(saved.openSubMenuId);
+ if (item != null) {
+ SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
+ onSubMenuSelected(subMenu);
+ }
+ }
+ }
+
+ @Override
+ public void onSubUiVisibilityChanged(boolean isVisible) {
+ if (isVisible) {
+ // Not a submenu, but treat it like one.
+ super.onSubMenuSelected(null);
+ } else {
+ mMenu.close(false);
+ }
+ }
+
+ private static class SavedState implements Parcelable {
+ public int openSubMenuId;
+
+ SavedState() {
+ }
+
+ SavedState(Parcel in) {
+ openSubMenuId = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(openSubMenuId);
+ }
+
+ @SuppressWarnings("unused")
+ public static final Parcelable.Creator CREATOR
+ = new Parcelable.Creator() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+ private class OverflowMenuButton extends ImageButton implements ActionMenuChildView, View_HasStateListenerSupport {
+ private final Set mListeners = new HashSet();
+
+ public OverflowMenuButton(Context context) {
+ super(context, null, R.attr.actionOverflowButtonStyle);
+
+ setClickable(true);
+ setFocusable(true);
+ setVisibility(VISIBLE);
+ setEnabled(true);
+ }
+
+ @Override
+ public boolean performClick() {
+ if (super.performClick()) {
+ return true;
+ }
+
+ playSoundEffect(SoundEffectConstants.CLICK);
+ showOverflowMenu();
+ return true;
+ }
+
+ public boolean needsDividerBefore() {
+ return false;
+ }
+
+ public boolean needsDividerAfter() {
+ return false;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ for (View_OnAttachStateChangeListener listener : mListeners) {
+ listener.onViewAttachedToWindow(this);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ for (View_OnAttachStateChangeListener listener : mListeners) {
+ listener.onViewDetachedFromWindow(this);
+ }
+
+ if (mOverflowPopup != null) mOverflowPopup.dismiss();
+ }
+
+ @Override
+ public void addOnAttachStateChangeListener(View_OnAttachStateChangeListener listener) {
+ mListeners.add(listener);
+ }
+
+ @Override
+ public void removeOnAttachStateChangeListener(View_OnAttachStateChangeListener listener) {
+ mListeners.remove(listener);
+ }
+ }
+
+ private class OverflowPopup extends MenuPopupHelper {
+ public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
+ boolean overflowOnly) {
+ super(context, menu, anchorView, overflowOnly);
+ setCallback(mPopupPresenterCallback);
+ }
+
+ @Override
+ public void onDismiss() {
+ super.onDismiss();
+ mMenu.close();
+ mOverflowPopup = null;
+ }
+ }
+
+ private class ActionButtonSubmenu extends MenuPopupHelper {
+ //UNUSED private SubMenuBuilder mSubMenu;
+
+ public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) {
+ super(context, subMenu);
+ //UNUSED mSubMenu = subMenu;
+
+ MenuItemImpl item = (MenuItemImpl) subMenu.getItem();
+ if (!item.isActionButton()) {
+ // Give a reasonable anchor to nested submenus.
+ setAnchorView(mOverflowButton == null ? (View) mMenuView : mOverflowButton);
+ }
+
+ setCallback(mPopupPresenterCallback);
+
+ boolean preserveIconSpacing = false;
+ final int count = subMenu.size();
+ for (int i = 0; i < count; i++) {
+ MenuItem childItem = subMenu.getItem(i);
+ if (childItem.isVisible() && childItem.getIcon() != null) {
+ preserveIconSpacing = true;
+ break;
+ }
+ }
+ setForceShowIcon(preserveIconSpacing);
+ }
+
+ @Override
+ public void onDismiss() {
+ super.onDismiss();
+ mActionButtonPopup = null;
+ mOpenSubMenuId = 0;
+ }
+ }
+
+ private class PopupPresenterCallback implements MenuPresenter.Callback {
+
+ @Override
+ public boolean onOpenSubMenu(MenuBuilder subMenu) {
+ if (subMenu == null) return false;
+
+ mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId();
+ return false;
+ }
+
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ if (menu instanceof SubMenuBuilder) {
+ ((SubMenuBuilder) menu).getRootMenu().close(false);
+ }
+ }
+ }
+
+ private class OpenOverflowRunnable implements Runnable {
+ private OverflowPopup mPopup;
+
+ public OpenOverflowRunnable(OverflowPopup popup) {
+ mPopup = popup;
+ }
+
+ public void run() {
+ mMenu.changeMenuMode();
+ final View menuView = (View) mMenuView;
+ if (menuView != null && menuView.getWindowToken() != null && mPopup.tryShow()) {
+ mOverflowPopup = mPopup;
+ }
+ mPostedOpenRunnable = null;
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuView.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuView.java
new file mode 100644
index 0000000000..0e3b1ae0d7
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ActionMenuView.java
@@ -0,0 +1,575 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.actionbarsherlock.internal.view.menu;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.LinearLayout;
+import com.actionbarsherlock.internal.widget.IcsLinearLayout;
+
+/**
+ * @hide
+ */
+public class ActionMenuView extends IcsLinearLayout implements MenuBuilder.ItemInvoker, MenuView {
+ //UNUSED private static final String TAG = "ActionMenuView";
+ private static final boolean IS_FROYO = Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;
+
+ static final int MIN_CELL_SIZE = 56; // dips
+ static final int GENERATED_ITEM_PADDING = 4; // dips
+
+ private MenuBuilder mMenu;
+
+ private boolean mReserveOverflow;
+ private ActionMenuPresenter mPresenter;
+ private boolean mFormatItems;
+ private int mFormatItemsWidth;
+ private int mMinCellSize;
+ private int mGeneratedItemPadding;
+ //UNUSED private int mMeasuredExtraWidth;
+
+ private boolean mFirst = true;
+
+ public ActionMenuView(Context context) {
+ this(context, null);
+ }
+
+ public ActionMenuView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setBaselineAligned(false);
+ final float density = context.getResources().getDisplayMetrics().density;
+ mMinCellSize = (int) (MIN_CELL_SIZE * density);
+ mGeneratedItemPadding = (int) (GENERATED_ITEM_PADDING * density);
+ }
+
+ public void setPresenter(ActionMenuPresenter presenter) {
+ mPresenter = presenter;
+ }
+
+ public boolean isExpandedFormat() {
+ return mFormatItems;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ if (IS_FROYO) {
+ super.onConfigurationChanged(newConfig);
+ }
+ mPresenter.updateMenuView(false);
+
+ if (mPresenter != null && mPresenter.isOverflowMenuShowing()) {
+ mPresenter.hideOverflowMenu();
+ mPresenter.showOverflowMenu();
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ //Need to trigger a relayout since we may have been added extremely
+ //late in the initial rendering (e.g., when contained in a ViewPager).
+ //See: https://github.com/JakeWharton/ActionBarSherlock/issues/272
+ if (!IS_FROYO && mFirst) {
+ mFirst = false;
+ requestLayout();
+ return;
+ }
+ super.onDraw(canvas);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // If we've been given an exact size to match, apply special formatting during layout.
+ final boolean wasFormatted = mFormatItems;
+ mFormatItems = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY;
+
+ if (wasFormatted != mFormatItems) {
+ mFormatItemsWidth = 0; // Reset this when switching modes
+ }
+
+ // Special formatting can change whether items can fit as action buttons.
+ // Kick the menu and update presenters when this changes.
+ final int widthSize = MeasureSpec.getMode(widthMeasureSpec);
+ if (mFormatItems && mMenu != null && widthSize != mFormatItemsWidth) {
+ mFormatItemsWidth = widthSize;
+ mMenu.onItemsChanged(true);
+ }
+
+ if (mFormatItems) {
+ onMeasureExactFormat(widthMeasureSpec, heightMeasureSpec);
+ } else {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ private void onMeasureExactFormat(int widthMeasureSpec, int heightMeasureSpec) {
+ // We already know the width mode is EXACTLY if we're here.
+ final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+
+ final int widthPadding = getPaddingLeft() + getPaddingRight();
+ final int heightPadding = getPaddingTop() + getPaddingBottom();
+
+ widthSize -= widthPadding;
+
+ // Divide the view into cells.
+ final int cellCount = widthSize / mMinCellSize;
+ final int cellSizeRemaining = widthSize % mMinCellSize;
+
+ if (cellCount == 0) {
+ // Give up, nothing fits.
+ setMeasuredDimension(widthSize, 0);
+ return;
+ }
+
+ final int cellSize = mMinCellSize + cellSizeRemaining / cellCount;
+
+ int cellsRemaining = cellCount;
+ int maxChildHeight = 0;
+ int maxCellsUsed = 0;
+ int expandableItemCount = 0;
+ int visibleItemCount = 0;
+ boolean hasOverflow = false;
+
+ // This is used as a bitfield to locate the smallest items present. Assumes childCount < 64.
+ long smallestItemsAt = 0;
+
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == GONE) continue;
+
+ final boolean isGeneratedItem = child instanceof ActionMenuItemView;
+ visibleItemCount++;
+
+ if (isGeneratedItem) {
+ // Reset padding for generated menu item views; it may change below
+ // and views are recycled.
+ child.setPadding(mGeneratedItemPadding, 0, mGeneratedItemPadding, 0);
+ }
+
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ lp.expanded = false;
+ lp.extraPixels = 0;
+ lp.cellsUsed = 0;
+ lp.expandable = false;
+ lp.leftMargin = 0;
+ lp.rightMargin = 0;
+ lp.preventEdgeOffset = isGeneratedItem && ((ActionMenuItemView) child).hasText();
+
+ // Overflow always gets 1 cell. No more, no less.
+ final int cellsAvailable = lp.isOverflowButton ? 1 : cellsRemaining;
+
+ final int cellsUsed = measureChildForCells(child, cellSize, cellsAvailable,
+ heightMeasureSpec, heightPadding);
+
+ maxCellsUsed = Math.max(maxCellsUsed, cellsUsed);
+ if (lp.expandable) expandableItemCount++;
+ if (lp.isOverflowButton) hasOverflow = true;
+
+ cellsRemaining -= cellsUsed;
+ maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
+ if (cellsUsed == 1) smallestItemsAt |= (1 << i);
+ }
+
+ // When we have overflow and a single expanded (text) item, we want to try centering it
+ // visually in the available space even though overflow consumes some of it.
+ final boolean centerSingleExpandedItem = hasOverflow && visibleItemCount == 2;
+
+ // Divide space for remaining cells if we have items that can expand.
+ // Try distributing whole leftover cells to smaller items first.
+
+ boolean needsExpansion = false;
+ while (expandableItemCount > 0 && cellsRemaining > 0) {
+ int minCells = Integer.MAX_VALUE;
+ long minCellsAt = 0; // Bit locations are indices of relevant child views
+ int minCellsItemCount = 0;
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ // Don't try to expand items that shouldn't.
+ if (!lp.expandable) continue;
+
+ // Mark indices of children that can receive an extra cell.
+ if (lp.cellsUsed < minCells) {
+ minCells = lp.cellsUsed;
+ minCellsAt = 1 << i;
+ minCellsItemCount = 1;
+ } else if (lp.cellsUsed == minCells) {
+ minCellsAt |= 1 << i;
+ minCellsItemCount++;
+ }
+ }
+
+ // Items that get expanded will always be in the set of smallest items when we're done.
+ smallestItemsAt |= minCellsAt;
+
+ if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop.
+
+ // We have enough cells, all minimum size items will be incremented.
+ minCells++;
+
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if ((minCellsAt & (1 << i)) == 0) {
+ // If this item is already at our small item count, mark it for later.
+ if (lp.cellsUsed == minCells) smallestItemsAt |= 1 << i;
+ continue;
+ }
+
+ if (centerSingleExpandedItem && lp.preventEdgeOffset && cellsRemaining == 1) {
+ // Add padding to this item such that it centers.
+ child.setPadding(mGeneratedItemPadding + cellSize, 0, mGeneratedItemPadding, 0);
+ }
+ lp.cellsUsed++;
+ lp.expanded = true;
+ cellsRemaining--;
+ }
+
+ needsExpansion = true;
+ }
+
+ // Divide any space left that wouldn't divide along cell boundaries
+ // evenly among the smallest items
+
+ final boolean singleItem = !hasOverflow && visibleItemCount == 1;
+ if (cellsRemaining > 0 && smallestItemsAt != 0 &&
+ (cellsRemaining < visibleItemCount - 1 || singleItem || maxCellsUsed > 1)) {
+ float expandCount = Long.bitCount(smallestItemsAt);
+
+ if (!singleItem) {
+ // The items at the far edges may only expand by half in order to pin to either side.
+ if ((smallestItemsAt & 1) != 0) {
+ LayoutParams lp = (LayoutParams) getChildAt(0).getLayoutParams();
+ if (!lp.preventEdgeOffset) expandCount -= 0.5f;
+ }
+ if ((smallestItemsAt & (1 << (childCount - 1))) != 0) {
+ LayoutParams lp = ((LayoutParams) getChildAt(childCount - 1).getLayoutParams());
+ if (!lp.preventEdgeOffset) expandCount -= 0.5f;
+ }
+ }
+
+ final int extraPixels = expandCount > 0 ?
+ (int) (cellsRemaining * cellSize / expandCount) : 0;
+
+ for (int i = 0; i < childCount; i++) {
+ if ((smallestItemsAt & (1 << i)) == 0) continue;
+
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (child instanceof ActionMenuItemView) {
+ // If this is one of our views, expand and measure at the larger size.
+ lp.extraPixels = extraPixels;
+ lp.expanded = true;
+ if (i == 0 && !lp.preventEdgeOffset) {
+ // First item gets part of its new padding pushed out of sight.
+ // The last item will get this implicitly from layout.
+ lp.leftMargin = -extraPixels / 2;
+ }
+ needsExpansion = true;
+ } else if (lp.isOverflowButton) {
+ lp.extraPixels = extraPixels;
+ lp.expanded = true;
+ lp.rightMargin = -extraPixels / 2;
+ needsExpansion = true;
+ } else {
+ // If we don't know what it is, give it some margins instead
+ // and let it center within its space. We still want to pin
+ // against the edges.
+ if (i != 0) {
+ lp.leftMargin = extraPixels / 2;
+ }
+ if (i != childCount - 1) {
+ lp.rightMargin = extraPixels / 2;
+ }
+ }
+ }
+
+ cellsRemaining = 0;
+ }
+
+ // Remeasure any items that have had extra space allocated to them.
+ if (needsExpansion) {
+ int heightSpec = MeasureSpec.makeMeasureSpec(heightSize - heightPadding, heightMode);
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if (!lp.expanded) continue;
+
+ final int width = lp.cellsUsed * cellSize + lp.extraPixels;
+ child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), heightSpec);
+ }
+ }
+
+ if (heightMode != MeasureSpec.EXACTLY) {
+ heightSize = maxChildHeight;
+ }
+
+ setMeasuredDimension(widthSize, heightSize);
+ //UNUSED mMeasuredExtraWidth = cellsRemaining * cellSize;
+ }
+
+ /**
+ * Measure a child view to fit within cell-based formatting. The child's width
+ * will be measured to a whole multiple of cellSize.
+ *
+ *
Sets the expandable and cellsUsed fields of LayoutParams.
+ *
+ * @param child Child to measure
+ * @param cellSize Size of one cell
+ * @param cellsRemaining Number of cells remaining that this view can expand to fill
+ * @param parentHeightMeasureSpec MeasureSpec used by the parent view
+ * @param parentHeightPadding Padding present in the parent view
+ * @return Number of cells this child was measured to occupy
+ */
+ static int measureChildForCells(View child, int cellSize, int cellsRemaining,
+ int parentHeightMeasureSpec, int parentHeightPadding) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ final int childHeightSize = MeasureSpec.getSize(parentHeightMeasureSpec) -
+ parentHeightPadding;
+ final int childHeightMode = MeasureSpec.getMode(parentHeightMeasureSpec);
+ final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode);
+
+ int cellsUsed = 0;
+ if (cellsRemaining > 0) {
+ final int childWidthSpec = MeasureSpec.makeMeasureSpec(
+ cellSize * cellsRemaining, MeasureSpec.AT_MOST);
+ child.measure(childWidthSpec, childHeightSpec);
+
+ final int measuredWidth = child.getMeasuredWidth();
+ cellsUsed = measuredWidth / cellSize;
+ if (measuredWidth % cellSize != 0) cellsUsed++;
+ }
+
+ final ActionMenuItemView itemView = child instanceof ActionMenuItemView ?
+ (ActionMenuItemView) child : null;
+ final boolean expandable = !lp.isOverflowButton && itemView != null && itemView.hasText();
+ lp.expandable = expandable;
+
+ lp.cellsUsed = cellsUsed;
+ final int targetWidth = cellsUsed * cellSize;
+ child.measure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
+ childHeightSpec);
+ return cellsUsed;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (!mFormatItems) {
+ super.onLayout(changed, left, top, right, bottom);
+ return;
+ }
+
+ final int childCount = getChildCount();
+ final int midVertical = (top + bottom) / 2;
+ final int dividerWidth = 0;//getDividerWidth();
+ int overflowWidth = 0;
+ //UNUSED int nonOverflowWidth = 0;
+ int nonOverflowCount = 0;
+ int widthRemaining = right - left - getPaddingRight() - getPaddingLeft();
+ boolean hasOverflow = false;
+ for (int i = 0; i < childCount; i++) {
+ final View v = getChildAt(i);
+ if (v.getVisibility() == GONE) {
+ continue;
+ }
+
+ LayoutParams p = (LayoutParams) v.getLayoutParams();
+ if (p.isOverflowButton) {
+ overflowWidth = v.getMeasuredWidth();
+ if (hasDividerBeforeChildAt(i)) {
+ overflowWidth += dividerWidth;
+ }
+
+ int height = v.getMeasuredHeight();
+ int r = getWidth() - getPaddingRight() - p.rightMargin;
+ int l = r - overflowWidth;
+ int t = midVertical - (height / 2);
+ int b = t + height;
+ v.layout(l, t, r, b);
+
+ widthRemaining -= overflowWidth;
+ hasOverflow = true;
+ } else {
+ final int size = v.getMeasuredWidth() + p.leftMargin + p.rightMargin;
+ //UNUSED nonOverflowWidth += size;
+ widthRemaining -= size;
+ //if (hasDividerBeforeChildAt(i)) {
+ //UNUSED nonOverflowWidth += dividerWidth;
+ //}
+ nonOverflowCount++;
+ }
+ }
+
+ if (childCount == 1 && !hasOverflow) {
+ // Center a single child
+ final View v = getChildAt(0);
+ final int width = v.getMeasuredWidth();
+ final int height = v.getMeasuredHeight();
+ final int midHorizontal = (right - left) / 2;
+ final int l = midHorizontal - width / 2;
+ final int t = midVertical - height / 2;
+ v.layout(l, t, l + width, t + height);
+ return;
+ }
+
+ final int spacerCount = nonOverflowCount - (hasOverflow ? 0 : 1);
+ final int spacerSize = Math.max(0, spacerCount > 0 ? widthRemaining / spacerCount : 0);
+
+ int startLeft = getPaddingLeft();
+ for (int i = 0; i < childCount; i++) {
+ final View v = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) v.getLayoutParams();
+ if (v.getVisibility() == GONE || lp.isOverflowButton) {
+ continue;
+ }
+
+ startLeft += lp.leftMargin;
+ int width = v.getMeasuredWidth();
+ int height = v.getMeasuredHeight();
+ int t = midVertical - height / 2;
+ v.layout(startLeft, t, startLeft + width, t + height);
+ startLeft += width + lp.rightMargin + spacerSize;
+ }
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mPresenter.dismissPopupMenus();
+ }
+
+ public boolean isOverflowReserved() {
+ return mReserveOverflow;
+ }
+
+ public void setOverflowReserved(boolean reserveOverflow) {
+ mReserveOverflow = reserveOverflow;
+ }
+
+ @Override
+ protected LayoutParams generateDefaultLayoutParams() {
+ LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+ params.gravity = Gravity.CENTER_VERTICAL;
+ return params;
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ if (p instanceof LayoutParams) {
+ LayoutParams result = new LayoutParams((LayoutParams) p);
+ if (result.gravity <= Gravity.NO_GRAVITY) {
+ result.gravity = Gravity.CENTER_VERTICAL;
+ }
+ return result;
+ }
+ return generateDefaultLayoutParams();
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p != null && p instanceof LayoutParams;
+ }
+
+ public LayoutParams generateOverflowButtonLayoutParams() {
+ LayoutParams result = generateDefaultLayoutParams();
+ result.isOverflowButton = true;
+ return result;
+ }
+
+ public boolean invokeItem(MenuItemImpl item) {
+ return mMenu.performItemAction(item, 0);
+ }
+
+ public int getWindowAnimations() {
+ return 0;
+ }
+
+ public void initialize(MenuBuilder menu) {
+ mMenu = menu;
+ }
+
+ //@Override
+ protected boolean hasDividerBeforeChildAt(int childIndex) {
+ if (childIndex == 0) {
+ return false;
+ }
+ final View childBefore = getChildAt(childIndex - 1);
+ final View child = getChildAt(childIndex);
+ boolean result = false;
+ if (childIndex < getChildCount() && childBefore instanceof ActionMenuChildView) {
+ result |= ((ActionMenuChildView) childBefore).needsDividerAfter();
+ }
+ if (childIndex > 0 && child instanceof ActionMenuChildView) {
+ result |= ((ActionMenuChildView) child).needsDividerBefore();
+ }
+ return result;
+ }
+
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ return false;
+ }
+
+ public interface ActionMenuChildView {
+ public boolean needsDividerBefore();
+ public boolean needsDividerAfter();
+ }
+
+ public static class LayoutParams extends LinearLayout.LayoutParams {
+ public boolean isOverflowButton;
+ public int cellsUsed;
+ public int extraPixels;
+ public boolean expandable;
+ public boolean preventEdgeOffset;
+
+ public boolean expanded;
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ }
+
+ public LayoutParams(LayoutParams other) {
+ super((LinearLayout.LayoutParams) other);
+ isOverflowButton = other.isOverflowButton;
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ isOverflowButton = false;
+ }
+
+ public LayoutParams(int width, int height, boolean isOverflowButton) {
+ super(width, height);
+ this.isOverflowButton = isOverflowButton;
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/BaseMenuPresenter.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/BaseMenuPresenter.java
new file mode 100644
index 0000000000..6da26f2ae7
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/BaseMenuPresenter.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.view.menu;
+
+import java.util.ArrayList;
+import android.content.Context;
+import android.os.Build;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Base class for MenuPresenters that have a consistent container view and item
+ * views. Behaves similarly to an AdapterView in that existing item views will
+ * be reused if possible when items change.
+ */
+public abstract class BaseMenuPresenter implements MenuPresenter {
+ private static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
+
+ protected Context mSystemContext;
+ protected Context mContext;
+ protected MenuBuilder mMenu;
+ protected LayoutInflater mSystemInflater;
+ protected LayoutInflater mInflater;
+ private Callback mCallback;
+
+ private int mMenuLayoutRes;
+ private int mItemLayoutRes;
+
+ protected MenuView mMenuView;
+
+ private int mId;
+
+ /**
+ * Construct a new BaseMenuPresenter.
+ *
+ * @param context Context for generating system-supplied views
+ * @param menuLayoutRes Layout resource ID for the menu container view
+ * @param itemLayoutRes Layout resource ID for a single item view
+ */
+ public BaseMenuPresenter(Context context, int menuLayoutRes, int itemLayoutRes) {
+ mSystemContext = context;
+ mSystemInflater = LayoutInflater.from(context);
+ mMenuLayoutRes = menuLayoutRes;
+ mItemLayoutRes = itemLayoutRes;
+ }
+
+ @Override
+ public void initForMenu(Context context, MenuBuilder menu) {
+ mContext = context;
+ mInflater = LayoutInflater.from(mContext);
+ mMenu = menu;
+ }
+
+ @Override
+ public MenuView getMenuView(ViewGroup root) {
+ if (mMenuView == null) {
+ mMenuView = (MenuView) mSystemInflater.inflate(mMenuLayoutRes, root, false);
+ mMenuView.initialize(mMenu);
+ updateMenuView(true);
+ }
+
+ return mMenuView;
+ }
+
+ /**
+ * Reuses item views when it can
+ */
+ public void updateMenuView(boolean cleared) {
+ final ViewGroup parent = (ViewGroup) mMenuView;
+ if (parent == null) return;
+
+ int childIndex = 0;
+ if (mMenu != null) {
+ mMenu.flagActionItems();
+ ArrayList visibleItems = mMenu.getVisibleItems();
+ final int itemCount = visibleItems.size();
+ for (int i = 0; i < itemCount; i++) {
+ MenuItemImpl item = visibleItems.get(i);
+ if (shouldIncludeItem(childIndex, item)) {
+ final View convertView = parent.getChildAt(childIndex);
+ final MenuItemImpl oldItem = convertView instanceof MenuView.ItemView ?
+ ((MenuView.ItemView) convertView).getItemData() : null;
+ final View itemView = getItemView(item, convertView, parent);
+ if (item != oldItem) {
+ // Don't let old states linger with new data.
+ itemView.setPressed(false);
+ if (IS_HONEYCOMB) itemView.jumpDrawablesToCurrentState();
+ }
+ if (itemView != convertView) {
+ addItemView(itemView, childIndex);
+ }
+ childIndex++;
+ }
+ }
+ }
+
+ // Remove leftover views.
+ while (childIndex < parent.getChildCount()) {
+ if (!filterLeftoverView(parent, childIndex)) {
+ childIndex++;
+ }
+ }
+ }
+
+ /**
+ * Add an item view at the given index.
+ *
+ * @param itemView View to add
+ * @param childIndex Index within the parent to insert at
+ */
+ protected void addItemView(View itemView, int childIndex) {
+ final ViewGroup currentParent = (ViewGroup) itemView.getParent();
+ if (currentParent != null) {
+ currentParent.removeView(itemView);
+ }
+ ((ViewGroup) mMenuView).addView(itemView, childIndex);
+ }
+
+ /**
+ * Filter the child view at index and remove it if appropriate.
+ * @param parent Parent to filter from
+ * @param childIndex Index to filter
+ * @return true if the child view at index was removed
+ */
+ protected boolean filterLeftoverView(ViewGroup parent, int childIndex) {
+ parent.removeViewAt(childIndex);
+ return true;
+ }
+
+ public void setCallback(Callback cb) {
+ mCallback = cb;
+ }
+
+ /**
+ * Create a new item view that can be re-bound to other item data later.
+ *
+ * @return The new item view
+ */
+ public MenuView.ItemView createItemView(ViewGroup parent) {
+ return (MenuView.ItemView) mSystemInflater.inflate(mItemLayoutRes, parent, false);
+ }
+
+ /**
+ * Prepare an item view for use. See AdapterView for the basic idea at work here.
+ * This may require creating a new item view, but well-behaved implementations will
+ * re-use the view passed as convertView if present. The returned view will be populated
+ * with data from the item parameter.
+ *
+ * @param item Item to present
+ * @param convertView Existing view to reuse
+ * @param parent Intended parent view - use for inflation.
+ * @return View that presents the requested menu item
+ */
+ public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) {
+ MenuView.ItemView itemView;
+ if (convertView instanceof MenuView.ItemView) {
+ itemView = (MenuView.ItemView) convertView;
+ } else {
+ itemView = createItemView(parent);
+ }
+ bindItemView(item, itemView);
+ return (View) itemView;
+ }
+
+ /**
+ * Bind item data to an existing item view.
+ *
+ * @param item Item to bind
+ * @param itemView View to populate with item data
+ */
+ public abstract void bindItemView(MenuItemImpl item, MenuView.ItemView itemView);
+
+ /**
+ * Filter item by child index and item data.
+ *
+ * @param childIndex Indended presentation index of this item
+ * @param item Item to present
+ * @return true if this item should be included in this menu presentation; false otherwise
+ */
+ public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) {
+ return true;
+ }
+
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ if (mCallback != null) {
+ mCallback.onCloseMenu(menu, allMenusAreClosing);
+ }
+ }
+
+ public boolean onSubMenuSelected(SubMenuBuilder menu) {
+ if (mCallback != null) {
+ return mCallback.onOpenSubMenu(menu);
+ }
+ return false;
+ }
+
+ public boolean flagActionItems() {
+ return false;
+ }
+
+ public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
+ return false;
+ }
+
+ public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
+ return false;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public void setId(int id) {
+ mId = id;
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ListMenuItemView.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ListMenuItemView.java
new file mode 100644
index 0000000000..ac25c37369
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/ListMenuItemView.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.view.menu;
+
+import com.actionbarsherlock.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RadioButton;
+import android.widget.TextView;
+
+/**
+ * The item view for each item in the ListView-based MenuViews.
+ */
+public class ListMenuItemView extends LinearLayout implements MenuView.ItemView {
+ private MenuItemImpl mItemData;
+
+ private ImageView mIconView;
+ private RadioButton mRadioButton;
+ private TextView mTitleView;
+ private CheckBox mCheckBox;
+ private TextView mShortcutView;
+
+ private Drawable mBackground;
+ private int mTextAppearance;
+ private Context mTextAppearanceContext;
+ private boolean mPreserveIconSpacing;
+
+ //UNUSED private int mMenuType;
+
+ private LayoutInflater mInflater;
+
+ private boolean mForceShowIcon;
+
+ final Context mContext;
+
+ public ListMenuItemView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs);
+ mContext = context;
+
+ TypedArray a =
+ context.obtainStyledAttributes(
+ attrs, R.styleable.SherlockMenuView, defStyle, 0);
+
+ mBackground = a.getDrawable(R.styleable.SherlockMenuView_itemBackground);
+ mTextAppearance = a.getResourceId(R.styleable.
+ SherlockMenuView_itemTextAppearance, -1);
+ mPreserveIconSpacing = a.getBoolean(
+ R.styleable.SherlockMenuView_preserveIconSpacing, false);
+ mTextAppearanceContext = context;
+
+ a.recycle();
+ }
+
+ public ListMenuItemView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ setBackgroundDrawable(mBackground);
+
+ mTitleView = (TextView) findViewById(R.id.abs__title);
+ if (mTextAppearance != -1) {
+ mTitleView.setTextAppearance(mTextAppearanceContext,
+ mTextAppearance);
+ }
+
+ mShortcutView = (TextView) findViewById(R.id.abs__shortcut);
+ }
+
+ public void initialize(MenuItemImpl itemData, int menuType) {
+ mItemData = itemData;
+ //UNUSED mMenuType = menuType;
+
+ setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
+
+ setTitle(itemData.getTitleForItemView(this));
+ setCheckable(itemData.isCheckable());
+ setShortcut(itemData.shouldShowShortcut(), itemData.getShortcut());
+ setIcon(itemData.getIcon());
+ setEnabled(itemData.isEnabled());
+ }
+
+ public void setForceShowIcon(boolean forceShow) {
+ mPreserveIconSpacing = mForceShowIcon = forceShow;
+ }
+
+ public void setTitle(CharSequence title) {
+ if (title != null) {
+ mTitleView.setText(title);
+
+ if (mTitleView.getVisibility() != VISIBLE) mTitleView.setVisibility(VISIBLE);
+ } else {
+ if (mTitleView.getVisibility() != GONE) mTitleView.setVisibility(GONE);
+ }
+ }
+
+ public MenuItemImpl getItemData() {
+ return mItemData;
+ }
+
+ public void setCheckable(boolean checkable) {
+
+ if (!checkable && mRadioButton == null && mCheckBox == null) {
+ return;
+ }
+
+ if (mRadioButton == null) {
+ insertRadioButton();
+ }
+ if (mCheckBox == null) {
+ insertCheckBox();
+ }
+
+ // Depending on whether its exclusive check or not, the checkbox or
+ // radio button will be the one in use (and the other will be otherCompoundButton)
+ final CompoundButton compoundButton;
+ final CompoundButton otherCompoundButton;
+
+ if (mItemData.isExclusiveCheckable()) {
+ compoundButton = mRadioButton;
+ otherCompoundButton = mCheckBox;
+ } else {
+ compoundButton = mCheckBox;
+ otherCompoundButton = mRadioButton;
+ }
+
+ if (checkable) {
+ compoundButton.setChecked(mItemData.isChecked());
+
+ final int newVisibility = checkable ? VISIBLE : GONE;
+ if (compoundButton.getVisibility() != newVisibility) {
+ compoundButton.setVisibility(newVisibility);
+ }
+
+ // Make sure the other compound button isn't visible
+ if (otherCompoundButton.getVisibility() != GONE) {
+ otherCompoundButton.setVisibility(GONE);
+ }
+ } else {
+ mCheckBox.setVisibility(GONE);
+ mRadioButton.setVisibility(GONE);
+ }
+ }
+
+ public void setChecked(boolean checked) {
+ CompoundButton compoundButton;
+
+ if (mItemData.isExclusiveCheckable()) {
+ if (mRadioButton == null) {
+ insertRadioButton();
+ }
+ compoundButton = mRadioButton;
+ } else {
+ if (mCheckBox == null) {
+ insertCheckBox();
+ }
+ compoundButton = mCheckBox;
+ }
+
+ compoundButton.setChecked(checked);
+ }
+
+ public void setShortcut(boolean showShortcut, char shortcutKey) {
+ final int newVisibility = (showShortcut && mItemData.shouldShowShortcut())
+ ? VISIBLE : GONE;
+
+ if (newVisibility == VISIBLE) {
+ mShortcutView.setText(mItemData.getShortcutLabel());
+ }
+
+ if (mShortcutView.getVisibility() != newVisibility) {
+ mShortcutView.setVisibility(newVisibility);
+ }
+ }
+
+ public void setIcon(Drawable icon) {
+ final boolean showIcon = mItemData.shouldShowIcon() || mForceShowIcon;
+ if (!showIcon && !mPreserveIconSpacing) {
+ return;
+ }
+
+ if (mIconView == null && icon == null && !mPreserveIconSpacing) {
+ return;
+ }
+
+ if (mIconView == null) {
+ insertIconView();
+ }
+
+ if (icon != null || mPreserveIconSpacing) {
+ mIconView.setImageDrawable(showIcon ? icon : null);
+
+ if (mIconView.getVisibility() != VISIBLE) {
+ mIconView.setVisibility(VISIBLE);
+ }
+ } else {
+ mIconView.setVisibility(GONE);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mIconView != null && mPreserveIconSpacing) {
+ // Enforce minimum icon spacing
+ ViewGroup.LayoutParams lp = getLayoutParams();
+ LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
+ if (lp.height > 0 && iconLp.width <= 0) {
+ iconLp.width = lp.height;
+ }
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ private void insertIconView() {
+ LayoutInflater inflater = getInflater();
+ mIconView = (ImageView) inflater.inflate(R.layout.abs__list_menu_item_icon,
+ this, false);
+ addView(mIconView, 0);
+ }
+
+ private void insertRadioButton() {
+ LayoutInflater inflater = getInflater();
+ mRadioButton =
+ (RadioButton) inflater.inflate(R.layout.abs__list_menu_item_radio,
+ this, false);
+ addView(mRadioButton);
+ }
+
+ private void insertCheckBox() {
+ LayoutInflater inflater = getInflater();
+ mCheckBox =
+ (CheckBox) inflater.inflate(R.layout.abs__list_menu_item_checkbox,
+ this, false);
+ addView(mCheckBox);
+ }
+
+ public boolean prefersCondensedTitle() {
+ return false;
+ }
+
+ public boolean showsIcon() {
+ return mForceShowIcon;
+ }
+
+ private LayoutInflater getInflater() {
+ if (mInflater == null) {
+ mInflater = LayoutInflater.from(mContext);
+ }
+ return mInflater;
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java
new file mode 100644
index 0000000000..179b8f0379
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java
@@ -0,0 +1,1335 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.view.menu;
+
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.SparseArray;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.View;
+
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.view.ActionProvider;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.SubMenu;
+
+/**
+ * Implementation of the {@link android.view.Menu} interface for creating a
+ * standard menu UI.
+ */
+public class MenuBuilder implements Menu {
+ //UNUSED private static final String TAG = "MenuBuilder";
+
+ private static final String PRESENTER_KEY = "android:menu:presenters";
+ private static final String ACTION_VIEW_STATES_KEY = "android:menu:actionviewstates";
+ private static final String EXPANDED_ACTION_VIEW_ID = "android:menu:expandedactionview";
+
+ private static final int[] sCategoryToOrder = new int[] {
+ 1, /* No category */
+ 4, /* CONTAINER */
+ 5, /* SYSTEM */
+ 3, /* SECONDARY */
+ 2, /* ALTERNATIVE */
+ 0, /* SELECTED_ALTERNATIVE */
+ };
+
+ private final Context mContext;
+ private final Resources mResources;
+
+ /**
+ * Whether the shortcuts should be qwerty-accessible. Use isQwertyMode()
+ * instead of accessing this directly.
+ */
+ private boolean mQwertyMode;
+
+ /**
+ * Whether the shortcuts should be visible on menus. Use isShortcutsVisible()
+ * instead of accessing this directly.
+ */
+ private boolean mShortcutsVisible;
+
+ /**
+ * Callback that will receive the various menu-related events generated by
+ * this class. Use getCallback to get a reference to the callback.
+ */
+ private Callback mCallback;
+
+ /** Contains all of the items for this menu */
+ private ArrayList mItems;
+
+ /** Contains only the items that are currently visible. This will be created/refreshed from
+ * {@link #getVisibleItems()} */
+ private ArrayList mVisibleItems;
+ /**
+ * Whether or not the items (or any one item's shown state) has changed since it was last
+ * fetched from {@link #getVisibleItems()}
+ */
+ private boolean mIsVisibleItemsStale;
+
+ /**
+ * Contains only the items that should appear in the Action Bar, if present.
+ */
+ private ArrayList mActionItems;
+ /**
+ * Contains items that should NOT appear in the Action Bar, if present.
+ */
+ private ArrayList mNonActionItems;
+
+ /**
+ * Whether or not the items (or any one item's action state) has changed since it was
+ * last fetched.
+ */
+ private boolean mIsActionItemsStale;
+
+ /**
+ * Default value for how added items should show in the action list.
+ */
+ private int mDefaultShowAsAction = MenuItem.SHOW_AS_ACTION_NEVER;
+
+ /**
+ * Current use case is Context Menus: As Views populate the context menu, each one has
+ * extra information that should be passed along. This is the current menu info that
+ * should be set on all items added to this menu.
+ */
+ private ContextMenuInfo mCurrentMenuInfo;
+
+ /** Header title for menu types that have a header (context and submenus) */
+ CharSequence mHeaderTitle;
+ /** Header icon for menu types that have a header and support icons (context) */
+ Drawable mHeaderIcon;
+ /** Header custom view for menu types that have a header and support custom views (context) */
+ View mHeaderView;
+
+ /**
+ * Contains the state of the View hierarchy for all menu views when the menu
+ * was frozen.
+ */
+ //UNUSED private SparseArray mFrozenViewStates;
+
+ /**
+ * Prevents onItemsChanged from doing its junk, useful for batching commands
+ * that may individually call onItemsChanged.
+ */
+ private boolean mPreventDispatchingItemsChanged = false;
+ private boolean mItemsChangedWhileDispatchPrevented = false;
+
+ private boolean mOptionalIconsVisible = false;
+
+ private boolean mIsClosing = false;
+
+ private ArrayList mTempShortcutItemList = new ArrayList();
+
+ private CopyOnWriteArrayList> mPresenters =
+ new CopyOnWriteArrayList>();
+
+ /**
+ * Currently expanded menu item; must be collapsed when we clear.
+ */
+ private MenuItemImpl mExpandedItem;
+
+ /**
+ * Called by menu to notify of close and selection changes.
+ */
+ public interface Callback {
+ /**
+ * Called when a menu item is selected.
+ * @param menu The menu that is the parent of the item
+ * @param item The menu item that is selected
+ * @return whether the menu item selection was handled
+ */
+ public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item);
+
+ /**
+ * Called when the mode of the menu changes (for example, from icon to expanded).
+ *
+ * @param menu the menu that has changed modes
+ */
+ public void onMenuModeChange(MenuBuilder menu);
+ }
+
+ /**
+ * Called by menu items to execute their associated action
+ */
+ public interface ItemInvoker {
+ public boolean invokeItem(MenuItemImpl item);
+ }
+
+ public MenuBuilder(Context context) {
+ mContext = context;
+ mResources = context.getResources();
+
+ mItems = new ArrayList();
+
+ mVisibleItems = new ArrayList();
+ mIsVisibleItemsStale = true;
+
+ mActionItems = new ArrayList();
+ mNonActionItems = new ArrayList();
+ mIsActionItemsStale = true;
+
+ setShortcutsVisibleInner(true);
+ }
+
+ public MenuBuilder setDefaultShowAsAction(int defaultShowAsAction) {
+ mDefaultShowAsAction = defaultShowAsAction;
+ return this;
+ }
+
+ /**
+ * Add a presenter to this menu. This will only hold a WeakReference;
+ * you do not need to explicitly remove a presenter, but you can using
+ * {@link #removeMenuPresenter(MenuPresenter)}.
+ *
+ * @param presenter The presenter to add
+ */
+ public void addMenuPresenter(MenuPresenter presenter) {
+ mPresenters.add(new WeakReference(presenter));
+ presenter.initForMenu(mContext, this);
+ mIsActionItemsStale = true;
+ }
+
+ /**
+ * Remove a presenter from this menu. That presenter will no longer
+ * receive notifications of updates to this menu's data.
+ *
+ * @param presenter The presenter to remove
+ */
+ public void removeMenuPresenter(MenuPresenter presenter) {
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter item = ref.get();
+ if (item == null || item == presenter) {
+ mPresenters.remove(ref);
+ }
+ }
+ }
+
+ private void dispatchPresenterUpdate(boolean cleared) {
+ if (mPresenters.isEmpty()) return;
+
+ stopDispatchingItemsChanged();
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else {
+ presenter.updateMenuView(cleared);
+ }
+ }
+ startDispatchingItemsChanged();
+ }
+
+ private boolean dispatchSubMenuSelected(SubMenuBuilder subMenu) {
+ if (mPresenters.isEmpty()) return false;
+
+ boolean result = false;
+
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else if (!result) {
+ result = presenter.onSubMenuSelected(subMenu);
+ }
+ }
+ return result;
+ }
+
+ private void dispatchSaveInstanceState(Bundle outState) {
+ if (mPresenters.isEmpty()) return;
+
+ SparseArray presenterStates = new SparseArray();
+
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else {
+ final int id = presenter.getId();
+ if (id > 0) {
+ final Parcelable state = presenter.onSaveInstanceState();
+ if (state != null) {
+ presenterStates.put(id, state);
+ }
+ }
+ }
+ }
+
+ outState.putSparseParcelableArray(PRESENTER_KEY, presenterStates);
+ }
+
+ private void dispatchRestoreInstanceState(Bundle state) {
+ SparseArray presenterStates = state.getSparseParcelableArray(PRESENTER_KEY);
+
+ if (presenterStates == null || mPresenters.isEmpty()) return;
+
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else {
+ final int id = presenter.getId();
+ if (id > 0) {
+ Parcelable parcel = presenterStates.get(id);
+ if (parcel != null) {
+ presenter.onRestoreInstanceState(parcel);
+ }
+ }
+ }
+ }
+ }
+
+ public void savePresenterStates(Bundle outState) {
+ dispatchSaveInstanceState(outState);
+ }
+
+ public void restorePresenterStates(Bundle state) {
+ dispatchRestoreInstanceState(state);
+ }
+
+ public void saveActionViewStates(Bundle outStates) {
+ SparseArray viewStates = null;
+
+ final int itemCount = size();
+ for (int i = 0; i < itemCount; i++) {
+ final MenuItem item = getItem(i);
+ final View v = item.getActionView();
+ if (v != null && v.getId() != View.NO_ID) {
+ if (viewStates == null) {
+ viewStates = new SparseArray();
+ }
+ v.saveHierarchyState(viewStates);
+ if (item.isActionViewExpanded()) {
+ outStates.putInt(EXPANDED_ACTION_VIEW_ID, item.getItemId());
+ }
+ }
+ if (item.hasSubMenu()) {
+ final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
+ subMenu.saveActionViewStates(outStates);
+ }
+ }
+
+ if (viewStates != null) {
+ outStates.putSparseParcelableArray(getActionViewStatesKey(), viewStates);
+ }
+ }
+
+ public void restoreActionViewStates(Bundle states) {
+ if (states == null) {
+ return;
+ }
+
+ SparseArray viewStates = states.getSparseParcelableArray(
+ getActionViewStatesKey());
+
+ final int itemCount = size();
+ for (int i = 0; i < itemCount; i++) {
+ final MenuItem item = getItem(i);
+ final View v = item.getActionView();
+ if (v != null && v.getId() != View.NO_ID) {
+ v.restoreHierarchyState(viewStates);
+ }
+ if (item.hasSubMenu()) {
+ final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
+ subMenu.restoreActionViewStates(states);
+ }
+ }
+
+ final int expandedId = states.getInt(EXPANDED_ACTION_VIEW_ID);
+ if (expandedId > 0) {
+ MenuItem itemToExpand = findItem(expandedId);
+ if (itemToExpand != null) {
+ itemToExpand.expandActionView();
+ }
+ }
+ }
+
+ protected String getActionViewStatesKey() {
+ return ACTION_VIEW_STATES_KEY;
+ }
+
+ public void setCallback(Callback cb) {
+ mCallback = cb;
+ }
+
+ /**
+ * Adds an item to the menu. The other add methods funnel to this.
+ */
+ private MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) {
+ final int ordering = getOrdering(categoryOrder);
+
+ final MenuItemImpl item = new MenuItemImpl(this, group, id, categoryOrder,
+ ordering, title, mDefaultShowAsAction);
+
+ if (mCurrentMenuInfo != null) {
+ // Pass along the current menu info
+ item.setMenuInfo(mCurrentMenuInfo);
+ }
+
+ mItems.add(findInsertIndex(mItems, ordering), item);
+ onItemsChanged(true);
+
+ return item;
+ }
+
+ public MenuItem add(CharSequence title) {
+ return addInternal(0, 0, 0, title);
+ }
+
+ public MenuItem add(int titleRes) {
+ return addInternal(0, 0, 0, mResources.getString(titleRes));
+ }
+
+ public MenuItem add(int group, int id, int categoryOrder, CharSequence title) {
+ return addInternal(group, id, categoryOrder, title);
+ }
+
+ public MenuItem add(int group, int id, int categoryOrder, int title) {
+ return addInternal(group, id, categoryOrder, mResources.getString(title));
+ }
+
+ public SubMenu addSubMenu(CharSequence title) {
+ return addSubMenu(0, 0, 0, title);
+ }
+
+ public SubMenu addSubMenu(int titleRes) {
+ return addSubMenu(0, 0, 0, mResources.getString(titleRes));
+ }
+
+ public SubMenu addSubMenu(int group, int id, int categoryOrder, CharSequence title) {
+ final MenuItemImpl item = (MenuItemImpl) addInternal(group, id, categoryOrder, title);
+ final SubMenuBuilder subMenu = new SubMenuBuilder(mContext, this, item);
+ item.setSubMenu(subMenu);
+
+ return subMenu;
+ }
+
+ public SubMenu addSubMenu(int group, int id, int categoryOrder, int title) {
+ return addSubMenu(group, id, categoryOrder, mResources.getString(title));
+ }
+
+ public int addIntentOptions(int group, int id, int categoryOrder, ComponentName caller,
+ Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems) {
+ PackageManager pm = mContext.getPackageManager();
+ final List lri =
+ pm.queryIntentActivityOptions(caller, specifics, intent, 0);
+ final int N = lri != null ? lri.size() : 0;
+
+ if ((flags & FLAG_APPEND_TO_GROUP) == 0) {
+ removeGroup(group);
+ }
+
+ for (int i=0; i= 0) {
+ outSpecificItems[ri.specificIndex] = item;
+ }
+ }
+
+ return N;
+ }
+
+ public void removeItem(int id) {
+ removeItemAtInt(findItemIndex(id), true);
+ }
+
+ public void removeGroup(int group) {
+ final int i = findGroupIndex(group);
+
+ if (i >= 0) {
+ final int maxRemovable = mItems.size() - i;
+ int numRemoved = 0;
+ while ((numRemoved++ < maxRemovable) && (mItems.get(i).getGroupId() == group)) {
+ // Don't force update for each one, this method will do it at the end
+ removeItemAtInt(i, false);
+ }
+
+ // Notify menu views
+ onItemsChanged(true);
+ }
+ }
+
+ /**
+ * Remove the item at the given index and optionally forces menu views to
+ * update.
+ *
+ * @param index The index of the item to be removed. If this index is
+ * invalid an exception is thrown.
+ * @param updateChildrenOnMenuViews Whether to force update on menu views.
+ * Please make sure you eventually call this after your batch of
+ * removals.
+ */
+ private void removeItemAtInt(int index, boolean updateChildrenOnMenuViews) {
+ if ((index < 0) || (index >= mItems.size())) return;
+
+ mItems.remove(index);
+
+ if (updateChildrenOnMenuViews) onItemsChanged(true);
+ }
+
+ public void removeItemAt(int index) {
+ removeItemAtInt(index, true);
+ }
+
+ public void clearAll() {
+ mPreventDispatchingItemsChanged = true;
+ clear();
+ clearHeader();
+ mPreventDispatchingItemsChanged = false;
+ mItemsChangedWhileDispatchPrevented = false;
+ onItemsChanged(true);
+ }
+
+ public void clear() {
+ if (mExpandedItem != null) {
+ collapseItemActionView(mExpandedItem);
+ }
+ mItems.clear();
+
+ onItemsChanged(true);
+ }
+
+ void setExclusiveItemChecked(MenuItem item) {
+ final int group = item.getGroupId();
+
+ final int N = mItems.size();
+ for (int i = 0; i < N; i++) {
+ MenuItemImpl curItem = mItems.get(i);
+ if (curItem.getGroupId() == group) {
+ if (!curItem.isExclusiveCheckable()) continue;
+ if (!curItem.isCheckable()) continue;
+
+ // Check the item meant to be checked, uncheck the others (that are in the group)
+ curItem.setCheckedInt(curItem == item);
+ }
+ }
+ }
+
+ public void setGroupCheckable(int group, boolean checkable, boolean exclusive) {
+ final int N = mItems.size();
+
+ for (int i = 0; i < N; i++) {
+ MenuItemImpl item = mItems.get(i);
+ if (item.getGroupId() == group) {
+ item.setExclusiveCheckable(exclusive);
+ item.setCheckable(checkable);
+ }
+ }
+ }
+
+ public void setGroupVisible(int group, boolean visible) {
+ final int N = mItems.size();
+
+ // We handle the notification of items being changed ourselves, so we use setVisibleInt rather
+ // than setVisible and at the end notify of items being changed
+
+ boolean changedAtLeastOneItem = false;
+ for (int i = 0; i < N; i++) {
+ MenuItemImpl item = mItems.get(i);
+ if (item.getGroupId() == group) {
+ if (item.setVisibleInt(visible)) changedAtLeastOneItem = true;
+ }
+ }
+
+ if (changedAtLeastOneItem) onItemsChanged(true);
+ }
+
+ public void setGroupEnabled(int group, boolean enabled) {
+ final int N = mItems.size();
+
+ for (int i = 0; i < N; i++) {
+ MenuItemImpl item = mItems.get(i);
+ if (item.getGroupId() == group) {
+ item.setEnabled(enabled);
+ }
+ }
+ }
+
+ public boolean hasVisibleItems() {
+ final int size = size();
+
+ for (int i = 0; i < size; i++) {
+ MenuItemImpl item = mItems.get(i);
+ if (item.isVisible()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public MenuItem findItem(int id) {
+ final int size = size();
+ for (int i = 0; i < size; i++) {
+ MenuItemImpl item = mItems.get(i);
+ if (item.getItemId() == id) {
+ return item;
+ } else if (item.hasSubMenu()) {
+ MenuItem possibleItem = item.getSubMenu().findItem(id);
+
+ if (possibleItem != null) {
+ return possibleItem;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public int findItemIndex(int id) {
+ final int size = size();
+
+ for (int i = 0; i < size; i++) {
+ MenuItemImpl item = mItems.get(i);
+ if (item.getItemId() == id) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ public int findGroupIndex(int group) {
+ return findGroupIndex(group, 0);
+ }
+
+ public int findGroupIndex(int group, int start) {
+ final int size = size();
+
+ if (start < 0) {
+ start = 0;
+ }
+
+ for (int i = start; i < size; i++) {
+ final MenuItemImpl item = mItems.get(i);
+
+ if (item.getGroupId() == group) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ public int size() {
+ return mItems.size();
+ }
+
+ /** {@inheritDoc} */
+ public MenuItem getItem(int index) {
+ return mItems.get(index);
+ }
+
+ public boolean isShortcutKey(int keyCode, KeyEvent event) {
+ return findItemWithShortcutForKey(keyCode, event) != null;
+ }
+
+ public void setQwertyMode(boolean isQwerty) {
+ mQwertyMode = isQwerty;
+
+ onItemsChanged(false);
+ }
+
+ /**
+ * Returns the ordering across all items. This will grab the category from
+ * the upper bits, find out how to order the category with respect to other
+ * categories, and combine it with the lower bits.
+ *
+ * @param categoryOrder The category order for a particular item (if it has
+ * not been or/add with a category, the default category is
+ * assumed).
+ * @return An ordering integer that can be used to order this item across
+ * all the items (even from other categories).
+ */
+ private static int getOrdering(int categoryOrder) {
+ final int index = (categoryOrder & CATEGORY_MASK) >> CATEGORY_SHIFT;
+
+ if (index < 0 || index >= sCategoryToOrder.length) {
+ throw new IllegalArgumentException("order does not contain a valid category.");
+ }
+
+ return (sCategoryToOrder[index] << CATEGORY_SHIFT) | (categoryOrder & USER_MASK);
+ }
+
+ /**
+ * @return whether the menu shortcuts are in qwerty mode or not
+ */
+ boolean isQwertyMode() {
+ return mQwertyMode;
+ }
+
+ /**
+ * Sets whether the shortcuts should be visible on menus. Devices without hardware
+ * key input will never make shortcuts visible even if this method is passed 'true'.
+ *
+ * @param shortcutsVisible Whether shortcuts should be visible (if true and a
+ * menu item does not have a shortcut defined, that item will
+ * still NOT show a shortcut)
+ */
+ public void setShortcutsVisible(boolean shortcutsVisible) {
+ if (mShortcutsVisible == shortcutsVisible) return;
+
+ setShortcutsVisibleInner(shortcutsVisible);
+ onItemsChanged(false);
+ }
+
+ private void setShortcutsVisibleInner(boolean shortcutsVisible) {
+ mShortcutsVisible = shortcutsVisible
+ && mResources.getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS
+ && mResources.getBoolean(
+ R.bool.abs__config_showMenuShortcutsWhenKeyboardPresent);
+ }
+
+ /**
+ * @return Whether shortcuts should be visible on menus.
+ */
+ public boolean isShortcutsVisible() {
+ return mShortcutsVisible;
+ }
+
+ Resources getResources() {
+ return mResources;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) {
+ return mCallback != null && mCallback.onMenuItemSelected(menu, item);
+ }
+
+ /**
+ * Dispatch a mode change event to this menu's callback.
+ */
+ public void changeMenuMode() {
+ if (mCallback != null) {
+ mCallback.onMenuModeChange(this);
+ }
+ }
+
+ private static int findInsertIndex(ArrayList items, int ordering) {
+ for (int i = items.size() - 1; i >= 0; i--) {
+ MenuItemImpl item = items.get(i);
+ if (item.getOrdering() <= ordering) {
+ return i + 1;
+ }
+ }
+
+ return 0;
+ }
+
+ public boolean performShortcut(int keyCode, KeyEvent event, int flags) {
+ final MenuItemImpl item = findItemWithShortcutForKey(keyCode, event);
+
+ boolean handled = false;
+
+ if (item != null) {
+ handled = performItemAction(item, flags);
+ }
+
+ if ((flags & FLAG_ALWAYS_PERFORM_CLOSE) != 0) {
+ close(true);
+ }
+
+ return handled;
+ }
+
+ /*
+ * This function will return all the menu and sub-menu items that can
+ * be directly (the shortcut directly corresponds) and indirectly
+ * (the ALT-enabled char corresponds to the shortcut) associated
+ * with the keyCode.
+ */
+ @SuppressWarnings("deprecation")
+ void findItemsWithShortcutForKey(List items, int keyCode, KeyEvent event) {
+ final boolean qwerty = isQwertyMode();
+ final int metaState = event.getMetaState();
+ final KeyCharacterMap.KeyData possibleChars = new KeyCharacterMap.KeyData();
+ // Get the chars associated with the keyCode (i.e using any chording combo)
+ final boolean isKeyCodeMapped = event.getKeyData(possibleChars);
+ // The delete key is not mapped to '\b' so we treat it specially
+ if (!isKeyCodeMapped && (keyCode != KeyEvent.KEYCODE_DEL)) {
+ return;
+ }
+
+ // Look for an item whose shortcut is this key.
+ final int N = mItems.size();
+ for (int i = 0; i < N; i++) {
+ MenuItemImpl item = mItems.get(i);
+ if (item.hasSubMenu()) {
+ ((MenuBuilder)item.getSubMenu()).findItemsWithShortcutForKey(items, keyCode, event);
+ }
+ final char shortcutChar = qwerty ? item.getAlphabeticShortcut() : item.getNumericShortcut();
+ if (((metaState & (KeyEvent.META_SHIFT_ON | KeyEvent.META_SYM_ON)) == 0) &&
+ (shortcutChar != 0) &&
+ (shortcutChar == possibleChars.meta[0]
+ || shortcutChar == possibleChars.meta[2]
+ || (qwerty && shortcutChar == '\b' &&
+ keyCode == KeyEvent.KEYCODE_DEL)) &&
+ item.isEnabled()) {
+ items.add(item);
+ }
+ }
+ }
+
+ /*
+ * We want to return the menu item associated with the key, but if there is no
+ * ambiguity (i.e. there is only one menu item corresponding to the key) we want
+ * to return it even if it's not an exact match; this allow the user to
+ * _not_ use the ALT key for example, making the use of shortcuts slightly more
+ * user-friendly. An example is on the G1, '!' and '1' are on the same key, and
+ * in Gmail, Menu+1 will trigger Menu+! (the actual shortcut).
+ *
+ * On the other hand, if two (or more) shortcuts corresponds to the same key,
+ * we have to only return the exact match.
+ */
+ @SuppressWarnings("deprecation")
+ MenuItemImpl findItemWithShortcutForKey(int keyCode, KeyEvent event) {
+ // Get all items that can be associated directly or indirectly with the keyCode
+ ArrayList items = mTempShortcutItemList;
+ items.clear();
+ findItemsWithShortcutForKey(items, keyCode, event);
+
+ if (items.isEmpty()) {
+ return null;
+ }
+
+ final int metaState = event.getMetaState();
+ final KeyCharacterMap.KeyData possibleChars = new KeyCharacterMap.KeyData();
+ // Get the chars associated with the keyCode (i.e using any chording combo)
+ event.getKeyData(possibleChars);
+
+ // If we have only one element, we can safely returns it
+ final int size = items.size();
+ if (size == 1) {
+ return items.get(0);
+ }
+
+ final boolean qwerty = isQwertyMode();
+ // If we found more than one item associated with the key,
+ // we have to return the exact match
+ for (int i = 0; i < size; i++) {
+ final MenuItemImpl item = items.get(i);
+ final char shortcutChar = qwerty ? item.getAlphabeticShortcut() :
+ item.getNumericShortcut();
+ if ((shortcutChar == possibleChars.meta[0] &&
+ (metaState & KeyEvent.META_ALT_ON) == 0)
+ || (shortcutChar == possibleChars.meta[2] &&
+ (metaState & KeyEvent.META_ALT_ON) != 0)
+ || (qwerty && shortcutChar == '\b' &&
+ keyCode == KeyEvent.KEYCODE_DEL)) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ public boolean performIdentifierAction(int id, int flags) {
+ // Look for an item whose identifier is the id.
+ return performItemAction(findItem(id), flags);
+ }
+
+ public boolean performItemAction(MenuItem item, int flags) {
+ MenuItemImpl itemImpl = (MenuItemImpl) item;
+
+ if (itemImpl == null || !itemImpl.isEnabled()) {
+ return false;
+ }
+
+ boolean invoked = itemImpl.invoke();
+
+ if (itemImpl.hasCollapsibleActionView()) {
+ invoked |= itemImpl.expandActionView();
+ if (invoked) close(true);
+ } else if (item.hasSubMenu()) {
+ close(false);
+
+ final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
+ final ActionProvider provider = item.getActionProvider();
+ if (provider != null && provider.hasSubMenu()) {
+ provider.onPrepareSubMenu(subMenu);
+ }
+ invoked |= dispatchSubMenuSelected(subMenu);
+ if (!invoked) close(true);
+ } else {
+ if ((flags & FLAG_PERFORM_NO_CLOSE) == 0) {
+ close(true);
+ }
+ }
+
+ return invoked;
+ }
+
+ /**
+ * Closes the visible menu.
+ *
+ * @param allMenusAreClosing Whether the menus are completely closing (true),
+ * or whether there is another menu coming in this menu's place
+ * (false). For example, if the menu is closing because a
+ * sub menu is about to be shown, allMenusAreClosing
+ * is false.
+ */
+ final void close(boolean allMenusAreClosing) {
+ if (mIsClosing) return;
+
+ mIsClosing = true;
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else {
+ presenter.onCloseMenu(this, allMenusAreClosing);
+ }
+ }
+ mIsClosing = false;
+ }
+
+ /** {@inheritDoc} */
+ public void close() {
+ close(true);
+ }
+
+ /**
+ * Called when an item is added or removed.
+ *
+ * @param structureChanged true if the menu structure changed,
+ * false if only item properties changed.
+ * (Visibility is a structural property since it affects layout.)
+ */
+ void onItemsChanged(boolean structureChanged) {
+ if (!mPreventDispatchingItemsChanged) {
+ if (structureChanged) {
+ mIsVisibleItemsStale = true;
+ mIsActionItemsStale = true;
+ }
+
+ dispatchPresenterUpdate(structureChanged);
+ } else {
+ mItemsChangedWhileDispatchPrevented = true;
+ }
+ }
+
+ /**
+ * Stop dispatching item changed events to presenters until
+ * {@link #startDispatchingItemsChanged()} is called. Useful when
+ * many menu operations are going to be performed as a batch.
+ */
+ public void stopDispatchingItemsChanged() {
+ if (!mPreventDispatchingItemsChanged) {
+ mPreventDispatchingItemsChanged = true;
+ mItemsChangedWhileDispatchPrevented = false;
+ }
+ }
+
+ public void startDispatchingItemsChanged() {
+ mPreventDispatchingItemsChanged = false;
+
+ if (mItemsChangedWhileDispatchPrevented) {
+ mItemsChangedWhileDispatchPrevented = false;
+ onItemsChanged(true);
+ }
+ }
+
+ /**
+ * Called by {@link MenuItemImpl} when its visible flag is changed.
+ * @param item The item that has gone through a visibility change.
+ */
+ void onItemVisibleChanged(MenuItemImpl item) {
+ // Notify of items being changed
+ mIsVisibleItemsStale = true;
+ onItemsChanged(true);
+ }
+
+ /**
+ * Called by {@link MenuItemImpl} when its action request status is changed.
+ * @param item The item that has gone through a change in action request status.
+ */
+ void onItemActionRequestChanged(MenuItemImpl item) {
+ // Notify of items being changed
+ mIsActionItemsStale = true;
+ onItemsChanged(true);
+ }
+
+ ArrayList getVisibleItems() {
+ if (!mIsVisibleItemsStale) return mVisibleItems;
+
+ // Refresh the visible items
+ mVisibleItems.clear();
+
+ final int itemsSize = mItems.size();
+ MenuItemImpl item;
+ for (int i = 0; i < itemsSize; i++) {
+ item = mItems.get(i);
+ if (item.isVisible()) mVisibleItems.add(item);
+ }
+
+ mIsVisibleItemsStale = false;
+ mIsActionItemsStale = true;
+
+ return mVisibleItems;
+ }
+
+ /**
+ * This method determines which menu items get to be 'action items' that will appear
+ * in an action bar and which items should be 'overflow items' in a secondary menu.
+ * The rules are as follows:
+ *
+ *
Items are considered for inclusion in the order specified within the menu.
+ * There is a limit of mMaxActionItems as a total count, optionally including the overflow
+ * menu button itself. This is a soft limit; if an item shares a group ID with an item
+ * previously included as an action item, the new item will stay with its group and become
+ * an action item itself even if it breaks the max item count limit. This is done to
+ * limit the conceptual complexity of the items presented within an action bar. Only a few
+ * unrelated concepts should be presented to the user in this space, and groups are treated
+ * as a single concept.
+ *
+ *
There is also a hard limit of consumed measurable space: mActionWidthLimit. This
+ * limit may be broken by a single item that exceeds the remaining space, but no further
+ * items may be added. If an item that is part of a group cannot fit within the remaining
+ * measured width, the entire group will be demoted to overflow. This is done to ensure room
+ * for navigation and other affordances in the action bar as well as reduce general UI clutter.
+ *
+ *
The space freed by demoting a full group cannot be consumed by future menu items.
+ * Once items begin to overflow, all future items become overflow items as well. This is
+ * to avoid inadvertent reordering that may break the app's intended design.
+ */
+ public void flagActionItems() {
+ if (!mIsActionItemsStale) {
+ return;
+ }
+
+ // Presenters flag action items as needed.
+ boolean flagged = false;
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else {
+ flagged |= presenter.flagActionItems();
+ }
+ }
+
+ if (flagged) {
+ mActionItems.clear();
+ mNonActionItems.clear();
+ ArrayList visibleItems = getVisibleItems();
+ final int itemsSize = visibleItems.size();
+ for (int i = 0; i < itemsSize; i++) {
+ MenuItemImpl item = visibleItems.get(i);
+ if (item.isActionButton()) {
+ mActionItems.add(item);
+ } else {
+ mNonActionItems.add(item);
+ }
+ }
+ } else {
+ // Nobody flagged anything, everything is a non-action item.
+ // (This happens during a first pass with no action-item presenters.)
+ mActionItems.clear();
+ mNonActionItems.clear();
+ mNonActionItems.addAll(getVisibleItems());
+ }
+ mIsActionItemsStale = false;
+ }
+
+ ArrayList getActionItems() {
+ flagActionItems();
+ return mActionItems;
+ }
+
+ ArrayList getNonActionItems() {
+ flagActionItems();
+ return mNonActionItems;
+ }
+
+ public void clearHeader() {
+ mHeaderIcon = null;
+ mHeaderTitle = null;
+ mHeaderView = null;
+
+ onItemsChanged(false);
+ }
+
+ private void setHeaderInternal(final int titleRes, final CharSequence title, final int iconRes,
+ final Drawable icon, final View view) {
+ final Resources r = getResources();
+
+ if (view != null) {
+ mHeaderView = view;
+
+ // If using a custom view, then the title and icon aren't used
+ mHeaderTitle = null;
+ mHeaderIcon = null;
+ } else {
+ if (titleRes > 0) {
+ mHeaderTitle = r.getText(titleRes);
+ } else if (title != null) {
+ mHeaderTitle = title;
+ }
+
+ if (iconRes > 0) {
+ mHeaderIcon = r.getDrawable(iconRes);
+ } else if (icon != null) {
+ mHeaderIcon = icon;
+ }
+
+ // If using the title or icon, then a custom view isn't used
+ mHeaderView = null;
+ }
+
+ // Notify of change
+ onItemsChanged(false);
+ }
+
+ /**
+ * Sets the header's title. This replaces the header view. Called by the
+ * builder-style methods of subclasses.
+ *
+ * @param title The new title.
+ * @return This MenuBuilder so additional setters can be called.
+ */
+ protected MenuBuilder setHeaderTitleInt(CharSequence title) {
+ setHeaderInternal(0, title, 0, null, null);
+ return this;
+ }
+
+ /**
+ * Sets the header's title. This replaces the header view. Called by the
+ * builder-style methods of subclasses.
+ *
+ * @param titleRes The new title (as a resource ID).
+ * @return This MenuBuilder so additional setters can be called.
+ */
+ protected MenuBuilder setHeaderTitleInt(int titleRes) {
+ setHeaderInternal(titleRes, null, 0, null, null);
+ return this;
+ }
+
+ /**
+ * Sets the header's icon. This replaces the header view. Called by the
+ * builder-style methods of subclasses.
+ *
+ * @param icon The new icon.
+ * @return This MenuBuilder so additional setters can be called.
+ */
+ protected MenuBuilder setHeaderIconInt(Drawable icon) {
+ setHeaderInternal(0, null, 0, icon, null);
+ return this;
+ }
+
+ /**
+ * Sets the header's icon. This replaces the header view. Called by the
+ * builder-style methods of subclasses.
+ *
+ * @param iconRes The new icon (as a resource ID).
+ * @return This MenuBuilder so additional setters can be called.
+ */
+ protected MenuBuilder setHeaderIconInt(int iconRes) {
+ setHeaderInternal(0, null, iconRes, null, null);
+ return this;
+ }
+
+ /**
+ * Sets the header's view. This replaces the title and icon. Called by the
+ * builder-style methods of subclasses.
+ *
+ * @param view The new view.
+ * @return This MenuBuilder so additional setters can be called.
+ */
+ protected MenuBuilder setHeaderViewInt(View view) {
+ setHeaderInternal(0, null, 0, null, view);
+ return this;
+ }
+
+ public CharSequence getHeaderTitle() {
+ return mHeaderTitle;
+ }
+
+ public Drawable getHeaderIcon() {
+ return mHeaderIcon;
+ }
+
+ public View getHeaderView() {
+ return mHeaderView;
+ }
+
+ /**
+ * Gets the root menu (if this is a submenu, find its root menu).
+ * @return The root menu.
+ */
+ public MenuBuilder getRootMenu() {
+ return this;
+ }
+
+ /**
+ * Sets the current menu info that is set on all items added to this menu
+ * (until this is called again with different menu info, in which case that
+ * one will be added to all subsequent item additions).
+ *
+ * @param menuInfo The extra menu information to add.
+ */
+ public void setCurrentMenuInfo(ContextMenuInfo menuInfo) {
+ mCurrentMenuInfo = menuInfo;
+ }
+
+ void setOptionalIconsVisible(boolean visible) {
+ mOptionalIconsVisible = visible;
+ }
+
+ boolean getOptionalIconsVisible() {
+ return mOptionalIconsVisible;
+ }
+
+ public boolean expandItemActionView(MenuItemImpl item) {
+ if (mPresenters.isEmpty()) return false;
+
+ boolean expanded = false;
+
+ stopDispatchingItemsChanged();
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else if ((expanded = presenter.expandItemActionView(this, item))) {
+ break;
+ }
+ }
+ startDispatchingItemsChanged();
+
+ if (expanded) {
+ mExpandedItem = item;
+ }
+ return expanded;
+ }
+
+ public boolean collapseItemActionView(MenuItemImpl item) {
+ if (mPresenters.isEmpty() || mExpandedItem != item) return false;
+
+ boolean collapsed = false;
+
+ stopDispatchingItemsChanged();
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else if ((collapsed = presenter.collapseItemActionView(this, item))) {
+ break;
+ }
+ }
+ startDispatchingItemsChanged();
+
+ if (collapsed) {
+ mExpandedItem = null;
+ }
+ return collapsed;
+ }
+
+ public MenuItemImpl getExpandedItem() {
+ return mExpandedItem;
+ }
+
+ public boolean bindNativeOverflow(android.view.Menu menu, android.view.MenuItem.OnMenuItemClickListener listener, HashMap map) {
+ final List nonActionItems = getNonActionItems();
+ if (nonActionItems == null || nonActionItems.size() == 0) {
+ return false;
+ }
+
+ boolean visible = false;
+ menu.clear();
+ for (MenuItemImpl nonActionItem : nonActionItems) {
+ if (!nonActionItem.isVisible()) {
+ continue;
+ }
+ visible = true;
+
+ android.view.MenuItem nativeItem;
+ if (nonActionItem.hasSubMenu()) {
+ android.view.SubMenu nativeSub = menu.addSubMenu(nonActionItem.getGroupId(), nonActionItem.getItemId(),
+ nonActionItem.getOrder(), nonActionItem.getTitle());
+
+ SubMenuBuilder subMenu = (SubMenuBuilder)nonActionItem.getSubMenu();
+ for (MenuItemImpl subItem : subMenu.getVisibleItems()) {
+ android.view.MenuItem nativeSubItem = nativeSub.add(subItem.getGroupId(), subItem.getItemId(),
+ subItem.getOrder(), subItem.getTitle());
+
+ nativeSubItem.setIcon(subItem.getIcon());
+ nativeSubItem.setOnMenuItemClickListener(listener);
+ nativeSubItem.setEnabled(subItem.isEnabled());
+ nativeSubItem.setIntent(subItem.getIntent());
+ nativeSubItem.setNumericShortcut(subItem.getNumericShortcut());
+ nativeSubItem.setAlphabeticShortcut(subItem.getAlphabeticShortcut());
+ nativeSubItem.setTitleCondensed(subItem.getTitleCondensed());
+ nativeSubItem.setCheckable(subItem.isCheckable());
+ nativeSubItem.setChecked(subItem.isChecked());
+
+ if (subItem.isExclusiveCheckable()) {
+ nativeSub.setGroupCheckable(subItem.getGroupId(), true, true);
+ }
+
+ map.put(nativeSubItem, subItem);
+ }
+
+ nativeItem = nativeSub.getItem();
+ } else {
+ nativeItem = menu.add(nonActionItem.getGroupId(), nonActionItem.getItemId(),
+ nonActionItem.getOrder(), nonActionItem.getTitle());
+ }
+ nativeItem.setIcon(nonActionItem.getIcon());
+ nativeItem.setOnMenuItemClickListener(listener);
+ nativeItem.setEnabled(nonActionItem.isEnabled());
+ nativeItem.setIntent(nonActionItem.getIntent());
+ nativeItem.setNumericShortcut(nonActionItem.getNumericShortcut());
+ nativeItem.setAlphabeticShortcut(nonActionItem.getAlphabeticShortcut());
+ nativeItem.setTitleCondensed(nonActionItem.getTitleCondensed());
+ nativeItem.setCheckable(nonActionItem.isCheckable());
+ nativeItem.setChecked(nonActionItem.isChecked());
+
+ if (nonActionItem.isExclusiveCheckable()) {
+ menu.setGroupCheckable(nonActionItem.getGroupId(), true, true);
+ }
+
+ map.put(nativeItem, nonActionItem);
+ }
+ return visible;
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuItemImpl.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuItemImpl.java
new file mode 100644
index 0000000000..f5359fb407
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuItemImpl.java
@@ -0,0 +1,647 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.view.menu;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewDebug;
+import android.widget.LinearLayout;
+
+import com.actionbarsherlock.view.ActionProvider;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.SubMenu;
+
+/**
+ * @hide
+ */
+public final class MenuItemImpl implements MenuItem {
+ private static final String TAG = "MenuItemImpl";
+
+ private static final int SHOW_AS_ACTION_MASK = SHOW_AS_ACTION_NEVER |
+ SHOW_AS_ACTION_IF_ROOM |
+ SHOW_AS_ACTION_ALWAYS;
+
+ private final int mId;
+ private final int mGroup;
+ private final int mCategoryOrder;
+ private final int mOrdering;
+ private CharSequence mTitle;
+ private CharSequence mTitleCondensed;
+ private Intent mIntent;
+ private char mShortcutNumericChar;
+ private char mShortcutAlphabeticChar;
+
+ /** The icon's drawable which is only created as needed */
+ private Drawable mIconDrawable;
+ /**
+ * The icon's resource ID which is used to get the Drawable when it is
+ * needed (if the Drawable isn't already obtained--only one of the two is
+ * needed).
+ */
+ private int mIconResId = NO_ICON;
+
+ /** The menu to which this item belongs */
+ private MenuBuilder mMenu;
+ /** If this item should launch a sub menu, this is the sub menu to launch */
+ private SubMenuBuilder mSubMenu;
+
+ private Runnable mItemCallback;
+ private MenuItem.OnMenuItemClickListener mClickListener;
+
+ private int mFlags = ENABLED;
+ private static final int CHECKABLE = 0x00000001;
+ private static final int CHECKED = 0x00000002;
+ private static final int EXCLUSIVE = 0x00000004;
+ private static final int HIDDEN = 0x00000008;
+ private static final int ENABLED = 0x00000010;
+ private static final int IS_ACTION = 0x00000020;
+
+ private int mShowAsAction = SHOW_AS_ACTION_NEVER;
+
+ private View mActionView;
+ private ActionProvider mActionProvider;
+ private OnActionExpandListener mOnActionExpandListener;
+ private boolean mIsActionViewExpanded = false;
+
+ /** Used for the icon resource ID if this item does not have an icon */
+ static final int NO_ICON = 0;
+
+ /**
+ * Current use case is for context menu: Extra information linked to the
+ * View that added this item to the context menu.
+ */
+ private ContextMenuInfo mMenuInfo;
+
+ private static String sPrependShortcutLabel;
+ private static String sEnterShortcutLabel;
+ private static String sDeleteShortcutLabel;
+ private static String sSpaceShortcutLabel;
+
+
+ /**
+ * Instantiates this menu item.
+ *
+ * @param menu
+ * @param group Item ordering grouping control. The item will be added after
+ * all other items whose order is <= this number, and before any
+ * that are larger than it. This can also be used to define
+ * groups of items for batch state changes. Normally use 0.
+ * @param id Unique item ID. Use 0 if you do not need a unique ID.
+ * @param categoryOrder The ordering for this item.
+ * @param title The text to display for the item.
+ */
+ MenuItemImpl(MenuBuilder menu, int group, int id, int categoryOrder, int ordering,
+ CharSequence title, int showAsAction) {
+
+ /* TODO if (sPrependShortcutLabel == null) {
+ // This is instantiated from the UI thread, so no chance of sync issues
+ sPrependShortcutLabel = menu.getContext().getResources().getString(
+ com.android.internal.R.string.prepend_shortcut_label);
+ sEnterShortcutLabel = menu.getContext().getResources().getString(
+ com.android.internal.R.string.menu_enter_shortcut_label);
+ sDeleteShortcutLabel = menu.getContext().getResources().getString(
+ com.android.internal.R.string.menu_delete_shortcut_label);
+ sSpaceShortcutLabel = menu.getContext().getResources().getString(
+ com.android.internal.R.string.menu_space_shortcut_label);
+ }*/
+
+ mMenu = menu;
+ mId = id;
+ mGroup = group;
+ mCategoryOrder = categoryOrder;
+ mOrdering = ordering;
+ mTitle = title;
+ mShowAsAction = showAsAction;
+ }
+
+ /**
+ * Invokes the item by calling various listeners or callbacks.
+ *
+ * @return true if the invocation was handled, false otherwise
+ */
+ public boolean invoke() {
+ if (mClickListener != null &&
+ mClickListener.onMenuItemClick(this)) {
+ return true;
+ }
+
+ if (mMenu.dispatchMenuItemSelected(mMenu.getRootMenu(), this)) {
+ return true;
+ }
+
+ if (mItemCallback != null) {
+ mItemCallback.run();
+ return true;
+ }
+
+ if (mIntent != null) {
+ try {
+ mMenu.getContext().startActivity(mIntent);
+ return true;
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "Can't find activity to handle intent; ignoring", e);
+ }
+ }
+
+ if (mActionProvider != null && mActionProvider.onPerformDefaultAction()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean isEnabled() {
+ return (mFlags & ENABLED) != 0;
+ }
+
+ public MenuItem setEnabled(boolean enabled) {
+ if (enabled) {
+ mFlags |= ENABLED;
+ } else {
+ mFlags &= ~ENABLED;
+ }
+
+ mMenu.onItemsChanged(false);
+
+ return this;
+ }
+
+ public int getGroupId() {
+ return mGroup;
+ }
+
+ @ViewDebug.CapturedViewProperty
+ public int getItemId() {
+ return mId;
+ }
+
+ public int getOrder() {
+ return mCategoryOrder;
+ }
+
+ public int getOrdering() {
+ return mOrdering;
+ }
+
+ public Intent getIntent() {
+ return mIntent;
+ }
+
+ public MenuItem setIntent(Intent intent) {
+ mIntent = intent;
+ return this;
+ }
+
+ Runnable getCallback() {
+ return mItemCallback;
+ }
+
+ public MenuItem setCallback(Runnable callback) {
+ mItemCallback = callback;
+ return this;
+ }
+
+ public char getAlphabeticShortcut() {
+ return mShortcutAlphabeticChar;
+ }
+
+ public MenuItem setAlphabeticShortcut(char alphaChar) {
+ if (mShortcutAlphabeticChar == alphaChar) return this;
+
+ mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
+
+ mMenu.onItemsChanged(false);
+
+ return this;
+ }
+
+ public char getNumericShortcut() {
+ return mShortcutNumericChar;
+ }
+
+ public MenuItem setNumericShortcut(char numericChar) {
+ if (mShortcutNumericChar == numericChar) return this;
+
+ mShortcutNumericChar = numericChar;
+
+ mMenu.onItemsChanged(false);
+
+ return this;
+ }
+
+ public MenuItem setShortcut(char numericChar, char alphaChar) {
+ mShortcutNumericChar = numericChar;
+ mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
+
+ mMenu.onItemsChanged(false);
+
+ return this;
+ }
+
+ /**
+ * @return The active shortcut (based on QWERTY-mode of the menu).
+ */
+ char getShortcut() {
+ return (mMenu.isQwertyMode() ? mShortcutAlphabeticChar : mShortcutNumericChar);
+ }
+
+ /**
+ * @return The label to show for the shortcut. This includes the chording
+ * key (for example 'Menu+a'). Also, any non-human readable
+ * characters should be human readable (for example 'Menu+enter').
+ */
+ String getShortcutLabel() {
+
+ char shortcut = getShortcut();
+ if (shortcut == 0) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder(sPrependShortcutLabel);
+ switch (shortcut) {
+
+ case '\n':
+ sb.append(sEnterShortcutLabel);
+ break;
+
+ case '\b':
+ sb.append(sDeleteShortcutLabel);
+ break;
+
+ case ' ':
+ sb.append(sSpaceShortcutLabel);
+ break;
+
+ default:
+ sb.append(shortcut);
+ break;
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * @return Whether this menu item should be showing shortcuts (depends on
+ * whether the menu should show shortcuts and whether this item has
+ * a shortcut defined)
+ */
+ boolean shouldShowShortcut() {
+ // Show shortcuts if the menu is supposed to show shortcuts AND this item has a shortcut
+ return mMenu.isShortcutsVisible() && (getShortcut() != 0);
+ }
+
+ public SubMenu getSubMenu() {
+ return mSubMenu;
+ }
+
+ public boolean hasSubMenu() {
+ return mSubMenu != null;
+ }
+
+ void setSubMenu(SubMenuBuilder subMenu) {
+ mSubMenu = subMenu;
+
+ subMenu.setHeaderTitle(getTitle());
+ }
+
+ @ViewDebug.CapturedViewProperty
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ /**
+ * Gets the title for a particular {@link ItemView}
+ *
+ * @param itemView The ItemView that is receiving the title
+ * @return Either the title or condensed title based on what the ItemView
+ * prefers
+ */
+ CharSequence getTitleForItemView(MenuView.ItemView itemView) {
+ return ((itemView != null) && itemView.prefersCondensedTitle())
+ ? getTitleCondensed()
+ : getTitle();
+ }
+
+ public MenuItem setTitle(CharSequence title) {
+ mTitle = title;
+
+ mMenu.onItemsChanged(false);
+
+ if (mSubMenu != null) {
+ mSubMenu.setHeaderTitle(title);
+ }
+
+ return this;
+ }
+
+ public MenuItem setTitle(int title) {
+ return setTitle(mMenu.getContext().getString(title));
+ }
+
+ public CharSequence getTitleCondensed() {
+ return mTitleCondensed != null ? mTitleCondensed : mTitle;
+ }
+
+ public MenuItem setTitleCondensed(CharSequence title) {
+ mTitleCondensed = title;
+
+ // Could use getTitle() in the loop below, but just cache what it would do here
+ if (title == null) {
+ title = mTitle;
+ }
+
+ mMenu.onItemsChanged(false);
+
+ return this;
+ }
+
+ public Drawable getIcon() {
+ if (mIconDrawable != null) {
+ return mIconDrawable;
+ }
+
+ if (mIconResId != NO_ICON) {
+ return mMenu.getResources().getDrawable(mIconResId);
+ }
+
+ return null;
+ }
+
+ public MenuItem setIcon(Drawable icon) {
+ mIconResId = NO_ICON;
+ mIconDrawable = icon;
+ mMenu.onItemsChanged(false);
+
+ return this;
+ }
+
+ public MenuItem setIcon(int iconResId) {
+ mIconDrawable = null;
+ mIconResId = iconResId;
+
+ // If we have a view, we need to push the Drawable to them
+ mMenu.onItemsChanged(false);
+
+ return this;
+ }
+
+ public boolean isCheckable() {
+ return (mFlags & CHECKABLE) == CHECKABLE;
+ }
+
+ public MenuItem setCheckable(boolean checkable) {
+ final int oldFlags = mFlags;
+ mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0);
+ if (oldFlags != mFlags) {
+ mMenu.onItemsChanged(false);
+ }
+
+ return this;
+ }
+
+ public void setExclusiveCheckable(boolean exclusive) {
+ mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0);
+ }
+
+ public boolean isExclusiveCheckable() {
+ return (mFlags & EXCLUSIVE) != 0;
+ }
+
+ public boolean isChecked() {
+ return (mFlags & CHECKED) == CHECKED;
+ }
+
+ public MenuItem setChecked(boolean checked) {
+ if ((mFlags & EXCLUSIVE) != 0) {
+ // Call the method on the Menu since it knows about the others in this
+ // exclusive checkable group
+ mMenu.setExclusiveItemChecked(this);
+ } else {
+ setCheckedInt(checked);
+ }
+
+ return this;
+ }
+
+ void setCheckedInt(boolean checked) {
+ final int oldFlags = mFlags;
+ mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0);
+ if (oldFlags != mFlags) {
+ mMenu.onItemsChanged(false);
+ }
+ }
+
+ public boolean isVisible() {
+ return (mFlags & HIDDEN) == 0;
+ }
+
+ /**
+ * Changes the visibility of the item. This method DOES NOT notify the
+ * parent menu of a change in this item, so this should only be called from
+ * methods that will eventually trigger this change. If unsure, use {@link #setVisible(boolean)}
+ * instead.
+ *
+ * @param shown Whether to show (true) or hide (false).
+ * @return Whether the item's shown state was changed
+ */
+ boolean setVisibleInt(boolean shown) {
+ final int oldFlags = mFlags;
+ mFlags = (mFlags & ~HIDDEN) | (shown ? 0 : HIDDEN);
+ return oldFlags != mFlags;
+ }
+
+ public MenuItem setVisible(boolean shown) {
+ // Try to set the shown state to the given state. If the shown state was changed
+ // (i.e. the previous state isn't the same as given state), notify the parent menu that
+ // the shown state has changed for this item
+ if (setVisibleInt(shown)) mMenu.onItemVisibleChanged(this);
+
+ return this;
+ }
+
+ public MenuItem setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener clickListener) {
+ mClickListener = clickListener;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return mTitle.toString();
+ }
+
+ void setMenuInfo(ContextMenuInfo menuInfo) {
+ mMenuInfo = menuInfo;
+ }
+
+ public ContextMenuInfo getMenuInfo() {
+ return mMenuInfo;
+ }
+
+ public void actionFormatChanged() {
+ mMenu.onItemActionRequestChanged(this);
+ }
+
+ /**
+ * @return Whether the menu should show icons for menu items.
+ */
+ public boolean shouldShowIcon() {
+ return mMenu.getOptionalIconsVisible();
+ }
+
+ public boolean isActionButton() {
+ return (mFlags & IS_ACTION) == IS_ACTION;
+ }
+
+ public boolean requestsActionButton() {
+ return (mShowAsAction & SHOW_AS_ACTION_IF_ROOM) == SHOW_AS_ACTION_IF_ROOM;
+ }
+
+ public boolean requiresActionButton() {
+ return (mShowAsAction & SHOW_AS_ACTION_ALWAYS) == SHOW_AS_ACTION_ALWAYS;
+ }
+
+ public void setIsActionButton(boolean isActionButton) {
+ if (isActionButton) {
+ mFlags |= IS_ACTION;
+ } else {
+ mFlags &= ~IS_ACTION;
+ }
+ }
+
+ public boolean showsTextAsAction() {
+ return (mShowAsAction & SHOW_AS_ACTION_WITH_TEXT) == SHOW_AS_ACTION_WITH_TEXT;
+ }
+
+ public void setShowAsAction(int actionEnum) {
+ switch (actionEnum & SHOW_AS_ACTION_MASK) {
+ case SHOW_AS_ACTION_ALWAYS:
+ case SHOW_AS_ACTION_IF_ROOM:
+ case SHOW_AS_ACTION_NEVER:
+ // Looks good!
+ break;
+
+ default:
+ // Mutually exclusive options selected!
+ throw new IllegalArgumentException("SHOW_AS_ACTION_ALWAYS, SHOW_AS_ACTION_IF_ROOM,"
+ + " and SHOW_AS_ACTION_NEVER are mutually exclusive.");
+ }
+ mShowAsAction = actionEnum;
+ mMenu.onItemActionRequestChanged(this);
+ }
+
+ public MenuItem setActionView(View view) {
+ mActionView = view;
+ mActionProvider = null;
+ if (view != null && view.getId() == View.NO_ID && mId > 0) {
+ view.setId(mId);
+ }
+ mMenu.onItemActionRequestChanged(this);
+ return this;
+ }
+
+ public MenuItem setActionView(int resId) {
+ final Context context = mMenu.getContext();
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ setActionView(inflater.inflate(resId, new LinearLayout(context), false));
+ return this;
+ }
+
+ public View getActionView() {
+ if (mActionView != null) {
+ return mActionView;
+ } else if (mActionProvider != null) {
+ mActionView = mActionProvider.onCreateActionView();
+ return mActionView;
+ } else {
+ return null;
+ }
+ }
+
+ public ActionProvider getActionProvider() {
+ return mActionProvider;
+ }
+
+ public MenuItem setActionProvider(ActionProvider actionProvider) {
+ mActionView = null;
+ mActionProvider = actionProvider;
+ mMenu.onItemsChanged(true); // Measurement can be changed
+ return this;
+ }
+
+ @Override
+ public MenuItem setShowAsActionFlags(int actionEnum) {
+ setShowAsAction(actionEnum);
+ return this;
+ }
+
+ @Override
+ public boolean expandActionView() {
+ if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) == 0 || mActionView == null) {
+ return false;
+ }
+
+ if (mOnActionExpandListener == null ||
+ mOnActionExpandListener.onMenuItemActionExpand(this)) {
+ return mMenu.expandItemActionView(this);
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean collapseActionView() {
+ if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) == 0) {
+ return false;
+ }
+ if (mActionView == null) {
+ // We're already collapsed if we have no action view.
+ return true;
+ }
+
+ if (mOnActionExpandListener == null ||
+ mOnActionExpandListener.onMenuItemActionCollapse(this)) {
+ return mMenu.collapseItemActionView(this);
+ }
+
+ return false;
+ }
+
+ @Override
+ public MenuItem setOnActionExpandListener(OnActionExpandListener listener) {
+ mOnActionExpandListener = listener;
+ return this;
+ }
+
+ public boolean hasCollapsibleActionView() {
+ return (mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) != 0 && mActionView != null;
+ }
+
+ public void setActionViewExpanded(boolean isExpanded) {
+ mIsActionViewExpanded = isExpanded;
+ mMenu.onItemsChanged(false);
+ }
+
+ public boolean isActionViewExpanded() {
+ return mIsActionViewExpanded;
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java
new file mode 100644
index 0000000000..aaf2997b74
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java
@@ -0,0 +1,310 @@
+package com.actionbarsherlock.internal.view.menu;
+
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View;
+import com.actionbarsherlock.internal.view.ActionProviderWrapper;
+import com.actionbarsherlock.internal.widget.CollapsibleActionViewWrapper;
+import com.actionbarsherlock.view.ActionProvider;
+import com.actionbarsherlock.view.CollapsibleActionView;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.SubMenu;
+
+public class MenuItemWrapper implements MenuItem, android.view.MenuItem.OnMenuItemClickListener {
+ private final android.view.MenuItem mNativeItem;
+ private SubMenu mSubMenu = null;
+ private OnMenuItemClickListener mMenuItemClickListener = null;
+ private OnActionExpandListener mActionExpandListener = null;
+ private android.view.MenuItem.OnActionExpandListener mNativeActionExpandListener = null;
+
+
+ public MenuItemWrapper(android.view.MenuItem nativeItem) {
+ if (nativeItem == null) {
+ throw new IllegalStateException("Wrapped menu item cannot be null.");
+ }
+ mNativeItem = nativeItem;
+ }
+
+
+ @Override
+ public int getItemId() {
+ return mNativeItem.getItemId();
+ }
+
+ @Override
+ public int getGroupId() {
+ return mNativeItem.getGroupId();
+ }
+
+ @Override
+ public int getOrder() {
+ return mNativeItem.getOrder();
+ }
+
+ @Override
+ public MenuItem setTitle(CharSequence title) {
+ mNativeItem.setTitle(title);
+ return this;
+ }
+
+ @Override
+ public MenuItem setTitle(int title) {
+ mNativeItem.setTitle(title);
+ return this;
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mNativeItem.getTitle();
+ }
+
+ @Override
+ public MenuItem setTitleCondensed(CharSequence title) {
+ mNativeItem.setTitleCondensed(title);
+ return this;
+ }
+
+ @Override
+ public CharSequence getTitleCondensed() {
+ return mNativeItem.getTitleCondensed();
+ }
+
+ @Override
+ public MenuItem setIcon(Drawable icon) {
+ mNativeItem.setIcon(icon);
+ return this;
+ }
+
+ @Override
+ public MenuItem setIcon(int iconRes) {
+ mNativeItem.setIcon(iconRes);
+ return this;
+ }
+
+ @Override
+ public Drawable getIcon() {
+ return mNativeItem.getIcon();
+ }
+
+ @Override
+ public MenuItem setIntent(Intent intent) {
+ mNativeItem.setIntent(intent);
+ return this;
+ }
+
+ @Override
+ public Intent getIntent() {
+ return mNativeItem.getIntent();
+ }
+
+ @Override
+ public MenuItem setShortcut(char numericChar, char alphaChar) {
+ mNativeItem.setShortcut(numericChar, alphaChar);
+ return this;
+ }
+
+ @Override
+ public MenuItem setNumericShortcut(char numericChar) {
+ mNativeItem.setNumericShortcut(numericChar);
+ return this;
+ }
+
+ @Override
+ public char getNumericShortcut() {
+ return mNativeItem.getNumericShortcut();
+ }
+
+ @Override
+ public MenuItem setAlphabeticShortcut(char alphaChar) {
+ mNativeItem.setAlphabeticShortcut(alphaChar);
+ return this;
+ }
+
+ @Override
+ public char getAlphabeticShortcut() {
+ return mNativeItem.getAlphabeticShortcut();
+ }
+
+ @Override
+ public MenuItem setCheckable(boolean checkable) {
+ mNativeItem.setCheckable(checkable);
+ return this;
+ }
+
+ @Override
+ public boolean isCheckable() {
+ return mNativeItem.isCheckable();
+ }
+
+ @Override
+ public MenuItem setChecked(boolean checked) {
+ mNativeItem.setChecked(checked);
+ return this;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return mNativeItem.isChecked();
+ }
+
+ @Override
+ public MenuItem setVisible(boolean visible) {
+ mNativeItem.setVisible(visible);
+ return this;
+ }
+
+ @Override
+ public boolean isVisible() {
+ return mNativeItem.isVisible();
+ }
+
+ @Override
+ public MenuItem setEnabled(boolean enabled) {
+ mNativeItem.setEnabled(enabled);
+ return this;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mNativeItem.isEnabled();
+ }
+
+ @Override
+ public boolean hasSubMenu() {
+ return mNativeItem.hasSubMenu();
+ }
+
+ @Override
+ public SubMenu getSubMenu() {
+ if (hasSubMenu() && (mSubMenu == null)) {
+ mSubMenu = new SubMenuWrapper(mNativeItem.getSubMenu());
+ }
+ return mSubMenu;
+ }
+
+ @Override
+ public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) {
+ mMenuItemClickListener = menuItemClickListener;
+ //Register ourselves as the listener to proxy
+ mNativeItem.setOnMenuItemClickListener(this);
+ return this;
+ }
+
+ @Override
+ public boolean onMenuItemClick(android.view.MenuItem item) {
+ if (mMenuItemClickListener != null) {
+ return mMenuItemClickListener.onMenuItemClick(this);
+ }
+ return false;
+ }
+
+ @Override
+ public ContextMenuInfo getMenuInfo() {
+ return mNativeItem.getMenuInfo();
+ }
+
+ @Override
+ public void setShowAsAction(int actionEnum) {
+ mNativeItem.setShowAsAction(actionEnum);
+ }
+
+ @Override
+ public MenuItem setShowAsActionFlags(int actionEnum) {
+ mNativeItem.setShowAsActionFlags(actionEnum);
+ return this;
+ }
+
+ @Override
+ public MenuItem setActionView(View view) {
+ if (view != null && view instanceof CollapsibleActionView) {
+ view = new CollapsibleActionViewWrapper(view);
+ }
+ mNativeItem.setActionView(view);
+ return this;
+ }
+
+ @Override
+ public MenuItem setActionView(int resId) {
+ //Allow the native menu to inflate the resource
+ mNativeItem.setActionView(resId);
+ if (resId != 0) {
+ //Get newly created view
+ View view = mNativeItem.getActionView();
+ if (view instanceof CollapsibleActionView) {
+ //Wrap it and re-set it
+ mNativeItem.setActionView(new CollapsibleActionViewWrapper(view));
+ }
+ }
+ return this;
+ }
+
+ @Override
+ public View getActionView() {
+ View actionView = mNativeItem.getActionView();
+ if (actionView instanceof CollapsibleActionViewWrapper) {
+ return ((CollapsibleActionViewWrapper)actionView).unwrap();
+ }
+ return actionView;
+ }
+
+ @Override
+ public MenuItem setActionProvider(ActionProvider actionProvider) {
+ mNativeItem.setActionProvider(new ActionProviderWrapper(actionProvider));
+ return this;
+ }
+
+ @Override
+ public ActionProvider getActionProvider() {
+ android.view.ActionProvider nativeProvider = mNativeItem.getActionProvider();
+ if (nativeProvider != null && nativeProvider instanceof ActionProviderWrapper) {
+ return ((ActionProviderWrapper)nativeProvider).unwrap();
+ }
+ return null;
+ }
+
+ @Override
+ public boolean expandActionView() {
+ return mNativeItem.expandActionView();
+ }
+
+ @Override
+ public boolean collapseActionView() {
+ return mNativeItem.collapseActionView();
+ }
+
+ @Override
+ public boolean isActionViewExpanded() {
+ return mNativeItem.isActionViewExpanded();
+ }
+
+ @Override
+ public MenuItem setOnActionExpandListener(OnActionExpandListener listener) {
+ mActionExpandListener = listener;
+
+ if (mNativeActionExpandListener == null) {
+ mNativeActionExpandListener = new android.view.MenuItem.OnActionExpandListener() {
+ @Override
+ public boolean onMenuItemActionExpand(android.view.MenuItem menuItem) {
+ if (mActionExpandListener != null) {
+ return mActionExpandListener.onMenuItemActionExpand(MenuItemWrapper.this);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(android.view.MenuItem menuItem) {
+ if (mActionExpandListener != null) {
+ return mActionExpandListener.onMenuItemActionCollapse(MenuItemWrapper.this);
+ }
+ return false;
+ }
+ };
+
+ //Register our inner-class as the listener to proxy method calls
+ mNativeItem.setOnActionExpandListener(mNativeActionExpandListener);
+ }
+
+ return this;
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuPopupHelper.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuPopupHelper.java
new file mode 100644
index 0000000000..f030de310a
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuPopupHelper.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.view.menu;
+
+import java.util.ArrayList;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.DataSetObserver;
+import android.os.Parcelable;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
+import android.widget.ListAdapter;
+import android.widget.PopupWindow;
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.internal.view.View_HasStateListenerSupport;
+import com.actionbarsherlock.internal.view.View_OnAttachStateChangeListener;
+import com.actionbarsherlock.internal.widget.IcsListPopupWindow;
+import com.actionbarsherlock.view.MenuItem;
+
+/**
+ * Presents a menu as a small, simple popup anchored to another view.
+ * @hide
+ */
+public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener,
+ ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener,
+ View_OnAttachStateChangeListener, MenuPresenter {
+ //UNUSED private static final String TAG = "MenuPopupHelper";
+
+ static final int ITEM_LAYOUT = R.layout.abs__popup_menu_item_layout;
+
+ private Context mContext;
+ private LayoutInflater mInflater;
+ private IcsListPopupWindow mPopup;
+ private MenuBuilder mMenu;
+ private int mPopupMaxWidth;
+ private View mAnchorView;
+ private boolean mOverflowOnly;
+ private ViewTreeObserver mTreeObserver;
+
+ private MenuAdapter mAdapter;
+
+ private Callback mPresenterCallback;
+
+ boolean mForceShowIcon;
+
+ private ViewGroup mMeasureParent;
+
+ public MenuPopupHelper(Context context, MenuBuilder menu) {
+ this(context, menu, null, false);
+ }
+
+ public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView) {
+ this(context, menu, anchorView, false);
+ }
+
+ public MenuPopupHelper(Context context, MenuBuilder menu,
+ View anchorView, boolean overflowOnly) {
+ mContext = context;
+ mInflater = LayoutInflater.from(context);
+ mMenu = menu;
+ mOverflowOnly = overflowOnly;
+
+ final Resources res = context.getResources();
+ mPopupMaxWidth = Math.max(res.getDisplayMetrics().widthPixels / 2,
+ res.getDimensionPixelSize(R.dimen.abs__config_prefDialogWidth));
+
+ mAnchorView = anchorView;
+
+ menu.addMenuPresenter(this);
+ }
+
+ public void setAnchorView(View anchor) {
+ mAnchorView = anchor;
+ }
+
+ public void setForceShowIcon(boolean forceShow) {
+ mForceShowIcon = forceShow;
+ }
+
+ public void show() {
+ if (!tryShow()) {
+ throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
+ }
+ }
+
+ public boolean tryShow() {
+ mPopup = new IcsListPopupWindow(mContext, null, R.attr.popupMenuStyle);
+ mPopup.setOnDismissListener(this);
+ mPopup.setOnItemClickListener(this);
+
+ mAdapter = new MenuAdapter(mMenu);
+ mPopup.setAdapter(mAdapter);
+ mPopup.setModal(true);
+
+ View anchor = mAnchorView;
+ if (anchor != null) {
+ final boolean addGlobalListener = mTreeObserver == null;
+ mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest
+ if (addGlobalListener) mTreeObserver.addOnGlobalLayoutListener(this);
+ ((View_HasStateListenerSupport)anchor).addOnAttachStateChangeListener(this);
+ mPopup.setAnchorView(anchor);
+ } else {
+ return false;
+ }
+
+ mPopup.setContentWidth(Math.min(measureContentWidth(mAdapter), mPopupMaxWidth));
+ mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+ mPopup.show();
+ mPopup.getListView().setOnKeyListener(this);
+ return true;
+ }
+
+ public void dismiss() {
+ if (isShowing()) {
+ mPopup.dismiss();
+ }
+ }
+
+ public void onDismiss() {
+ mPopup = null;
+ mMenu.close();
+ if (mTreeObserver != null) {
+ if (!mTreeObserver.isAlive()) mTreeObserver = mAnchorView.getViewTreeObserver();
+ mTreeObserver.removeGlobalOnLayoutListener(this);
+ mTreeObserver = null;
+ }
+ ((View_HasStateListenerSupport)mAnchorView).removeOnAttachStateChangeListener(this);
+ }
+
+ public boolean isShowing() {
+ return mPopup != null && mPopup.isShowing();
+ }
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ MenuAdapter adapter = mAdapter;
+ adapter.mAdapterMenu.performItemAction(adapter.getItem(position), 0);
+ }
+
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_MENU) {
+ dismiss();
+ return true;
+ }
+ return false;
+ }
+
+ private int measureContentWidth(ListAdapter adapter) {
+ // Menus don't tend to be long, so this is more sane than it looks.
+ int width = 0;
+ View itemView = null;
+ int itemType = 0;
+ final int widthMeasureSpec =
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int heightMeasureSpec =
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int count = adapter.getCount();
+ for (int i = 0; i < count; i++) {
+ final int positionType = adapter.getItemViewType(i);
+ if (positionType != itemType) {
+ itemType = positionType;
+ itemView = null;
+ }
+ if (mMeasureParent == null) {
+ mMeasureParent = new FrameLayout(mContext);
+ }
+ itemView = adapter.getView(i, itemView, mMeasureParent);
+ itemView.measure(widthMeasureSpec, heightMeasureSpec);
+ width = Math.max(width, itemView.getMeasuredWidth());
+ }
+ return width;
+ }
+
+ @Override
+ public void onGlobalLayout() {
+ if (isShowing()) {
+ final View anchor = mAnchorView;
+ if (anchor == null || !anchor.isShown()) {
+ dismiss();
+ } else if (isShowing()) {
+ // Recompute window size and position
+ mPopup.show();
+ }
+ }
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ if (mTreeObserver != null) {
+ if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver();
+ mTreeObserver.removeGlobalOnLayoutListener(this);
+ }
+ ((View_HasStateListenerSupport)v).removeOnAttachStateChangeListener(this);
+ }
+
+ @Override
+ public void initForMenu(Context context, MenuBuilder menu) {
+ // Don't need to do anything; we added as a presenter in the constructor.
+ }
+
+ @Override
+ public MenuView getMenuView(ViewGroup root) {
+ throw new UnsupportedOperationException("MenuPopupHelpers manage their own views");
+ }
+
+ @Override
+ public void updateMenuView(boolean cleared) {
+ if (mAdapter != null) mAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void setCallback(Callback cb) {
+ mPresenterCallback = cb;
+ }
+
+ @Override
+ public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+ if (subMenu.hasVisibleItems()) {
+ MenuPopupHelper subPopup = new MenuPopupHelper(mContext, subMenu, mAnchorView, false);
+ subPopup.setCallback(mPresenterCallback);
+
+ boolean preserveIconSpacing = false;
+ final int count = subMenu.size();
+ for (int i = 0; i < count; i++) {
+ MenuItem childItem = subMenu.getItem(i);
+ if (childItem.isVisible() && childItem.getIcon() != null) {
+ preserveIconSpacing = true;
+ break;
+ }
+ }
+ subPopup.setForceShowIcon(preserveIconSpacing);
+
+ if (subPopup.tryShow()) {
+ if (mPresenterCallback != null) {
+ mPresenterCallback.onOpenSubMenu(subMenu);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ // Only care about the (sub)menu we're presenting.
+ if (menu != mMenu) return;
+
+ dismiss();
+ if (mPresenterCallback != null) {
+ mPresenterCallback.onCloseMenu(menu, allMenusAreClosing);
+ }
+ }
+
+ @Override
+ public boolean flagActionItems() {
+ return false;
+ }
+
+ public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
+ return false;
+ }
+
+ public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
+ return false;
+ }
+
+ @Override
+ public int getId() {
+ return 0;
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ return null;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ }
+
+ private class MenuAdapter extends BaseAdapter {
+ private MenuBuilder mAdapterMenu;
+ private int mExpandedIndex = -1;
+
+ public MenuAdapter(MenuBuilder menu) {
+ mAdapterMenu = menu;
+ registerDataSetObserver(new ExpandedIndexObserver());
+ findExpandedIndex();
+ }
+
+ public int getCount() {
+ ArrayList items = mOverflowOnly ?
+ mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
+ if (mExpandedIndex < 0) {
+ return items.size();
+ }
+ return items.size() - 1;
+ }
+
+ public MenuItemImpl getItem(int position) {
+ ArrayList items = mOverflowOnly ?
+ mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
+ if (mExpandedIndex >= 0 && position >= mExpandedIndex) {
+ position++;
+ }
+ return items.get(position);
+ }
+
+ public long getItemId(int position) {
+ // Since a menu item's ID is optional, we'll use the position as an
+ // ID for the item in the AdapterView
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mInflater.inflate(ITEM_LAYOUT, parent, false);
+ }
+
+ MenuView.ItemView itemView = (MenuView.ItemView) convertView;
+ if (mForceShowIcon) {
+ ((ListMenuItemView) convertView).setForceShowIcon(true);
+ }
+ itemView.initialize(getItem(position), 0);
+ return convertView;
+ }
+
+ void findExpandedIndex() {
+ final MenuItemImpl expandedItem = mMenu.getExpandedItem();
+ if (expandedItem != null) {
+ final ArrayList items = mMenu.getNonActionItems();
+ final int count = items.size();
+ for (int i = 0; i < count; i++) {
+ final MenuItemImpl item = items.get(i);
+ if (item == expandedItem) {
+ mExpandedIndex = i;
+ return;
+ }
+ }
+ }
+ mExpandedIndex = -1;
+ }
+ }
+
+ private class ExpandedIndexObserver extends DataSetObserver {
+ @Override
+ public void onChanged() {
+ mAdapter.findExpandedIndex();
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuPresenter.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuPresenter.java
new file mode 100644
index 0000000000..c3f35472c5
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuPresenter.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.view.menu;
+
+import android.content.Context;
+import android.os.Parcelable;
+import android.view.ViewGroup;
+
+/**
+ * A MenuPresenter is responsible for building views for a Menu object.
+ * It takes over some responsibility from the old style monolithic MenuBuilder class.
+ */
+public interface MenuPresenter {
+ /**
+ * Called by menu implementation to notify another component of open/close events.
+ */
+ public interface Callback {
+ /**
+ * Called when a menu is closing.
+ * @param menu
+ * @param allMenusAreClosing
+ */
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing);
+
+ /**
+ * Called when a submenu opens. Useful for notifying the application
+ * of menu state so that it does not attempt to hide the action bar
+ * while a submenu is open or similar.
+ *
+ * @param subMenu Submenu currently being opened
+ * @return true if the Callback will handle presenting the submenu, false if
+ * the presenter should attempt to do so.
+ */
+ public boolean onOpenSubMenu(MenuBuilder subMenu);
+ }
+
+ /**
+ * Initialize this presenter for the given context and menu.
+ * This method is called by MenuBuilder when a presenter is
+ * added. See {@link MenuBuilder#addMenuPresenter(MenuPresenter)}
+ *
+ * @param context Context for this presenter; used for view creation and resource management
+ * @param menu Menu to host
+ */
+ public void initForMenu(Context context, MenuBuilder menu);
+
+ /**
+ * Retrieve a MenuView to display the menu specified in
+ * {@link #initForMenu(Context, Menu)}.
+ *
+ * @param root Intended parent of the MenuView.
+ * @return A freshly created MenuView.
+ */
+ public MenuView getMenuView(ViewGroup root);
+
+ /**
+ * Update the menu UI in response to a change. Called by
+ * MenuBuilder during the normal course of operation.
+ *
+ * @param cleared true if the menu was entirely cleared
+ */
+ public void updateMenuView(boolean cleared);
+
+ /**
+ * Set a callback object that will be notified of menu events
+ * related to this specific presentation.
+ * @param cb Callback that will be notified of future events
+ */
+ public void setCallback(Callback cb);
+
+ /**
+ * Called by Menu implementations to indicate that a submenu item
+ * has been selected. An active Callback should be notified, and
+ * if applicable the presenter should present the submenu.
+ *
+ * @param subMenu SubMenu being opened
+ * @return true if the the event was handled, false otherwise.
+ */
+ public boolean onSubMenuSelected(SubMenuBuilder subMenu);
+
+ /**
+ * Called by Menu implementations to indicate that a menu or submenu is
+ * closing. Presenter implementations should close the representation
+ * of the menu indicated as necessary and notify a registered callback.
+ *
+ * @param menu Menu or submenu that is closing.
+ * @param allMenusAreClosing True if all associated menus are closing.
+ */
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing);
+
+ /**
+ * Called by Menu implementations to flag items that will be shown as actions.
+ * @return true if this presenter changed the action status of any items.
+ */
+ public boolean flagActionItems();
+
+ /**
+ * Called when a menu item with a collapsable action view should expand its action view.
+ *
+ * @param menu Menu containing the item to be expanded
+ * @param item Item to be expanded
+ * @return true if this presenter expanded the action view, false otherwise.
+ */
+ public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item);
+
+ /**
+ * Called when a menu item with a collapsable action view should collapse its action view.
+ *
+ * @param menu Menu containing the item to be collapsed
+ * @param item Item to be collapsed
+ * @return true if this presenter collapsed the action view, false otherwise.
+ */
+ public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item);
+
+ /**
+ * Returns an ID for determining how to save/restore instance state.
+ * @return a valid ID value.
+ */
+ public int getId();
+
+ /**
+ * Returns a Parcelable describing the current state of the presenter.
+ * It will be passed to the {@link #onRestoreInstanceState(Parcelable)}
+ * method of the presenter sharing the same ID later.
+ * @return The saved instance state
+ */
+ public Parcelable onSaveInstanceState();
+
+ /**
+ * Supplies the previously saved instance state to be restored.
+ * @param state The previously saved instance state
+ */
+ public void onRestoreInstanceState(Parcelable state);
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuView.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuView.java
new file mode 100644
index 0000000000..323ba2d88d
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuView.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.view.menu;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Minimal interface for a menu view. {@link #initialize(MenuBuilder)} must be called for the
+ * menu to be functional.
+ *
+ * @hide
+ */
+public interface MenuView {
+ /**
+ * Initializes the menu to the given menu. This should be called after the
+ * view is inflated.
+ *
+ * @param menu The menu that this MenuView should display.
+ */
+ public void initialize(MenuBuilder menu);
+
+ /**
+ * Returns the default animations to be used for this menu when entering/exiting.
+ * @return A resource ID for the default animations to be used for this menu.
+ */
+ public int getWindowAnimations();
+
+ /**
+ * Minimal interface for a menu item view. {@link #initialize(MenuItemImpl, int)} must be called
+ * for the item to be functional.
+ */
+ public interface ItemView {
+ /**
+ * Initializes with the provided MenuItemData. This should be called after the view is
+ * inflated.
+ * @param itemData The item that this ItemView should display.
+ * @param menuType The type of this menu, one of
+ * {@link MenuBuilder#TYPE_ICON}, {@link MenuBuilder#TYPE_EXPANDED},
+ * {@link MenuBuilder#TYPE_DIALOG}).
+ */
+ public void initialize(MenuItemImpl itemData, int menuType);
+
+ /**
+ * Gets the item data that this view is displaying.
+ * @return the item data, or null if there is not one
+ */
+ public MenuItemImpl getItemData();
+
+ /**
+ * Sets the title of the item view.
+ * @param title The title to set.
+ */
+ public void setTitle(CharSequence title);
+
+ /**
+ * Sets the enabled state of the item view.
+ * @param enabled Whether the item view should be enabled.
+ */
+ public void setEnabled(boolean enabled);
+
+ /**
+ * Displays the checkbox for the item view. This does not ensure the item view will be
+ * checked, for that use {@link #setChecked}.
+ * @param checkable Whether to display the checkbox or to hide it
+ */
+ public void setCheckable(boolean checkable);
+
+ /**
+ * Checks the checkbox for the item view. If the checkbox is hidden, it will NOT be
+ * made visible, call {@link #setCheckable(boolean)} for that.
+ * @param checked Whether the checkbox should be checked
+ */
+ public void setChecked(boolean checked);
+
+ /**
+ * Sets the shortcut for the item.
+ * @param showShortcut Whether a shortcut should be shown(if false, the value of
+ * shortcutKey should be ignored).
+ * @param shortcutKey The shortcut key that should be shown on the ItemView.
+ */
+ public void setShortcut(boolean showShortcut, char shortcutKey);
+
+ /**
+ * Set the icon of this item view.
+ * @param icon The icon of this item. null to hide the icon.
+ */
+ public void setIcon(Drawable icon);
+
+ /**
+ * Whether this item view prefers displaying the condensed title rather
+ * than the normal title. If a condensed title is not available, the
+ * normal title will be used.
+ *
+ * @return Whether this item view prefers displaying the condensed
+ * title.
+ */
+ public boolean prefersCondensedTitle();
+
+ /**
+ * Whether this item view shows an icon.
+ *
+ * @return Whether this item view shows an icon.
+ */
+ public boolean showsIcon();
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java
new file mode 100644
index 0000000000..3d4dd42fda
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java
@@ -0,0 +1,185 @@
+package com.actionbarsherlock.internal.view.menu;
+
+import java.util.WeakHashMap;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.view.KeyEvent;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.SubMenu;
+
+public class MenuWrapper implements Menu {
+ private final android.view.Menu mNativeMenu;
+
+ private final WeakHashMap mNativeMap =
+ new WeakHashMap();
+
+
+ public MenuWrapper(android.view.Menu nativeMenu) {
+ mNativeMenu = nativeMenu;
+ }
+
+ public android.view.Menu unwrap() {
+ return mNativeMenu;
+ }
+
+ private MenuItem addInternal(android.view.MenuItem nativeItem) {
+ MenuItem item = new MenuItemWrapper(nativeItem);
+ mNativeMap.put(nativeItem, item);
+ return item;
+ }
+
+ @Override
+ public MenuItem add(CharSequence title) {
+ return addInternal(mNativeMenu.add(title));
+ }
+
+ @Override
+ public MenuItem add(int titleRes) {
+ return addInternal(mNativeMenu.add(titleRes));
+ }
+
+ @Override
+ public MenuItem add(int groupId, int itemId, int order, CharSequence title) {
+ return addInternal(mNativeMenu.add(groupId, itemId, order, title));
+ }
+
+ @Override
+ public MenuItem add(int groupId, int itemId, int order, int titleRes) {
+ return addInternal(mNativeMenu.add(groupId, itemId, order, titleRes));
+ }
+
+ private SubMenu addInternal(android.view.SubMenu nativeSubMenu) {
+ SubMenu subMenu = new SubMenuWrapper(nativeSubMenu);
+ android.view.MenuItem nativeItem = nativeSubMenu.getItem();
+ MenuItem item = subMenu.getItem();
+ mNativeMap.put(nativeItem, item);
+ return subMenu;
+ }
+
+ @Override
+ public SubMenu addSubMenu(CharSequence title) {
+ return addInternal(mNativeMenu.addSubMenu(title));
+ }
+
+ @Override
+ public SubMenu addSubMenu(int titleRes) {
+ return addInternal(mNativeMenu.addSubMenu(titleRes));
+ }
+
+ @Override
+ public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) {
+ return addInternal(mNativeMenu.addSubMenu(groupId, itemId, order, title));
+ }
+
+ @Override
+ public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) {
+ return addInternal(mNativeMenu.addSubMenu(groupId, itemId, order, titleRes));
+ }
+
+ @Override
+ public int addIntentOptions(int groupId, int itemId, int order, ComponentName caller, Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems) {
+ int result;
+ if (outSpecificItems != null) {
+ android.view.MenuItem[] nativeOutItems = new android.view.MenuItem[outSpecificItems.length];
+ result = mNativeMenu.addIntentOptions(groupId, itemId, order, caller, specifics, intent, flags, nativeOutItems);
+ for (int i = 0, length = outSpecificItems.length; i < length; i++) {
+ outSpecificItems[i] = new MenuItemWrapper(nativeOutItems[i]);
+ }
+ } else {
+ result = mNativeMenu.addIntentOptions(groupId, itemId, order, caller, specifics, intent, flags, null);
+ }
+ return result;
+ }
+
+ @Override
+ public void removeItem(int id) {
+ mNativeMenu.removeItem(id);
+ }
+
+ @Override
+ public void removeGroup(int groupId) {
+ mNativeMenu.removeGroup(groupId);
+ }
+
+ @Override
+ public void clear() {
+ mNativeMap.clear();
+ mNativeMenu.clear();
+ }
+
+ @Override
+ public void setGroupCheckable(int group, boolean checkable, boolean exclusive) {
+ mNativeMenu.setGroupCheckable(group, checkable, exclusive);
+ }
+
+ @Override
+ public void setGroupVisible(int group, boolean visible) {
+ mNativeMenu.setGroupVisible(group, visible);
+ }
+
+ @Override
+ public void setGroupEnabled(int group, boolean enabled) {
+ mNativeMenu.setGroupEnabled(group, enabled);
+ }
+
+ @Override
+ public boolean hasVisibleItems() {
+ return mNativeMenu.hasVisibleItems();
+ }
+
+ @Override
+ public MenuItem findItem(int id) {
+ android.view.MenuItem nativeItem = mNativeMenu.findItem(id);
+ return findItem(nativeItem);
+ }
+
+ public MenuItem findItem(android.view.MenuItem nativeItem) {
+ if (nativeItem == null) {
+ return null;
+ }
+
+ MenuItem wrapped = mNativeMap.get(nativeItem);
+ if (wrapped != null) {
+ return wrapped;
+ }
+
+ return addInternal(nativeItem);
+ }
+
+ @Override
+ public int size() {
+ return mNativeMenu.size();
+ }
+
+ @Override
+ public MenuItem getItem(int index) {
+ android.view.MenuItem nativeItem = mNativeMenu.getItem(index);
+ return findItem(nativeItem);
+ }
+
+ @Override
+ public void close() {
+ mNativeMenu.close();
+ }
+
+ @Override
+ public boolean performShortcut(int keyCode, KeyEvent event, int flags) {
+ return mNativeMenu.performShortcut(keyCode, event, flags);
+ }
+
+ @Override
+ public boolean isShortcutKey(int keyCode, KeyEvent event) {
+ return mNativeMenu.isShortcutKey(keyCode, event);
+ }
+
+ @Override
+ public boolean performIdentifierAction(int id, int flags) {
+ return mNativeMenu.performIdentifierAction(id, flags);
+ }
+
+ @Override
+ public void setQwertyMode(boolean isQwerty) {
+ mNativeMenu.setQwertyMode(isQwerty);
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/SubMenuBuilder.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/SubMenuBuilder.java
new file mode 100644
index 0000000000..6679cf3860
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/SubMenuBuilder.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.view.menu;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.SubMenu;
+
+/**
+ * The model for a sub menu, which is an extension of the menu. Most methods are proxied to
+ * the parent menu.
+ */
+public class SubMenuBuilder extends MenuBuilder implements SubMenu {
+ private MenuBuilder mParentMenu;
+ private MenuItemImpl mItem;
+
+ public SubMenuBuilder(Context context, MenuBuilder parentMenu, MenuItemImpl item) {
+ super(context);
+
+ mParentMenu = parentMenu;
+ mItem = item;
+ }
+
+ @Override
+ public void setQwertyMode(boolean isQwerty) {
+ mParentMenu.setQwertyMode(isQwerty);
+ }
+
+ @Override
+ public boolean isQwertyMode() {
+ return mParentMenu.isQwertyMode();
+ }
+
+ @Override
+ public void setShortcutsVisible(boolean shortcutsVisible) {
+ mParentMenu.setShortcutsVisible(shortcutsVisible);
+ }
+
+ @Override
+ public boolean isShortcutsVisible() {
+ return mParentMenu.isShortcutsVisible();
+ }
+
+ public Menu getParentMenu() {
+ return mParentMenu;
+ }
+
+ public MenuItem getItem() {
+ return mItem;
+ }
+
+ @Override
+ public void setCallback(Callback callback) {
+ mParentMenu.setCallback(callback);
+ }
+
+ @Override
+ public MenuBuilder getRootMenu() {
+ return mParentMenu;
+ }
+
+ @Override
+ boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) {
+ return super.dispatchMenuItemSelected(menu, item) ||
+ mParentMenu.dispatchMenuItemSelected(menu, item);
+ }
+
+ public SubMenu setIcon(Drawable icon) {
+ mItem.setIcon(icon);
+ return this;
+ }
+
+ public SubMenu setIcon(int iconRes) {
+ mItem.setIcon(iconRes);
+ return this;
+ }
+
+ public SubMenu setHeaderIcon(Drawable icon) {
+ return (SubMenu) super.setHeaderIconInt(icon);
+ }
+
+ public SubMenu setHeaderIcon(int iconRes) {
+ return (SubMenu) super.setHeaderIconInt(iconRes);
+ }
+
+ public SubMenu setHeaderTitle(CharSequence title) {
+ return (SubMenu) super.setHeaderTitleInt(title);
+ }
+
+ public SubMenu setHeaderTitle(int titleRes) {
+ return (SubMenu) super.setHeaderTitleInt(titleRes);
+ }
+
+ public SubMenu setHeaderView(View view) {
+ return (SubMenu) super.setHeaderViewInt(view);
+ }
+
+ @Override
+ public boolean expandItemActionView(MenuItemImpl item) {
+ return mParentMenu.expandItemActionView(item);
+ }
+
+ @Override
+ public boolean collapseItemActionView(MenuItemImpl item) {
+ return mParentMenu.collapseItemActionView(item);
+ }
+
+ @Override
+ public String getActionViewStatesKey() {
+ final int itemId = mItem != null ? mItem.getItemId() : 0;
+ if (itemId == 0) {
+ return null;
+ }
+ return super.getActionViewStatesKey() + ":" + itemId;
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/SubMenuWrapper.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/SubMenuWrapper.java
new file mode 100644
index 0000000000..7d307acb10
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/view/menu/SubMenuWrapper.java
@@ -0,0 +1,72 @@
+package com.actionbarsherlock.internal.view.menu;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.SubMenu;
+
+public class SubMenuWrapper extends MenuWrapper implements SubMenu {
+ private final android.view.SubMenu mNativeSubMenu;
+ private MenuItem mItem = null;
+
+ public SubMenuWrapper(android.view.SubMenu nativeSubMenu) {
+ super(nativeSubMenu);
+ mNativeSubMenu = nativeSubMenu;
+ }
+
+
+ @Override
+ public SubMenu setHeaderTitle(int titleRes) {
+ mNativeSubMenu.setHeaderTitle(titleRes);
+ return this;
+ }
+
+ @Override
+ public SubMenu setHeaderTitle(CharSequence title) {
+ mNativeSubMenu.setHeaderTitle(title);
+ return this;
+ }
+
+ @Override
+ public SubMenu setHeaderIcon(int iconRes) {
+ mNativeSubMenu.setHeaderIcon(iconRes);
+ return this;
+ }
+
+ @Override
+ public SubMenu setHeaderIcon(Drawable icon) {
+ mNativeSubMenu.setHeaderIcon(icon);
+ return this;
+ }
+
+ @Override
+ public SubMenu setHeaderView(View view) {
+ mNativeSubMenu.setHeaderView(view);
+ return this;
+ }
+
+ @Override
+ public void clearHeader() {
+ mNativeSubMenu.clearHeader();
+ }
+
+ @Override
+ public SubMenu setIcon(int iconRes) {
+ mNativeSubMenu.setIcon(iconRes);
+ return this;
+ }
+
+ @Override
+ public SubMenu setIcon(Drawable icon) {
+ mNativeSubMenu.setIcon(icon);
+ return this;
+ }
+
+ @Override
+ public MenuItem getItem() {
+ if (mItem == null) {
+ mItem = new MenuItemWrapper(mNativeSubMenu.getItem());
+ }
+ return mItem;
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/AbsActionBarView.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/AbsActionBarView.java
new file mode 100644
index 0000000000..3a4a446756
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/AbsActionBarView.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.actionbarsherlock.internal.widget;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.internal.nineoldandroids.animation.Animator;
+import com.actionbarsherlock.internal.nineoldandroids.animation.AnimatorSet;
+import com.actionbarsherlock.internal.nineoldandroids.animation.ObjectAnimator;
+import com.actionbarsherlock.internal.nineoldandroids.view.NineViewGroup;
+import com.actionbarsherlock.internal.view.menu.ActionMenuPresenter;
+import com.actionbarsherlock.internal.view.menu.ActionMenuView;
+
+import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getBoolean;
+
+public abstract class AbsActionBarView extends NineViewGroup {
+ protected ActionMenuView mMenuView;
+ protected ActionMenuPresenter mActionMenuPresenter;
+ protected ActionBarContainer mSplitView;
+ protected boolean mSplitActionBar;
+ protected boolean mSplitWhenNarrow;
+ protected int mContentHeight;
+
+ final Context mContext;
+
+ protected Animator mVisibilityAnim;
+ protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
+
+ private static final /*Time*/Interpolator sAlphaInterpolator = new DecelerateInterpolator();
+
+ private static final int FADE_DURATION = 200;
+
+ public AbsActionBarView(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ public AbsActionBarView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ }
+
+ public AbsActionBarView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mContext = context;
+ }
+
+ /*
+ * Must be public so we can dispatch pre-2.2 via ActionBarImpl.
+ */
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
+ super.onConfigurationChanged(newConfig);
+ } else if (mMenuView != null) {
+ mMenuView.onConfigurationChanged(newConfig);
+ }
+
+ // Action bar can change size on configuration changes.
+ // Reread the desired height from the theme-specified style.
+ TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.SherlockActionBar,
+ R.attr.actionBarStyle, 0);
+ setContentHeight(a.getLayoutDimension(R.styleable.SherlockActionBar_height, 0));
+ a.recycle();
+ if (mSplitWhenNarrow) {
+ setSplitActionBar(getResources_getBoolean(getContext(),
+ R.bool.abs__split_action_bar_is_narrow));
+ }
+ if (mActionMenuPresenter != null) {
+ mActionMenuPresenter.onConfigurationChanged(newConfig);
+ }
+ }
+
+ /**
+ * Sets whether the bar should be split right now, no questions asked.
+ * @param split true if the bar should split
+ */
+ public void setSplitActionBar(boolean split) {
+ mSplitActionBar = split;
+ }
+
+ /**
+ * Sets whether the bar should split if we enter a narrow screen configuration.
+ * @param splitWhenNarrow true if the bar should check to split after a config change
+ */
+ public void setSplitWhenNarrow(boolean splitWhenNarrow) {
+ mSplitWhenNarrow = splitWhenNarrow;
+ }
+
+ public void setContentHeight(int height) {
+ mContentHeight = height;
+ requestLayout();
+ }
+
+ public int getContentHeight() {
+ return mContentHeight;
+ }
+
+ public void setSplitView(ActionBarContainer splitView) {
+ mSplitView = splitView;
+ }
+
+ /**
+ * @return Current visibility or if animating, the visibility being animated to.
+ */
+ public int getAnimatedVisibility() {
+ if (mVisibilityAnim != null) {
+ return mVisAnimListener.mFinalVisibility;
+ }
+ return getVisibility();
+ }
+
+ public void animateToVisibility(int visibility) {
+ if (mVisibilityAnim != null) {
+ mVisibilityAnim.cancel();
+ }
+ if (visibility == VISIBLE) {
+ if (getVisibility() != VISIBLE) {
+ setAlpha(0);
+ if (mSplitView != null && mMenuView != null) {
+ mMenuView.setAlpha(0);
+ }
+ }
+ ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1);
+ anim.setDuration(FADE_DURATION);
+ anim.setInterpolator(sAlphaInterpolator);
+ if (mSplitView != null && mMenuView != null) {
+ AnimatorSet set = new AnimatorSet();
+ ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, "alpha", 1);
+ splitAnim.setDuration(FADE_DURATION);
+ set.addListener(mVisAnimListener.withFinalVisibility(visibility));
+ set.play(anim).with(splitAnim);
+ set.start();
+ } else {
+ anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
+ anim.start();
+ }
+ } else {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0);
+ anim.setDuration(FADE_DURATION);
+ anim.setInterpolator(sAlphaInterpolator);
+ if (mSplitView != null && mMenuView != null) {
+ AnimatorSet set = new AnimatorSet();
+ ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, "alpha", 0);
+ splitAnim.setDuration(FADE_DURATION);
+ set.addListener(mVisAnimListener.withFinalVisibility(visibility));
+ set.play(anim).with(splitAnim);
+ set.start();
+ } else {
+ anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
+ anim.start();
+ }
+ }
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ if (mVisibilityAnim != null) {
+ mVisibilityAnim.end();
+ }
+ super.setVisibility(visibility);
+ }
+
+ public boolean showOverflowMenu() {
+ if (mActionMenuPresenter != null) {
+ return mActionMenuPresenter.showOverflowMenu();
+ }
+ return false;
+ }
+
+ public void postShowOverflowMenu() {
+ post(new Runnable() {
+ public void run() {
+ showOverflowMenu();
+ }
+ });
+ }
+
+ public boolean hideOverflowMenu() {
+ if (mActionMenuPresenter != null) {
+ return mActionMenuPresenter.hideOverflowMenu();
+ }
+ return false;
+ }
+
+ public boolean isOverflowMenuShowing() {
+ if (mActionMenuPresenter != null) {
+ return mActionMenuPresenter.isOverflowMenuShowing();
+ }
+ return false;
+ }
+
+ public boolean isOverflowReserved() {
+ return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved();
+ }
+
+ public void dismissPopupMenus() {
+ if (mActionMenuPresenter != null) {
+ mActionMenuPresenter.dismissPopupMenus();
+ }
+ }
+
+ protected int measureChildView(View child, int availableWidth, int childSpecHeight,
+ int spacing) {
+ child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
+ childSpecHeight);
+
+ availableWidth -= child.getMeasuredWidth();
+ availableWidth -= spacing;
+
+ return Math.max(0, availableWidth);
+ }
+
+ protected int positionChild(View child, int x, int y, int contentHeight) {
+ int childWidth = child.getMeasuredWidth();
+ int childHeight = child.getMeasuredHeight();
+ int childTop = y + (contentHeight - childHeight) / 2;
+
+ child.layout(x, childTop, x + childWidth, childTop + childHeight);
+
+ return childWidth;
+ }
+
+ protected int positionChildInverse(View child, int x, int y, int contentHeight) {
+ int childWidth = child.getMeasuredWidth();
+ int childHeight = child.getMeasuredHeight();
+ int childTop = y + (contentHeight - childHeight) / 2;
+
+ child.layout(x - childWidth, childTop, x, childTop + childHeight);
+
+ return childWidth;
+ }
+
+ protected class VisibilityAnimListener implements Animator.AnimatorListener {
+ private boolean mCanceled = false;
+ int mFinalVisibility;
+
+ public VisibilityAnimListener withFinalVisibility(int visibility) {
+ mFinalVisibility = visibility;
+ return this;
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ setVisibility(VISIBLE);
+ mVisibilityAnim = animation;
+ mCanceled = false;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCanceled) return;
+
+ mVisibilityAnim = null;
+ setVisibility(mFinalVisibility);
+ if (mSplitView != null && mMenuView != null) {
+ mMenuView.setVisibility(mFinalVisibility);
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCanceled = true;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/ActionBarContainer.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/ActionBarContainer.java
new file mode 100644
index 0000000000..1d9c68b37d
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/ActionBarContainer.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.internal.nineoldandroids.widget.NineFrameLayout;
+
+/**
+ * This class acts as a container for the action bar view and action mode context views.
+ * It applies special styles as needed to help handle animated transitions between them.
+ * @hide
+ */
+public class ActionBarContainer extends NineFrameLayout {
+ private boolean mIsTransitioning;
+ private View mTabContainer;
+ private ActionBarView mActionBarView;
+
+ private Drawable mBackground;
+ private Drawable mStackedBackground;
+ private Drawable mSplitBackground;
+ private boolean mIsSplit;
+ private boolean mIsStacked;
+
+ public ActionBarContainer(Context context) {
+ this(context, null);
+ }
+
+ public ActionBarContainer(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ setBackgroundDrawable(null);
+
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.SherlockActionBar);
+ mBackground = a.getDrawable(R.styleable.SherlockActionBar_background);
+ mStackedBackground = a.getDrawable(
+ R.styleable.SherlockActionBar_backgroundStacked);
+
+ //Fix for issue #379
+ if (mStackedBackground instanceof ColorDrawable && Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+ Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(bitmap);
+ mStackedBackground.draw(c);
+ int color = bitmap.getPixel(0, 0);
+ bitmap.recycle();
+ mStackedBackground = new IcsColorDrawable(color);
+ }
+
+ if (getId() == R.id.abs__split_action_bar) {
+ mIsSplit = true;
+ mSplitBackground = a.getDrawable(
+ R.styleable.SherlockActionBar_backgroundSplit);
+ }
+ a.recycle();
+
+ setWillNotDraw(mIsSplit ? mSplitBackground == null :
+ mBackground == null && mStackedBackground == null);
+ }
+
+ @Override
+ public void onFinishInflate() {
+ super.onFinishInflate();
+ mActionBarView = (ActionBarView) findViewById(R.id.abs__action_bar);
+ }
+
+ public void setPrimaryBackground(Drawable bg) {
+ mBackground = bg;
+ invalidate();
+ }
+
+ public void setStackedBackground(Drawable bg) {
+ mStackedBackground = bg;
+ invalidate();
+ }
+
+ public void setSplitBackground(Drawable bg) {
+ mSplitBackground = bg;
+ invalidate();
+ }
+
+ /**
+ * Set the action bar into a "transitioning" state. While transitioning
+ * the bar will block focus and touch from all of its descendants. This
+ * prevents the user from interacting with the bar while it is animating
+ * in or out.
+ *
+ * @param isTransitioning true if the bar is currently transitioning, false otherwise.
+ */
+ public void setTransitioning(boolean isTransitioning) {
+ mIsTransitioning = isTransitioning;
+ setDescendantFocusability(isTransitioning ? FOCUS_BLOCK_DESCENDANTS
+ : FOCUS_AFTER_DESCENDANTS);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return mIsTransitioning || super.onInterceptTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ super.onTouchEvent(ev);
+
+ // An action bar always eats touch events.
+ return true;
+ }
+
+ @Override
+ public boolean onHoverEvent(MotionEvent ev) {
+ super.onHoverEvent(ev);
+
+ // An action bar always eats hover events.
+ return true;
+ }
+
+ public void setTabContainer(ScrollingTabContainerView tabView) {
+ if (mTabContainer != null) {
+ removeView(mTabContainer);
+ }
+ mTabContainer = tabView;
+ if (tabView != null) {
+ addView(tabView);
+ final ViewGroup.LayoutParams lp = tabView.getLayoutParams();
+ lp.width = LayoutParams.MATCH_PARENT;
+ lp.height = LayoutParams.WRAP_CONTENT;
+ tabView.setAllowCollapse(false);
+ }
+ }
+
+ public View getTabContainer() {
+ return mTabContainer;
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ if (getWidth() == 0 || getHeight() == 0) {
+ return;
+ }
+
+ if (mIsSplit) {
+ if (mSplitBackground != null) mSplitBackground.draw(canvas);
+ } else {
+ if (mBackground != null) {
+ mBackground.draw(canvas);
+ }
+ if (mStackedBackground != null && mIsStacked) {
+ mStackedBackground.draw(canvas);
+ }
+ }
+ }
+
+ //This causes the animation reflection to fail on pre-HC platforms
+ //@Override
+ //public android.view.ActionMode startActionModeForChild(View child, android.view.ActionMode.Callback callback) {
+ // // No starting an action mode for an action bar child! (Where would it go?)
+ // return null;
+ //}
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ if (mActionBarView == null) return;
+
+ final LayoutParams lp = (LayoutParams) mActionBarView.getLayoutParams();
+ final int actionBarViewHeight = mActionBarView.isCollapsed() ? 0 :
+ mActionBarView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
+
+ if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
+ final int mode = MeasureSpec.getMode(heightMeasureSpec);
+ if (mode == MeasureSpec.AT_MOST) {
+ final int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
+ setMeasuredDimension(getMeasuredWidth(),
+ Math.min(actionBarViewHeight + mTabContainer.getMeasuredHeight(),
+ maxHeight));
+ }
+ }
+ }
+
+ @Override
+ public void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+
+ final boolean hasTabs = mTabContainer != null && mTabContainer.getVisibility() != GONE;
+
+ if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
+ final int containerHeight = getMeasuredHeight();
+ final int tabHeight = mTabContainer.getMeasuredHeight();
+
+ if ((mActionBarView.getDisplayOptions() & ActionBar.DISPLAY_SHOW_HOME) == 0) {
+ // Not showing home, put tabs on top.
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+
+ if (child == mTabContainer) continue;
+
+ if (!mActionBarView.isCollapsed()) {
+ child.offsetTopAndBottom(tabHeight);
+ }
+ }
+ mTabContainer.layout(l, 0, r, tabHeight);
+ } else {
+ mTabContainer.layout(l, containerHeight - tabHeight, r, containerHeight);
+ }
+ }
+
+ boolean needsInvalidate = false;
+ if (mIsSplit) {
+ if (mSplitBackground != null) {
+ mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
+ needsInvalidate = true;
+ }
+ } else {
+ if (mBackground != null) {
+ mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(),
+ mActionBarView.getRight(), mActionBarView.getBottom());
+ needsInvalidate = true;
+ }
+ if ((mIsStacked = hasTabs && mStackedBackground != null)) {
+ mStackedBackground.setBounds(mTabContainer.getLeft(), mTabContainer.getTop(),
+ mTabContainer.getRight(), mTabContainer.getBottom());
+ needsInvalidate = true;
+ }
+ }
+
+ if (needsInvalidate) {
+ invalidate();
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/ActionBarContextView.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/ActionBarContextView.java
new file mode 100644
index 0000000000..9ec250f387
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/ActionBarContextView.java
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.actionbarsherlock.internal.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.internal.nineoldandroids.animation.Animator;
+import com.actionbarsherlock.internal.nineoldandroids.animation.Animator.AnimatorListener;
+import com.actionbarsherlock.internal.nineoldandroids.animation.AnimatorSet;
+import com.actionbarsherlock.internal.nineoldandroids.animation.ObjectAnimator;
+import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy;
+import com.actionbarsherlock.internal.nineoldandroids.widget.NineLinearLayout;
+import com.actionbarsherlock.internal.view.menu.ActionMenuPresenter;
+import com.actionbarsherlock.internal.view.menu.ActionMenuView;
+import com.actionbarsherlock.internal.view.menu.MenuBuilder;
+import com.actionbarsherlock.view.ActionMode;
+
+/**
+ * @hide
+ */
+public class ActionBarContextView extends AbsActionBarView implements AnimatorListener {
+ //UNUSED private static final String TAG = "ActionBarContextView";
+
+ private CharSequence mTitle;
+ private CharSequence mSubtitle;
+
+ private NineLinearLayout mClose;
+ private View mCustomView;
+ private LinearLayout mTitleLayout;
+ private TextView mTitleView;
+ private TextView mSubtitleView;
+ private int mTitleStyleRes;
+ private int mSubtitleStyleRes;
+ private Drawable mSplitBackground;
+
+ private Animator mCurrentAnimation;
+ private boolean mAnimateInOnLayout;
+ private int mAnimationMode;
+
+ private static final int ANIMATE_IDLE = 0;
+ private static final int ANIMATE_IN = 1;
+ private static final int ANIMATE_OUT = 2;
+
+ public ActionBarContextView(Context context) {
+ this(context, null);
+ }
+
+ public ActionBarContextView(Context context, AttributeSet attrs) {
+ this(context, attrs, R.attr.actionModeStyle);
+ }
+
+ public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SherlockActionMode, defStyle, 0);
+ setBackgroundDrawable(a.getDrawable(
+ R.styleable.SherlockActionMode_background));
+ mTitleStyleRes = a.getResourceId(
+ R.styleable.SherlockActionMode_titleTextStyle, 0);
+ mSubtitleStyleRes = a.getResourceId(
+ R.styleable.SherlockActionMode_subtitleTextStyle, 0);
+
+ mContentHeight = a.getLayoutDimension(
+ R.styleable.SherlockActionMode_height, 0);
+
+ mSplitBackground = a.getDrawable(
+ R.styleable.SherlockActionMode_backgroundSplit);
+
+ a.recycle();
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mActionMenuPresenter != null) {
+ mActionMenuPresenter.hideOverflowMenu();
+ mActionMenuPresenter.hideSubMenus();
+ }
+ }
+
+ @Override
+ public void setSplitActionBar(boolean split) {
+ if (mSplitActionBar != split) {
+ if (mActionMenuPresenter != null) {
+ // Mode is already active; move everything over and adjust the menu itself.
+ final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.MATCH_PARENT);
+ if (!split) {
+ mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
+ mMenuView.setBackgroundDrawable(null);
+ final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
+ if (oldParent != null) oldParent.removeView(mMenuView);
+ addView(mMenuView, layoutParams);
+ } else {
+ // Allow full screen width in split mode.
+ mActionMenuPresenter.setWidthLimit(
+ getContext().getResources().getDisplayMetrics().widthPixels, true);
+ // No limit to the item count; use whatever will fit.
+ mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
+ // Span the whole width
+ layoutParams.width = LayoutParams.MATCH_PARENT;
+ layoutParams.height = mContentHeight;
+ mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
+ mMenuView.setBackgroundDrawable(mSplitBackground);
+ final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
+ if (oldParent != null) oldParent.removeView(mMenuView);
+ mSplitView.addView(mMenuView, layoutParams);
+ }
+ }
+ super.setSplitActionBar(split);
+ }
+ }
+
+ public void setContentHeight(int height) {
+ mContentHeight = height;
+ }
+
+ public void setCustomView(View view) {
+ if (mCustomView != null) {
+ removeView(mCustomView);
+ }
+ mCustomView = view;
+ if (mTitleLayout != null) {
+ removeView(mTitleLayout);
+ mTitleLayout = null;
+ }
+ if (view != null) {
+ addView(view);
+ }
+ requestLayout();
+ }
+
+ public void setTitle(CharSequence title) {
+ mTitle = title;
+ initTitle();
+ }
+
+ public void setSubtitle(CharSequence subtitle) {
+ mSubtitle = subtitle;
+ initTitle();
+ }
+
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ public CharSequence getSubtitle() {
+ return mSubtitle;
+ }
+
+ private void initTitle() {
+ if (mTitleLayout == null) {
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ inflater.inflate(R.layout.abs__action_bar_title_item, this);
+ mTitleLayout = (LinearLayout) getChildAt(getChildCount() - 1);
+ mTitleView = (TextView) mTitleLayout.findViewById(R.id.abs__action_bar_title);
+ mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.abs__action_bar_subtitle);
+ if (mTitleStyleRes != 0) {
+ mTitleView.setTextAppearance(mContext, mTitleStyleRes);
+ }
+ if (mSubtitleStyleRes != 0) {
+ mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes);
+ }
+ }
+
+ mTitleView.setText(mTitle);
+ mSubtitleView.setText(mSubtitle);
+
+ final boolean hasTitle = !TextUtils.isEmpty(mTitle);
+ final boolean hasSubtitle = !TextUtils.isEmpty(mSubtitle);
+ mSubtitleView.setVisibility(hasSubtitle ? VISIBLE : GONE);
+ mTitleLayout.setVisibility(hasTitle || hasSubtitle ? VISIBLE : GONE);
+ if (mTitleLayout.getParent() == null) {
+ addView(mTitleLayout);
+ }
+ }
+
+ public void initForMode(final ActionMode mode) {
+ if (mClose == null) {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ mClose = (NineLinearLayout)inflater.inflate(R.layout.abs__action_mode_close_item, this, false);
+ addView(mClose);
+ } else if (mClose.getParent() == null) {
+ addView(mClose);
+ }
+
+ View closeButton = mClose.findViewById(R.id.abs__action_mode_close_button);
+ closeButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ mode.finish();
+ }
+ });
+
+ final MenuBuilder menu = (MenuBuilder) mode.getMenu();
+ if (mActionMenuPresenter != null) {
+ mActionMenuPresenter.dismissPopupMenus();
+ }
+ mActionMenuPresenter = new ActionMenuPresenter(mContext);
+ mActionMenuPresenter.setReserveOverflow(true);
+
+ final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.MATCH_PARENT);
+ if (!mSplitActionBar) {
+ menu.addMenuPresenter(mActionMenuPresenter);
+ mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
+ mMenuView.setBackgroundDrawable(null);
+ addView(mMenuView, layoutParams);
+ } else {
+ // Allow full screen width in split mode.
+ mActionMenuPresenter.setWidthLimit(
+ getContext().getResources().getDisplayMetrics().widthPixels, true);
+ // No limit to the item count; use whatever will fit.
+ mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
+ // Span the whole width
+ layoutParams.width = LayoutParams.MATCH_PARENT;
+ layoutParams.height = mContentHeight;
+ menu.addMenuPresenter(mActionMenuPresenter);
+ mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
+ mMenuView.setBackgroundDrawable(mSplitBackground);
+ mSplitView.addView(mMenuView, layoutParams);
+ }
+
+ mAnimateInOnLayout = true;
+ }
+
+ public void closeMode() {
+ if (mAnimationMode == ANIMATE_OUT) {
+ // Called again during close; just finish what we were doing.
+ return;
+ }
+ if (mClose == null) {
+ killMode();
+ return;
+ }
+
+ finishAnimation();
+ mAnimationMode = ANIMATE_OUT;
+ mCurrentAnimation = makeOutAnimation();
+ mCurrentAnimation.start();
+ }
+
+ private void finishAnimation() {
+ final Animator a = mCurrentAnimation;
+ if (a != null) {
+ mCurrentAnimation = null;
+ a.end();
+ }
+ }
+
+ public void killMode() {
+ finishAnimation();
+ removeAllViews();
+ if (mSplitView != null) {
+ mSplitView.removeView(mMenuView);
+ }
+ mCustomView = null;
+ mMenuView = null;
+ mAnimateInOnLayout = false;
+ }
+
+ @Override
+ public boolean showOverflowMenu() {
+ if (mActionMenuPresenter != null) {
+ return mActionMenuPresenter.showOverflowMenu();
+ }
+ return false;
+ }
+
+ @Override
+ public boolean hideOverflowMenu() {
+ if (mActionMenuPresenter != null) {
+ return mActionMenuPresenter.hideOverflowMenu();
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isOverflowMenuShowing() {
+ if (mActionMenuPresenter != null) {
+ return mActionMenuPresenter.isOverflowMenuShowing();
+ }
+ return false;
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+ // Used by custom views if they don't supply layout params. Everything else
+ // added to an ActionBarContextView should have them already.
+ return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ }
+
+ @Override
+ public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new MarginLayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ if (widthMode != MeasureSpec.EXACTLY) {
+ throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
+ "with android:layout_width=\"match_parent\" (or fill_parent)");
+ }
+
+ final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ if (heightMode == MeasureSpec.UNSPECIFIED) {
+ throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
+ "with android:layout_height=\"wrap_content\"");
+ }
+
+ final int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
+
+ int maxHeight = mContentHeight > 0 ?
+ mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
+
+ final int verticalPadding = getPaddingTop() + getPaddingBottom();
+ int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
+ final int height = maxHeight - verticalPadding;
+ final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
+
+ if (mClose != null) {
+ availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0);
+ MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
+ availableWidth -= lp.leftMargin + lp.rightMargin;
+ }
+
+ if (mMenuView != null && mMenuView.getParent() == this) {
+ availableWidth = measureChildView(mMenuView, availableWidth,
+ childSpecHeight, 0);
+ }
+
+ if (mTitleLayout != null && mCustomView == null) {
+ availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0);
+ }
+
+ if (mCustomView != null) {
+ ViewGroup.LayoutParams lp = mCustomView.getLayoutParams();
+ final int customWidthMode = lp.width != LayoutParams.WRAP_CONTENT ?
+ MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
+ final int customWidth = lp.width >= 0 ?
+ Math.min(lp.width, availableWidth) : availableWidth;
+ final int customHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
+ MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
+ final int customHeight = lp.height >= 0 ?
+ Math.min(lp.height, height) : height;
+ mCustomView.measure(MeasureSpec.makeMeasureSpec(customWidth, customWidthMode),
+ MeasureSpec.makeMeasureSpec(customHeight, customHeightMode));
+ }
+
+ if (mContentHeight <= 0) {
+ int measuredHeight = 0;
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ View v = getChildAt(i);
+ int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
+ if (paddedViewHeight > measuredHeight) {
+ measuredHeight = paddedViewHeight;
+ }
+ }
+ setMeasuredDimension(contentWidth, measuredHeight);
+ } else {
+ setMeasuredDimension(contentWidth, maxHeight);
+ }
+ }
+
+ private Animator makeInAnimation() {
+ mClose.setTranslationX(-mClose.getWidth() -
+ ((MarginLayoutParams) mClose.getLayoutParams()).leftMargin);
+ ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX", 0);
+ buttonAnimator.setDuration(200);
+ buttonAnimator.addListener(this);
+ buttonAnimator.setInterpolator(new DecelerateInterpolator());
+
+ AnimatorSet set = new AnimatorSet();
+ AnimatorSet.Builder b = set.play(buttonAnimator);
+
+ if (mMenuView != null) {
+ final int count = mMenuView.getChildCount();
+ if (count > 0) {
+ for (int i = count - 1, j = 0; i >= 0; i--, j++) {
+ AnimatorProxy child = AnimatorProxy.wrap(mMenuView.getChildAt(i));
+ child.setScaleY(0);
+ ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0, 1);
+ a.setDuration(100);
+ a.setStartDelay(j * 70);
+ b.with(a);
+ }
+ }
+ }
+
+ return set;
+ }
+
+ private Animator makeOutAnimation() {
+ ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX",
+ -mClose.getWidth() - ((MarginLayoutParams) mClose.getLayoutParams()).leftMargin);
+ buttonAnimator.setDuration(200);
+ buttonAnimator.addListener(this);
+ buttonAnimator.setInterpolator(new DecelerateInterpolator());
+
+ AnimatorSet set = new AnimatorSet();
+ AnimatorSet.Builder b = set.play(buttonAnimator);
+
+ if (mMenuView != null) {
+ final int count = mMenuView.getChildCount();
+ if (count > 0) {
+ for (int i = 0; i < 0; i++) {
+ AnimatorProxy child = AnimatorProxy.wrap(mMenuView.getChildAt(i));
+ child.setScaleY(0);
+ ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0);
+ a.setDuration(100);
+ a.setStartDelay(i * 70);
+ b.with(a);
+ }
+ }
+ }
+
+ return set;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int x = getPaddingLeft();
+ final int y = getPaddingTop();
+ final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
+
+ if (mClose != null && mClose.getVisibility() != GONE) {
+ MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
+ x += lp.leftMargin;
+ x += positionChild(mClose, x, y, contentHeight);
+ x += lp.rightMargin;
+
+ if (mAnimateInOnLayout) {
+ mAnimationMode = ANIMATE_IN;
+ mCurrentAnimation = makeInAnimation();
+ mCurrentAnimation.start();
+ mAnimateInOnLayout = false;
+ }
+ }
+
+ if (mTitleLayout != null && mCustomView == null) {
+ x += positionChild(mTitleLayout, x, y, contentHeight);
+ }
+
+ if (mCustomView != null) {
+ x += positionChild(mCustomView, x, y, contentHeight);
+ }
+
+ x = r - l - getPaddingRight();
+
+ if (mMenuView != null) {
+ x -= positionChildInverse(mMenuView, x, y, contentHeight);
+ }
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mAnimationMode == ANIMATE_OUT) {
+ killMode();
+ }
+ mAnimationMode = ANIMATE_IDLE;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return false;
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+ // Action mode started
+ //TODO event.setSource(this);
+ event.setClassName(getClass().getName());
+ event.setPackageName(getContext().getPackageName());
+ event.setContentDescription(mTitle);
+ } else {
+ //TODO super.onInitializeAccessibilityEvent(event);
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/ActionBarView.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/ActionBarView.java
new file mode 100644
index 0000000000..4636de17f0
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/ActionBarView.java
@@ -0,0 +1,1548 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.widget;
+
+import org.xmlpull.v1.XmlPullParser;
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.SpinnerAdapter;
+import android.widget.TextView;
+
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
+import com.actionbarsherlock.internal.ActionBarSherlockCompat;
+import com.actionbarsherlock.internal.view.menu.ActionMenuItem;
+import com.actionbarsherlock.internal.view.menu.ActionMenuPresenter;
+import com.actionbarsherlock.internal.view.menu.ActionMenuView;
+import com.actionbarsherlock.internal.view.menu.MenuBuilder;
+import com.actionbarsherlock.internal.view.menu.MenuItemImpl;
+import com.actionbarsherlock.internal.view.menu.MenuPresenter;
+import com.actionbarsherlock.internal.view.menu.MenuView;
+import com.actionbarsherlock.internal.view.menu.SubMenuBuilder;
+import com.actionbarsherlock.view.CollapsibleActionView;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.Window;
+
+import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getBoolean;
+
+/**
+ * @hide
+ */
+public class ActionBarView extends AbsActionBarView {
+ private static final String TAG = "ActionBarView";
+ private static final boolean DEBUG = false;
+
+ /**
+ * Display options applied by default
+ */
+ public static final int DISPLAY_DEFAULT = 0;
+
+ /**
+ * Display options that require re-layout as opposed to a simple invalidate
+ */
+ private static final int DISPLAY_RELAYOUT_MASK =
+ ActionBar.DISPLAY_SHOW_HOME |
+ ActionBar.DISPLAY_USE_LOGO |
+ ActionBar.DISPLAY_HOME_AS_UP |
+ ActionBar.DISPLAY_SHOW_CUSTOM |
+ ActionBar.DISPLAY_SHOW_TITLE;
+
+ private static final int DEFAULT_CUSTOM_GRAVITY = Gravity.LEFT | Gravity.CENTER_VERTICAL;
+
+ private int mNavigationMode;
+ private int mDisplayOptions = -1;
+ private CharSequence mTitle;
+ private CharSequence mSubtitle;
+ private Drawable mIcon;
+ private Drawable mLogo;
+
+ private HomeView mHomeLayout;
+ private HomeView mExpandedHomeLayout;
+ private LinearLayout mTitleLayout;
+ private TextView mTitleView;
+ private TextView mSubtitleView;
+ private View mTitleUpView;
+
+ private IcsSpinner mSpinner;
+ private IcsLinearLayout mListNavLayout;
+ private ScrollingTabContainerView mTabScrollView;
+ private View mCustomNavView;
+ private IcsProgressBar mProgressView;
+ private IcsProgressBar mIndeterminateProgressView;
+
+ private int mProgressBarPadding;
+ private int mItemPadding;
+
+ private int mTitleStyleRes;
+ private int mSubtitleStyleRes;
+ private int mProgressStyle;
+ private int mIndeterminateProgressStyle;
+
+ private boolean mUserTitle;
+ private boolean mIncludeTabs;
+ private boolean mIsCollapsable;
+ private boolean mIsCollapsed;
+
+ private MenuBuilder mOptionsMenu;
+
+ private ActionBarContextView mContextView;
+
+ private ActionMenuItem mLogoNavItem;
+
+ private SpinnerAdapter mSpinnerAdapter;
+ private OnNavigationListener mCallback;
+
+ //UNUSED private Runnable mTabSelector;
+
+ private ExpandedActionViewMenuPresenter mExpandedMenuPresenter;
+ View mExpandedActionView;
+
+ Window.Callback mWindowCallback;
+
+ @SuppressWarnings("rawtypes")
+ private final IcsAdapterView.OnItemSelectedListener mNavItemSelectedListener =
+ new IcsAdapterView.OnItemSelectedListener() {
+ public void onItemSelected(IcsAdapterView parent, View view, int position, long id) {
+ if (mCallback != null) {
+ mCallback.onNavigationItemSelected(position, id);
+ }
+ }
+ public void onNothingSelected(IcsAdapterView parent) {
+ // Do nothing
+ }
+ };
+
+ private final OnClickListener mExpandedActionViewUpListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final MenuItemImpl item = mExpandedMenuPresenter.mCurrentExpandedItem;
+ if (item != null) {
+ item.collapseActionView();
+ }
+ }
+ };
+
+ private final OnClickListener mUpClickListener = new OnClickListener() {
+ public void onClick(View v) {
+ mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem);
+ }
+ };
+
+ public ActionBarView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ // Background is always provided by the container.
+ setBackgroundResource(0);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SherlockActionBar,
+ R.attr.actionBarStyle, 0);
+
+ ApplicationInfo appInfo = context.getApplicationInfo();
+ PackageManager pm = context.getPackageManager();
+ mNavigationMode = a.getInt(R.styleable.SherlockActionBar_navigationMode,
+ ActionBar.NAVIGATION_MODE_STANDARD);
+ mTitle = a.getText(R.styleable.SherlockActionBar_title);
+ mSubtitle = a.getText(R.styleable.SherlockActionBar_subtitle);
+
+ mLogo = a.getDrawable(R.styleable.SherlockActionBar_logo);
+ if (mLogo == null) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+ if (context instanceof Activity) {
+ //Even though native methods existed in API 9 and 10 they don't work
+ //so just parse the manifest to look for the logo pre-Honeycomb
+ final int resId = loadLogoFromManifest((Activity) context);
+ if (resId != 0) {
+ mLogo = context.getResources().getDrawable(resId);
+ }
+ }
+ } else {
+ if (context instanceof Activity) {
+ try {
+ mLogo = pm.getActivityLogo(((Activity) context).getComponentName());
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Activity component name not found!", e);
+ }
+ }
+ if (mLogo == null) {
+ mLogo = appInfo.loadLogo(pm);
+ }
+ }
+ }
+
+ mIcon = a.getDrawable(R.styleable.SherlockActionBar_icon);
+ if (mIcon == null) {
+ if (context instanceof Activity) {
+ try {
+ mIcon = pm.getActivityIcon(((Activity) context).getComponentName());
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Activity component name not found!", e);
+ }
+ }
+ if (mIcon == null) {
+ mIcon = appInfo.loadIcon(pm);
+ }
+ }
+
+ final LayoutInflater inflater = LayoutInflater.from(context);
+
+ final int homeResId = a.getResourceId(
+ R.styleable.SherlockActionBar_homeLayout,
+ R.layout.abs__action_bar_home);
+
+ mHomeLayout = (HomeView) inflater.inflate(homeResId, this, false);
+
+ mExpandedHomeLayout = (HomeView) inflater.inflate(homeResId, this, false);
+ mExpandedHomeLayout.setUp(true);
+ mExpandedHomeLayout.setOnClickListener(mExpandedActionViewUpListener);
+ mExpandedHomeLayout.setContentDescription(getResources().getText(
+ R.string.abs__action_bar_up_description));
+
+ mTitleStyleRes = a.getResourceId(R.styleable.SherlockActionBar_titleTextStyle, 0);
+ mSubtitleStyleRes = a.getResourceId(R.styleable.SherlockActionBar_subtitleTextStyle, 0);
+ mProgressStyle = a.getResourceId(R.styleable.SherlockActionBar_progressBarStyle, 0);
+ mIndeterminateProgressStyle = a.getResourceId(
+ R.styleable.SherlockActionBar_indeterminateProgressStyle, 0);
+
+ mProgressBarPadding = a.getDimensionPixelOffset(R.styleable.SherlockActionBar_progressBarPadding, 0);
+ mItemPadding = a.getDimensionPixelOffset(R.styleable.SherlockActionBar_itemPadding, 0);
+
+ setDisplayOptions(a.getInt(R.styleable.SherlockActionBar_displayOptions, DISPLAY_DEFAULT));
+
+ final int customNavId = a.getResourceId(R.styleable.SherlockActionBar_customNavigationLayout, 0);
+ if (customNavId != 0) {
+ mCustomNavView = inflater.inflate(customNavId, this, false);
+ mNavigationMode = ActionBar.NAVIGATION_MODE_STANDARD;
+ setDisplayOptions(mDisplayOptions | ActionBar.DISPLAY_SHOW_CUSTOM);
+ }
+
+ mContentHeight = a.getLayoutDimension(R.styleable.SherlockActionBar_height, 0);
+
+ a.recycle();
+
+ mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle);
+ mHomeLayout.setOnClickListener(mUpClickListener);
+ mHomeLayout.setClickable(true);
+ mHomeLayout.setFocusable(true);
+ }
+
+ /**
+ * Attempt to programmatically load the logo from the manifest file of an
+ * activity by using an XML pull parser. This should allow us to read the
+ * logo attribute regardless of the platform it is being run on.
+ *
+ * @param activity Activity instance.
+ * @return Logo resource ID.
+ */
+ private static int loadLogoFromManifest(Activity activity) {
+ int logo = 0;
+ try {
+ final String thisPackage = activity.getClass().getName();
+ if (DEBUG) Log.i(TAG, "Parsing AndroidManifest.xml for " + thisPackage);
+
+ final String packageName = activity.getApplicationInfo().packageName;
+ final AssetManager am = activity.createPackageContext(packageName, 0).getAssets();
+ final XmlResourceParser xml = am.openXmlResourceParser("AndroidManifest.xml");
+
+ int eventType = xml.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ String name = xml.getName();
+
+ if ("application".equals(name)) {
+ //Check if the has the attribute
+ if (DEBUG) Log.d(TAG, "Got ");
+
+ for (int i = xml.getAttributeCount() - 1; i >= 0; i--) {
+ if (DEBUG) Log.d(TAG, xml.getAttributeName(i) + ": " + xml.getAttributeValue(i));
+
+ if ("logo".equals(xml.getAttributeName(i))) {
+ logo = xml.getAttributeResourceValue(i, 0);
+ break; //out of for loop
+ }
+ }
+ } else if ("activity".equals(name)) {
+ //Check if the is us and has the attribute
+ if (DEBUG) Log.d(TAG, "Got ");
+ Integer activityLogo = null;
+ String activityPackage = null;
+ boolean isOurActivity = false;
+
+ for (int i = xml.getAttributeCount() - 1; i >= 0; i--) {
+ if (DEBUG) Log.d(TAG, xml.getAttributeName(i) + ": " + xml.getAttributeValue(i));
+
+ //We need both uiOptions and name attributes
+ String attrName = xml.getAttributeName(i);
+ if ("logo".equals(attrName)) {
+ activityLogo = xml.getAttributeResourceValue(i, 0);
+ } else if ("name".equals(attrName)) {
+ activityPackage = ActionBarSherlockCompat.cleanActivityName(packageName, xml.getAttributeValue(i));
+ if (!thisPackage.equals(activityPackage)) {
+ break; //on to the next
+ }
+ isOurActivity = true;
+ }
+
+ //Make sure we have both attributes before processing
+ if ((activityLogo != null) && (activityPackage != null)) {
+ //Our activity, logo specified, override with our value
+ logo = activityLogo.intValue();
+ }
+ }
+ if (isOurActivity) {
+ //If we matched our activity but it had no logo don't
+ //do any more processing of the manifest
+ break;
+ }
+ }
+ }
+ eventType = xml.nextToken();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ if (DEBUG) Log.i(TAG, "Returning " + Integer.toHexString(logo));
+ return logo;
+ }
+
+ /*
+ * Must be public so we can dispatch pre-2.2 via ActionBarImpl.
+ */
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ mTitleView = null;
+ mSubtitleView = null;
+ mTitleUpView = null;
+ if (mTitleLayout != null && mTitleLayout.getParent() == this) {
+ removeView(mTitleLayout);
+ }
+ mTitleLayout = null;
+ if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+ initTitle();
+ }
+
+ if (mTabScrollView != null && mIncludeTabs) {
+ ViewGroup.LayoutParams lp = mTabScrollView.getLayoutParams();
+ if (lp != null) {
+ lp.width = LayoutParams.WRAP_CONTENT;
+ lp.height = LayoutParams.MATCH_PARENT;
+ }
+ mTabScrollView.setAllowCollapse(true);
+ }
+ }
+
+ /**
+ * Set the window callback used to invoke menu items; used for dispatching home button presses.
+ * @param cb Window callback to dispatch to
+ */
+ public void setWindowCallback(Window.Callback cb) {
+ mWindowCallback = cb;
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ //UNUSED removeCallbacks(mTabSelector);
+ if (mActionMenuPresenter != null) {
+ mActionMenuPresenter.hideOverflowMenu();
+ mActionMenuPresenter.hideSubMenus();
+ }
+ }
+
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return false;
+ }
+
+ public void initProgress() {
+ mProgressView = new IcsProgressBar(mContext, null, 0, mProgressStyle);
+ mProgressView.setId(R.id.abs__progress_horizontal);
+ mProgressView.setMax(10000);
+ addView(mProgressView);
+ }
+
+ public void initIndeterminateProgress() {
+ mIndeterminateProgressView = new IcsProgressBar(mContext, null, 0, mIndeterminateProgressStyle);
+ mIndeterminateProgressView.setId(R.id.abs__progress_circular);
+ addView(mIndeterminateProgressView);
+ }
+
+ @Override
+ public void setSplitActionBar(boolean splitActionBar) {
+ if (mSplitActionBar != splitActionBar) {
+ if (mMenuView != null) {
+ final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
+ if (oldParent != null) {
+ oldParent.removeView(mMenuView);
+ }
+ if (splitActionBar) {
+ if (mSplitView != null) {
+ mSplitView.addView(mMenuView);
+ }
+ } else {
+ addView(mMenuView);
+ }
+ }
+ if (mSplitView != null) {
+ mSplitView.setVisibility(splitActionBar ? VISIBLE : GONE);
+ }
+ super.setSplitActionBar(splitActionBar);
+ }
+ }
+
+ public boolean isSplitActionBar() {
+ return mSplitActionBar;
+ }
+
+ public boolean hasEmbeddedTabs() {
+ return mIncludeTabs;
+ }
+
+ public void setEmbeddedTabView(ScrollingTabContainerView tabs) {
+ if (mTabScrollView != null) {
+ removeView(mTabScrollView);
+ }
+ mTabScrollView = tabs;
+ mIncludeTabs = tabs != null;
+ if (mIncludeTabs && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) {
+ addView(mTabScrollView);
+ ViewGroup.LayoutParams lp = mTabScrollView.getLayoutParams();
+ lp.width = LayoutParams.WRAP_CONTENT;
+ lp.height = LayoutParams.MATCH_PARENT;
+ tabs.setAllowCollapse(true);
+ }
+ }
+
+ public void setCallback(OnNavigationListener callback) {
+ mCallback = callback;
+ }
+
+ public void setMenu(Menu menu, MenuPresenter.Callback cb) {
+ if (menu == mOptionsMenu) return;
+
+ if (mOptionsMenu != null) {
+ mOptionsMenu.removeMenuPresenter(mActionMenuPresenter);
+ mOptionsMenu.removeMenuPresenter(mExpandedMenuPresenter);
+ }
+
+ MenuBuilder builder = (MenuBuilder) menu;
+ mOptionsMenu = builder;
+ if (mMenuView != null) {
+ final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
+ if (oldParent != null) {
+ oldParent.removeView(mMenuView);
+ }
+ }
+ if (mActionMenuPresenter == null) {
+ mActionMenuPresenter = new ActionMenuPresenter(mContext);
+ mActionMenuPresenter.setCallback(cb);
+ mActionMenuPresenter.setId(R.id.abs__action_menu_presenter);
+ mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
+ }
+
+ ActionMenuView menuView;
+ final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.MATCH_PARENT);
+ if (!mSplitActionBar) {
+ mActionMenuPresenter.setExpandedActionViewsExclusive(
+ getResources_getBoolean(getContext(),
+ R.bool.abs__action_bar_expanded_action_views_exclusive));
+ configPresenters(builder);
+ menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
+ final ViewGroup oldParent = (ViewGroup) menuView.getParent();
+ if (oldParent != null && oldParent != this) {
+ oldParent.removeView(menuView);
+ }
+ addView(menuView, layoutParams);
+ } else {
+ mActionMenuPresenter.setExpandedActionViewsExclusive(false);
+ // Allow full screen width in split mode.
+ mActionMenuPresenter.setWidthLimit(
+ getContext().getResources().getDisplayMetrics().widthPixels, true);
+ // No limit to the item count; use whatever will fit.
+ mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
+ // Span the whole width
+ layoutParams.width = LayoutParams.MATCH_PARENT;
+ configPresenters(builder);
+ menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
+ if (mSplitView != null) {
+ final ViewGroup oldParent = (ViewGroup) menuView.getParent();
+ if (oldParent != null && oldParent != mSplitView) {
+ oldParent.removeView(menuView);
+ }
+ menuView.setVisibility(getAnimatedVisibility());
+ mSplitView.addView(menuView, layoutParams);
+ } else {
+ // We'll add this later if we missed it this time.
+ menuView.setLayoutParams(layoutParams);
+ }
+ }
+ mMenuView = menuView;
+ }
+
+ private void configPresenters(MenuBuilder builder) {
+ if (builder != null) {
+ builder.addMenuPresenter(mActionMenuPresenter);
+ builder.addMenuPresenter(mExpandedMenuPresenter);
+ } else {
+ mActionMenuPresenter.initForMenu(mContext, null);
+ mExpandedMenuPresenter.initForMenu(mContext, null);
+ mActionMenuPresenter.updateMenuView(true);
+ mExpandedMenuPresenter.updateMenuView(true);
+ }
+ }
+
+ public boolean hasExpandedActionView() {
+ return mExpandedMenuPresenter != null &&
+ mExpandedMenuPresenter.mCurrentExpandedItem != null;
+ }
+
+ public void collapseActionView() {
+ final MenuItemImpl item = mExpandedMenuPresenter == null ? null :
+ mExpandedMenuPresenter.mCurrentExpandedItem;
+ if (item != null) {
+ item.collapseActionView();
+ }
+ }
+
+ public void setCustomNavigationView(View view) {
+ final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0;
+ if (mCustomNavView != null && showCustom) {
+ removeView(mCustomNavView);
+ }
+ mCustomNavView = view;
+ if (mCustomNavView != null && showCustom) {
+ addView(mCustomNavView);
+ }
+ }
+
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ /**
+ * Set the action bar title. This will always replace or override window titles.
+ * @param title Title to set
+ *
+ * @see #setWindowTitle(CharSequence)
+ */
+ public void setTitle(CharSequence title) {
+ mUserTitle = true;
+ setTitleImpl(title);
+ }
+
+ /**
+ * Set the window title. A window title will always be replaced or overridden by a user title.
+ * @param title Title to set
+ *
+ * @see #setTitle(CharSequence)
+ */
+ public void setWindowTitle(CharSequence title) {
+ if (!mUserTitle) {
+ setTitleImpl(title);
+ }
+ }
+
+ private void setTitleImpl(CharSequence title) {
+ mTitle = title;
+ if (mTitleView != null) {
+ mTitleView.setText(title);
+ final boolean visible = mExpandedActionView == null &&
+ (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0 &&
+ (!TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mSubtitle));
+ mTitleLayout.setVisibility(visible ? VISIBLE : GONE);
+ }
+ if (mLogoNavItem != null) {
+ mLogoNavItem.setTitle(title);
+ }
+ }
+
+ public CharSequence getSubtitle() {
+ return mSubtitle;
+ }
+
+ public void setSubtitle(CharSequence subtitle) {
+ mSubtitle = subtitle;
+ if (mSubtitleView != null) {
+ mSubtitleView.setText(subtitle);
+ mSubtitleView.setVisibility(subtitle != null ? VISIBLE : GONE);
+ final boolean visible = mExpandedActionView == null &&
+ (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0 &&
+ (!TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mSubtitle));
+ mTitleLayout.setVisibility(visible ? VISIBLE : GONE);
+ }
+ }
+
+ public void setHomeButtonEnabled(boolean enable) {
+ mHomeLayout.setEnabled(enable);
+ mHomeLayout.setFocusable(enable);
+ // Make sure the home button has an accurate content description for accessibility.
+ if (!enable) {
+ mHomeLayout.setContentDescription(null);
+ } else if ((mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
+ mHomeLayout.setContentDescription(mContext.getResources().getText(
+ R.string.abs__action_bar_up_description));
+ } else {
+ mHomeLayout.setContentDescription(mContext.getResources().getText(
+ R.string.abs__action_bar_home_description));
+ }
+ }
+
+ public void setDisplayOptions(int options) {
+ final int flagsChanged = mDisplayOptions == -1 ? -1 : options ^ mDisplayOptions;
+ mDisplayOptions = options;
+
+ if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) {
+ final boolean showHome = (options & ActionBar.DISPLAY_SHOW_HOME) != 0;
+ final int vis = showHome && mExpandedActionView == null ? VISIBLE : GONE;
+ mHomeLayout.setVisibility(vis);
+
+ if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
+ final boolean setUp = (options & ActionBar.DISPLAY_HOME_AS_UP) != 0;
+ mHomeLayout.setUp(setUp);
+
+ // Showing home as up implicitly enables interaction with it.
+ // In honeycomb it was always enabled, so make this transition
+ // a bit easier for developers in the common case.
+ // (It would be silly to show it as up without responding to it.)
+ if (setUp) {
+ setHomeButtonEnabled(true);
+ }
+ }
+
+ if ((flagsChanged & ActionBar.DISPLAY_USE_LOGO) != 0) {
+ final boolean logoVis = mLogo != null && (options & ActionBar.DISPLAY_USE_LOGO) != 0;
+ mHomeLayout.setIcon(logoVis ? mLogo : mIcon);
+ }
+
+ if ((flagsChanged & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+ if ((options & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+ initTitle();
+ } else {
+ removeView(mTitleLayout);
+ }
+ }
+
+ if (mTitleLayout != null && (flagsChanged &
+ (ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME)) != 0) {
+ final boolean homeAsUp = (mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0;
+ mTitleUpView.setVisibility(!showHome ? (homeAsUp ? VISIBLE : INVISIBLE) : GONE);
+ mTitleLayout.setEnabled(!showHome && homeAsUp);
+ }
+
+ if ((flagsChanged & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) {
+ if ((options & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
+ addView(mCustomNavView);
+ } else {
+ removeView(mCustomNavView);
+ }
+ }
+
+ requestLayout();
+ } else {
+ invalidate();
+ }
+
+ // Make sure the home button has an accurate content description for accessibility.
+ if (!mHomeLayout.isEnabled()) {
+ mHomeLayout.setContentDescription(null);
+ } else if ((options & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
+ mHomeLayout.setContentDescription(mContext.getResources().getText(
+ R.string.abs__action_bar_up_description));
+ } else {
+ mHomeLayout.setContentDescription(mContext.getResources().getText(
+ R.string.abs__action_bar_home_description));
+ }
+ }
+
+ public void setIcon(Drawable icon) {
+ mIcon = icon;
+ if (icon != null &&
+ ((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) == 0 || mLogo == null)) {
+ mHomeLayout.setIcon(icon);
+ }
+ }
+
+ public void setIcon(int resId) {
+ setIcon(mContext.getResources().getDrawable(resId));
+ }
+
+ public void setLogo(Drawable logo) {
+ mLogo = logo;
+ if (logo != null && (mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0) {
+ mHomeLayout.setIcon(logo);
+ }
+ }
+
+ public void setLogo(int resId) {
+ setLogo(mContext.getResources().getDrawable(resId));
+ }
+
+ public void setNavigationMode(int mode) {
+ final int oldMode = mNavigationMode;
+ if (mode != oldMode) {
+ switch (oldMode) {
+ case ActionBar.NAVIGATION_MODE_LIST:
+ if (mListNavLayout != null) {
+ removeView(mListNavLayout);
+ }
+ break;
+ case ActionBar.NAVIGATION_MODE_TABS:
+ if (mTabScrollView != null && mIncludeTabs) {
+ removeView(mTabScrollView);
+ }
+ }
+
+ switch (mode) {
+ case ActionBar.NAVIGATION_MODE_LIST:
+ if (mSpinner == null) {
+ mSpinner = new IcsSpinner(mContext, null,
+ R.attr.actionDropDownStyle);
+ mListNavLayout = (IcsLinearLayout) LayoutInflater.from(mContext)
+ .inflate(R.layout.abs__action_bar_tab_bar_view, null);
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
+ params.gravity = Gravity.CENTER;
+ mListNavLayout.addView(mSpinner, params);
+ }
+ if (mSpinner.getAdapter() != mSpinnerAdapter) {
+ mSpinner.setAdapter(mSpinnerAdapter);
+ }
+ mSpinner.setOnItemSelectedListener(mNavItemSelectedListener);
+ addView(mListNavLayout);
+ break;
+ case ActionBar.NAVIGATION_MODE_TABS:
+ if (mTabScrollView != null && mIncludeTabs) {
+ addView(mTabScrollView);
+ }
+ break;
+ }
+ mNavigationMode = mode;
+ requestLayout();
+ }
+ }
+
+ public void setDropdownAdapter(SpinnerAdapter adapter) {
+ mSpinnerAdapter = adapter;
+ if (mSpinner != null) {
+ mSpinner.setAdapter(adapter);
+ }
+ }
+
+ public SpinnerAdapter getDropdownAdapter() {
+ return mSpinnerAdapter;
+ }
+
+ public void setDropdownSelectedPosition(int position) {
+ mSpinner.setSelection(position);
+ }
+
+ public int getDropdownSelectedPosition() {
+ return mSpinner.getSelectedItemPosition();
+ }
+
+ public View getCustomNavigationView() {
+ return mCustomNavView;
+ }
+
+ public int getNavigationMode() {
+ return mNavigationMode;
+ }
+
+ public int getDisplayOptions() {
+ return mDisplayOptions;
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+ // Used by custom nav views if they don't supply layout params. Everything else
+ // added to an ActionBarView should have them already.
+ return new ActionBar.LayoutParams(DEFAULT_CUSTOM_GRAVITY);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ addView(mHomeLayout);
+
+ if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
+ final ViewParent parent = mCustomNavView.getParent();
+ if (parent != this) {
+ if (parent instanceof ViewGroup) {
+ ((ViewGroup) parent).removeView(mCustomNavView);
+ }
+ addView(mCustomNavView);
+ }
+ }
+ }
+
+ private void initTitle() {
+ if (mTitleLayout == null) {
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ mTitleLayout = (LinearLayout) inflater.inflate(R.layout.abs__action_bar_title_item,
+ this, false);
+ mTitleView = (TextView) mTitleLayout.findViewById(R.id.abs__action_bar_title);
+ mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.abs__action_bar_subtitle);
+ mTitleUpView = mTitleLayout.findViewById(R.id.abs__up);
+
+ mTitleLayout.setOnClickListener(mUpClickListener);
+
+ if (mTitleStyleRes != 0) {
+ mTitleView.setTextAppearance(mContext, mTitleStyleRes);
+ }
+ if (mTitle != null) {
+ mTitleView.setText(mTitle);
+ }
+
+ if (mSubtitleStyleRes != 0) {
+ mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes);
+ }
+ if (mSubtitle != null) {
+ mSubtitleView.setText(mSubtitle);
+ mSubtitleView.setVisibility(VISIBLE);
+ }
+
+ final boolean homeAsUp = (mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0;
+ final boolean showHome = (mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0;
+ mTitleUpView.setVisibility(!showHome ? (homeAsUp ? VISIBLE : INVISIBLE) : GONE);
+ mTitleLayout.setEnabled(homeAsUp && !showHome);
+ }
+
+ addView(mTitleLayout);
+ if (mExpandedActionView != null ||
+ (TextUtils.isEmpty(mTitle) && TextUtils.isEmpty(mSubtitle))) {
+ // Don't show while in expanded mode or with empty text
+ mTitleLayout.setVisibility(GONE);
+ }
+ }
+
+ public void setContextView(ActionBarContextView view) {
+ mContextView = view;
+ }
+
+ public void setCollapsable(boolean collapsable) {
+ mIsCollapsable = collapsable;
+ }
+
+ public boolean isCollapsed() {
+ return mIsCollapsed;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int childCount = getChildCount();
+ if (mIsCollapsable) {
+ int visibleChildren = 0;
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE &&
+ !(child == mMenuView && mMenuView.getChildCount() == 0)) {
+ visibleChildren++;
+ }
+ }
+
+ if (visibleChildren == 0) {
+ // No size for an empty action bar when collapsable.
+ setMeasuredDimension(0, 0);
+ mIsCollapsed = true;
+ return;
+ }
+ }
+ mIsCollapsed = false;
+
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ if (widthMode != MeasureSpec.EXACTLY) {
+ throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
+ "with android:layout_width=\"match_parent\" (or fill_parent)");
+ }
+
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ if (heightMode != MeasureSpec.AT_MOST) {
+ throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
+ "with android:layout_height=\"wrap_content\"");
+ }
+
+ int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
+
+ int maxHeight = mContentHeight > 0 ?
+ mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
+
+ final int verticalPadding = getPaddingTop() + getPaddingBottom();
+ final int paddingLeft = getPaddingLeft();
+ final int paddingRight = getPaddingRight();
+ final int height = maxHeight - verticalPadding;
+ final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
+
+ int availableWidth = contentWidth - paddingLeft - paddingRight;
+ int leftOfCenter = availableWidth / 2;
+ int rightOfCenter = leftOfCenter;
+
+ HomeView homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout;
+
+ if (homeLayout.getVisibility() != GONE) {
+ final ViewGroup.LayoutParams lp = homeLayout.getLayoutParams();
+ int homeWidthSpec;
+ if (lp.width < 0) {
+ homeWidthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST);
+ } else {
+ homeWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
+ }
+ homeLayout.measure(homeWidthSpec,
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ final int homeWidth = homeLayout.getMeasuredWidth() + homeLayout.getLeftOffset();
+ availableWidth = Math.max(0, availableWidth - homeWidth);
+ leftOfCenter = Math.max(0, availableWidth - homeWidth);
+ }
+
+ if (mMenuView != null && mMenuView.getParent() == this) {
+ availableWidth = measureChildView(mMenuView, availableWidth,
+ childSpecHeight, 0);
+ rightOfCenter = Math.max(0, rightOfCenter - mMenuView.getMeasuredWidth());
+ }
+
+ if (mIndeterminateProgressView != null &&
+ mIndeterminateProgressView.getVisibility() != GONE) {
+ availableWidth = measureChildView(mIndeterminateProgressView, availableWidth,
+ childSpecHeight, 0);
+ rightOfCenter = Math.max(0,
+ rightOfCenter - mIndeterminateProgressView.getMeasuredWidth());
+ }
+
+ final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE &&
+ (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0;
+
+ if (mExpandedActionView == null) {
+ switch (mNavigationMode) {
+ case ActionBar.NAVIGATION_MODE_LIST:
+ if (mListNavLayout != null) {
+ final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding;
+ availableWidth = Math.max(0, availableWidth - itemPaddingSize);
+ leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize);
+ mListNavLayout.measure(
+ MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ final int listNavWidth = mListNavLayout.getMeasuredWidth();
+ availableWidth = Math.max(0, availableWidth - listNavWidth);
+ leftOfCenter = Math.max(0, leftOfCenter - listNavWidth);
+ }
+ break;
+ case ActionBar.NAVIGATION_MODE_TABS:
+ if (mTabScrollView != null) {
+ final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding;
+ availableWidth = Math.max(0, availableWidth - itemPaddingSize);
+ leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize);
+ mTabScrollView.measure(
+ MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ final int tabWidth = mTabScrollView.getMeasuredWidth();
+ availableWidth = Math.max(0, availableWidth - tabWidth);
+ leftOfCenter = Math.max(0, leftOfCenter - tabWidth);
+ }
+ break;
+ }
+ }
+
+ View customView = null;
+ if (mExpandedActionView != null) {
+ customView = mExpandedActionView;
+ } else if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 &&
+ mCustomNavView != null) {
+ customView = mCustomNavView;
+ }
+
+ if (customView != null) {
+ final ViewGroup.LayoutParams lp = generateLayoutParams(customView.getLayoutParams());
+ final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
+ (ActionBar.LayoutParams) lp : null;
+
+ int horizontalMargin = 0;
+ int verticalMargin = 0;
+ if (ablp != null) {
+ horizontalMargin = ablp.leftMargin + ablp.rightMargin;
+ verticalMargin = ablp.topMargin + ablp.bottomMargin;
+ }
+
+ // If the action bar is wrapping to its content height, don't allow a custom
+ // view to MATCH_PARENT.
+ int customNavHeightMode;
+ if (mContentHeight <= 0) {
+ customNavHeightMode = MeasureSpec.AT_MOST;
+ } else {
+ customNavHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
+ MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
+ }
+ final int customNavHeight = Math.max(0,
+ (lp.height >= 0 ? Math.min(lp.height, height) : height) - verticalMargin);
+
+ final int customNavWidthMode = lp.width != LayoutParams.WRAP_CONTENT ?
+ MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
+ int customNavWidth = Math.max(0,
+ (lp.width >= 0 ? Math.min(lp.width, availableWidth) : availableWidth)
+ - horizontalMargin);
+ final int hgrav = (ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY) &
+ Gravity.HORIZONTAL_GRAVITY_MASK;
+
+ // Centering a custom view is treated specially; we try to center within the whole
+ // action bar rather than in the available space.
+ if (hgrav == Gravity.CENTER_HORIZONTAL && lp.width == LayoutParams.MATCH_PARENT) {
+ customNavWidth = Math.min(leftOfCenter, rightOfCenter) * 2;
+ }
+
+ customView.measure(
+ MeasureSpec.makeMeasureSpec(customNavWidth, customNavWidthMode),
+ MeasureSpec.makeMeasureSpec(customNavHeight, customNavHeightMode));
+ availableWidth -= horizontalMargin + customView.getMeasuredWidth();
+ }
+
+ if (mExpandedActionView == null && showTitle) {
+ availableWidth = measureChildView(mTitleLayout, availableWidth,
+ MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.EXACTLY), 0);
+ leftOfCenter = Math.max(0, leftOfCenter - mTitleLayout.getMeasuredWidth());
+ }
+
+ if (mContentHeight <= 0) {
+ int measuredHeight = 0;
+ for (int i = 0; i < childCount; i++) {
+ View v = getChildAt(i);
+ int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
+ if (paddedViewHeight > measuredHeight) {
+ measuredHeight = paddedViewHeight;
+ }
+ }
+ setMeasuredDimension(contentWidth, measuredHeight);
+ } else {
+ setMeasuredDimension(contentWidth, maxHeight);
+ }
+
+ if (mContextView != null) {
+ mContextView.setContentHeight(getMeasuredHeight());
+ }
+
+ if (mProgressView != null && mProgressView.getVisibility() != GONE) {
+ mProgressView.measure(MeasureSpec.makeMeasureSpec(
+ contentWidth - mProgressBarPadding * 2, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST));
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int x = getPaddingLeft();
+ final int y = getPaddingTop();
+ final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
+
+ if (contentHeight <= 0) {
+ // Nothing to do if we can't see anything.
+ return;
+ }
+
+ HomeView homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout;
+ if (homeLayout.getVisibility() != GONE) {
+ final int leftOffset = homeLayout.getLeftOffset();
+ x += positionChild(homeLayout, x + leftOffset, y, contentHeight) + leftOffset;
+ }
+
+ if (mExpandedActionView == null) {
+ final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE &&
+ (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0;
+ if (showTitle) {
+ x += positionChild(mTitleLayout, x, y, contentHeight);
+ }
+
+ switch (mNavigationMode) {
+ case ActionBar.NAVIGATION_MODE_STANDARD:
+ break;
+ case ActionBar.NAVIGATION_MODE_LIST:
+ if (mListNavLayout != null) {
+ if (showTitle) x += mItemPadding;
+ x += positionChild(mListNavLayout, x, y, contentHeight) + mItemPadding;
+ }
+ break;
+ case ActionBar.NAVIGATION_MODE_TABS:
+ if (mTabScrollView != null) {
+ if (showTitle) x += mItemPadding;
+ x += positionChild(mTabScrollView, x, y, contentHeight) + mItemPadding;
+ }
+ break;
+ }
+ }
+
+ int menuLeft = r - l - getPaddingRight();
+ if (mMenuView != null && mMenuView.getParent() == this) {
+ positionChildInverse(mMenuView, menuLeft, y, contentHeight);
+ menuLeft -= mMenuView.getMeasuredWidth();
+ }
+
+ if (mIndeterminateProgressView != null &&
+ mIndeterminateProgressView.getVisibility() != GONE) {
+ positionChildInverse(mIndeterminateProgressView, menuLeft, y, contentHeight);
+ menuLeft -= mIndeterminateProgressView.getMeasuredWidth();
+ }
+
+ View customView = null;
+ if (mExpandedActionView != null) {
+ customView = mExpandedActionView;
+ } else if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 &&
+ mCustomNavView != null) {
+ customView = mCustomNavView;
+ }
+ if (customView != null) {
+ ViewGroup.LayoutParams lp = customView.getLayoutParams();
+ final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
+ (ActionBar.LayoutParams) lp : null;
+
+ final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY;
+ final int navWidth = customView.getMeasuredWidth();
+
+ int topMargin = 0;
+ int bottomMargin = 0;
+ if (ablp != null) {
+ x += ablp.leftMargin;
+ menuLeft -= ablp.rightMargin;
+ topMargin = ablp.topMargin;
+ bottomMargin = ablp.bottomMargin;
+ }
+
+ int hgravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+ // See if we actually have room to truly center; if not push against left or right.
+ if (hgravity == Gravity.CENTER_HORIZONTAL) {
+ final int centeredLeft = ((getRight() - getLeft()) - navWidth) / 2;
+ if (centeredLeft < x) {
+ hgravity = Gravity.LEFT;
+ } else if (centeredLeft + navWidth > menuLeft) {
+ hgravity = Gravity.RIGHT;
+ }
+ } else if (gravity == -1) {
+ hgravity = Gravity.LEFT;
+ }
+
+ int xpos = 0;
+ switch (hgravity) {
+ case Gravity.CENTER_HORIZONTAL:
+ xpos = ((getRight() - getLeft()) - navWidth) / 2;
+ break;
+ case Gravity.LEFT:
+ xpos = x;
+ break;
+ case Gravity.RIGHT:
+ xpos = menuLeft - navWidth;
+ break;
+ }
+
+ int vgravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
+
+ if (gravity == -1) {
+ vgravity = Gravity.CENTER_VERTICAL;
+ }
+
+ int ypos = 0;
+ switch (vgravity) {
+ case Gravity.CENTER_VERTICAL:
+ final int paddedTop = getPaddingTop();
+ final int paddedBottom = getBottom() - getTop() - getPaddingBottom();
+ ypos = ((paddedBottom - paddedTop) - customView.getMeasuredHeight()) / 2;
+ break;
+ case Gravity.TOP:
+ ypos = getPaddingTop() + topMargin;
+ break;
+ case Gravity.BOTTOM:
+ ypos = getHeight() - getPaddingBottom() - customView.getMeasuredHeight()
+ - bottomMargin;
+ break;
+ }
+ final int customWidth = customView.getMeasuredWidth();
+ customView.layout(xpos, ypos, xpos + customWidth,
+ ypos + customView.getMeasuredHeight());
+ x += customWidth;
+ }
+
+ if (mProgressView != null) {
+ mProgressView.bringToFront();
+ final int halfProgressHeight = mProgressView.getMeasuredHeight() / 2;
+ mProgressView.layout(mProgressBarPadding, -halfProgressHeight,
+ mProgressBarPadding + mProgressView.getMeasuredWidth(), halfProgressHeight);
+ }
+ }
+
+ @Override
+ public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new ActionBar.LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ public ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
+ if (lp == null) {
+ lp = generateDefaultLayoutParams();
+ }
+ return lp;
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+ SavedState state = new SavedState(superState);
+
+ if (mExpandedMenuPresenter != null && mExpandedMenuPresenter.mCurrentExpandedItem != null) {
+ state.expandedMenuItemId = mExpandedMenuPresenter.mCurrentExpandedItem.getItemId();
+ }
+
+ state.isOverflowOpen = isOverflowMenuShowing();
+
+ return state;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable p) {
+ SavedState state = (SavedState) p;
+
+ super.onRestoreInstanceState(state.getSuperState());
+
+ if (state.expandedMenuItemId != 0 &&
+ mExpandedMenuPresenter != null && mOptionsMenu != null) {
+ final MenuItem item = mOptionsMenu.findItem(state.expandedMenuItemId);
+ if (item != null) {
+ item.expandActionView();
+ }
+ }
+
+ if (state.isOverflowOpen) {
+ postShowOverflowMenu();
+ }
+ }
+
+ static class SavedState extends BaseSavedState {
+ int expandedMenuItemId;
+ boolean isOverflowOpen;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ expandedMenuItemId = in.readInt();
+ isOverflowOpen = in.readInt() != 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeInt(expandedMenuItemId);
+ out.writeInt(isOverflowOpen ? 1 : 0);
+ }
+
+ public static final Parcelable.Creator CREATOR =
+ new Parcelable.Creator() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+ public static class HomeView extends FrameLayout {
+ private View mUpView;
+ private ImageView mIconView;
+ private int mUpWidth;
+
+ public HomeView(Context context) {
+ this(context, null);
+ }
+
+ public HomeView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setUp(boolean isUp) {
+ mUpView.setVisibility(isUp ? VISIBLE : GONE);
+ }
+
+ public void setIcon(Drawable icon) {
+ mIconView.setImageDrawable(icon);
+ }
+
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ onPopulateAccessibilityEvent(event);
+ return true;
+ }
+
+ @Override
+ public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ super.onPopulateAccessibilityEvent(event);
+ }
+ final CharSequence cdesc = getContentDescription();
+ if (!TextUtils.isEmpty(cdesc)) {
+ event.getText().add(cdesc);
+ }
+ }
+
+ @Override
+ public boolean dispatchHoverEvent(MotionEvent event) {
+ // Don't allow children to hover; we want this to be treated as a single component.
+ return onHoverEvent(event);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mUpView = findViewById(R.id.abs__up);
+ mIconView = (ImageView) findViewById(R.id.abs__home);
+ }
+
+ public int getLeftOffset() {
+ return mUpView.getVisibility() == GONE ? mUpWidth : 0;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0);
+ final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
+ mUpWidth = upLp.leftMargin + mUpView.getMeasuredWidth() + upLp.rightMargin;
+ int width = mUpView.getVisibility() == GONE ? 0 : mUpWidth;
+ int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin;
+ measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0);
+ final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
+ width += iconLp.leftMargin + mIconView.getMeasuredWidth() + iconLp.rightMargin;
+ height = Math.max(height,
+ iconLp.topMargin + mIconView.getMeasuredHeight() + iconLp.bottomMargin);
+
+ final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+
+ switch (widthMode) {
+ case MeasureSpec.AT_MOST:
+ width = Math.min(width, widthSize);
+ break;
+ case MeasureSpec.EXACTLY:
+ width = widthSize;
+ break;
+ case MeasureSpec.UNSPECIFIED:
+ default:
+ break;
+ }
+ switch (heightMode) {
+ case MeasureSpec.AT_MOST:
+ height = Math.min(height, heightSize);
+ break;
+ case MeasureSpec.EXACTLY:
+ height = heightSize;
+ break;
+ case MeasureSpec.UNSPECIFIED:
+ default:
+ break;
+ }
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final int vCenter = (b - t) / 2;
+ //UNUSED int width = r - l;
+ int upOffset = 0;
+ if (mUpView.getVisibility() != GONE) {
+ final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
+ final int upHeight = mUpView.getMeasuredHeight();
+ final int upWidth = mUpView.getMeasuredWidth();
+ final int upTop = vCenter - upHeight / 2;
+ mUpView.layout(0, upTop, upWidth, upTop + upHeight);
+ upOffset = upLp.leftMargin + upWidth + upLp.rightMargin;
+ //UNUSED width -= upOffset;
+ l += upOffset;
+ }
+ final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
+ final int iconHeight = mIconView.getMeasuredHeight();
+ final int iconWidth = mIconView.getMeasuredWidth();
+ final int hCenter = (r - l) / 2;
+ final int iconLeft = upOffset + Math.max(iconLp.leftMargin, hCenter - iconWidth / 2);
+ final int iconTop = Math.max(iconLp.topMargin, vCenter - iconHeight / 2);
+ mIconView.layout(iconLeft, iconTop, iconLeft + iconWidth, iconTop + iconHeight);
+ }
+ }
+
+ private class ExpandedActionViewMenuPresenter implements MenuPresenter {
+ MenuBuilder mMenu;
+ MenuItemImpl mCurrentExpandedItem;
+
+ @Override
+ public void initForMenu(Context context, MenuBuilder menu) {
+ // Clear the expanded action view when menus change.
+ if (mMenu != null && mCurrentExpandedItem != null) {
+ mMenu.collapseItemActionView(mCurrentExpandedItem);
+ }
+ mMenu = menu;
+ }
+
+ @Override
+ public MenuView getMenuView(ViewGroup root) {
+ return null;
+ }
+
+ @Override
+ public void updateMenuView(boolean cleared) {
+ // Make sure the expanded item we have is still there.
+ if (mCurrentExpandedItem != null) {
+ boolean found = false;
+
+ if (mMenu != null) {
+ final int count = mMenu.size();
+ for (int i = 0; i < count; i++) {
+ final MenuItem item = mMenu.getItem(i);
+ if (item == mCurrentExpandedItem) {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ // The item we had expanded disappeared. Collapse.
+ collapseItemActionView(mMenu, mCurrentExpandedItem);
+ }
+ }
+ }
+
+ @Override
+ public void setCallback(Callback cb) {
+ }
+
+ @Override
+ public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+ return false;
+ }
+
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ }
+
+ @Override
+ public boolean flagActionItems() {
+ return false;
+ }
+
+ @Override
+ public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
+ mExpandedActionView = item.getActionView();
+ mExpandedHomeLayout.setIcon(mIcon.getConstantState().newDrawable(/* TODO getResources() */));
+ mCurrentExpandedItem = item;
+ if (mExpandedActionView.getParent() != ActionBarView.this) {
+ addView(mExpandedActionView);
+ }
+ if (mExpandedHomeLayout.getParent() != ActionBarView.this) {
+ addView(mExpandedHomeLayout);
+ }
+ mHomeLayout.setVisibility(GONE);
+ if (mTitleLayout != null) mTitleLayout.setVisibility(GONE);
+ if (mTabScrollView != null) mTabScrollView.setVisibility(GONE);
+ if (mSpinner != null) mSpinner.setVisibility(GONE);
+ if (mCustomNavView != null) mCustomNavView.setVisibility(GONE);
+ requestLayout();
+ item.setActionViewExpanded(true);
+
+ if (mExpandedActionView instanceof CollapsibleActionView) {
+ ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded();
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
+ // Do this before detaching the actionview from the hierarchy, in case
+ // it needs to dismiss the soft keyboard, etc.
+ if (mExpandedActionView instanceof CollapsibleActionView) {
+ ((CollapsibleActionView) mExpandedActionView).onActionViewCollapsed();
+ }
+
+ removeView(mExpandedActionView);
+ removeView(mExpandedHomeLayout);
+ mExpandedActionView = null;
+ if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0) {
+ mHomeLayout.setVisibility(VISIBLE);
+ }
+ if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+ if (mTitleLayout == null) {
+ initTitle();
+ } else {
+ mTitleLayout.setVisibility(VISIBLE);
+ }
+ }
+ if (mTabScrollView != null && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) {
+ mTabScrollView.setVisibility(VISIBLE);
+ }
+ if (mSpinner != null && mNavigationMode == ActionBar.NAVIGATION_MODE_LIST) {
+ mSpinner.setVisibility(VISIBLE);
+ }
+ if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
+ mCustomNavView.setVisibility(VISIBLE);
+ }
+ mExpandedHomeLayout.setIcon(null);
+ mCurrentExpandedItem = null;
+ requestLayout();
+ item.setActionViewExpanded(false);
+
+ return true;
+ }
+
+ @Override
+ public int getId() {
+ return 0;
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ return null;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/CapitalizingButton.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/CapitalizingButton.java
new file mode 100644
index 0000000000..fa3698f3b4
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/CapitalizingButton.java
@@ -0,0 +1,40 @@
+package com.actionbarsherlock.internal.widget;
+
+import java.util.Locale;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.widget.Button;
+
+public class CapitalizingButton extends Button {
+ private static final boolean SANS_ICE_CREAM = Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH;
+ private static final boolean IS_GINGERBREAD = Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD;
+
+ private static final int[] R_styleable_Button = new int[] {
+ android.R.attr.textAllCaps
+ };
+ private static final int R_styleable_Button_textAllCaps = 0;
+
+ private boolean mAllCaps;
+
+ public CapitalizingButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R_styleable_Button);
+ mAllCaps = a.getBoolean(R_styleable_Button_textAllCaps, true);
+ a.recycle();
+ }
+
+ public void setTextCompat(CharSequence text) {
+ if (SANS_ICE_CREAM && mAllCaps && text != null) {
+ if (IS_GINGERBREAD) {
+ setText(text.toString().toUpperCase(Locale.ROOT));
+ } else {
+ setText(text.toString().toUpperCase());
+ }
+ } else {
+ setText(text);
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/CapitalizingTextView.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/CapitalizingTextView.java
new file mode 100644
index 0000000000..cae8b8aed3
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/CapitalizingTextView.java
@@ -0,0 +1,50 @@
+package com.actionbarsherlock.internal.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import java.util.Locale;
+
+public class CapitalizingTextView extends TextView {
+ private static final boolean SANS_ICE_CREAM = Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH;
+ private static final boolean IS_GINGERBREAD = Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD;
+
+ private static final int[] R_styleable_TextView = new int[] {
+ android.R.attr.textAllCaps
+ };
+ private static final int R_styleable_TextView_textAllCaps = 0;
+
+ private boolean mAllCaps;
+
+ public CapitalizingTextView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CapitalizingTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R_styleable_TextView, defStyle, 0);
+ mAllCaps = a.getBoolean(R_styleable_TextView_textAllCaps, true);
+ a.recycle();
+ }
+
+ public void setTextCompat(CharSequence text) {
+ if (SANS_ICE_CREAM && mAllCaps && text != null) {
+ if (IS_GINGERBREAD) {
+ try {
+ setText(text.toString().toUpperCase(Locale.ROOT));
+ } catch (NoSuchFieldError e) {
+ //Some manufacturer broke Locale.ROOT. See #572.
+ setText(text.toString().toUpperCase());
+ }
+ } else {
+ setText(text.toString().toUpperCase());
+ }
+ } else {
+ setText(text);
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/CollapsibleActionViewWrapper.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/CollapsibleActionViewWrapper.java
new file mode 100644
index 0000000000..14f092c81f
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/CollapsibleActionViewWrapper.java
@@ -0,0 +1,30 @@
+package com.actionbarsherlock.internal.widget;
+
+import android.view.View;
+import android.widget.FrameLayout;
+import com.actionbarsherlock.view.CollapsibleActionView;
+
+/**
+ * Wraps an ABS collapsible action view in a native container that delegates the calls.
+ */
+public class CollapsibleActionViewWrapper extends FrameLayout implements android.view.CollapsibleActionView {
+ private final CollapsibleActionView child;
+
+ public CollapsibleActionViewWrapper(View child) {
+ super(child.getContext());
+ this.child = (CollapsibleActionView) child;
+ addView(child);
+ }
+
+ @Override public void onActionViewExpanded() {
+ child.onActionViewExpanded();
+ }
+
+ @Override public void onActionViewCollapsed() {
+ child.onActionViewCollapsed();
+ }
+
+ public View unwrap() {
+ return getChildAt(0);
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/FakeDialogPhoneWindow.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/FakeDialogPhoneWindow.java
new file mode 100644
index 0000000000..ad1b4f0a85
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/FakeDialogPhoneWindow.java
@@ -0,0 +1,64 @@
+package com.actionbarsherlock.internal.widget;
+
+import static android.view.View.MeasureSpec.EXACTLY;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.widget.LinearLayout;
+import com.actionbarsherlock.R;
+
+public class FakeDialogPhoneWindow extends LinearLayout {
+ final TypedValue mMinWidthMajor = new TypedValue();
+ final TypedValue mMinWidthMinor = new TypedValue();
+
+ public FakeDialogPhoneWindow(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SherlockTheme);
+
+ a.getValue(R.styleable.SherlockTheme_windowMinWidthMajor, mMinWidthMajor);
+ a.getValue(R.styleable.SherlockTheme_windowMinWidthMinor, mMinWidthMinor);
+
+ a.recycle();
+ }
+
+ /* Stolen from PhoneWindow */
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
+ final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
+
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ int width = getMeasuredWidth();
+ boolean measure = false;
+
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
+
+ final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor;
+
+ if (tv.type != TypedValue.TYPE_NULL) {
+ final int min;
+ if (tv.type == TypedValue.TYPE_DIMENSION) {
+ min = (int)tv.getDimension(metrics);
+ } else if (tv.type == TypedValue.TYPE_FRACTION) {
+ min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels);
+ } else {
+ min = 0;
+ }
+
+ if (width < min) {
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
+ measure = true;
+ }
+ }
+
+ // TODO: Support height?
+
+ if (measure) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsAbsSpinner.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsAbsSpinner.java
new file mode 100644
index 0000000000..ce0cb3bcaa
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsAbsSpinner.java
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.widget;
+
+import android.content.Context;
+import android.database.DataSetObserver;
+import android.graphics.Rect;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.SpinnerAdapter;
+
+/**
+ * An abstract base class for spinner widgets. SDK users will probably not
+ * need to use this class.
+ *
+ * @attr ref android.R.styleable#AbsSpinner_entries
+ */
+public abstract class IcsAbsSpinner extends IcsAdapterView {
+ private static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
+
+ SpinnerAdapter mAdapter;
+
+ int mHeightMeasureSpec;
+ int mWidthMeasureSpec;
+ boolean mBlockLayoutRequests;
+
+ int mSelectionLeftPadding = 0;
+ int mSelectionTopPadding = 0;
+ int mSelectionRightPadding = 0;
+ int mSelectionBottomPadding = 0;
+ final Rect mSpinnerPadding = new Rect();
+
+ final RecycleBin mRecycler = new RecycleBin();
+ private DataSetObserver mDataSetObserver;
+
+ /** Temporary frame to hold a child View's frame rectangle */
+ private Rect mTouchFrame;
+
+ public IcsAbsSpinner(Context context) {
+ super(context);
+ initAbsSpinner();
+ }
+
+ public IcsAbsSpinner(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public IcsAbsSpinner(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ initAbsSpinner();
+
+ /*
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.AbsSpinner, defStyle, 0);
+
+ CharSequence[] entries = a.getTextArray(R.styleable.AbsSpinner_entries);
+ if (entries != null) {
+ ArrayAdapter adapter =
+ new ArrayAdapter(context,
+ R.layout.simple_spinner_item, entries);
+ adapter.setDropDownViewResource(R.layout.simple_spinner_dropdown_item);
+ setAdapter(adapter);
+ }
+
+ a.recycle();
+ */
+ }
+
+ /**
+ * Common code for different constructor flavors
+ */
+ private void initAbsSpinner() {
+ setFocusable(true);
+ setWillNotDraw(false);
+ }
+
+ /**
+ * The Adapter is used to provide the data which backs this Spinner.
+ * It also provides methods to transform spinner items based on their position
+ * relative to the selected item.
+ * @param adapter The SpinnerAdapter to use for this Spinner
+ */
+ @Override
+ public void setAdapter(SpinnerAdapter adapter) {
+ if (null != mAdapter) {
+ mAdapter.unregisterDataSetObserver(mDataSetObserver);
+ resetList();
+ }
+
+ mAdapter = adapter;
+
+ mOldSelectedPosition = INVALID_POSITION;
+ mOldSelectedRowId = INVALID_ROW_ID;
+
+ if (mAdapter != null) {
+ mOldItemCount = mItemCount;
+ mItemCount = mAdapter.getCount();
+ checkFocus();
+
+ mDataSetObserver = new AdapterDataSetObserver();
+ mAdapter.registerDataSetObserver(mDataSetObserver);
+
+ int position = mItemCount > 0 ? 0 : INVALID_POSITION;
+
+ setSelectedPositionInt(position);
+ setNextSelectedPositionInt(position);
+
+ if (mItemCount == 0) {
+ // Nothing selected
+ checkSelectionChanged();
+ }
+
+ } else {
+ checkFocus();
+ resetList();
+ // Nothing selected
+ checkSelectionChanged();
+ }
+
+ requestLayout();
+ }
+
+ /**
+ * Clear out all children from the list
+ */
+ void resetList() {
+ mDataChanged = false;
+ mNeedSync = false;
+
+ removeAllViewsInLayout();
+ mOldSelectedPosition = INVALID_POSITION;
+ mOldSelectedRowId = INVALID_ROW_ID;
+
+ setSelectedPositionInt(INVALID_POSITION);
+ setNextSelectedPositionInt(INVALID_POSITION);
+ invalidate();
+ }
+
+ /**
+ * @see android.view.View#measure(int, int)
+ *
+ * Figure out the dimensions of this Spinner. The width comes from
+ * the widthMeasureSpec as Spinnners can't have their width set to
+ * UNSPECIFIED. The height is based on the height of the selected item
+ * plus padding.
+ */
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int widthSize;
+ int heightSize;
+
+ final int mPaddingLeft = getPaddingLeft();
+ final int mPaddingTop = getPaddingTop();
+ final int mPaddingRight = getPaddingRight();
+ final int mPaddingBottom = getPaddingBottom();
+
+ mSpinnerPadding.left = mPaddingLeft > mSelectionLeftPadding ? mPaddingLeft
+ : mSelectionLeftPadding;
+ mSpinnerPadding.top = mPaddingTop > mSelectionTopPadding ? mPaddingTop
+ : mSelectionTopPadding;
+ mSpinnerPadding.right = mPaddingRight > mSelectionRightPadding ? mPaddingRight
+ : mSelectionRightPadding;
+ mSpinnerPadding.bottom = mPaddingBottom > mSelectionBottomPadding ? mPaddingBottom
+ : mSelectionBottomPadding;
+
+ if (mDataChanged) {
+ handleDataChanged();
+ }
+
+ int preferredHeight = 0;
+ int preferredWidth = 0;
+ boolean needsMeasuring = true;
+
+ int selectedPosition = getSelectedItemPosition();
+ if (selectedPosition >= 0 && mAdapter != null && selectedPosition < mAdapter.getCount()) {
+ // Try looking in the recycler. (Maybe we were measured once already)
+ View view = mRecycler.get(selectedPosition);
+ if (view == null) {
+ // Make a new one
+ view = mAdapter.getView(selectedPosition, null, this);
+ }
+
+ if (view != null) {
+ // Put in recycler for re-measuring and/or layout
+ mRecycler.put(selectedPosition, view);
+ }
+
+ if (view != null) {
+ if (view.getLayoutParams() == null) {
+ mBlockLayoutRequests = true;
+ view.setLayoutParams(generateDefaultLayoutParams());
+ mBlockLayoutRequests = false;
+ }
+ measureChild(view, widthMeasureSpec, heightMeasureSpec);
+
+ preferredHeight = getChildHeight(view) + mSpinnerPadding.top + mSpinnerPadding.bottom;
+ preferredWidth = getChildWidth(view) + mSpinnerPadding.left + mSpinnerPadding.right;
+
+ needsMeasuring = false;
+ }
+ }
+
+ if (needsMeasuring) {
+ // No views -- just use padding
+ preferredHeight = mSpinnerPadding.top + mSpinnerPadding.bottom;
+ if (widthMode == MeasureSpec.UNSPECIFIED) {
+ preferredWidth = mSpinnerPadding.left + mSpinnerPadding.right;
+ }
+ }
+
+ preferredHeight = Math.max(preferredHeight, getSuggestedMinimumHeight());
+ preferredWidth = Math.max(preferredWidth, getSuggestedMinimumWidth());
+
+ if (IS_HONEYCOMB) {
+ heightSize = resolveSizeAndState(preferredHeight, heightMeasureSpec, 0);
+ widthSize = resolveSizeAndState(preferredWidth, widthMeasureSpec, 0);
+ } else {
+ heightSize = resolveSize(preferredHeight, heightMeasureSpec);
+ widthSize = resolveSize(preferredWidth, widthMeasureSpec);
+ }
+
+ setMeasuredDimension(widthSize, heightSize);
+ mHeightMeasureSpec = heightMeasureSpec;
+ mWidthMeasureSpec = widthMeasureSpec;
+ }
+
+ int getChildHeight(View child) {
+ return child.getMeasuredHeight();
+ }
+
+ int getChildWidth(View child) {
+ return child.getMeasuredWidth();
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+ return new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ }
+
+ void recycleAllViews() {
+ final int childCount = getChildCount();
+ final IcsAbsSpinner.RecycleBin recycleBin = mRecycler;
+ final int position = mFirstPosition;
+
+ // All views go in recycler
+ for (int i = 0; i < childCount; i++) {
+ View v = getChildAt(i);
+ int index = position + i;
+ recycleBin.put(index, v);
+ }
+ }
+
+ /**
+ * Jump directly to a specific item in the adapter data.
+ */
+ public void setSelection(int position, boolean animate) {
+ // Animate only if requested position is already on screen somewhere
+ boolean shouldAnimate = animate && mFirstPosition <= position &&
+ position <= mFirstPosition + getChildCount() - 1;
+ setSelectionInt(position, shouldAnimate);
+ }
+
+ @Override
+ public void setSelection(int position) {
+ setNextSelectedPositionInt(position);
+ requestLayout();
+ invalidate();
+ }
+
+
+ /**
+ * Makes the item at the supplied position selected.
+ *
+ * @param position Position to select
+ * @param animate Should the transition be animated
+ *
+ */
+ void setSelectionInt(int position, boolean animate) {
+ if (position != mOldSelectedPosition) {
+ mBlockLayoutRequests = true;
+ int delta = position - mSelectedPosition;
+ setNextSelectedPositionInt(position);
+ layout(delta, animate);
+ mBlockLayoutRequests = false;
+ }
+ }
+
+ abstract void layout(int delta, boolean animate);
+
+ @Override
+ public View getSelectedView() {
+ if (mItemCount > 0 && mSelectedPosition >= 0) {
+ return getChildAt(mSelectedPosition - mFirstPosition);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Override to prevent spamming ourselves with layout requests
+ * as we place views
+ *
+ * @see android.view.View#requestLayout()
+ */
+ @Override
+ public void requestLayout() {
+ if (!mBlockLayoutRequests) {
+ super.requestLayout();
+ }
+ }
+
+ @Override
+ public SpinnerAdapter getAdapter() {
+ return mAdapter;
+ }
+
+ @Override
+ public int getCount() {
+ return mItemCount;
+ }
+
+ /**
+ * Maps a point to a position in the list.
+ *
+ * @param x X in local coordinate
+ * @param y Y in local coordinate
+ * @return The position of the item which contains the specified point, or
+ * {@link #INVALID_POSITION} if the point does not intersect an item.
+ */
+ public int pointToPosition(int x, int y) {
+ Rect frame = mTouchFrame;
+ if (frame == null) {
+ mTouchFrame = new Rect();
+ frame = mTouchFrame;
+ }
+
+ final int count = getChildCount();
+ for (int i = count - 1; i >= 0; i--) {
+ View child = getChildAt(i);
+ if (child.getVisibility() == View.VISIBLE) {
+ child.getHitRect(frame);
+ if (frame.contains(x, y)) {
+ return mFirstPosition + i;
+ }
+ }
+ }
+ return INVALID_POSITION;
+ }
+
+ static class SavedState extends BaseSavedState {
+ long selectedId;
+ int position;
+
+ /**
+ * Constructor called from {@link AbsSpinner#onSaveInstanceState()}
+ */
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ /**
+ * Constructor called from {@link #CREATOR}
+ */
+ private SavedState(Parcel in) {
+ super(in);
+ selectedId = in.readLong();
+ position = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeLong(selectedId);
+ out.writeInt(position);
+ }
+
+ @Override
+ public String toString() {
+ return "AbsSpinner.SavedState{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " selectedId=" + selectedId
+ + " position=" + position + "}";
+ }
+
+ public static final Parcelable.Creator CREATOR
+ = new Parcelable.Creator() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+ SavedState ss = new SavedState(superState);
+ ss.selectedId = getSelectedItemId();
+ if (ss.selectedId >= 0) {
+ ss.position = getSelectedItemPosition();
+ } else {
+ ss.position = INVALID_POSITION;
+ }
+ return ss;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ SavedState ss = (SavedState) state;
+
+ super.onRestoreInstanceState(ss.getSuperState());
+
+ if (ss.selectedId >= 0) {
+ mDataChanged = true;
+ mNeedSync = true;
+ mSyncRowId = ss.selectedId;
+ mSyncPosition = ss.position;
+ mSyncMode = SYNC_SELECTED_POSITION;
+ requestLayout();
+ }
+ }
+
+ class RecycleBin {
+ private final SparseArray mScrapHeap = new SparseArray();
+
+ public void put(int position, View v) {
+ mScrapHeap.put(position, v);
+ }
+
+ View get(int position) {
+ // System.out.print("Looking for " + position);
+ View result = mScrapHeap.get(position);
+ if (result != null) {
+ // System.out.println(" HIT");
+ mScrapHeap.delete(position);
+ } else {
+ // System.out.println(" MISS");
+ }
+ return result;
+ }
+
+ void clear() {
+ final SparseArray scrapHeap = mScrapHeap;
+ final int count = scrapHeap.size();
+ for (int i = 0; i < count; i++) {
+ final View view = scrapHeap.valueAt(i);
+ if (view != null) {
+ removeDetachedView(view, true);
+ }
+ }
+ scrapHeap.clear();
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsAdapterView.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsAdapterView.java
new file mode 100644
index 0000000000..c786dc5c19
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsAdapterView.java
@@ -0,0 +1,1160 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.widget;
+
+import android.content.Context;
+import android.database.DataSetObserver;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.view.ContextMenu;
+import android.view.SoundEffectConstants;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.Adapter;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+
+
+/**
+ * An AdapterView is a view whose children are determined by an {@link Adapter}.
+ *
+ *
+ * See {@link ListView}, {@link GridView}, {@link Spinner} and
+ * {@link Gallery} for commonly used subclasses of AdapterView.
+ *
+ *
+ */
+public abstract class IcsAdapterView extends ViewGroup {
+
+ /**
+ * The item view type returned by {@link Adapter#getItemViewType(int)} when
+ * the adapter does not want the item's view recycled.
+ */
+ public static final int ITEM_VIEW_TYPE_IGNORE = -1;
+
+ /**
+ * The item view type returned by {@link Adapter#getItemViewType(int)} when
+ * the item is a header or footer.
+ */
+ public static final int ITEM_VIEW_TYPE_HEADER_OR_FOOTER = -2;
+
+ /**
+ * The position of the first child displayed
+ */
+ @ViewDebug.ExportedProperty(category = "scrolling")
+ int mFirstPosition = 0;
+
+ /**
+ * The offset in pixels from the top of the AdapterView to the top
+ * of the view to select during the next layout.
+ */
+ int mSpecificTop;
+
+ /**
+ * Position from which to start looking for mSyncRowId
+ */
+ int mSyncPosition;
+
+ /**
+ * Row id to look for when data has changed
+ */
+ long mSyncRowId = INVALID_ROW_ID;
+
+ /**
+ * Height of the view when mSyncPosition and mSyncRowId where set
+ */
+ long mSyncHeight;
+
+ /**
+ * True if we need to sync to mSyncRowId
+ */
+ boolean mNeedSync = false;
+
+ /**
+ * Indicates whether to sync based on the selection or position. Possible
+ * values are {@link #SYNC_SELECTED_POSITION} or
+ * {@link #SYNC_FIRST_POSITION}.
+ */
+ int mSyncMode;
+
+ /**
+ * Our height after the last layout
+ */
+ private int mLayoutHeight;
+
+ /**
+ * Sync based on the selected child
+ */
+ static final int SYNC_SELECTED_POSITION = 0;
+
+ /**
+ * Sync based on the first child displayed
+ */
+ static final int SYNC_FIRST_POSITION = 1;
+
+ /**
+ * Maximum amount of time to spend in {@link #findSyncPosition()}
+ */
+ static final int SYNC_MAX_DURATION_MILLIS = 100;
+
+ /**
+ * Indicates that this view is currently being laid out.
+ */
+ boolean mInLayout = false;
+
+ /**
+ * The listener that receives notifications when an item is selected.
+ */
+ OnItemSelectedListener mOnItemSelectedListener;
+
+ /**
+ * The listener that receives notifications when an item is clicked.
+ */
+ OnItemClickListener mOnItemClickListener;
+
+ /**
+ * The listener that receives notifications when an item is long clicked.
+ */
+ OnItemLongClickListener mOnItemLongClickListener;
+
+ /**
+ * True if the data has changed since the last layout
+ */
+ boolean mDataChanged;
+
+ /**
+ * The position within the adapter's data set of the item to select
+ * during the next layout.
+ */
+ @ViewDebug.ExportedProperty(category = "list")
+ int mNextSelectedPosition = INVALID_POSITION;
+
+ /**
+ * The item id of the item to select during the next layout.
+ */
+ long mNextSelectedRowId = INVALID_ROW_ID;
+
+ /**
+ * The position within the adapter's data set of the currently selected item.
+ */
+ @ViewDebug.ExportedProperty(category = "list")
+ int mSelectedPosition = INVALID_POSITION;
+
+ /**
+ * The item id of the currently selected item.
+ */
+ long mSelectedRowId = INVALID_ROW_ID;
+
+ /**
+ * View to show if there are no items to show.
+ */
+ private View mEmptyView;
+
+ /**
+ * The number of items in the current adapter.
+ */
+ @ViewDebug.ExportedProperty(category = "list")
+ int mItemCount;
+
+ /**
+ * The number of items in the adapter before a data changed event occurred.
+ */
+ int mOldItemCount;
+
+ /**
+ * Represents an invalid position. All valid positions are in the range 0 to 1 less than the
+ * number of items in the current adapter.
+ */
+ public static final int INVALID_POSITION = -1;
+
+ /**
+ * Represents an empty or invalid row id
+ */
+ public static final long INVALID_ROW_ID = Long.MIN_VALUE;
+
+ /**
+ * The last selected position we used when notifying
+ */
+ int mOldSelectedPosition = INVALID_POSITION;
+
+ /**
+ * The id of the last selected position we used when notifying
+ */
+ long mOldSelectedRowId = INVALID_ROW_ID;
+
+ /**
+ * Indicates what focusable state is requested when calling setFocusable().
+ * In addition to this, this view has other criteria for actually
+ * determining the focusable state (such as whether its empty or the text
+ * filter is shown).
+ *
+ * @see #setFocusable(boolean)
+ * @see #checkFocus()
+ */
+ private boolean mDesiredFocusableState;
+ private boolean mDesiredFocusableInTouchModeState;
+
+ private SelectionNotifier mSelectionNotifier;
+ /**
+ * When set to true, calls to requestLayout() will not propagate up the parent hierarchy.
+ * This is used to layout the children during a layout pass.
+ */
+ boolean mBlockLayoutRequests = false;
+
+ public IcsAdapterView(Context context) {
+ super(context);
+ }
+
+ public IcsAdapterView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public IcsAdapterView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * Register a callback to be invoked when an item in this AdapterView has
+ * been clicked.
+ *
+ * @param listener The callback that will be invoked.
+ */
+ public void setOnItemClickListener(OnItemClickListener listener) {
+ mOnItemClickListener = listener;
+ }
+
+ /**
+ * @return The callback to be invoked with an item in this AdapterView has
+ * been clicked, or null id no callback has been set.
+ */
+ public final OnItemClickListener getOnItemClickListener() {
+ return mOnItemClickListener;
+ }
+
+ /**
+ * Call the OnItemClickListener, if it is defined.
+ *
+ * @param view The view within the AdapterView that was clicked.
+ * @param position The position of the view in the adapter.
+ * @param id The row id of the item that was clicked.
+ * @return True if there was an assigned OnItemClickListener that was
+ * called, false otherwise is returned.
+ */
+ public boolean performItemClick(View view, int position, long id) {
+ if (mOnItemClickListener != null) {
+ playSoundEffect(SoundEffectConstants.CLICK);
+ if (view != null) {
+ view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+ }
+ mOnItemClickListener.onItemClick(/*this*/null, view, position, id);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when an item in this
+ * view has been clicked and held.
+ */
+ public interface OnItemLongClickListener {
+ /**
+ * Callback method to be invoked when an item in this view has been
+ * clicked and held.
+ *
+ * Implementers can call getItemAtPosition(position) if they need to access
+ * the data associated with the selected item.
+ *
+ * @param parent The AbsListView where the click happened
+ * @param view The view within the AbsListView that was clicked
+ * @param position The position of the view in the list
+ * @param id The row id of the item that was clicked
+ *
+ * @return true if the callback consumed the long click, false otherwise
+ */
+ boolean onItemLongClick(IcsAdapterView> parent, View view, int position, long id);
+ }
+
+
+ /**
+ * Register a callback to be invoked when an item in this AdapterView has
+ * been clicked and held
+ *
+ * @param listener The callback that will run
+ */
+ public void setOnItemLongClickListener(OnItemLongClickListener listener) {
+ if (!isLongClickable()) {
+ setLongClickable(true);
+ }
+ mOnItemLongClickListener = listener;
+ }
+
+ /**
+ * @return The callback to be invoked with an item in this AdapterView has
+ * been clicked and held, or null id no callback as been set.
+ */
+ public final OnItemLongClickListener getOnItemLongClickListener() {
+ return mOnItemLongClickListener;
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when
+ * an item in this view has been selected.
+ */
+ public interface OnItemSelectedListener {
+ /**
+ *
Callback method to be invoked when an item in this view has been
+ * selected. This callback is invoked only when the newly selected
+ * position is different from the previously selected position or if
+ * there was no selected item.
+ *
+ * Impelmenters can call getItemAtPosition(position) if they need to access the
+ * data associated with the selected item.
+ *
+ * @param parent The AdapterView where the selection happened
+ * @param view The view within the AdapterView that was clicked
+ * @param position The position of the view in the adapter
+ * @param id The row id of the item that is selected
+ */
+ void onItemSelected(IcsAdapterView> parent, View view, int position, long id);
+
+ /**
+ * Callback method to be invoked when the selection disappears from this
+ * view. The selection can disappear for instance when touch is activated
+ * or when the adapter becomes empty.
+ *
+ * @param parent The AdapterView that now contains no selected item.
+ */
+ void onNothingSelected(IcsAdapterView> parent);
+ }
+
+
+ /**
+ * Register a callback to be invoked when an item in this AdapterView has
+ * been selected.
+ *
+ * @param listener The callback that will run
+ */
+ public void setOnItemSelectedListener(OnItemSelectedListener listener) {
+ mOnItemSelectedListener = listener;
+ }
+
+ public final OnItemSelectedListener getOnItemSelectedListener() {
+ return mOnItemSelectedListener;
+ }
+
+ /**
+ * Extra menu information provided to the
+ * {@link android.view.View.OnCreateContextMenuListener#onCreateContextMenu(ContextMenu, View, ContextMenuInfo) }
+ * callback when a context menu is brought up for this AdapterView.
+ *
+ */
+ public static class AdapterContextMenuInfo implements ContextMenu.ContextMenuInfo {
+
+ public AdapterContextMenuInfo(View targetView, int position, long id) {
+ this.targetView = targetView;
+ this.position = position;
+ this.id = id;
+ }
+
+ /**
+ * The child view for which the context menu is being displayed. This
+ * will be one of the children of this AdapterView.
+ */
+ public View targetView;
+
+ /**
+ * The position in the adapter for which the context menu is being
+ * displayed.
+ */
+ public int position;
+
+ /**
+ * The row id of the item for which the context menu is being displayed.
+ */
+ public long id;
+ }
+
+ /**
+ * Returns the adapter currently associated with this widget.
+ *
+ * @return The adapter used to provide this view's content.
+ */
+ public abstract T getAdapter();
+
+ /**
+ * Sets the adapter that provides the data and the views to represent the data
+ * in this widget.
+ *
+ * @param adapter The adapter to use to create this view's content.
+ */
+ public abstract void setAdapter(T adapter);
+
+ /**
+ * This method is not supported and throws an UnsupportedOperationException when called.
+ *
+ * @param child Ignored.
+ *
+ * @throws UnsupportedOperationException Every time this method is invoked.
+ */
+ @Override
+ public void addView(View child) {
+ throw new UnsupportedOperationException("addView(View) is not supported in AdapterView");
+ }
+
+ /**
+ * This method is not supported and throws an UnsupportedOperationException when called.
+ *
+ * @param child Ignored.
+ * @param index Ignored.
+ *
+ * @throws UnsupportedOperationException Every time this method is invoked.
+ */
+ @Override
+ public void addView(View child, int index) {
+ throw new UnsupportedOperationException("addView(View, int) is not supported in AdapterView");
+ }
+
+ /**
+ * This method is not supported and throws an UnsupportedOperationException when called.
+ *
+ * @param child Ignored.
+ * @param params Ignored.
+ *
+ * @throws UnsupportedOperationException Every time this method is invoked.
+ */
+ @Override
+ public void addView(View child, LayoutParams params) {
+ throw new UnsupportedOperationException("addView(View, LayoutParams) "
+ + "is not supported in AdapterView");
+ }
+
+ /**
+ * This method is not supported and throws an UnsupportedOperationException when called.
+ *
+ * @param child Ignored.
+ * @param index Ignored.
+ * @param params Ignored.
+ *
+ * @throws UnsupportedOperationException Every time this method is invoked.
+ */
+ @Override
+ public void addView(View child, int index, LayoutParams params) {
+ throw new UnsupportedOperationException("addView(View, int, LayoutParams) "
+ + "is not supported in AdapterView");
+ }
+
+ /**
+ * This method is not supported and throws an UnsupportedOperationException when called.
+ *
+ * @param child Ignored.
+ *
+ * @throws UnsupportedOperationException Every time this method is invoked.
+ */
+ @Override
+ public void removeView(View child) {
+ throw new UnsupportedOperationException("removeView(View) is not supported in AdapterView");
+ }
+
+ /**
+ * This method is not supported and throws an UnsupportedOperationException when called.
+ *
+ * @param index Ignored.
+ *
+ * @throws UnsupportedOperationException Every time this method is invoked.
+ */
+ @Override
+ public void removeViewAt(int index) {
+ throw new UnsupportedOperationException("removeViewAt(int) is not supported in AdapterView");
+ }
+
+ /**
+ * This method is not supported and throws an UnsupportedOperationException when called.
+ *
+ * @throws UnsupportedOperationException Every time this method is invoked.
+ */
+ @Override
+ public void removeAllViews() {
+ throw new UnsupportedOperationException("removeAllViews() is not supported in AdapterView");
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ mLayoutHeight = getHeight();
+ }
+
+ /**
+ * Return the position of the currently selected item within the adapter's data set
+ *
+ * @return int Position (starting at 0), or {@link #INVALID_POSITION} if there is nothing selected.
+ */
+ @ViewDebug.CapturedViewProperty
+ public int getSelectedItemPosition() {
+ return mNextSelectedPosition;
+ }
+
+ /**
+ * @return The id corresponding to the currently selected item, or {@link #INVALID_ROW_ID}
+ * if nothing is selected.
+ */
+ @ViewDebug.CapturedViewProperty
+ public long getSelectedItemId() {
+ return mNextSelectedRowId;
+ }
+
+ /**
+ * @return The view corresponding to the currently selected item, or null
+ * if nothing is selected
+ */
+ public abstract View getSelectedView();
+
+ /**
+ * @return The data corresponding to the currently selected item, or
+ * null if there is nothing selected.
+ */
+ public Object getSelectedItem() {
+ T adapter = getAdapter();
+ int selection = getSelectedItemPosition();
+ if (adapter != null && adapter.getCount() > 0 && selection >= 0) {
+ return adapter.getItem(selection);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @return The number of items owned by the Adapter associated with this
+ * AdapterView. (This is the number of data items, which may be
+ * larger than the number of visible views.)
+ */
+ @ViewDebug.CapturedViewProperty
+ public int getCount() {
+ return mItemCount;
+ }
+
+ /**
+ * Get the position within the adapter's data set for the view, where view is a an adapter item
+ * or a descendant of an adapter item.
+ *
+ * @param view an adapter item, or a descendant of an adapter item. This must be visible in this
+ * AdapterView at the time of the call.
+ * @return the position within the adapter's data set of the view, or {@link #INVALID_POSITION}
+ * if the view does not correspond to a list item (or it is not currently visible).
+ */
+ public int getPositionForView(View view) {
+ View listItem = view;
+ try {
+ View v;
+ while (!(v = (View) listItem.getParent()).equals(this)) {
+ listItem = v;
+ }
+ } catch (ClassCastException e) {
+ // We made it up to the window without find this list view
+ return INVALID_POSITION;
+ }
+
+ // Search the children for the list item
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ if (getChildAt(i).equals(listItem)) {
+ return mFirstPosition + i;
+ }
+ }
+
+ // Child not found!
+ return INVALID_POSITION;
+ }
+
+ /**
+ * Returns the position within the adapter's data set for the first item
+ * displayed on screen.
+ *
+ * @return The position within the adapter's data set
+ */
+ public int getFirstVisiblePosition() {
+ return mFirstPosition;
+ }
+
+ /**
+ * Returns the position within the adapter's data set for the last item
+ * displayed on screen.
+ *
+ * @return The position within the adapter's data set
+ */
+ public int getLastVisiblePosition() {
+ return mFirstPosition + getChildCount() - 1;
+ }
+
+ /**
+ * Sets the currently selected item. To support accessibility subclasses that
+ * override this method must invoke the overriden super method first.
+ *
+ * @param position Index (starting at 0) of the data item to be selected.
+ */
+ public abstract void setSelection(int position);
+
+ /**
+ * Sets the view to show if the adapter is empty
+ */
+ public void setEmptyView(View emptyView) {
+ mEmptyView = emptyView;
+
+ final T adapter = getAdapter();
+ final boolean empty = ((adapter == null) || adapter.isEmpty());
+ updateEmptyStatus(empty);
+ }
+
+ /**
+ * When the current adapter is empty, the AdapterView can display a special view
+ * call the empty view. The empty view is used to provide feedback to the user
+ * that no data is available in this AdapterView.
+ *
+ * @return The view to show if the adapter is empty.
+ */
+ public View getEmptyView() {
+ return mEmptyView;
+ }
+
+ /**
+ * Indicates whether this view is in filter mode. Filter mode can for instance
+ * be enabled by a user when typing on the keyboard.
+ *
+ * @return True if the view is in filter mode, false otherwise.
+ */
+ boolean isInFilterMode() {
+ return false;
+ }
+
+ @Override
+ public void setFocusable(boolean focusable) {
+ final T adapter = getAdapter();
+ final boolean empty = adapter == null || adapter.getCount() == 0;
+
+ mDesiredFocusableState = focusable;
+ if (!focusable) {
+ mDesiredFocusableInTouchModeState = false;
+ }
+
+ super.setFocusable(focusable && (!empty || isInFilterMode()));
+ }
+
+ @Override
+ public void setFocusableInTouchMode(boolean focusable) {
+ final T adapter = getAdapter();
+ final boolean empty = adapter == null || adapter.getCount() == 0;
+
+ mDesiredFocusableInTouchModeState = focusable;
+ if (focusable) {
+ mDesiredFocusableState = true;
+ }
+
+ super.setFocusableInTouchMode(focusable && (!empty || isInFilterMode()));
+ }
+
+ void checkFocus() {
+ final T adapter = getAdapter();
+ final boolean empty = adapter == null || adapter.getCount() == 0;
+ final boolean focusable = !empty || isInFilterMode();
+ // The order in which we set focusable in touch mode/focusable may matter
+ // for the client, see View.setFocusableInTouchMode() comments for more
+ // details
+ super.setFocusableInTouchMode(focusable && mDesiredFocusableInTouchModeState);
+ super.setFocusable(focusable && mDesiredFocusableState);
+ if (mEmptyView != null) {
+ updateEmptyStatus((adapter == null) || adapter.isEmpty());
+ }
+ }
+
+ /**
+ * Update the status of the list based on the empty parameter. If empty is true and
+ * we have an empty view, display it. In all the other cases, make sure that the listview
+ * is VISIBLE and that the empty view is GONE (if it's not null).
+ */
+ private void updateEmptyStatus(boolean empty) {
+ if (isInFilterMode()) {
+ empty = false;
+ }
+
+ if (empty) {
+ if (mEmptyView != null) {
+ mEmptyView.setVisibility(View.VISIBLE);
+ setVisibility(View.GONE);
+ } else {
+ // If the caller just removed our empty view, make sure the list view is visible
+ setVisibility(View.VISIBLE);
+ }
+
+ // We are now GONE, so pending layouts will not be dispatched.
+ // Force one here to make sure that the state of the list matches
+ // the state of the adapter.
+ if (mDataChanged) {
+ this.onLayout(false, getLeft(), getTop(), getRight(), getBottom());
+ }
+ } else {
+ if (mEmptyView != null) mEmptyView.setVisibility(View.GONE);
+ setVisibility(View.VISIBLE);
+ }
+ }
+
+ /**
+ * Gets the data associated with the specified position in the list.
+ *
+ * @param position Which data to get
+ * @return The data associated with the specified position in the list
+ */
+ public Object getItemAtPosition(int position) {
+ T adapter = getAdapter();
+ return (adapter == null || position < 0) ? null : adapter.getItem(position);
+ }
+
+ public long getItemIdAtPosition(int position) {
+ T adapter = getAdapter();
+ return (adapter == null || position < 0) ? INVALID_ROW_ID : adapter.getItemId(position);
+ }
+
+ @Override
+ public void setOnClickListener(OnClickListener l) {
+ throw new RuntimeException("Don't call setOnClickListener for an AdapterView. "
+ + "You probably want setOnItemClickListener instead");
+ }
+
+ /**
+ * Override to prevent freezing of any views created by the adapter.
+ */
+ @Override
+ protected void dispatchSaveInstanceState(SparseArray container) {
+ dispatchFreezeSelfOnly(container);
+ }
+
+ /**
+ * Override to prevent thawing of any views created by the adapter.
+ */
+ @Override
+ protected void dispatchRestoreInstanceState(SparseArray container) {
+ dispatchThawSelfOnly(container);
+ }
+
+ class AdapterDataSetObserver extends DataSetObserver {
+
+ private Parcelable mInstanceState = null;
+
+ @Override
+ public void onChanged() {
+ mDataChanged = true;
+ mOldItemCount = mItemCount;
+ mItemCount = getAdapter().getCount();
+
+ // Detect the case where a cursor that was previously invalidated has
+ // been repopulated with new data.
+ if (IcsAdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
+ && mOldItemCount == 0 && mItemCount > 0) {
+ IcsAdapterView.this.onRestoreInstanceState(mInstanceState);
+ mInstanceState = null;
+ } else {
+ rememberSyncState();
+ }
+ checkFocus();
+ requestLayout();
+ }
+
+ @Override
+ public void onInvalidated() {
+ mDataChanged = true;
+
+ if (IcsAdapterView.this.getAdapter().hasStableIds()) {
+ // Remember the current state for the case where our hosting activity is being
+ // stopped and later restarted
+ mInstanceState = IcsAdapterView.this.onSaveInstanceState();
+ }
+
+ // Data is invalid so we should reset our state
+ mOldItemCount = mItemCount;
+ mItemCount = 0;
+ mSelectedPosition = INVALID_POSITION;
+ mSelectedRowId = INVALID_ROW_ID;
+ mNextSelectedPosition = INVALID_POSITION;
+ mNextSelectedRowId = INVALID_ROW_ID;
+ mNeedSync = false;
+
+ checkFocus();
+ requestLayout();
+ }
+
+ public void clearSavedState() {
+ mInstanceState = null;
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ removeCallbacks(mSelectionNotifier);
+ }
+
+ private class SelectionNotifier implements Runnable {
+ public void run() {
+ if (mDataChanged) {
+ // Data has changed between when this SelectionNotifier
+ // was posted and now. We need to wait until the AdapterView
+ // has been synched to the new data.
+ if (getAdapter() != null) {
+ post(this);
+ }
+ } else {
+ fireOnSelected();
+ }
+ }
+ }
+
+ void selectionChanged() {
+ if (mOnItemSelectedListener != null) {
+ if (mInLayout || mBlockLayoutRequests) {
+ // If we are in a layout traversal, defer notification
+ // by posting. This ensures that the view tree is
+ // in a consistent state and is able to accomodate
+ // new layout or invalidate requests.
+ if (mSelectionNotifier == null) {
+ mSelectionNotifier = new SelectionNotifier();
+ }
+ post(mSelectionNotifier);
+ } else {
+ fireOnSelected();
+ }
+ }
+
+ // we fire selection events here not in View
+ if (mSelectedPosition != ListView.INVALID_POSITION && isShown() && !isInTouchMode()) {
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
+ }
+ }
+
+ private void fireOnSelected() {
+ if (mOnItemSelectedListener == null)
+ return;
+
+ int selection = this.getSelectedItemPosition();
+ if (selection >= 0) {
+ View v = getSelectedView();
+ mOnItemSelectedListener.onItemSelected(this, v, selection,
+ getAdapter().getItemId(selection));
+ } else {
+ mOnItemSelectedListener.onNothingSelected(this);
+ }
+ }
+
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ View selectedView = getSelectedView();
+ if (selectedView != null && selectedView.getVisibility() == VISIBLE
+ && selectedView.dispatchPopulateAccessibilityEvent(event)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+ if (super.onRequestSendAccessibilityEvent(child, event)) {
+ // Add a record for ourselves as well.
+ AccessibilityEvent record = AccessibilityEvent.obtain();
+ onInitializeAccessibilityEvent(record);
+ // Populate with the text of the requesting child.
+ child.dispatchPopulateAccessibilityEvent(record);
+ event.appendRecord(record);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setScrollable(isScrollableForAccessibility());
+ View selectedView = getSelectedView();
+ if (selectedView != null) {
+ info.setEnabled(selectedView.isEnabled());
+ }
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setScrollable(isScrollableForAccessibility());
+ View selectedView = getSelectedView();
+ if (selectedView != null) {
+ event.setEnabled(selectedView.isEnabled());
+ }
+ event.setCurrentItemIndex(getSelectedItemPosition());
+ event.setFromIndex(getFirstVisiblePosition());
+ event.setToIndex(getLastVisiblePosition());
+ event.setItemCount(getCount());
+ }
+
+ private boolean isScrollableForAccessibility() {
+ T adapter = getAdapter();
+ if (adapter != null) {
+ final int itemCount = adapter.getCount();
+ return itemCount > 0
+ && (getFirstVisiblePosition() > 0 || getLastVisiblePosition() < itemCount - 1);
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean canAnimate() {
+ return super.canAnimate() && mItemCount > 0;
+ }
+
+ void handleDataChanged() {
+ final int count = mItemCount;
+ boolean found = false;
+
+ if (count > 0) {
+
+ int newPos;
+
+ // Find the row we are supposed to sync to
+ if (mNeedSync) {
+ // Update this first, since setNextSelectedPositionInt inspects
+ // it
+ mNeedSync = false;
+
+ // See if we can find a position in the new data with the same
+ // id as the old selection
+ newPos = findSyncPosition();
+ if (newPos >= 0) {
+ // Verify that new selection is selectable
+ int selectablePos = lookForSelectablePosition(newPos, true);
+ if (selectablePos == newPos) {
+ // Same row id is selected
+ setNextSelectedPositionInt(newPos);
+ found = true;
+ }
+ }
+ }
+ if (!found) {
+ // Try to use the same position if we can't find matching data
+ newPos = getSelectedItemPosition();
+
+ // Pin position to the available range
+ if (newPos >= count) {
+ newPos = count - 1;
+ }
+ if (newPos < 0) {
+ newPos = 0;
+ }
+
+ // Make sure we select something selectable -- first look down
+ int selectablePos = lookForSelectablePosition(newPos, true);
+ if (selectablePos < 0) {
+ // Looking down didn't work -- try looking up
+ selectablePos = lookForSelectablePosition(newPos, false);
+ }
+ if (selectablePos >= 0) {
+ setNextSelectedPositionInt(selectablePos);
+ checkSelectionChanged();
+ found = true;
+ }
+ }
+ }
+ if (!found) {
+ // Nothing is selected
+ mSelectedPosition = INVALID_POSITION;
+ mSelectedRowId = INVALID_ROW_ID;
+ mNextSelectedPosition = INVALID_POSITION;
+ mNextSelectedRowId = INVALID_ROW_ID;
+ mNeedSync = false;
+ checkSelectionChanged();
+ }
+ }
+
+ void checkSelectionChanged() {
+ if ((mSelectedPosition != mOldSelectedPosition) || (mSelectedRowId != mOldSelectedRowId)) {
+ selectionChanged();
+ mOldSelectedPosition = mSelectedPosition;
+ mOldSelectedRowId = mSelectedRowId;
+ }
+ }
+
+ /**
+ * Searches the adapter for a position matching mSyncRowId. The search starts at mSyncPosition
+ * and then alternates between moving up and moving down until 1) we find the right position, or
+ * 2) we run out of time, or 3) we have looked at every position
+ *
+ * @return Position of the row that matches mSyncRowId, or {@link #INVALID_POSITION} if it can't
+ * be found
+ */
+ int findSyncPosition() {
+ int count = mItemCount;
+
+ if (count == 0) {
+ return INVALID_POSITION;
+ }
+
+ long idToMatch = mSyncRowId;
+ int seed = mSyncPosition;
+
+ // If there isn't a selection don't hunt for it
+ if (idToMatch == INVALID_ROW_ID) {
+ return INVALID_POSITION;
+ }
+
+ // Pin seed to reasonable values
+ seed = Math.max(0, seed);
+ seed = Math.min(count - 1, seed);
+
+ long endTime = SystemClock.uptimeMillis() + SYNC_MAX_DURATION_MILLIS;
+
+ long rowId;
+
+ // first position scanned so far
+ int first = seed;
+
+ // last position scanned so far
+ int last = seed;
+
+ // True if we should move down on the next iteration
+ boolean next = false;
+
+ // True when we have looked at the first item in the data
+ boolean hitFirst;
+
+ // True when we have looked at the last item in the data
+ boolean hitLast;
+
+ // Get the item ID locally (instead of getItemIdAtPosition), so
+ // we need the adapter
+ T adapter = getAdapter();
+ if (adapter == null) {
+ return INVALID_POSITION;
+ }
+
+ while (SystemClock.uptimeMillis() <= endTime) {
+ rowId = adapter.getItemId(seed);
+ if (rowId == idToMatch) {
+ // Found it!
+ return seed;
+ }
+
+ hitLast = last == count - 1;
+ hitFirst = first == 0;
+
+ if (hitLast && hitFirst) {
+ // Looked at everything
+ break;
+ }
+
+ if (hitFirst || (next && !hitLast)) {
+ // Either we hit the top, or we are trying to move down
+ last++;
+ seed = last;
+ // Try going up next time
+ next = false;
+ } else if (hitLast || (!next && !hitFirst)) {
+ // Either we hit the bottom, or we are trying to move up
+ first--;
+ seed = first;
+ // Try going down next time
+ next = true;
+ }
+
+ }
+
+ return INVALID_POSITION;
+ }
+
+ /**
+ * Find a position that can be selected (i.e., is not a separator).
+ *
+ * @param position The starting position to look at.
+ * @param lookDown Whether to look down for other positions.
+ * @return The next selectable position starting at position and then searching either up or
+ * down. Returns {@link #INVALID_POSITION} if nothing can be found.
+ */
+ int lookForSelectablePosition(int position, boolean lookDown) {
+ return position;
+ }
+
+ /**
+ * Utility to keep mSelectedPosition and mSelectedRowId in sync
+ * @param position Our current position
+ */
+ void setSelectedPositionInt(int position) {
+ mSelectedPosition = position;
+ mSelectedRowId = getItemIdAtPosition(position);
+ }
+
+ /**
+ * Utility to keep mNextSelectedPosition and mNextSelectedRowId in sync
+ * @param position Intended value for mSelectedPosition the next time we go
+ * through layout
+ */
+ void setNextSelectedPositionInt(int position) {
+ mNextSelectedPosition = position;
+ mNextSelectedRowId = getItemIdAtPosition(position);
+ // If we are trying to sync to the selection, update that too
+ if (mNeedSync && mSyncMode == SYNC_SELECTED_POSITION && position >= 0) {
+ mSyncPosition = position;
+ mSyncRowId = mNextSelectedRowId;
+ }
+ }
+
+ /**
+ * Remember enough information to restore the screen state when the data has
+ * changed.
+ *
+ */
+ void rememberSyncState() {
+ if (getChildCount() > 0) {
+ mNeedSync = true;
+ mSyncHeight = mLayoutHeight;
+ if (mSelectedPosition >= 0) {
+ // Sync the selection state
+ View v = getChildAt(mSelectedPosition - mFirstPosition);
+ mSyncRowId = mNextSelectedRowId;
+ mSyncPosition = mNextSelectedPosition;
+ if (v != null) {
+ mSpecificTop = v.getTop();
+ }
+ mSyncMode = SYNC_SELECTED_POSITION;
+ } else {
+ // Sync the based on the offset of the first view
+ View v = getChildAt(0);
+ T adapter = getAdapter();
+ if (mFirstPosition >= 0 && mFirstPosition < adapter.getCount()) {
+ mSyncRowId = adapter.getItemId(mFirstPosition);
+ } else {
+ mSyncRowId = NO_ID;
+ }
+ mSyncPosition = mFirstPosition;
+ if (v != null) {
+ mSpecificTop = v.getTop();
+ }
+ mSyncMode = SYNC_FIRST_POSITION;
+ }
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsColorDrawable.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsColorDrawable.java
new file mode 100644
index 0000000000..a78b3f71b3
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsColorDrawable.java
@@ -0,0 +1,41 @@
+package com.actionbarsherlock.internal.widget;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+
+/**
+ * A version of {@link android.graphics.drawable.ColorDrawable} that respects bounds.
+ */
+public class IcsColorDrawable extends Drawable {
+ private int color;
+ private final Paint paint = new Paint();
+
+ public IcsColorDrawable(int color) {
+ this.color = color;
+ }
+
+ @Override public void draw(Canvas canvas) {
+ if ((color >>> 24) != 0) {
+ paint.setColor(color);
+ canvas.drawRect(getBounds(), paint);
+ }
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ if (alpha != (color >>> 24)) {
+ color = (color & 0x00FFFFFF) & (alpha << 24);
+ invalidateSelf();
+ }
+ }
+
+ @Override public void setColorFilter(ColorFilter colorFilter) {
+ //Ignored
+ }
+
+ @Override public int getOpacity() {
+ return color >>> 24;
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsLinearLayout.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsLinearLayout.java
new file mode 100644
index 0000000000..4947c41df5
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsLinearLayout.java
@@ -0,0 +1,410 @@
+package com.actionbarsherlock.internal.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.actionbarsherlock.internal.nineoldandroids.widget.NineLinearLayout;
+
+/**
+ * A simple extension of a regular linear layout that supports the divider API
+ * of Android 4.0+. The dividers are added adjacent to the children by changing
+ * their layout params. If you need to rely on the margins which fall in the
+ * same orientation as the layout you should wrap the child in a simple
+ * {@link android.widget.FrameLayout} so it can receive the margin.
+ */
+public class IcsLinearLayout extends NineLinearLayout {
+ private static final int[] R_styleable_LinearLayout = new int[] {
+ /* 0 */ android.R.attr.divider,
+ /* 1 */ android.R.attr.measureWithLargestChild,
+ /* 2 */ android.R.attr.showDividers,
+ /* 3 */ android.R.attr.dividerPadding,
+ };
+ private static final int LinearLayout_divider = 0;
+ private static final int LinearLayout_measureWithLargestChild = 1;
+ private static final int LinearLayout_showDividers = 2;
+ private static final int LinearLayout_dividerPadding = 3;
+
+ /**
+ * Don't show any dividers.
+ */
+ public static final int SHOW_DIVIDER_NONE = 0;
+ /**
+ * Show a divider at the beginning of the group.
+ */
+ public static final int SHOW_DIVIDER_BEGINNING = 1;
+ /**
+ * Show dividers between each item in the group.
+ */
+ public static final int SHOW_DIVIDER_MIDDLE = 2;
+ /**
+ * Show a divider at the end of the group.
+ */
+ public static final int SHOW_DIVIDER_END = 4;
+
+
+ private Drawable mDivider;
+ private int mDividerWidth;
+ private int mDividerHeight;
+ private int mShowDividers;
+ private int mDividerPadding;
+
+ private boolean mUseLargestChild;
+
+ public IcsLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, /*com.android.internal.R.styleable.*/R_styleable_LinearLayout);
+
+ setDividerDrawable(a.getDrawable(/*com.android.internal.R.styleable.*/LinearLayout_divider));
+ mShowDividers = a.getInt(/*com.android.internal.R.styleable.*/LinearLayout_showDividers, SHOW_DIVIDER_NONE);
+ mDividerPadding = a.getDimensionPixelSize(/*com.android.internal.R.styleable.*/LinearLayout_dividerPadding, 0);
+ mUseLargestChild = a.getBoolean(/*com.android.internal.R.styleable.*/LinearLayout_measureWithLargestChild, false);
+
+ a.recycle();
+ }
+
+ /**
+ * Set how dividers should be shown between items in this layout
+ *
+ * @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING},
+ * {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END},
+ * or {@link #SHOW_DIVIDER_NONE} to show no dividers.
+ */
+ public void setShowDividers(int showDividers) {
+ if (showDividers != mShowDividers) {
+ requestLayout();
+ invalidate(); //XXX This is required if you are toggling a divider off
+ }
+ mShowDividers = showDividers;
+ }
+
+ /**
+ * @return A flag set indicating how dividers should be shown around items.
+ * @see #setShowDividers(int)
+ */
+ public int getShowDividers() {
+ return mShowDividers;
+ }
+
+ /**
+ * Set a drawable to be used as a divider between items.
+ * @param divider Drawable that will divide each item.
+ * @see #setShowDividers(int)
+ */
+ public void setDividerDrawable(Drawable divider) {
+ if (divider == mDivider) {
+ return;
+ }
+ mDivider = divider;
+ if (divider != null) {
+ mDividerWidth = divider.getIntrinsicWidth();
+ mDividerHeight = divider.getIntrinsicHeight();
+ } else {
+ mDividerWidth = 0;
+ mDividerHeight = 0;
+ }
+ setWillNotDraw(divider == null);
+ requestLayout();
+ }
+
+ /**
+ * Set padding displayed on both ends of dividers.
+ *
+ * @param padding Padding value in pixels that will be applied to each end
+ *
+ * @see #setShowDividers(int)
+ * @see #setDividerDrawable(Drawable)
+ * @see #getDividerPadding()
+ */
+ public void setDividerPadding(int padding) {
+ mDividerPadding = padding;
+ }
+
+ /**
+ * Get the padding size used to inset dividers in pixels
+ *
+ * @see #setShowDividers(int)
+ * @see #setDividerDrawable(Drawable)
+ * @see #setDividerPadding(int)
+ */
+ public int getDividerPadding() {
+ return mDividerPadding;
+ }
+
+ /**
+ * Get the width of the current divider drawable.
+ *
+ * @hide Used internally by framework.
+ */
+ public int getDividerWidth() {
+ return mDividerWidth;
+ }
+
+ @Override
+ protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
+ final int index = indexOfChild(child);
+ final int orientation = getOrientation();
+ final LayoutParams params = (LayoutParams) child.getLayoutParams();
+ if (hasDividerBeforeChildAt(index)) {
+ if (orientation == VERTICAL) {
+ //Account for the divider by pushing everything up
+ params.topMargin = mDividerHeight;
+ } else {
+ //Account for the divider by pushing everything left
+ params.leftMargin = mDividerWidth;
+ }
+ }
+
+ final int count = getChildCount();
+ if (index == count - 1) {
+ if (hasDividerBeforeChildAt(count)) {
+ if (orientation == VERTICAL) {
+ params.bottomMargin = mDividerHeight;
+ } else {
+ params.rightMargin = mDividerWidth;
+ }
+ }
+ }
+ super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mDivider != null) {
+ if (getOrientation() == VERTICAL) {
+ drawDividersVertical(canvas);
+ } else {
+ drawDividersHorizontal(canvas);
+ }
+ }
+ super.onDraw(canvas);
+ }
+
+ void drawDividersVertical(Canvas canvas) {
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+
+ if (child != null && child.getVisibility() != GONE) {
+ if (hasDividerBeforeChildAt(i)) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ final int top = child.getTop() - lp.topMargin/* - mDividerHeight*/;
+ drawHorizontalDivider(canvas, top);
+ }
+ }
+ }
+
+ if (hasDividerBeforeChildAt(count)) {
+ final View child = getChildAt(count - 1);
+ int bottom = 0;
+ if (child == null) {
+ bottom = getHeight() - getPaddingBottom() - mDividerHeight;
+ } else {
+ //final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ bottom = child.getBottom()/* + lp.bottomMargin*/;
+ }
+ drawHorizontalDivider(canvas, bottom);
+ }
+ }
+
+ void drawDividersHorizontal(Canvas canvas) {
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+
+ if (child != null && child.getVisibility() != GONE) {
+ if (hasDividerBeforeChildAt(i)) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ final int left = child.getLeft() - lp.leftMargin/* - mDividerWidth*/;
+ drawVerticalDivider(canvas, left);
+ }
+ }
+ }
+
+ if (hasDividerBeforeChildAt(count)) {
+ final View child = getChildAt(count - 1);
+ int right = 0;
+ if (child == null) {
+ right = getWidth() - getPaddingRight() - mDividerWidth;
+ } else {
+ //final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ right = child.getRight()/* + lp.rightMargin*/;
+ }
+ drawVerticalDivider(canvas, right);
+ }
+ }
+
+ void drawHorizontalDivider(Canvas canvas, int top) {
+ mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,
+ getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
+ mDivider.draw(canvas);
+ }
+
+ void drawVerticalDivider(Canvas canvas, int left) {
+ mDivider.setBounds(left, getPaddingTop() + mDividerPadding,
+ left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
+ mDivider.draw(canvas);
+ }
+
+ /**
+ * Determines where to position dividers between children.
+ *
+ * @param childIndex Index of child to check for preceding divider
+ * @return true if there should be a divider before the child at childIndex
+ * @hide Pending API consideration. Currently only used internally by the system.
+ */
+ protected boolean hasDividerBeforeChildAt(int childIndex) {
+ if (childIndex == 0) {
+ return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
+ } else if (childIndex == getChildCount()) {
+ return (mShowDividers & SHOW_DIVIDER_END) != 0;
+ } else if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) {
+ boolean hasVisibleViewBefore = false;
+ for (int i = childIndex - 1; i >= 0; i--) {
+ if (getChildAt(i).getVisibility() != GONE) {
+ hasVisibleViewBefore = true;
+ break;
+ }
+ }
+ return hasVisibleViewBefore;
+ }
+ return false;
+ }
+
+ /**
+ * When true, all children with a weight will be considered having
+ * the minimum size of the largest child. If false, all children are
+ * measured normally.
+ *
+ * @return True to measure children with a weight using the minimum
+ * size of the largest child, false otherwise.
+ *
+ * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
+ */
+ public boolean isMeasureWithLargestChildEnabled() {
+ return mUseLargestChild;
+ }
+
+ /**
+ * When set to true, all children with a weight will be considered having
+ * the minimum size of the largest child. If false, all children are
+ * measured normally.
+ *
+ * Disabled by default.
+ *
+ * @param enabled True to measure children with a weight using the
+ * minimum size of the largest child, false otherwise.
+ *
+ * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
+ */
+ public void setMeasureWithLargestChildEnabled(boolean enabled) {
+ mUseLargestChild = enabled;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ if (mUseLargestChild) {
+ final int orientation = getOrientation();
+ switch (orientation) {
+ case HORIZONTAL:
+ useLargestChildHorizontal();
+ break;
+
+ case VERTICAL:
+ useLargestChildVertical();
+ break;
+ }
+ }
+ }
+
+ private void useLargestChildHorizontal() {
+ final int childCount = getChildCount();
+
+ // Find largest child width
+ int largestChildWidth = 0;
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ largestChildWidth = Math.max(child.getMeasuredWidth(), largestChildWidth);
+ }
+
+ int totalWidth = 0;
+ // Re-measure childs
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+
+ if (child == null || child.getVisibility() == View.GONE) {
+ continue;
+ }
+
+ final LinearLayout.LayoutParams lp =
+ (LinearLayout.LayoutParams) child.getLayoutParams();
+
+ float childExtra = lp.weight;
+ if (childExtra > 0) {
+ child.measure(
+ MeasureSpec.makeMeasureSpec(largestChildWidth,
+ MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(),
+ MeasureSpec.EXACTLY));
+ totalWidth += largestChildWidth;
+
+ } else {
+ totalWidth += child.getMeasuredWidth();
+ }
+
+ totalWidth += lp.leftMargin + lp.rightMargin;
+ }
+
+ totalWidth += getPaddingLeft() + getPaddingRight();
+ setMeasuredDimension(totalWidth, getMeasuredHeight());
+ }
+
+ private void useLargestChildVertical() {
+ final int childCount = getChildCount();
+
+ // Find largest child width
+ int largestChildHeight = 0;
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ largestChildHeight = Math.max(child.getMeasuredHeight(), largestChildHeight);
+ }
+
+ int totalHeight = 0;
+ // Re-measure childs
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+
+ if (child == null || child.getVisibility() == View.GONE) {
+ continue;
+ }
+
+ final LinearLayout.LayoutParams lp =
+ (LinearLayout.LayoutParams) child.getLayoutParams();
+
+ float childExtra = lp.weight;
+ if (childExtra > 0) {
+ child.measure(
+ MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
+ MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(largestChildHeight,
+ MeasureSpec.EXACTLY));
+ totalHeight += largestChildHeight;
+
+ } else {
+ totalHeight += child.getMeasuredHeight();
+ }
+
+ totalHeight += lp.leftMargin + lp.rightMargin;
+ }
+
+ totalHeight += getPaddingLeft() + getPaddingRight();
+ setMeasuredDimension(getMeasuredWidth(), totalHeight);
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsListPopupWindow.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsListPopupWindow.java
new file mode 100644
index 0000000000..d13c6cea97
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsListPopupWindow.java
@@ -0,0 +1,644 @@
+package com.actionbarsherlock.internal.widget;
+
+import com.actionbarsherlock.R;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.DataSetObserver;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.PopupWindow;
+
+/**
+ * A proxy between pre- and post-Honeycomb implementations of this class.
+ */
+public class IcsListPopupWindow {
+ /**
+ * This value controls the length of time that the user
+ * must leave a pointer down without scrolling to expand
+ * the autocomplete dropdown list to cover the IME.
+ */
+ private static final int EXPAND_LIST_TIMEOUT = 250;
+
+ private Context mContext;
+ private PopupWindow mPopup;
+ private ListAdapter mAdapter;
+ private DropDownListView mDropDownList;
+
+ private int mDropDownHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
+ private int mDropDownWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
+ private int mDropDownHorizontalOffset;
+ private int mDropDownVerticalOffset;
+ private boolean mDropDownVerticalOffsetSet;
+
+ private int mListItemExpandMaximum = Integer.MAX_VALUE;
+
+ private View mPromptView;
+ private int mPromptPosition = POSITION_PROMPT_ABOVE;
+
+ private DataSetObserver mObserver;
+
+ private View mDropDownAnchorView;
+
+ private Drawable mDropDownListHighlight;
+
+ private AdapterView.OnItemClickListener mItemClickListener;
+ private AdapterView.OnItemSelectedListener mItemSelectedListener;
+
+ private final ResizePopupRunnable mResizePopupRunnable = new ResizePopupRunnable();
+ private final PopupTouchInterceptor mTouchInterceptor = new PopupTouchInterceptor();
+ private final PopupScrollListener mScrollListener = new PopupScrollListener();
+ private final ListSelectorHider mHideSelector = new ListSelectorHider();
+
+ private Handler mHandler = new Handler();
+
+ private Rect mTempRect = new Rect();
+
+ private boolean mModal;
+
+ public static final int POSITION_PROMPT_ABOVE = 0;
+ public static final int POSITION_PROMPT_BELOW = 1;
+
+ public IcsListPopupWindow(Context context) {
+ this(context, null, R.attr.listPopupWindowStyle);
+ }
+
+ public IcsListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
+ mContext = context;
+ mPopup = new PopupWindow(context, attrs, defStyleAttr);
+ mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
+ }
+
+ public IcsListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ mContext = context;
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+ Context wrapped = new ContextThemeWrapper(context, defStyleRes);
+ mPopup = new PopupWindow(wrapped, attrs, defStyleAttr);
+ } else {
+ mPopup = new PopupWindow(context, attrs, defStyleAttr, defStyleRes);
+ }
+ mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
+ }
+
+ public void setAdapter(ListAdapter adapter) {
+ if (mObserver == null) {
+ mObserver = new PopupDataSetObserver();
+ } else if (mAdapter != null) {
+ mAdapter.unregisterDataSetObserver(mObserver);
+ }
+ mAdapter = adapter;
+ if (mAdapter != null) {
+ adapter.registerDataSetObserver(mObserver);
+ }
+
+ if (mDropDownList != null) {
+ mDropDownList.setAdapter(mAdapter);
+ }
+ }
+
+ public void setPromptPosition(int position) {
+ mPromptPosition = position;
+ }
+
+ public void setModal(boolean modal) {
+ mModal = true;
+ mPopup.setFocusable(modal);
+ }
+
+ public void setBackgroundDrawable(Drawable d) {
+ mPopup.setBackgroundDrawable(d);
+ }
+
+ public void setAnchorView(View anchor) {
+ mDropDownAnchorView = anchor;
+ }
+
+ public void setHorizontalOffset(int offset) {
+ mDropDownHorizontalOffset = offset;
+ }
+
+ public void setVerticalOffset(int offset) {
+ mDropDownVerticalOffset = offset;
+ mDropDownVerticalOffsetSet = true;
+ }
+
+ public void setContentWidth(int width) {
+ Drawable popupBackground = mPopup.getBackground();
+ if (popupBackground != null) {
+ popupBackground.getPadding(mTempRect);
+ mDropDownWidth = mTempRect.left + mTempRect.right + width;
+ } else {
+ mDropDownWidth = width;
+ }
+ }
+
+ public void setOnItemClickListener(AdapterView.OnItemClickListener clickListener) {
+ mItemClickListener = clickListener;
+ }
+
+ public void show() {
+ int height = buildDropDown();
+
+ int widthSpec = 0;
+ int heightSpec = 0;
+
+ boolean noInputMethod = isInputMethodNotNeeded();
+ //XXX mPopup.setAllowScrollingAnchorParent(!noInputMethod);
+
+ if (mPopup.isShowing()) {
+ if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
+ // The call to PopupWindow's update method below can accept -1 for any
+ // value you do not want to update.
+ widthSpec = -1;
+ } else if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ widthSpec = mDropDownAnchorView.getWidth();
+ } else {
+ widthSpec = mDropDownWidth;
+ }
+
+ if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
+ // The call to PopupWindow's update method below can accept -1 for any
+ // value you do not want to update.
+ heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.MATCH_PARENT;
+ if (noInputMethod) {
+ mPopup.setWindowLayoutMode(
+ mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
+ ViewGroup.LayoutParams.MATCH_PARENT : 0, 0);
+ } else {
+ mPopup.setWindowLayoutMode(
+ mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
+ ViewGroup.LayoutParams.MATCH_PARENT : 0,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ }
+ } else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ heightSpec = height;
+ } else {
+ heightSpec = mDropDownHeight;
+ }
+
+ mPopup.setOutsideTouchable(true);
+
+ mPopup.update(mDropDownAnchorView, mDropDownHorizontalOffset,
+ mDropDownVerticalOffset, widthSpec, heightSpec);
+ } else {
+ if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
+ widthSpec = ViewGroup.LayoutParams.MATCH_PARENT;
+ } else {
+ if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ mPopup.setWidth(mDropDownAnchorView.getWidth());
+ } else {
+ mPopup.setWidth(mDropDownWidth);
+ }
+ }
+
+ if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
+ heightSpec = ViewGroup.LayoutParams.MATCH_PARENT;
+ } else {
+ if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ mPopup.setHeight(height);
+ } else {
+ mPopup.setHeight(mDropDownHeight);
+ }
+ }
+
+ mPopup.setWindowLayoutMode(widthSpec, heightSpec);
+ //XXX mPopup.setClipToScreenEnabled(true);
+
+ // use outside touchable to dismiss drop down when touching outside of it, so
+ // only set this if the dropdown is not always visible
+ mPopup.setOutsideTouchable(true);
+ mPopup.setTouchInterceptor(mTouchInterceptor);
+ mPopup.showAsDropDown(mDropDownAnchorView,
+ mDropDownHorizontalOffset, mDropDownVerticalOffset);
+ mDropDownList.setSelection(ListView.INVALID_POSITION);
+
+ if (!mModal || mDropDownList.isInTouchMode()) {
+ clearListSelection();
+ }
+ if (!mModal) {
+ mHandler.post(mHideSelector);
+ }
+ }
+ }
+
+ public void dismiss() {
+ mPopup.dismiss();
+ if (mPromptView != null) {
+ final ViewParent parent = mPromptView.getParent();
+ if (parent instanceof ViewGroup) {
+ final ViewGroup group = (ViewGroup) parent;
+ group.removeView(mPromptView);
+ }
+ }
+ mPopup.setContentView(null);
+ mDropDownList = null;
+ mHandler.removeCallbacks(mResizePopupRunnable);
+ }
+
+ public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
+ mPopup.setOnDismissListener(listener);
+ }
+
+ public void setInputMethodMode(int mode) {
+ mPopup.setInputMethodMode(mode);
+ }
+
+ public void clearListSelection() {
+ final DropDownListView list = mDropDownList;
+ if (list != null) {
+ // WARNING: Please read the comment where mListSelectionHidden is declared
+ list.mListSelectionHidden = true;
+ //XXX list.hideSelector();
+ list.requestLayout();
+ }
+ }
+
+ public boolean isShowing() {
+ return mPopup.isShowing();
+ }
+
+ private boolean isInputMethodNotNeeded() {
+ return mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
+ }
+
+ public ListView getListView() {
+ return mDropDownList;
+ }
+
+ private int buildDropDown() {
+ ViewGroup dropDownView;
+ int otherHeights = 0;
+
+ if (mDropDownList == null) {
+ Context context = mContext;
+
+ mDropDownList = new DropDownListView(context, !mModal);
+ if (mDropDownListHighlight != null) {
+ mDropDownList.setSelector(mDropDownListHighlight);
+ }
+ mDropDownList.setAdapter(mAdapter);
+ mDropDownList.setOnItemClickListener(mItemClickListener);
+ mDropDownList.setFocusable(true);
+ mDropDownList.setFocusableInTouchMode(true);
+ mDropDownList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ public void onItemSelected(AdapterView> parent, View view,
+ int position, long id) {
+
+ if (position != -1) {
+ DropDownListView dropDownList = mDropDownList;
+
+ if (dropDownList != null) {
+ dropDownList.mListSelectionHidden = false;
+ }
+ }
+ }
+
+ public void onNothingSelected(AdapterView> parent) {
+ }
+ });
+ mDropDownList.setOnScrollListener(mScrollListener);
+
+ if (mItemSelectedListener != null) {
+ mDropDownList.setOnItemSelectedListener(mItemSelectedListener);
+ }
+
+ dropDownView = mDropDownList;
+
+ View hintView = mPromptView;
+ if (hintView != null) {
+ // if an hint has been specified, we accomodate more space for it and
+ // add a text view in the drop down menu, at the bottom of the list
+ LinearLayout hintContainer = new LinearLayout(context);
+ hintContainer.setOrientation(LinearLayout.VERTICAL);
+
+ LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, 0, 1.0f
+ );
+
+ switch (mPromptPosition) {
+ case POSITION_PROMPT_BELOW:
+ hintContainer.addView(dropDownView, hintParams);
+ hintContainer.addView(hintView);
+ break;
+
+ case POSITION_PROMPT_ABOVE:
+ hintContainer.addView(hintView);
+ hintContainer.addView(dropDownView, hintParams);
+ break;
+
+ default:
+ break;
+ }
+
+ // measure the hint's height to find how much more vertical space
+ // we need to add to the drop down's height
+ int widthSpec = MeasureSpec.makeMeasureSpec(mDropDownWidth, MeasureSpec.AT_MOST);
+ int heightSpec = MeasureSpec.UNSPECIFIED;
+ hintView.measure(widthSpec, heightSpec);
+
+ hintParams = (LinearLayout.LayoutParams) hintView.getLayoutParams();
+ otherHeights = hintView.getMeasuredHeight() + hintParams.topMargin
+ + hintParams.bottomMargin;
+
+ dropDownView = hintContainer;
+ }
+
+ mPopup.setContentView(dropDownView);
+ } else {
+ dropDownView = (ViewGroup) mPopup.getContentView();
+ final View view = mPromptView;
+ if (view != null) {
+ LinearLayout.LayoutParams hintParams =
+ (LinearLayout.LayoutParams) view.getLayoutParams();
+ otherHeights = view.getMeasuredHeight() + hintParams.topMargin
+ + hintParams.bottomMargin;
+ }
+ }
+
+ // getMaxAvailableHeight() subtracts the padding, so we put it back
+ // to get the available height for the whole window
+ int padding = 0;
+ Drawable background = mPopup.getBackground();
+ if (background != null) {
+ background.getPadding(mTempRect);
+ padding = mTempRect.top + mTempRect.bottom;
+
+ // If we don't have an explicit vertical offset, determine one from the window
+ // background so that content will line up.
+ if (!mDropDownVerticalOffsetSet) {
+ mDropDownVerticalOffset = -mTempRect.top;
+ }
+ }
+
+ // Max height available on the screen for a popup.
+ boolean ignoreBottomDecorations =
+ mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
+ final int maxHeight = /*mPopup.*/getMaxAvailableHeight(
+ mDropDownAnchorView, mDropDownVerticalOffset, ignoreBottomDecorations);
+
+ if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
+ return maxHeight + padding;
+ }
+
+ final int listContent = /*mDropDownList.*/measureHeightOfChildren(MeasureSpec.UNSPECIFIED,
+ 0, -1/*ListView.NO_POSITION*/, maxHeight - otherHeights, -1);
+ // add padding only if the list has items in it, that way we don't show
+ // the popup if it is not needed
+ if (listContent > 0) otherHeights += padding;
+
+ return listContent + otherHeights;
+ }
+
+ private int getMaxAvailableHeight(View anchor, int yOffset, boolean ignoreBottomDecorations) {
+ final Rect displayFrame = new Rect();
+ anchor.getWindowVisibleDisplayFrame(displayFrame);
+
+ final int[] anchorPos = new int[2];
+ anchor.getLocationOnScreen(anchorPos);
+
+ int bottomEdge = displayFrame.bottom;
+ if (ignoreBottomDecorations) {
+ Resources res = anchor.getContext().getResources();
+ bottomEdge = res.getDisplayMetrics().heightPixels;
+ }
+ final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
+ final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
+
+ // anchorPos[1] is distance from anchor to top of screen
+ int returnedHeight = Math.max(distanceToBottom, distanceToTop);
+ if (mPopup.getBackground() != null) {
+ mPopup.getBackground().getPadding(mTempRect);
+ returnedHeight -= mTempRect.top + mTempRect.bottom;
+ }
+
+ return returnedHeight;
+ }
+
+ private int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition,
+ final int maxHeight, int disallowPartialChildPosition) {
+
+ final ListAdapter adapter = mAdapter;
+ if (adapter == null) {
+ return mDropDownList.getListPaddingTop() + mDropDownList.getListPaddingBottom();
+ }
+
+ // Include the padding of the list
+ int returnedHeight = mDropDownList.getListPaddingTop() + mDropDownList.getListPaddingBottom();
+ final int dividerHeight = ((mDropDownList.getDividerHeight() > 0) && mDropDownList.getDivider() != null) ? mDropDownList.getDividerHeight() : 0;
+ // The previous height value that was less than maxHeight and contained
+ // no partial children
+ int prevHeightWithoutPartialChild = 0;
+ int i;
+ View child;
+
+ // mItemCount - 1 since endPosition parameter is inclusive
+ endPosition = (endPosition == -1/*NO_POSITION*/) ? adapter.getCount() - 1 : endPosition;
+
+ for (i = startPosition; i <= endPosition; ++i) {
+ child = mAdapter.getView(i, null, mDropDownList);
+ if (mDropDownList.getCacheColorHint() != 0) {
+ child.setDrawingCacheBackgroundColor(mDropDownList.getCacheColorHint());
+ }
+
+ measureScrapChild(child, i, widthMeasureSpec);
+
+ if (i > 0) {
+ // Count the divider for all but one child
+ returnedHeight += dividerHeight;
+ }
+
+ returnedHeight += child.getMeasuredHeight();
+
+ if (returnedHeight >= maxHeight) {
+ // We went over, figure out which height to return. If returnedHeight > maxHeight,
+ // then the i'th position did not fit completely.
+ return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1)
+ && (i > disallowPartialChildPosition) // We've past the min pos
+ && (prevHeightWithoutPartialChild > 0) // We have a prev height
+ && (returnedHeight != maxHeight) // i'th child did not fit completely
+ ? prevHeightWithoutPartialChild
+ : maxHeight;
+ }
+
+ if ((disallowPartialChildPosition >= 0) && (i >= disallowPartialChildPosition)) {
+ prevHeightWithoutPartialChild = returnedHeight;
+ }
+ }
+
+ // At this point, we went through the range of children, and they each
+ // completely fit, so return the returnedHeight
+ return returnedHeight;
+ }
+ private void measureScrapChild(View child, int position, int widthMeasureSpec) {
+ ListView.LayoutParams p = (ListView.LayoutParams) child.getLayoutParams();
+ if (p == null) {
+ p = new ListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT, 0);
+ child.setLayoutParams(p);
+ }
+ //XXX p.viewType = mAdapter.getItemViewType(position);
+ //XXX p.forceAdd = true;
+
+ int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec,
+ mDropDownList.getPaddingLeft() + mDropDownList.getPaddingRight(), p.width);
+ int lpHeight = p.height;
+ int childHeightSpec;
+ if (lpHeight > 0) {
+ childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
+ } else {
+ childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ }
+ child.measure(childWidthSpec, childHeightSpec);
+ }
+
+ private static class DropDownListView extends ListView {
+ /*
+ * WARNING: This is a workaround for a touch mode issue.
+ *
+ * Touch mode is propagated lazily to windows. This causes problems in
+ * the following scenario:
+ * - Type something in the AutoCompleteTextView and get some results
+ * - Move down with the d-pad to select an item in the list
+ * - Move up with the d-pad until the selection disappears
+ * - Type more text in the AutoCompleteTextView *using the soft keyboard*
+ * and get new results; you are now in touch mode
+ * - The selection comes back on the first item in the list, even though
+ * the list is supposed to be in touch mode
+ *
+ * Using the soft keyboard triggers the touch mode change but that change
+ * is propagated to our window only after the first list layout, therefore
+ * after the list attempts to resurrect the selection.
+ *
+ * The trick to work around this issue is to pretend the list is in touch
+ * mode when we know that the selection should not appear, that is when
+ * we know the user moved the selection away from the list.
+ *
+ * This boolean is set to true whenever we explicitly hide the list's
+ * selection and reset to false whenever we know the user moved the
+ * selection back to the list.
+ *
+ * When this boolean is true, isInTouchMode() returns true, otherwise it
+ * returns super.isInTouchMode().
+ */
+ private boolean mListSelectionHidden;
+
+ private boolean mHijackFocus;
+
+ public DropDownListView(Context context, boolean hijackFocus) {
+ super(context, null, /*com.android.internal.*/R.attr.dropDownListViewStyle);
+ mHijackFocus = hijackFocus;
+ // TODO: Add an API to control this
+ setCacheColorHint(0); // Transparent, since the background drawable could be anything.
+ }
+
+ //XXX @Override
+ //View obtainView(int position, boolean[] isScrap) {
+ // View view = super.obtainView(position, isScrap);
+
+ // if (view instanceof TextView) {
+ // ((TextView) view).setHorizontallyScrolling(true);
+ // }
+
+ // return view;
+ //}
+
+ @Override
+ public boolean isInTouchMode() {
+ // WARNING: Please read the comment where mListSelectionHidden is declared
+ return (mHijackFocus && mListSelectionHidden) || super.isInTouchMode();
+ }
+
+ @Override
+ public boolean hasWindowFocus() {
+ return mHijackFocus || super.hasWindowFocus();
+ }
+
+ @Override
+ public boolean isFocused() {
+ return mHijackFocus || super.isFocused();
+ }
+
+ @Override
+ public boolean hasFocus() {
+ return mHijackFocus || super.hasFocus();
+ }
+ }
+
+ private class PopupDataSetObserver extends DataSetObserver {
+ @Override
+ public void onChanged() {
+ if (isShowing()) {
+ // Resize the popup to fit new content
+ show();
+ }
+ }
+
+ @Override
+ public void onInvalidated() {
+ dismiss();
+ }
+ }
+
+ private class ListSelectorHider implements Runnable {
+ public void run() {
+ clearListSelection();
+ }
+ }
+
+ private class ResizePopupRunnable implements Runnable {
+ public void run() {
+ if (mDropDownList != null && mDropDownList.getCount() > mDropDownList.getChildCount() &&
+ mDropDownList.getChildCount() <= mListItemExpandMaximum) {
+ mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+ show();
+ }
+ }
+ }
+
+ private class PopupTouchInterceptor implements OnTouchListener {
+ public boolean onTouch(View v, MotionEvent event) {
+ final int action = event.getAction();
+ final int x = (int) event.getX();
+ final int y = (int) event.getY();
+
+ if (action == MotionEvent.ACTION_DOWN &&
+ mPopup != null && mPopup.isShowing() &&
+ (x >= 0 && x < mPopup.getWidth() && y >= 0 && y < mPopup.getHeight())) {
+ mHandler.postDelayed(mResizePopupRunnable, EXPAND_LIST_TIMEOUT);
+ } else if (action == MotionEvent.ACTION_UP) {
+ mHandler.removeCallbacks(mResizePopupRunnable);
+ }
+ return false;
+ }
+ }
+
+ private class PopupScrollListener implements ListView.OnScrollListener {
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+
+ }
+
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ if (scrollState == SCROLL_STATE_TOUCH_SCROLL &&
+ !isInputMethodNotNeeded() && mPopup.getContentView() != null) {
+ mHandler.removeCallbacks(mResizePopupRunnable);
+ mResizePopupRunnable.run();
+ }
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsProgressBar.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsProgressBar.java
new file mode 100644
index 0000000000..1c02d4acad
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsProgressBar.java
@@ -0,0 +1,1193 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.internal.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ClipDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.RoundRectShape;
+import android.graphics.drawable.shapes.Shape;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.Transformation;
+import android.widget.RemoteViews.RemoteView;
+
+
+/**
+ *
+ * Visual indicator of progress in some operation. Displays a bar to the user
+ * representing how far the operation has progressed; the application can
+ * change the amount of progress (modifying the length of the bar) as it moves
+ * forward. There is also a secondary progress displayable on a progress bar
+ * which is useful for displaying intermediate progress, such as the buffer
+ * level during a streaming playback progress bar.
+ *
+ *
+ *
+ * A progress bar can also be made indeterminate. In indeterminate mode, the
+ * progress bar shows a cyclic animation without an indication of progress. This mode is used by
+ * applications when the length of the task is unknown. The indeterminate progress bar can be either
+ * a spinning wheel or a horizontal bar.
+ *
+ *
+ *
The following code example shows how a progress bar can be used from
+ * a worker thread to update the user interface to notify the user of progress:
+ *
To add a progress bar to a layout file, you can use the {@code <ProgressBar>} element.
+ * By default, the progress bar is a spinning wheel (an indeterminate indicator). To change to a
+ * horizontal progress bar, apply the {@link android.R.style#Widget_ProgressBar_Horizontal
+ * Widget.ProgressBar.Horizontal} style, like so:
If you will use the progress bar to show real progress, you must use the horizontal bar. You
+ * can then increment the progress with {@link #incrementProgressBy incrementProgressBy()} or
+ * {@link #setProgress setProgress()}. By default, the progress bar is full when it reaches 100. If
+ * necessary, you can adjust the maximum value (the value for a full bar) using the {@link
+ * android.R.styleable#ProgressBar_max android:max} attribute. Other attributes available are listed
+ * below.
+ *
+ *
Another common style to apply to the progress bar is {@link
+ * android.R.style#Widget_ProgressBar_Small Widget.ProgressBar.Small}, which shows a smaller
+ * version of the spinning wheel—useful when waiting for content to load.
+ * For example, you can insert this kind of progress bar into your default layout for
+ * a view that will be populated by some content fetched from the Internet—the spinning wheel
+ * appears immediately and when your application receives the content, it replaces the progress bar
+ * with the loaded content. For example:
The "inverse" styles provide an inverse color scheme for the spinner, which may be necessary
+ * if your application uses a light colored theme (a white background).
Indicate whether this progress bar is in indeterminate mode.
+ *
+ * @return true if the progress bar is in indeterminate mode
+ */
+ @ViewDebug.ExportedProperty(category = "progress")
+ public synchronized boolean isIndeterminate() {
+ return mIndeterminate;
+ }
+
+ /**
+ *
Change the indeterminate mode for this progress bar. In indeterminate
+ * mode, the progress is ignored and the progress bar shows an infinite
+ * animation instead.
+ *
+ * If this progress bar's style only supports indeterminate mode (such as the circular
+ * progress bars), then this will be ignored.
+ *
+ * @param indeterminate true to enable the indeterminate mode
+ */
+ public synchronized void setIndeterminate(boolean indeterminate) {
+ if ((!mOnlyIndeterminate || !mIndeterminate) && indeterminate != mIndeterminate) {
+ mIndeterminate = indeterminate;
+
+ if (indeterminate) {
+ // swap between indeterminate and regular backgrounds
+ mCurrentDrawable = mIndeterminateDrawable;
+ startAnimation();
+ } else {
+ mCurrentDrawable = mProgressDrawable;
+ stopAnimation();
+ }
+ }
+ }
+
+ /**
+ *
Get the drawable used to draw the progress bar in
+ * indeterminate mode.
+ *
+ * @attr ref android.R.styleable#Spinner_prompt
+ */
+public class IcsSpinner extends IcsAbsSpinner implements OnClickListener {
+ //private static final String TAG = "Spinner";
+
+ // Only measure this many items to get a decent max width.
+ private static final int MAX_ITEMS_MEASURED = 15;
+
+ /**
+ * Use a dialog window for selecting spinner options.
+ */
+ //public static final int MODE_DIALOG = 0;
+
+ /**
+ * Use a dropdown anchored to the Spinner for selecting spinner options.
+ */
+ public static final int MODE_DROPDOWN = 1;
+
+ /**
+ * Use the theme-supplied value to select the dropdown mode.
+ */
+ //private static final int MODE_THEME = -1;
+
+ private SpinnerPopup mPopup;
+ private DropDownAdapter mTempAdapter;
+ int mDropDownWidth;
+
+ private int mGravity;
+ private boolean mDisableChildrenWhenDisabled;
+
+ private Rect mTempRect = new Rect();
+
+ public IcsSpinner(Context context, AttributeSet attrs) {
+ this(context, attrs, R.attr.actionDropDownStyle);
+ }
+
+ /**
+ * Construct a new spinner with the given context's theme, the supplied attribute set,
+ * and default style.
+ *
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ * @param attrs The attributes of the XML tag that is inflating the view.
+ * @param defStyle The default style to apply to this view. If 0, no style
+ * will be applied (beyond what is included in the theme). This may
+ * either be an attribute resource, whose value will be retrieved
+ * from the current theme, or an explicit style resource.
+ */
+ public IcsSpinner(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.SherlockSpinner, defStyle, 0);
+
+
+ DropdownPopup popup = new DropdownPopup(context, attrs, defStyle);
+
+ mDropDownWidth = a.getLayoutDimension(
+ R.styleable.SherlockSpinner_android_dropDownWidth,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ popup.setBackgroundDrawable(a.getDrawable(
+ R.styleable.SherlockSpinner_android_popupBackground));
+ final int verticalOffset = a.getDimensionPixelOffset(
+ R.styleable.SherlockSpinner_android_dropDownVerticalOffset, 0);
+ if (verticalOffset != 0) {
+ popup.setVerticalOffset(verticalOffset);
+ }
+
+ final int horizontalOffset = a.getDimensionPixelOffset(
+ R.styleable.SherlockSpinner_android_dropDownHorizontalOffset, 0);
+ if (horizontalOffset != 0) {
+ popup.setHorizontalOffset(horizontalOffset);
+ }
+
+ mPopup = popup;
+
+ mGravity = a.getInt(R.styleable.SherlockSpinner_android_gravity, Gravity.CENTER);
+
+ mPopup.setPromptText(a.getString(R.styleable.SherlockSpinner_android_prompt));
+
+ mDisableChildrenWhenDisabled = true;
+
+ a.recycle();
+
+ // Base constructor can call setAdapter before we initialize mPopup.
+ // Finish setting things up if this happened.
+ if (mTempAdapter != null) {
+ mPopup.setAdapter(mTempAdapter);
+ mTempAdapter = null;
+ }
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ if (mDisableChildrenWhenDisabled) {
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ getChildAt(i).setEnabled(enabled);
+ }
+ }
+ }
+
+ /**
+ * Describes how the selected item view is positioned. Currently only the horizontal component
+ * is used. The default is determined by the current theme.
+ *
+ * @param gravity See {@link android.view.Gravity}
+ *
+ * @attr ref android.R.styleable#Spinner_gravity
+ */
+ public void setGravity(int gravity) {
+ if (mGravity != gravity) {
+ if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) {
+ gravity |= Gravity.LEFT;
+ }
+ mGravity = gravity;
+ requestLayout();
+ }
+ }
+
+ @Override
+ public void setAdapter(SpinnerAdapter adapter) {
+ super.setAdapter(adapter);
+
+ if (mPopup != null) {
+ mPopup.setAdapter(new DropDownAdapter(adapter));
+ } else {
+ mTempAdapter = new DropDownAdapter(adapter);
+ }
+ }
+
+ @Override
+ public int getBaseline() {
+ View child = null;
+
+ if (getChildCount() > 0) {
+ child = getChildAt(0);
+ } else if (mAdapter != null && mAdapter.getCount() > 0) {
+ child = makeAndAddView(0);
+ mRecycler.put(0, child);
+ removeAllViewsInLayout();
+ }
+
+ if (child != null) {
+ final int childBaseline = child.getBaseline();
+ return childBaseline >= 0 ? child.getTop() + childBaseline : -1;
+ } else {
+ return -1;
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ if (mPopup != null && mPopup.isShowing()) {
+ mPopup.dismiss();
+ }
+ }
+
+ /**
+ *
A spinner does not support item click events. Calling this method
+ * will raise an exception.
+ *
+ * @param l this listener will be ignored
+ */
+ @Override
+ public void setOnItemClickListener(OnItemClickListener l) {
+ throw new RuntimeException("setOnItemClickListener cannot be used with a spinner.");
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mPopup != null && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) {
+ final int measuredWidth = getMeasuredWidth();
+ setMeasuredDimension(Math.min(Math.max(measuredWidth,
+ measureContentWidth(getAdapter(), getBackground())),
+ MeasureSpec.getSize(widthMeasureSpec)),
+ getMeasuredHeight());
+ }
+ }
+
+ /**
+ * @see android.view.View#onLayout(boolean,int,int,int,int)
+ *
+ * Creates and positions all views
+ *
+ */
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ mInLayout = true;
+ layout(0, false);
+ mInLayout = false;
+ }
+
+ /**
+ * Creates and positions all views for this Spinner.
+ *
+ * @param delta Change in the selected position. +1 moves selection is moving to the right,
+ * so views are scrolling to the left. -1 means selection is moving to the left.
+ */
+ @Override
+ void layout(int delta, boolean animate) {
+ int childrenLeft = mSpinnerPadding.left;
+ int childrenWidth = getRight() - getLeft() - mSpinnerPadding.left - mSpinnerPadding.right;
+
+ if (mDataChanged) {
+ handleDataChanged();
+ }
+
+ // Handle the empty set by removing all views
+ if (mItemCount == 0) {
+ resetList();
+ return;
+ }
+
+ if (mNextSelectedPosition >= 0) {
+ setSelectedPositionInt(mNextSelectedPosition);
+ }
+
+ recycleAllViews();
+
+ // Clear out old views
+ removeAllViewsInLayout();
+
+ // Make selected view and position it
+ mFirstPosition = mSelectedPosition;
+ View sel = makeAndAddView(mSelectedPosition);
+ int width = sel.getMeasuredWidth();
+ int selectedOffset = childrenLeft;
+ switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.CENTER_HORIZONTAL:
+ selectedOffset = childrenLeft + (childrenWidth / 2) - (width / 2);
+ break;
+ case Gravity.RIGHT:
+ selectedOffset = childrenLeft + childrenWidth - width;
+ break;
+ }
+ sel.offsetLeftAndRight(selectedOffset);
+
+ // Flush any cached views that did not get reused above
+ mRecycler.clear();
+
+ invalidate();
+
+ checkSelectionChanged();
+
+ mDataChanged = false;
+ mNeedSync = false;
+ setNextSelectedPositionInt(mSelectedPosition);
+ }
+
+ /**
+ * Obtain a view, either by pulling an existing view from the recycler or
+ * by getting a new one from the adapter. If we are animating, make sure
+ * there is enough information in the view's layout parameters to animate
+ * from the old to new positions.
+ *
+ * @param position Position in the spinner for the view to obtain
+ * @return A view that has been added to the spinner
+ */
+ private View makeAndAddView(int position) {
+
+ View child;
+
+ if (!mDataChanged) {
+ child = mRecycler.get(position);
+ if (child != null) {
+ // Position the view
+ setUpChild(child);
+
+ return child;
+ }
+ }
+
+ // Nothing found in the recycler -- ask the adapter for a view
+ child = mAdapter.getView(position, null, this);
+
+ // Position the view
+ setUpChild(child);
+
+ return child;
+ }
+
+ /**
+ * Helper for makeAndAddView to set the position of a view
+ * and fill out its layout paramters.
+ *
+ * @param child The view to position
+ */
+ private void setUpChild(View child) {
+
+ // Respect layout params that are already in the view. Otherwise
+ // make some up...
+ ViewGroup.LayoutParams lp = child.getLayoutParams();
+ if (lp == null) {
+ lp = generateDefaultLayoutParams();
+ }
+
+ addViewInLayout(child, 0, lp);
+
+ child.setSelected(hasFocus());
+ if (mDisableChildrenWhenDisabled) {
+ child.setEnabled(isEnabled());
+ }
+
+ // Get measure specs
+ int childHeightSpec = ViewGroup.getChildMeasureSpec(mHeightMeasureSpec,
+ mSpinnerPadding.top + mSpinnerPadding.bottom, lp.height);
+ int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,
+ mSpinnerPadding.left + mSpinnerPadding.right, lp.width);
+
+ // Measure child
+ child.measure(childWidthSpec, childHeightSpec);
+
+ int childLeft;
+ int childRight;
+
+ // Position vertically based on gravity setting
+ int childTop = mSpinnerPadding.top
+ + ((getMeasuredHeight() - mSpinnerPadding.bottom -
+ mSpinnerPadding.top - child.getMeasuredHeight()) / 2);
+ int childBottom = childTop + child.getMeasuredHeight();
+
+ int width = child.getMeasuredWidth();
+ childLeft = 0;
+ childRight = childLeft + width;
+
+ child.layout(childLeft, childTop, childRight, childBottom);
+ }
+
+ @Override
+ public boolean performClick() {
+ boolean handled = super.performClick();
+
+ if (!handled) {
+ handled = true;
+
+ if (!mPopup.isShowing()) {
+ mPopup.show();
+ }
+ }
+
+ return handled;
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ setSelection(which);
+ dialog.dismiss();
+ }
+
+ /**
+ * Sets the prompt to display when the dialog is shown.
+ * @param prompt the prompt to set
+ */
+ public void setPrompt(CharSequence prompt) {
+ mPopup.setPromptText(prompt);
+ }
+
+ /**
+ * Sets the prompt to display when the dialog is shown.
+ * @param promptId the resource ID of the prompt to display when the dialog is shown
+ */
+ public void setPromptId(int promptId) {
+ setPrompt(getContext().getText(promptId));
+ }
+
+ /**
+ * @return The prompt to display when the dialog is shown
+ */
+ public CharSequence getPrompt() {
+ return mPopup.getHintText();
+ }
+
+ int measureContentWidth(SpinnerAdapter adapter, Drawable background) {
+ if (adapter == null) {
+ return 0;
+ }
+
+ int width = 0;
+ View itemView = null;
+ int itemType = 0;
+ final int widthMeasureSpec =
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int heightMeasureSpec =
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+
+ // Make sure the number of items we'll measure is capped. If it's a huge data set
+ // with wildly varying sizes, oh well.
+ int start = Math.max(0, getSelectedItemPosition());
+ final int end = Math.min(adapter.getCount(), start + MAX_ITEMS_MEASURED);
+ final int count = end - start;
+ start = Math.max(0, start - (MAX_ITEMS_MEASURED - count));
+ for (int i = start; i < end; i++) {
+ final int positionType = adapter.getItemViewType(i);
+ if (positionType != itemType) {
+ itemType = positionType;
+ itemView = null;
+ }
+ itemView = adapter.getView(i, itemView, this);
+ if (itemView.getLayoutParams() == null) {
+ itemView.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ }
+ itemView.measure(widthMeasureSpec, heightMeasureSpec);
+ width = Math.max(width, itemView.getMeasuredWidth());
+ }
+
+ // Add background padding to measured width
+ if (background != null) {
+ background.getPadding(mTempRect);
+ width += mTempRect.left + mTempRect.right;
+ }
+
+ return width;
+ }
+
+ /**
+ *
Wrapper class for an Adapter. Transforms the embedded Adapter instance
+ * into a ListAdapter.
Creates a new ListAdapter wrapper for the specified adapter.
+ *
+ * @param adapter the Adapter to transform into a ListAdapter
+ */
+ public DropDownAdapter(SpinnerAdapter adapter) {
+ this.mAdapter = adapter;
+ if (adapter instanceof ListAdapter) {
+ this.mListAdapter = (ListAdapter) adapter;
+ }
+ }
+
+ public int getCount() {
+ return mAdapter == null ? 0 : mAdapter.getCount();
+ }
+
+ public Object getItem(int position) {
+ return mAdapter == null ? null : mAdapter.getItem(position);
+ }
+
+ public long getItemId(int position) {
+ return mAdapter == null ? -1 : mAdapter.getItemId(position);
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return getDropDownView(position, convertView, parent);
+ }
+
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ return mAdapter == null ? null :
+ mAdapter.getDropDownView(position, convertView, parent);
+ }
+
+ public boolean hasStableIds() {
+ return mAdapter != null && mAdapter.hasStableIds();
+ }
+
+ public void registerDataSetObserver(DataSetObserver observer) {
+ if (mAdapter != null) {
+ mAdapter.registerDataSetObserver(observer);
+ }
+ }
+
+ public void unregisterDataSetObserver(DataSetObserver observer) {
+ if (mAdapter != null) {
+ mAdapter.unregisterDataSetObserver(observer);
+ }
+ }
+
+ /**
+ * If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call.
+ * Otherwise, return true.
+ */
+ public boolean areAllItemsEnabled() {
+ final ListAdapter adapter = mListAdapter;
+ if (adapter != null) {
+ return adapter.areAllItemsEnabled();
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call.
+ * Otherwise, return true.
+ */
+ public boolean isEnabled(int position) {
+ final ListAdapter adapter = mListAdapter;
+ if (adapter != null) {
+ return adapter.isEnabled(position);
+ } else {
+ return true;
+ }
+ }
+
+ public int getItemViewType(int position) {
+ return 0;
+ }
+
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ public boolean isEmpty() {
+ return getCount() == 0;
+ }
+ }
+
+ /**
+ * Implements some sort of popup selection interface for selecting a spinner option.
+ * Allows for different spinner modes.
+ */
+ private interface SpinnerPopup {
+ public void setAdapter(ListAdapter adapter);
+
+ /**
+ * Show the popup
+ */
+ public void show();
+
+ /**
+ * Dismiss the popup
+ */
+ public void dismiss();
+
+ /**
+ * @return true if the popup is showing, false otherwise.
+ */
+ public boolean isShowing();
+
+ /**
+ * Set hint text to be displayed to the user. This should provide
+ * a description of the choice being made.
+ * @param hintText Hint text to set.
+ */
+ public void setPromptText(CharSequence hintText);
+ public CharSequence getHintText();
+ }
+
+ /*
+ private class DialogPopup implements SpinnerPopup, DialogInterface.OnClickListener {
+ private AlertDialog mPopup;
+ private ListAdapter mListAdapter;
+ private CharSequence mPrompt;
+
+ public void dismiss() {
+ mPopup.dismiss();
+ mPopup = null;
+ }
+
+ public boolean isShowing() {
+ return mPopup != null ? mPopup.isShowing() : false;
+ }
+
+ public void setAdapter(ListAdapter adapter) {
+ mListAdapter = adapter;
+ }
+
+ public void setPromptText(CharSequence hintText) {
+ mPrompt = hintText;
+ }
+
+ public CharSequence getHintText() {
+ return mPrompt;
+ }
+
+ public void show() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ if (mPrompt != null) {
+ builder.setTitle(mPrompt);
+ }
+ mPopup = builder.setSingleChoiceItems(mListAdapter,
+ getSelectedItemPosition(), this).show();
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ setSelection(which);
+ dismiss();
+ }
+ }
+ */
+
+ private class DropdownPopup extends IcsListPopupWindow implements SpinnerPopup {
+ private CharSequence mHintText;
+ private ListAdapter mAdapter;
+
+ public DropdownPopup(Context context, AttributeSet attrs, int defStyleRes) {
+ super(context, attrs, 0, defStyleRes);
+
+ setAnchorView(IcsSpinner.this);
+ setModal(true);
+ setPromptPosition(POSITION_PROMPT_ABOVE);
+ setOnItemClickListener(new OnItemClickListener() {
+ @SuppressWarnings("rawtypes")
+ public void onItemClick(AdapterView parent, View v, int position, long id) {
+ IcsSpinner.this.setSelection(position);
+ dismiss();
+ }
+ });
+ }
+
+ @Override
+ public void setAdapter(ListAdapter adapter) {
+ super.setAdapter(adapter);
+ mAdapter = adapter;
+ }
+
+ public CharSequence getHintText() {
+ return mHintText;
+ }
+
+ public void setPromptText(CharSequence hintText) {
+ // Hint text is ignored for dropdowns, but maintain it here.
+ mHintText = hintText;
+ }
+
+ @Override
+ public void show() {
+ final int spinnerPaddingLeft = IcsSpinner.this.getPaddingLeft();
+ if (mDropDownWidth == WRAP_CONTENT) {
+ final int spinnerWidth = IcsSpinner.this.getWidth();
+ final int spinnerPaddingRight = IcsSpinner.this.getPaddingRight();
+ setContentWidth(Math.max(
+ measureContentWidth((SpinnerAdapter) mAdapter, getBackground()),
+ spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight));
+ } else if (mDropDownWidth == MATCH_PARENT) {
+ final int spinnerWidth = IcsSpinner.this.getWidth();
+ final int spinnerPaddingRight = IcsSpinner.this.getPaddingRight();
+ setContentWidth(spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight);
+ } else {
+ setContentWidth(mDropDownWidth);
+ }
+ final Drawable background = getBackground();
+ int bgOffset = 0;
+ if (background != null) {
+ background.getPadding(mTempRect);
+ bgOffset = -mTempRect.left;
+ }
+ setHorizontalOffset(bgOffset + spinnerPaddingLeft);
+ setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+ super.show();
+ getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+ setSelection(IcsSpinner.this.getSelectedItemPosition());
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsView.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsView.java
new file mode 100644
index 0000000000..a7185d082c
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/IcsView.java
@@ -0,0 +1,21 @@
+package com.actionbarsherlock.internal.widget;
+
+import android.view.View;
+
+final class IcsView {
+ //No instances
+ private IcsView() {}
+
+ /**
+ * Return only the state bits of {@link #getMeasuredWidthAndState()}
+ * and {@link #getMeasuredHeightAndState()}, combined into one integer.
+ * The width component is in the regular bits {@link #MEASURED_STATE_MASK}
+ * and the height component is at the shifted bits
+ * {@link #MEASURED_HEIGHT_STATE_SHIFT}>>{@link #MEASURED_STATE_MASK}.
+ */
+ public static int getMeasuredStateInt(View child) {
+ return (child.getMeasuredWidth()&View.MEASURED_STATE_MASK)
+ | ((child.getMeasuredHeight()>>View.MEASURED_HEIGHT_STATE_SHIFT)
+ & (View.MEASURED_STATE_MASK>>View.MEASURED_HEIGHT_STATE_SHIFT));
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java
new file mode 100644
index 0000000000..48fb5d8b4f
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java
@@ -0,0 +1,546 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.actionbarsherlock.internal.widget;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils.TruncateAt;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.internal.nineoldandroids.animation.Animator;
+import com.actionbarsherlock.internal.nineoldandroids.animation.ObjectAnimator;
+import com.actionbarsherlock.internal.nineoldandroids.widget.NineHorizontalScrollView;
+
+/**
+ * This widget implements the dynamic action bar tab behavior that can change
+ * across different configurations or circumstances.
+ */
+public class ScrollingTabContainerView extends NineHorizontalScrollView
+ implements IcsAdapterView.OnItemSelectedListener {
+ //UNUSED private static final String TAG = "ScrollingTabContainerView";
+ Runnable mTabSelector;
+ private TabClickListener mTabClickListener;
+
+ private IcsLinearLayout mTabLayout;
+ private IcsSpinner mTabSpinner;
+ private boolean mAllowCollapse;
+
+ private LayoutInflater mInflater;
+
+ int mMaxTabWidth;
+ private int mContentHeight;
+ private int mSelectedTabIndex;
+
+ protected Animator mVisibilityAnim;
+ protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
+
+ private static final /*Time*/Interpolator sAlphaInterpolator = new DecelerateInterpolator();
+
+ private static final int FADE_DURATION = 200;
+
+ public ScrollingTabContainerView(Context context) {
+ super(context);
+ setHorizontalScrollBarEnabled(false);
+
+ TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.SherlockActionBar,
+ R.attr.actionBarStyle, 0);
+ setContentHeight(a.getLayoutDimension(R.styleable.SherlockActionBar_height, 0));
+ a.recycle();
+
+ mInflater = LayoutInflater.from(context);
+
+ mTabLayout = createTabLayout();
+ addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ }
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY;
+ setFillViewport(lockedExpanded);
+
+ final int childCount = mTabLayout.getChildCount();
+ if (childCount > 1 &&
+ (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) {
+ if (childCount > 2) {
+ mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) * 0.4f);
+ } else {
+ mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2;
+ }
+ } else {
+ mMaxTabWidth = -1;
+ }
+
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.EXACTLY);
+
+ final boolean canCollapse = !lockedExpanded && mAllowCollapse;
+
+ if (canCollapse) {
+ // See if we should expand
+ mTabLayout.measure(MeasureSpec.UNSPECIFIED, heightMeasureSpec);
+ if (mTabLayout.getMeasuredWidth() > MeasureSpec.getSize(widthMeasureSpec)) {
+ performCollapse();
+ } else {
+ performExpand();
+ }
+ } else {
+ performExpand();
+ }
+
+ final int oldWidth = getMeasuredWidth();
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ final int newWidth = getMeasuredWidth();
+
+ if (lockedExpanded && oldWidth != newWidth) {
+ // Recenter the tab display if we're at a new (scrollable) size.
+ setTabSelected(mSelectedTabIndex);
+ }
+ }
+
+ /**
+ * Indicates whether this view is collapsed into a dropdown menu instead
+ * of traditional tabs.
+ * @return true if showing as a spinner
+ */
+ private boolean isCollapsed() {
+ return mTabSpinner != null && mTabSpinner.getParent() == this;
+ }
+
+ public void setAllowCollapse(boolean allowCollapse) {
+ mAllowCollapse = allowCollapse;
+ }
+
+ private void performCollapse() {
+ if (isCollapsed()) return;
+
+ if (mTabSpinner == null) {
+ mTabSpinner = createSpinner();
+ }
+ removeView(mTabLayout);
+ addView(mTabSpinner, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ if (mTabSpinner.getAdapter() == null) {
+ mTabSpinner.setAdapter(new TabAdapter());
+ }
+ if (mTabSelector != null) {
+ removeCallbacks(mTabSelector);
+ mTabSelector = null;
+ }
+ mTabSpinner.setSelection(mSelectedTabIndex);
+ }
+
+ private boolean performExpand() {
+ if (!isCollapsed()) return false;
+
+ removeView(mTabSpinner);
+ addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ setTabSelected(mTabSpinner.getSelectedItemPosition());
+ return false;
+ }
+
+ public void setTabSelected(int position) {
+ mSelectedTabIndex = position;
+ final int tabCount = mTabLayout.getChildCount();
+ for (int i = 0; i < tabCount; i++) {
+ final View child = mTabLayout.getChildAt(i);
+ final boolean isSelected = i == position;
+ child.setSelected(isSelected);
+ if (isSelected) {
+ animateToTab(position);
+ }
+ }
+ }
+
+ public void setContentHeight(int contentHeight) {
+ mContentHeight = contentHeight;
+ requestLayout();
+ }
+
+ private IcsLinearLayout createTabLayout() {
+ final IcsLinearLayout tabLayout = (IcsLinearLayout) LayoutInflater.from(getContext())
+ .inflate(R.layout.abs__action_bar_tab_bar_view, null);
+ tabLayout.setMeasureWithLargestChildEnabled(true);
+ tabLayout.setLayoutParams(new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT));
+ return tabLayout;
+ }
+
+ private IcsSpinner createSpinner() {
+ final IcsSpinner spinner = new IcsSpinner(getContext(), null,
+ R.attr.actionDropDownStyle);
+ spinner.setLayoutParams(new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT));
+ spinner.setOnItemSelectedListener(this);
+ return spinner;
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ // Action bar can change size on configuration changes.
+ // Reread the desired height from the theme-specified style.
+ TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.SherlockActionBar,
+ R.attr.actionBarStyle, 0);
+ setContentHeight(a.getLayoutDimension(R.styleable.SherlockActionBar_height, 0));
+ a.recycle();
+ }
+
+ public void animateToVisibility(int visibility) {
+ if (mVisibilityAnim != null) {
+ mVisibilityAnim.cancel();
+ }
+ if (visibility == VISIBLE) {
+ if (getVisibility() != VISIBLE) {
+ setAlpha(0);
+ }
+ ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1);
+ anim.setDuration(FADE_DURATION);
+ anim.setInterpolator(sAlphaInterpolator);
+
+ anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
+ anim.start();
+ } else {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0);
+ anim.setDuration(FADE_DURATION);
+ anim.setInterpolator(sAlphaInterpolator);
+
+ anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
+ anim.start();
+ }
+ }
+
+ public void animateToTab(final int position) {
+ final View tabView = mTabLayout.getChildAt(position);
+ if (mTabSelector != null) {
+ removeCallbacks(mTabSelector);
+ }
+ mTabSelector = new Runnable() {
+ public void run() {
+ final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2;
+ smoothScrollTo(scrollPos, 0);
+ mTabSelector = null;
+ }
+ };
+ post(mTabSelector);
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (mTabSelector != null) {
+ // Re-post the selector we saved
+ post(mTabSelector);
+ }
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mTabSelector != null) {
+ removeCallbacks(mTabSelector);
+ }
+ }
+
+ private TabView createTabView(ActionBar.Tab tab, boolean forAdapter) {
+ //Workaround for not being able to pass a defStyle on pre-3.0
+ final TabView tabView = (TabView)mInflater.inflate(R.layout.abs__action_bar_tab, null);
+ tabView.init(this, tab, forAdapter);
+
+ if (forAdapter) {
+ tabView.setBackgroundDrawable(null);
+ tabView.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT,
+ mContentHeight));
+ } else {
+ tabView.setFocusable(true);
+
+ if (mTabClickListener == null) {
+ mTabClickListener = new TabClickListener();
+ }
+ tabView.setOnClickListener(mTabClickListener);
+ }
+ return tabView;
+ }
+
+ public void addTab(ActionBar.Tab tab, boolean setSelected) {
+ TabView tabView = createTabView(tab, false);
+ mTabLayout.addView(tabView, new IcsLinearLayout.LayoutParams(0,
+ LayoutParams.MATCH_PARENT, 1));
+ if (mTabSpinner != null) {
+ ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
+ }
+ if (setSelected) {
+ tabView.setSelected(true);
+ }
+ if (mAllowCollapse) {
+ requestLayout();
+ }
+ }
+
+ public void addTab(ActionBar.Tab tab, int position, boolean setSelected) {
+ final TabView tabView = createTabView(tab, false);
+ mTabLayout.addView(tabView, position, new IcsLinearLayout.LayoutParams(
+ 0, LayoutParams.MATCH_PARENT, 1));
+ if (mTabSpinner != null) {
+ ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
+ }
+ if (setSelected) {
+ tabView.setSelected(true);
+ }
+ if (mAllowCollapse) {
+ requestLayout();
+ }
+ }
+
+ public void updateTab(int position) {
+ ((TabView) mTabLayout.getChildAt(position)).update();
+ if (mTabSpinner != null) {
+ ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
+ }
+ if (mAllowCollapse) {
+ requestLayout();
+ }
+ }
+
+ public void removeTabAt(int position) {
+ mTabLayout.removeViewAt(position);
+ if (mTabSpinner != null) {
+ ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
+ }
+ if (mAllowCollapse) {
+ requestLayout();
+ }
+ }
+
+ public void removeAllTabs() {
+ mTabLayout.removeAllViews();
+ if (mTabSpinner != null) {
+ ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
+ }
+ if (mAllowCollapse) {
+ requestLayout();
+ }
+ }
+
+ @Override
+ public void onItemSelected(IcsAdapterView> parent, View view, int position, long id) {
+ TabView tabView = (TabView) view;
+ tabView.getTab().select();
+ }
+
+ @Override
+ public void onNothingSelected(IcsAdapterView> parent) {
+ }
+
+ public static class TabView extends LinearLayout {
+ private ScrollingTabContainerView mParent;
+ private ActionBar.Tab mTab;
+ private CapitalizingTextView mTextView;
+ private ImageView mIconView;
+ private View mCustomView;
+
+ public TabView(Context context, AttributeSet attrs) {
+ //TODO super(context, null, R.attr.actionBarTabStyle);
+ super(context, attrs);
+ }
+
+ public void init(ScrollingTabContainerView parent, ActionBar.Tab tab, boolean forList) {
+ mParent = parent;
+ mTab = tab;
+
+ if (forList) {
+ setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
+ }
+
+ update();
+ }
+
+ public void bindTab(ActionBar.Tab tab) {
+ mTab = tab;
+ update();
+ }
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ // Re-measure if we went beyond our maximum size.
+ if (mParent.mMaxTabWidth > 0 && getMeasuredWidth() > mParent.mMaxTabWidth) {
+ super.onMeasure(MeasureSpec.makeMeasureSpec(mParent.mMaxTabWidth, MeasureSpec.EXACTLY),
+ heightMeasureSpec);
+ }
+ }
+
+ public void update() {
+ final ActionBar.Tab tab = mTab;
+ final View custom = tab.getCustomView();
+ if (custom != null) {
+ final ViewParent customParent = custom.getParent();
+ if (customParent != this) {
+ if (customParent != null) ((ViewGroup) customParent).removeView(custom);
+ addView(custom);
+ }
+ mCustomView = custom;
+ if (mTextView != null) mTextView.setVisibility(GONE);
+ if (mIconView != null) {
+ mIconView.setVisibility(GONE);
+ mIconView.setImageDrawable(null);
+ }
+ } else {
+ if (mCustomView != null) {
+ removeView(mCustomView);
+ mCustomView = null;
+ }
+
+ final Drawable icon = tab.getIcon();
+ final CharSequence text = tab.getText();
+
+ if (icon != null) {
+ if (mIconView == null) {
+ ImageView iconView = new ImageView(getContext());
+ LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+ lp.gravity = Gravity.CENTER_VERTICAL;
+ iconView.setLayoutParams(lp);
+ addView(iconView, 0);
+ mIconView = iconView;
+ }
+ mIconView.setImageDrawable(icon);
+ mIconView.setVisibility(VISIBLE);
+ } else if (mIconView != null) {
+ mIconView.setVisibility(GONE);
+ mIconView.setImageDrawable(null);
+ }
+
+ if (text != null) {
+ if (mTextView == null) {
+ CapitalizingTextView textView = new CapitalizingTextView(getContext(), null,
+ R.attr.actionBarTabTextStyle);
+ textView.setEllipsize(TruncateAt.END);
+ LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+ lp.gravity = Gravity.CENTER_VERTICAL;
+ textView.setLayoutParams(lp);
+ addView(textView);
+ mTextView = textView;
+ }
+ mTextView.setTextCompat(text);
+ mTextView.setVisibility(VISIBLE);
+ } else if (mTextView != null) {
+ mTextView.setVisibility(GONE);
+ mTextView.setText(null);
+ }
+
+ if (mIconView != null) {
+ mIconView.setContentDescription(tab.getContentDescription());
+ }
+ }
+ }
+
+ public ActionBar.Tab getTab() {
+ return mTab;
+ }
+ }
+
+ private class TabAdapter extends BaseAdapter {
+ @Override
+ public int getCount() {
+ return mTabLayout.getChildCount();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return ((TabView) mTabLayout.getChildAt(position)).getTab();
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = createTabView((ActionBar.Tab) getItem(position), true);
+ } else {
+ ((TabView) convertView).bindTab((ActionBar.Tab) getItem(position));
+ }
+ return convertView;
+ }
+ }
+
+ private class TabClickListener implements OnClickListener {
+ public void onClick(View view) {
+ TabView tabView = (TabView) view;
+ tabView.getTab().select();
+ final int tabCount = mTabLayout.getChildCount();
+ for (int i = 0; i < tabCount; i++) {
+ final View child = mTabLayout.getChildAt(i);
+ child.setSelected(child == view);
+ }
+ }
+ }
+
+ protected class VisibilityAnimListener implements Animator.AnimatorListener {
+ private boolean mCanceled = false;
+ private int mFinalVisibility;
+
+ public VisibilityAnimListener withFinalVisibility(int visibility) {
+ mFinalVisibility = visibility;
+ return this;
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ setVisibility(VISIBLE);
+ mVisibilityAnim = animation;
+ mCanceled = false;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCanceled) return;
+
+ mVisibilityAnim = null;
+ setVisibility(mFinalVisibility);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCanceled = true;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/ActionMode.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/ActionMode.java
new file mode 100644
index 0000000000..81b4cd4d20
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/ActionMode.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.view;
+
+import android.view.View;
+
+
+/**
+ * Represents a contextual mode of the user interface. Action modes can be used for
+ * modal interactions with content and replace parts of the normal UI until finished.
+ * Examples of good action modes include selection modes, search, content editing, etc.
+ */
+public abstract class ActionMode {
+ private Object mTag;
+
+ /**
+ * Set a tag object associated with this ActionMode.
+ *
+ *
Like the tag available to views, this allows applications to associate arbitrary
+ * data with an ActionMode for later reference.
+ *
+ * @param tag Tag to associate with this ActionMode
+ *
+ * @see #getTag()
+ */
+ public void setTag(Object tag) {
+ mTag = tag;
+ }
+
+ /**
+ * Retrieve the tag object associated with this ActionMode.
+ *
+ *
Like the tag available to views, this allows applications to associate arbitrary
+ * data with an ActionMode for later reference.
+ *
+ * @return Tag associated with this ActionMode
+ *
+ * @see #setTag(Object)
+ */
+ public Object getTag() {
+ return mTag;
+ }
+
+ /**
+ * Set the title of the action mode. This method will have no visible effect if
+ * a custom view has been set.
+ *
+ * @param title Title string to set
+ *
+ * @see #setTitle(int)
+ * @see #setCustomView(View)
+ */
+ public abstract void setTitle(CharSequence title);
+
+ /**
+ * Set the title of the action mode. This method will have no visible effect if
+ * a custom view has been set.
+ *
+ * @param resId Resource ID of a string to set as the title
+ *
+ * @see #setTitle(CharSequence)
+ * @see #setCustomView(View)
+ */
+ public abstract void setTitle(int resId);
+
+ /**
+ * Set the subtitle of the action mode. This method will have no visible effect if
+ * a custom view has been set.
+ *
+ * @param subtitle Subtitle string to set
+ *
+ * @see #setSubtitle(int)
+ * @see #setCustomView(View)
+ */
+ public abstract void setSubtitle(CharSequence subtitle);
+
+ /**
+ * Set the subtitle of the action mode. This method will have no visible effect if
+ * a custom view has been set.
+ *
+ * @param resId Resource ID of a string to set as the subtitle
+ *
+ * @see #setSubtitle(CharSequence)
+ * @see #setCustomView(View)
+ */
+ public abstract void setSubtitle(int resId);
+
+ /**
+ * Set a custom view for this action mode. The custom view will take the place of
+ * the title and subtitle. Useful for things like search boxes.
+ *
+ * @param view Custom view to use in place of the title/subtitle.
+ *
+ * @see #setTitle(CharSequence)
+ * @see #setSubtitle(CharSequence)
+ */
+ public abstract void setCustomView(View view);
+
+ /**
+ * Invalidate the action mode and refresh menu content. The mode's
+ * {@link ActionMode.Callback} will have its
+ * {@link Callback#onPrepareActionMode(ActionMode, Menu)} method called.
+ * If it returns true the menu will be scanned for updated content and any relevant changes
+ * will be reflected to the user.
+ */
+ public abstract void invalidate();
+
+ /**
+ * Finish and close this action mode. The action mode's {@link ActionMode.Callback} will
+ * have its {@link Callback#onDestroyActionMode(ActionMode)} method called.
+ */
+ public abstract void finish();
+
+ /**
+ * Returns the menu of actions that this action mode presents.
+ * @return The action mode's menu.
+ */
+ public abstract Menu getMenu();
+
+ /**
+ * Returns the current title of this action mode.
+ * @return Title text
+ */
+ public abstract CharSequence getTitle();
+
+ /**
+ * Returns the current subtitle of this action mode.
+ * @return Subtitle text
+ */
+ public abstract CharSequence getSubtitle();
+
+ /**
+ * Returns the current custom view for this action mode.
+ * @return The current custom view
+ */
+ public abstract View getCustomView();
+
+ /**
+ * Returns a {@link MenuInflater} with the ActionMode's context.
+ */
+ public abstract MenuInflater getMenuInflater();
+
+ /**
+ * Returns whether the UI presenting this action mode can take focus or not.
+ * This is used by internal components within the framework that would otherwise
+ * present an action mode UI that requires focus, such as an EditText as a custom view.
+ *
+ * @return true if the UI used to show this action mode can take focus
+ * @hide Internal use only
+ */
+ public boolean isUiFocusable() {
+ return true;
+ }
+
+ /**
+ * Callback interface for action modes. Supplied to
+ * {@link View#startActionMode(Callback)}, a Callback
+ * configures and handles events raised by a user's interaction with an action mode.
+ *
+ *
An action mode's lifecycle is as follows:
+ *
+ *
{@link Callback#onCreateActionMode(ActionMode, Menu)} once on initial
+ * creation
+ *
{@link Callback#onPrepareActionMode(ActionMode, Menu)} after creation
+ * and any time the {@link ActionMode} is invalidated
+ *
{@link Callback#onActionItemClicked(ActionMode, MenuItem)} any time a
+ * contextual action button is clicked
+ *
{@link Callback#onDestroyActionMode(ActionMode)} when the action mode
+ * is closed
+ *
+ */
+ public interface Callback {
+ /**
+ * Called when action mode is first created. The menu supplied will be used to
+ * generate action buttons for the action mode.
+ *
+ * @param mode ActionMode being created
+ * @param menu Menu used to populate action buttons
+ * @return true if the action mode should be created, false if entering this
+ * mode should be aborted.
+ */
+ public boolean onCreateActionMode(ActionMode mode, Menu menu);
+
+ /**
+ * Called to refresh an action mode's action menu whenever it is invalidated.
+ *
+ * @param mode ActionMode being prepared
+ * @param menu Menu used to populate action buttons
+ * @return true if the menu or action mode was updated, false otherwise.
+ */
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu);
+
+ /**
+ * Called to report a user click on an action button.
+ *
+ * @param mode The current ActionMode
+ * @param item The item that was clicked
+ * @return true if this callback handled the event, false if the standard MenuItem
+ * invocation should continue.
+ */
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item);
+
+ /**
+ * Called when an action mode is about to be exited and destroyed.
+ *
+ * @param mode The current ActionMode being destroyed
+ */
+ public void onDestroyActionMode(ActionMode mode);
+ }
+}
\ No newline at end of file
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/ActionProvider.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/ActionProvider.java
new file mode 100644
index 0000000000..ae7cb1fe03
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/ActionProvider.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.view;
+
+import android.content.Context;
+import android.view.View;
+
+/**
+ * This class is a mediator for accomplishing a given task, for example sharing a file.
+ * It is responsible for creating a view that performs an action that accomplishes the task.
+ * This class also implements other functions such a performing a default action.
+ *
+ * An ActionProvider can be optionally specified for a {@link MenuItem} and in such a
+ * case it will be responsible for creating the action view that appears in the
+ * {@link android.app.ActionBar} as a substitute for the menu item when the item is
+ * displayed as an action item. Also the provider is responsible for performing a
+ * default action if a menu item placed on the overflow menu of the ActionBar is
+ * selected and none of the menu item callbacks has handled the selection. For this
+ * case the provider can also optionally provide a sub-menu for accomplishing the
+ * task at hand.
+ *
+ *
+ * There are two ways for using an action provider for creating and handling of action views:
+ *
+ *
+ * Setting the action provider on a {@link MenuItem} directly by calling
+ * {@link MenuItem#setActionProvider(ActionProvider)}.
+ *
+ *
+ * Declaring the action provider in the menu XML resource. For example:
+ *
+ *
+ * @see MenuItem#setActionProvider(ActionProvider)
+ * @see MenuItem#getActionProvider()
+ */
+public abstract class ActionProvider {
+ private SubUiVisibilityListener mSubUiVisibilityListener;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context Context for accessing resources.
+ */
+ public ActionProvider(Context context) {
+ }
+
+ /**
+ * Factory method for creating new action views.
+ *
+ * @return A new action view.
+ */
+ public abstract View onCreateActionView();
+
+ /**
+ * Performs an optional default action.
+ *
+ * For the case of an action provider placed in a menu item not shown as an action this
+ * method is invoked if previous callbacks for processing menu selection has handled
+ * the event.
+ *
+ *
+ * A menu item selection is processed in the following order:
+ *
+ *
+ * Receiving a call to {@link MenuItem.OnMenuItemClickListener#onMenuItemClick
+ * MenuItem.OnMenuItemClickListener.onMenuItemClick}.
+ *
+ *
+ * Receiving a call to {@link android.app.Activity#onOptionsItemSelected(MenuItem)
+ * Activity.onOptionsItemSelected(MenuItem)}
+ *
+ *
+ * Receiving a call to {@link android.app.Fragment#onOptionsItemSelected(MenuItem)
+ * Fragment.onOptionsItemSelected(MenuItem)}
+ *
+ *
+ * Launching the {@link android.content.Intent} set via
+ * {@link MenuItem#setIntent(android.content.Intent) MenuItem.setIntent(android.content.Intent)}
+ *
+ *
+ * Invoking this method.
+ *
+ *
+ *
+ *
+ * The default implementation does not perform any action and returns false.
+ *
+ */
+ public boolean onPerformDefaultAction() {
+ return false;
+ }
+
+ /**
+ * Determines if this ActionProvider has a submenu associated with it.
+ *
+ *
Associated submenus will be shown when an action view is not. This
+ * provider instance will receive a call to {@link #onPrepareSubMenu(SubMenu)}
+ * after the call to {@link #onPerformDefaultAction()} and before a submenu is
+ * displayed to the user.
+ *
+ * @return true if the item backed by this provider should have an associated submenu
+ */
+ public boolean hasSubMenu() {
+ return false;
+ }
+
+ /**
+ * Called to prepare an associated submenu for the menu item backed by this ActionProvider.
+ *
+ *
if {@link #hasSubMenu()} returns true, this method will be called when the
+ * menu item is selected to prepare the submenu for presentation to the user. Apps
+ * may use this to create or alter submenu content right before display.
+ *
+ * @param subMenu Submenu that will be displayed
+ */
+ public void onPrepareSubMenu(SubMenu subMenu) {
+ }
+
+ /**
+ * Notify the system that the visibility of an action view's sub-UI such as
+ * an anchored popup has changed. This will affect how other system
+ * visibility notifications occur.
+ *
+ * @hide Pending future API approval
+ */
+ public void subUiVisibilityChanged(boolean isVisible) {
+ if (mSubUiVisibilityListener != null) {
+ mSubUiVisibilityListener.onSubUiVisibilityChanged(isVisible);
+ }
+ }
+
+ /**
+ * @hide Internal use only
+ */
+ public void setSubUiVisibilityListener(SubUiVisibilityListener listener) {
+ mSubUiVisibilityListener = listener;
+ }
+
+ /**
+ * @hide Internal use only
+ */
+ public interface SubUiVisibilityListener {
+ public void onSubUiVisibilityChanged(boolean isVisible);
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/CollapsibleActionView.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/CollapsibleActionView.java
new file mode 100644
index 0000000000..43281b013c
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/CollapsibleActionView.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.view;
+
+/**
+ * When a {@link View} implements this interface it will receive callbacks
+ * when expanded or collapsed as an action view alongside the optional,
+ * app-specified callbacks to {@link OnActionExpandListener}.
+ *
+ *
See {@link MenuItem} for more information about action views.
+ * See {@link android.app.ActionBar} for more information about the action bar.
+ */
+public interface CollapsibleActionView {
+ /**
+ * Called when this view is expanded as an action view.
+ * See {@link MenuItem#expandActionView()}.
+ */
+ public void onActionViewExpanded();
+
+ /**
+ * Called when this view is collapsed as an action view.
+ * See {@link MenuItem#collapseActionView()}.
+ */
+ public void onActionViewCollapsed();
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/Menu.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/Menu.java
new file mode 100644
index 0000000000..951f4ccef8
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/Menu.java
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.view;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.view.KeyEvent;
+
+/**
+ * Interface for managing the items in a menu.
+ *
+ * By default, every Activity supports an options menu of actions or options.
+ * You can add items to this menu and handle clicks on your additions. The
+ * easiest way of adding menu items is inflating an XML file into the
+ * {@link Menu} via {@link MenuInflater}. The easiest way of attaching code to
+ * clicks is via {@link Activity#onOptionsItemSelected(MenuItem)} and
+ * {@link Activity#onContextItemSelected(MenuItem)}.
+ *
+ * Different menu types support different features:
+ *
+ *
Context menus: Do not support item shortcuts and item icons.
+ *
Options menus: The icon menus do not support item check
+ * marks and only show the item's
+ * {@link MenuItem#setTitleCondensed(CharSequence) condensed title}. The
+ * expanded menus (only available if six or more menu items are visible,
+ * reached via the 'More' item in the icon menu) do not show item icons, and
+ * item check marks are discouraged.
+ *
Sub menus: Do not support item icons, or nested sub menus.
+ *
+ *
+ *
+ *
Developer Guides
+ *
For more information about creating menus, read the
+ * Menus developer guide.
+ *
+ */
+public interface Menu {
+
+ /**
+ * This is the part of an order integer that the user can provide.
+ * @hide
+ */
+ static final int USER_MASK = 0x0000ffff;
+ /**
+ * Bit shift of the user portion of the order integer.
+ * @hide
+ */
+ static final int USER_SHIFT = 0;
+
+ /**
+ * This is the part of an order integer that supplies the category of the
+ * item.
+ * @hide
+ */
+ static final int CATEGORY_MASK = 0xffff0000;
+ /**
+ * Bit shift of the category portion of the order integer.
+ * @hide
+ */
+ static final int CATEGORY_SHIFT = 16;
+
+ /**
+ * Value to use for group and item identifier integers when you don't care
+ * about them.
+ */
+ static final int NONE = 0;
+
+ /**
+ * First value for group and item identifier integers.
+ */
+ static final int FIRST = 1;
+
+ // Implementation note: Keep these CATEGORY_* in sync with the category enum
+ // in attrs.xml
+
+ /**
+ * Category code for the order integer for items/groups that are part of a
+ * container -- or/add this with your base value.
+ */
+ static final int CATEGORY_CONTAINER = 0x00010000;
+
+ /**
+ * Category code for the order integer for items/groups that are provided by
+ * the system -- or/add this with your base value.
+ */
+ static final int CATEGORY_SYSTEM = 0x00020000;
+
+ /**
+ * Category code for the order integer for items/groups that are
+ * user-supplied secondary (infrequently used) options -- or/add this with
+ * your base value.
+ */
+ static final int CATEGORY_SECONDARY = 0x00030000;
+
+ /**
+ * Category code for the order integer for items/groups that are
+ * alternative actions on the data that is currently displayed -- or/add
+ * this with your base value.
+ */
+ static final int CATEGORY_ALTERNATIVE = 0x00040000;
+
+ /**
+ * Flag for {@link #addIntentOptions}: if set, do not automatically remove
+ * any existing menu items in the same group.
+ */
+ static final int FLAG_APPEND_TO_GROUP = 0x0001;
+
+ /**
+ * Flag for {@link #performShortcut}: if set, do not close the menu after
+ * executing the shortcut.
+ */
+ static final int FLAG_PERFORM_NO_CLOSE = 0x0001;
+
+ /**
+ * Flag for {@link #performShortcut(int, KeyEvent, int)}: if set, always
+ * close the menu after executing the shortcut. Closing the menu also resets
+ * the prepared state.
+ */
+ static final int FLAG_ALWAYS_PERFORM_CLOSE = 0x0002;
+
+ /**
+ * Add a new item to the menu. This item displays the given title for its
+ * label.
+ *
+ * @param title The text to display for the item.
+ * @return The newly added menu item.
+ */
+ public MenuItem add(CharSequence title);
+
+ /**
+ * Add a new item to the menu. This item displays the given title for its
+ * label.
+ *
+ * @param titleRes Resource identifier of title string.
+ * @return The newly added menu item.
+ */
+ public MenuItem add(int titleRes);
+
+ /**
+ * Add a new item to the menu. This item displays the given title for its
+ * label.
+ *
+ * @param groupId The group identifier that this item should be part of.
+ * This can be used to define groups of items for batch state
+ * changes. Normally use {@link #NONE} if an item should not be in a
+ * group.
+ * @param itemId Unique item ID. Use {@link #NONE} if you do not need a
+ * unique ID.
+ * @param order The order for the item. Use {@link #NONE} if you do not care
+ * about the order. See {@link MenuItem#getOrder()}.
+ * @param title The text to display for the item.
+ * @return The newly added menu item.
+ */
+ public MenuItem add(int groupId, int itemId, int order, CharSequence title);
+
+ /**
+ * Variation on {@link #add(int, int, int, CharSequence)} that takes a
+ * string resource identifier instead of the string itself.
+ *
+ * @param groupId The group identifier that this item should be part of.
+ * This can also be used to define groups of items for batch state
+ * changes. Normally use {@link #NONE} if an item should not be in a
+ * group.
+ * @param itemId Unique item ID. Use {@link #NONE} if you do not need a
+ * unique ID.
+ * @param order The order for the item. Use {@link #NONE} if you do not care
+ * about the order. See {@link MenuItem#getOrder()}.
+ * @param titleRes Resource identifier of title string.
+ * @return The newly added menu item.
+ */
+ public MenuItem add(int groupId, int itemId, int order, int titleRes);
+
+ /**
+ * Add a new sub-menu to the menu. This item displays the given title for
+ * its label. To modify other attributes on the submenu's menu item, use
+ * {@link SubMenu#getItem()}.
+ *
+ * @param title The text to display for the item.
+ * @return The newly added sub-menu
+ */
+ SubMenu addSubMenu(final CharSequence title);
+
+ /**
+ * Add a new sub-menu to the menu. This item displays the given title for
+ * its label. To modify other attributes on the submenu's menu item, use
+ * {@link SubMenu#getItem()}.
+ *
+ * @param titleRes Resource identifier of title string.
+ * @return The newly added sub-menu
+ */
+ SubMenu addSubMenu(final int titleRes);
+
+ /**
+ * Add a new sub-menu to the menu. This item displays the given
+ * title for its label. To modify other attributes on the
+ * submenu's menu item, use {@link SubMenu#getItem()}.
+ *
+ * Note that you can only have one level of sub-menus, i.e. you cannnot add
+ * a subMenu to a subMenu: An {@link UnsupportedOperationException} will be
+ * thrown if you try.
+ *
+ * @param groupId The group identifier that this item should be part of.
+ * This can also be used to define groups of items for batch state
+ * changes. Normally use {@link #NONE} if an item should not be in a
+ * group.
+ * @param itemId Unique item ID. Use {@link #NONE} if you do not need a
+ * unique ID.
+ * @param order The order for the item. Use {@link #NONE} if you do not care
+ * about the order. See {@link MenuItem#getOrder()}.
+ * @param title The text to display for the item.
+ * @return The newly added sub-menu
+ */
+ SubMenu addSubMenu(final int groupId, final int itemId, int order, final CharSequence title);
+
+ /**
+ * Variation on {@link #addSubMenu(int, int, int, CharSequence)} that takes
+ * a string resource identifier for the title instead of the string itself.
+ *
+ * @param groupId The group identifier that this item should be part of.
+ * This can also be used to define groups of items for batch state
+ * changes. Normally use {@link #NONE} if an item should not be in a group.
+ * @param itemId Unique item ID. Use {@link #NONE} if you do not need a unique ID.
+ * @param order The order for the item. Use {@link #NONE} if you do not care about the
+ * order. See {@link MenuItem#getOrder()}.
+ * @param titleRes Resource identifier of title string.
+ * @return The newly added sub-menu
+ */
+ SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes);
+
+ /**
+ * Add a group of menu items corresponding to actions that can be performed
+ * for a particular Intent. The Intent is most often configured with a null
+ * action, the data that the current activity is working with, and includes
+ * either the {@link Intent#CATEGORY_ALTERNATIVE} or
+ * {@link Intent#CATEGORY_SELECTED_ALTERNATIVE} to find activities that have
+ * said they would like to be included as optional action. You can, however,
+ * use any Intent you want.
+ *
+ *
+ * See {@link android.content.pm.PackageManager#queryIntentActivityOptions}
+ * for more * details on the caller, specifics, and
+ * intent arguments. The list returned by that function is used
+ * to populate the resulting menu items.
+ *
+ *
+ * All of the menu items of possible options for the intent will be added
+ * with the given group and id. You can use the group to control ordering of
+ * the items in relation to other items in the menu. Normally this function
+ * will automatically remove any existing items in the menu in the same
+ * group and place a divider above and below the added items; this behavior
+ * can be modified with the flags parameter. For each of the
+ * generated items {@link MenuItem#setIntent} is called to associate the
+ * appropriate Intent with the item; this means the activity will
+ * automatically be started for you without having to do anything else.
+ *
+ * @param groupId The group identifier that the items should be part of.
+ * This can also be used to define groups of items for batch state
+ * changes. Normally use {@link #NONE} if the items should not be in
+ * a group.
+ * @param itemId Unique item ID. Use {@link #NONE} if you do not need a
+ * unique ID.
+ * @param order The order for the items. Use {@link #NONE} if you do not
+ * care about the order. See {@link MenuItem#getOrder()}.
+ * @param caller The current activity component name as defined by
+ * queryIntentActivityOptions().
+ * @param specifics Specific items to place first as defined by
+ * queryIntentActivityOptions().
+ * @param intent Intent describing the kinds of items to populate in the
+ * list as defined by queryIntentActivityOptions().
+ * @param flags Additional options controlling how the items are added.
+ * @param outSpecificItems Optional array in which to place the menu items
+ * that were generated for each of the specifics that were
+ * requested. Entries may be null if no activity was found for that
+ * specific action.
+ * @return The number of menu items that were added.
+ *
+ * @see #FLAG_APPEND_TO_GROUP
+ * @see MenuItem#setIntent
+ * @see android.content.pm.PackageManager#queryIntentActivityOptions
+ */
+ public int addIntentOptions(int groupId, int itemId, int order,
+ ComponentName caller, Intent[] specifics,
+ Intent intent, int flags, MenuItem[] outSpecificItems);
+
+ /**
+ * Remove the item with the given identifier.
+ *
+ * @param id The item to be removed. If there is no item with this
+ * identifier, nothing happens.
+ */
+ public void removeItem(int id);
+
+ /**
+ * Remove all items in the given group.
+ *
+ * @param groupId The group to be removed. If there are no items in this
+ * group, nothing happens.
+ */
+ public void removeGroup(int groupId);
+
+ /**
+ * Remove all existing items from the menu, leaving it empty as if it had
+ * just been created.
+ */
+ public void clear();
+
+ /**
+ * Control whether a particular group of items can show a check mark. This
+ * is similar to calling {@link MenuItem#setCheckable} on all of the menu items
+ * with the given group identifier, but in addition you can control whether
+ * this group contains a mutually-exclusive set items. This should be called
+ * after the items of the group have been added to the menu.
+ *
+ * @param group The group of items to operate on.
+ * @param checkable Set to true to allow a check mark, false to
+ * disallow. The default is false.
+ * @param exclusive If set to true, only one item in this group can be
+ * checked at a time; checking an item will automatically
+ * uncheck all others in the group. If set to false, each
+ * item can be checked independently of the others.
+ *
+ * @see MenuItem#setCheckable
+ * @see MenuItem#setChecked
+ */
+ public void setGroupCheckable(int group, boolean checkable, boolean exclusive);
+
+ /**
+ * Show or hide all menu items that are in the given group.
+ *
+ * @param group The group of items to operate on.
+ * @param visible If true the items are visible, else they are hidden.
+ *
+ * @see MenuItem#setVisible
+ */
+ public void setGroupVisible(int group, boolean visible);
+
+ /**
+ * Enable or disable all menu items that are in the given group.
+ *
+ * @param group The group of items to operate on.
+ * @param enabled If true the items will be enabled, else they will be disabled.
+ *
+ * @see MenuItem#setEnabled
+ */
+ public void setGroupEnabled(int group, boolean enabled);
+
+ /**
+ * Return whether the menu currently has item items that are visible.
+ *
+ * @return True if there is one or more item visible,
+ * else false.
+ */
+ public boolean hasVisibleItems();
+
+ /**
+ * Return the menu item with a particular identifier.
+ *
+ * @param id The identifier to find.
+ *
+ * @return The menu item object, or null if there is no item with
+ * this identifier.
+ */
+ public MenuItem findItem(int id);
+
+ /**
+ * Get the number of items in the menu. Note that this will change any
+ * times items are added or removed from the menu.
+ *
+ * @return The item count.
+ */
+ public int size();
+
+ /**
+ * Gets the menu item at the given index.
+ *
+ * @param index The index of the menu item to return.
+ * @return The menu item.
+ * @exception IndexOutOfBoundsException
+ * when {@code index < 0 || >= size()}
+ */
+ public MenuItem getItem(int index);
+
+ /**
+ * Closes the menu, if open.
+ */
+ public void close();
+
+ /**
+ * Execute the menu item action associated with the given shortcut
+ * character.
+ *
+ * @param keyCode The keycode of the shortcut key.
+ * @param event Key event message.
+ * @param flags Additional option flags or 0.
+ *
+ * @return If the given shortcut exists and is shown, returns
+ * true; else returns false.
+ *
+ * @see #FLAG_PERFORM_NO_CLOSE
+ */
+ public boolean performShortcut(int keyCode, KeyEvent event, int flags);
+
+ /**
+ * Is a keypress one of the defined shortcut keys for this window.
+ * @param keyCode the key code from {@link KeyEvent} to check.
+ * @param event the {@link KeyEvent} to use to help check.
+ */
+ boolean isShortcutKey(int keyCode, KeyEvent event);
+
+ /**
+ * Execute the menu item action associated with the given menu identifier.
+ *
+ * @param id Identifier associated with the menu item.
+ * @param flags Additional option flags or 0.
+ *
+ * @return If the given identifier exists and is shown, returns
+ * true; else returns false.
+ *
+ * @see #FLAG_PERFORM_NO_CLOSE
+ */
+ public boolean performIdentifierAction(int id, int flags);
+
+
+ /**
+ * Control whether the menu should be running in qwerty mode (alphabetic
+ * shortcuts) or 12-key mode (numeric shortcuts).
+ *
+ * @param isQwerty If true the menu will use alphabetic shortcuts; else it
+ * will use numeric shortcuts.
+ */
+ public void setQwertyMode(boolean isQwerty);
+}
+
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/MenuInflater.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/MenuInflater.java
new file mode 100644
index 0000000000..5a0f40859b
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/MenuInflater.java
@@ -0,0 +1,495 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ * 2011 Jake Wharton
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.view;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.util.Xml;
+import android.view.InflateException;
+import android.view.View;
+
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.internal.view.menu.MenuItemImpl;
+
+/**
+ * This class is used to instantiate menu XML files into Menu objects.
+ *
+ * For performance reasons, menu inflation relies heavily on pre-processing of
+ * XML files that is done at build time. Therefore, it is not currently possible
+ * to use MenuInflater with an XmlPullParser over a plain XML file at runtime;
+ * it only works with an XmlPullParser returned from a compiled resource (R.
+ * something file.)
+ */
+public class MenuInflater {
+ private static final String LOG_TAG = "MenuInflater";
+
+ /** Menu tag name in XML. */
+ private static final String XML_MENU = "menu";
+
+ /** Group tag name in XML. */
+ private static final String XML_GROUP = "group";
+
+ /** Item tag name in XML. */
+ private static final String XML_ITEM = "item";
+
+ private static final int NO_ID = 0;
+
+ private static final Class>[] ACTION_VIEW_CONSTRUCTOR_SIGNATURE = new Class[] {Context.class};
+
+ private static final Class>[] ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE = ACTION_VIEW_CONSTRUCTOR_SIGNATURE;
+
+ private final Object[] mActionViewConstructorArguments;
+
+ private final Object[] mActionProviderConstructorArguments;
+
+ private Context mContext;
+ private Object mRealOwner;
+
+ /**
+ * Constructs a menu inflater.
+ *
+ * @see Activity#getMenuInflater()
+ */
+ public MenuInflater(Context context) {
+ mContext = context;
+ mRealOwner = context;
+ mActionViewConstructorArguments = new Object[] {context};
+ mActionProviderConstructorArguments = mActionViewConstructorArguments;
+ }
+
+ /**
+ * Constructs a menu inflater.
+ *
+ * @see Activity#getMenuInflater()
+ * @hide
+ */
+ public MenuInflater(Context context, Object realOwner) {
+ mContext = context;
+ mRealOwner = realOwner;
+ mActionViewConstructorArguments = new Object[] {context};
+ mActionProviderConstructorArguments = mActionViewConstructorArguments;
+ }
+
+ /**
+ * Inflate a menu hierarchy from the specified XML resource. Throws
+ * {@link InflateException} if there is an error.
+ *
+ * @param menuRes Resource ID for an XML layout resource to load (e.g.,
+ * R.menu.main_activity)
+ * @param menu The Menu to inflate into. The items and submenus will be
+ * added to this Menu.
+ */
+ public void inflate(int menuRes, Menu menu) {
+ XmlResourceParser parser = null;
+ try {
+ parser = mContext.getResources().getLayout(menuRes);
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ parseMenu(parser, attrs, menu);
+ } catch (XmlPullParserException e) {
+ throw new InflateException("Error inflating menu XML", e);
+ } catch (IOException e) {
+ throw new InflateException("Error inflating menu XML", e);
+ } finally {
+ if (parser != null) parser.close();
+ }
+ }
+
+ /**
+ * Called internally to fill the given menu. If a sub menu is seen, it will
+ * call this recursively.
+ */
+ private void parseMenu(XmlPullParser parser, AttributeSet attrs, Menu menu)
+ throws XmlPullParserException, IOException {
+ MenuState menuState = new MenuState(menu);
+
+ int eventType = parser.getEventType();
+ String tagName;
+ boolean lookingForEndOfUnknownTag = false;
+ String unknownTagName = null;
+
+ // This loop will skip to the menu start tag
+ do {
+ if (eventType == XmlPullParser.START_TAG) {
+ tagName = parser.getName();
+ if (tagName.equals(XML_MENU)) {
+ // Go to next tag
+ eventType = parser.next();
+ break;
+ }
+
+ throw new RuntimeException("Expecting menu, got " + tagName);
+ }
+ eventType = parser.next();
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+
+ boolean reachedEndOfMenu = false;
+ while (!reachedEndOfMenu) {
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ if (lookingForEndOfUnknownTag) {
+ break;
+ }
+
+ tagName = parser.getName();
+ if (tagName.equals(XML_GROUP)) {
+ menuState.readGroup(attrs);
+ } else if (tagName.equals(XML_ITEM)) {
+ menuState.readItem(attrs);
+ } else if (tagName.equals(XML_MENU)) {
+ // A menu start tag denotes a submenu for an item
+ SubMenu subMenu = menuState.addSubMenuItem();
+
+ // Parse the submenu into returned SubMenu
+ parseMenu(parser, attrs, subMenu);
+ } else {
+ lookingForEndOfUnknownTag = true;
+ unknownTagName = tagName;
+ }
+ break;
+
+ case XmlPullParser.END_TAG:
+ tagName = parser.getName();
+ if (lookingForEndOfUnknownTag && tagName.equals(unknownTagName)) {
+ lookingForEndOfUnknownTag = false;
+ unknownTagName = null;
+ } else if (tagName.equals(XML_GROUP)) {
+ menuState.resetGroup();
+ } else if (tagName.equals(XML_ITEM)) {
+ // Add the item if it hasn't been added (if the item was
+ // a submenu, it would have been added already)
+ if (!menuState.hasAddedItem()) {
+ if (menuState.itemActionProvider != null &&
+ menuState.itemActionProvider.hasSubMenu()) {
+ menuState.addSubMenuItem();
+ } else {
+ menuState.addItem();
+ }
+ }
+ } else if (tagName.equals(XML_MENU)) {
+ reachedEndOfMenu = true;
+ }
+ break;
+
+ case XmlPullParser.END_DOCUMENT:
+ throw new RuntimeException("Unexpected end of document");
+ }
+
+ eventType = parser.next();
+ }
+ }
+
+ private static class InflatedOnMenuItemClickListener
+ implements MenuItem.OnMenuItemClickListener {
+ private static final Class>[] PARAM_TYPES = new Class[] { MenuItem.class };
+
+ private Object mRealOwner;
+ private Method mMethod;
+
+ public InflatedOnMenuItemClickListener(Object realOwner, String methodName) {
+ mRealOwner = realOwner;
+ Class> c = realOwner.getClass();
+ try {
+ mMethod = c.getMethod(methodName, PARAM_TYPES);
+ } catch (Exception e) {
+ InflateException ex = new InflateException(
+ "Couldn't resolve menu item onClick handler " + methodName +
+ " in class " + c.getName());
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+
+ public boolean onMenuItemClick(MenuItem item) {
+ try {
+ if (mMethod.getReturnType() == Boolean.TYPE) {
+ return (Boolean) mMethod.invoke(mRealOwner, item);
+ } else {
+ mMethod.invoke(mRealOwner, item);
+ return true;
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * State for the current menu.
+ *
+ * Groups can not be nested unless there is another menu (which will have
+ * its state class).
+ */
+ private class MenuState {
+ private Menu menu;
+
+ /*
+ * Group state is set on items as they are added, allowing an item to
+ * override its group state. (As opposed to set on items at the group end tag.)
+ */
+ private int groupId;
+ private int groupCategory;
+ private int groupOrder;
+ private int groupCheckable;
+ private boolean groupVisible;
+ private boolean groupEnabled;
+
+ private boolean itemAdded;
+ private int itemId;
+ private int itemCategoryOrder;
+ private CharSequence itemTitle;
+ private CharSequence itemTitleCondensed;
+ private int itemIconResId;
+ private char itemAlphabeticShortcut;
+ private char itemNumericShortcut;
+ /**
+ * Sync to attrs.xml enum:
+ * - 0: none
+ * - 1: all
+ * - 2: exclusive
+ */
+ private int itemCheckable;
+ private boolean itemChecked;
+ private boolean itemVisible;
+ private boolean itemEnabled;
+
+ /**
+ * Sync to attrs.xml enum, values in MenuItem:
+ * - 0: never
+ * - 1: ifRoom
+ * - 2: always
+ * - -1: Safe sentinel for "no value".
+ */
+ private int itemShowAsAction;
+
+ private int itemActionViewLayout;
+ private String itemActionViewClassName;
+ private String itemActionProviderClassName;
+
+ private String itemListenerMethodName;
+
+ private ActionProvider itemActionProvider;
+
+ private static final int defaultGroupId = NO_ID;
+ private static final int defaultItemId = NO_ID;
+ private static final int defaultItemCategory = 0;
+ private static final int defaultItemOrder = 0;
+ private static final int defaultItemCheckable = 0;
+ private static final boolean defaultItemChecked = false;
+ private static final boolean defaultItemVisible = true;
+ private static final boolean defaultItemEnabled = true;
+
+ public MenuState(final Menu menu) {
+ this.menu = menu;
+
+ resetGroup();
+ }
+
+ public void resetGroup() {
+ groupId = defaultGroupId;
+ groupCategory = defaultItemCategory;
+ groupOrder = defaultItemOrder;
+ groupCheckable = defaultItemCheckable;
+ groupVisible = defaultItemVisible;
+ groupEnabled = defaultItemEnabled;
+ }
+
+ /**
+ * Called when the parser is pointing to a group tag.
+ */
+ public void readGroup(AttributeSet attrs) {
+ TypedArray a = mContext.obtainStyledAttributes(attrs,
+ R.styleable.SherlockMenuGroup);
+
+ groupId = a.getResourceId(R.styleable.SherlockMenuGroup_android_id, defaultGroupId);
+ groupCategory = a.getInt(R.styleable.SherlockMenuGroup_android_menuCategory, defaultItemCategory);
+ groupOrder = a.getInt(R.styleable.SherlockMenuGroup_android_orderInCategory, defaultItemOrder);
+ groupCheckable = a.getInt(R.styleable.SherlockMenuGroup_android_checkableBehavior, defaultItemCheckable);
+ groupVisible = a.getBoolean(R.styleable.SherlockMenuGroup_android_visible, defaultItemVisible);
+ groupEnabled = a.getBoolean(R.styleable.SherlockMenuGroup_android_enabled, defaultItemEnabled);
+
+ a.recycle();
+ }
+
+ /**
+ * Called when the parser is pointing to an item tag.
+ */
+ public void readItem(AttributeSet attrs) {
+ TypedArray a = mContext.obtainStyledAttributes(attrs,
+ R.styleable.SherlockMenuItem);
+
+ // Inherit attributes from the group as default value
+ itemId = a.getResourceId(R.styleable.SherlockMenuItem_android_id, defaultItemId);
+ final int category = a.getInt(R.styleable.SherlockMenuItem_android_menuCategory, groupCategory);
+ final int order = a.getInt(R.styleable.SherlockMenuItem_android_orderInCategory, groupOrder);
+ itemCategoryOrder = (category & Menu.CATEGORY_MASK) | (order & Menu.USER_MASK);
+ itemTitle = a.getText(R.styleable.SherlockMenuItem_android_title);
+ itemTitleCondensed = a.getText(R.styleable.SherlockMenuItem_android_titleCondensed);
+ itemIconResId = a.getResourceId(R.styleable.SherlockMenuItem_android_icon, 0);
+ itemAlphabeticShortcut =
+ getShortcut(a.getString(R.styleable.SherlockMenuItem_android_alphabeticShortcut));
+ itemNumericShortcut =
+ getShortcut(a.getString(R.styleable.SherlockMenuItem_android_numericShortcut));
+ if (a.hasValue(R.styleable.SherlockMenuItem_android_checkable)) {
+ // Item has attribute checkable, use it
+ itemCheckable = a.getBoolean(R.styleable.SherlockMenuItem_android_checkable, false) ? 1 : 0;
+ } else {
+ // Item does not have attribute, use the group's (group can have one more state
+ // for checkable that represents the exclusive checkable)
+ itemCheckable = groupCheckable;
+ }
+
+ itemChecked = a.getBoolean(R.styleable.SherlockMenuItem_android_checked, defaultItemChecked);
+ itemVisible = a.getBoolean(R.styleable.SherlockMenuItem_android_visible, groupVisible);
+ itemEnabled = a.getBoolean(R.styleable.SherlockMenuItem_android_enabled, groupEnabled);
+
+ TypedValue value = new TypedValue();
+ a.getValue(R.styleable.SherlockMenuItem_android_showAsAction, value);
+ itemShowAsAction = value.type == TypedValue.TYPE_INT_HEX ? value.data : -1;
+
+ itemListenerMethodName = a.getString(R.styleable.SherlockMenuItem_android_onClick);
+ itemActionViewLayout = a.getResourceId(R.styleable.SherlockMenuItem_android_actionLayout, 0);
+
+ // itemActionViewClassName = a.getString(R.styleable.SherlockMenuItem_android_actionViewClass);
+ value = new TypedValue();
+ a.getValue(R.styleable.SherlockMenuItem_android_actionViewClass, value);
+ itemActionViewClassName = value.type == TypedValue.TYPE_STRING ? value.string.toString() : null;
+
+ // itemActionProviderClassName = a.getString(R.styleable.SherlockMenuItem_android_actionProviderClass);
+ value = new TypedValue();
+ a.getValue(R.styleable.SherlockMenuItem_android_actionProviderClass, value);
+ itemActionProviderClassName = value.type == TypedValue.TYPE_STRING ? value.string.toString() : null;
+
+ final boolean hasActionProvider = itemActionProviderClassName != null;
+ if (hasActionProvider && itemActionViewLayout == 0 && itemActionViewClassName == null) {
+ itemActionProvider = newInstance(itemActionProviderClassName,
+ ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE,
+ mActionProviderConstructorArguments);
+ } else {
+ if (hasActionProvider) {
+ Log.w(LOG_TAG, "Ignoring attribute 'actionProviderClass'."
+ + " Action view already specified.");
+ }
+ itemActionProvider = null;
+ }
+
+ a.recycle();
+
+ itemAdded = false;
+ }
+
+ private char getShortcut(String shortcutString) {
+ if (shortcutString == null) {
+ return 0;
+ } else {
+ return shortcutString.charAt(0);
+ }
+ }
+
+ private void setItem(MenuItem item) {
+ item.setChecked(itemChecked)
+ .setVisible(itemVisible)
+ .setEnabled(itemEnabled)
+ .setCheckable(itemCheckable >= 1)
+ .setTitleCondensed(itemTitleCondensed)
+ .setIcon(itemIconResId)
+ .setAlphabeticShortcut(itemAlphabeticShortcut)
+ .setNumericShortcut(itemNumericShortcut);
+
+ if (itemShowAsAction >= 0) {
+ item.setShowAsAction(itemShowAsAction);
+ }
+
+ if (itemListenerMethodName != null) {
+ if (mContext.isRestricted()) {
+ throw new IllegalStateException("The android:onClick attribute cannot "
+ + "be used within a restricted context");
+ }
+ item.setOnMenuItemClickListener(
+ new InflatedOnMenuItemClickListener(mRealOwner, itemListenerMethodName));
+ }
+
+ if (itemCheckable >= 2) {
+ if (item instanceof MenuItemImpl) {
+ MenuItemImpl impl = (MenuItemImpl) item;
+ impl.setExclusiveCheckable(true);
+ } else {
+ menu.setGroupCheckable(groupId, true, true);
+ }
+ }
+
+ boolean actionViewSpecified = false;
+ if (itemActionViewClassName != null) {
+ View actionView = (View) newInstance(itemActionViewClassName,
+ ACTION_VIEW_CONSTRUCTOR_SIGNATURE, mActionViewConstructorArguments);
+ item.setActionView(actionView);
+ actionViewSpecified = true;
+ }
+ if (itemActionViewLayout > 0) {
+ if (!actionViewSpecified) {
+ item.setActionView(itemActionViewLayout);
+ actionViewSpecified = true;
+ } else {
+ Log.w(LOG_TAG, "Ignoring attribute 'itemActionViewLayout'."
+ + " Action view already specified.");
+ }
+ }
+ if (itemActionProvider != null) {
+ item.setActionProvider(itemActionProvider);
+ }
+ }
+
+ public void addItem() {
+ itemAdded = true;
+ setItem(menu.add(groupId, itemId, itemCategoryOrder, itemTitle));
+ }
+
+ public SubMenu addSubMenuItem() {
+ itemAdded = true;
+ SubMenu subMenu = menu.addSubMenu(groupId, itemId, itemCategoryOrder, itemTitle);
+ setItem(subMenu.getItem());
+ return subMenu;
+ }
+
+ public boolean hasAddedItem() {
+ return itemAdded;
+ }
+
+ @SuppressWarnings("unchecked")
+ private T newInstance(String className, Class>[] constructorSignature,
+ Object[] arguments) {
+ try {
+ Class> clazz = mContext.getClassLoader().loadClass(className);
+ Constructor> constructor = clazz.getConstructor(constructorSignature);
+ return (T) constructor.newInstance(arguments);
+ } catch (Exception e) {
+ Log.w(LOG_TAG, "Cannot instantiate class: " + className, e);
+ }
+ return null;
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/MenuItem.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/MenuItem.java
new file mode 100644
index 0000000000..7fc3aa4306
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/MenuItem.java
@@ -0,0 +1,598 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.view;
+
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View;
+
+/**
+ * Interface for direct access to a previously created menu item.
+ *
+ * An Item is returned by calling one of the {@link android.view.Menu#add}
+ * methods.
+ *
+ * For a feature set of specific menu types, see {@link Menu}.
+ *
+ *
+ *
Developer Guides
+ *
For information about creating menus, read the
+ * Menus developer guide.
+ *
+ */
+public interface MenuItem {
+ /*
+ * These should be kept in sync with attrs.xml enum constants for showAsAction
+ */
+ /** Never show this item as a button in an Action Bar. */
+ public static final int SHOW_AS_ACTION_NEVER = android.view.MenuItem.SHOW_AS_ACTION_NEVER;
+ /** Show this item as a button in an Action Bar if the system decides there is room for it. */
+ public static final int SHOW_AS_ACTION_IF_ROOM = android.view.MenuItem.SHOW_AS_ACTION_IF_ROOM;
+ /**
+ * Always show this item as a button in an Action Bar.
+ * Use sparingly! If too many items are set to always show in the Action Bar it can
+ * crowd the Action Bar and degrade the user experience on devices with smaller screens.
+ * A good rule of thumb is to have no more than 2 items set to always show at a time.
+ */
+ public static final int SHOW_AS_ACTION_ALWAYS = android.view.MenuItem.SHOW_AS_ACTION_ALWAYS;
+
+ /**
+ * When this item is in the action bar, always show it with a text label even if
+ * it also has an icon specified.
+ */
+ public static final int SHOW_AS_ACTION_WITH_TEXT = android.view.MenuItem.SHOW_AS_ACTION_WITH_TEXT;
+
+ /**
+ * This item's action view collapses to a normal menu item.
+ * When expanded, the action view temporarily takes over
+ * a larger segment of its container.
+ */
+ public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = android.view.MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW;
+
+ /**
+ * Interface definition for a callback to be invoked when a menu item is
+ * clicked.
+ *
+ * @see Activity#onContextItemSelected(MenuItem)
+ * @see Activity#onOptionsItemSelected(MenuItem)
+ */
+ public interface OnMenuItemClickListener {
+ /**
+ * Called when a menu item has been invoked. This is the first code
+ * that is executed; if it returns true, no other callbacks will be
+ * executed.
+ *
+ * @param item The menu item that was invoked.
+ *
+ * @return Return true to consume this click and prevent others from
+ * executing.
+ */
+ public boolean onMenuItemClick(MenuItem item);
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when a menu item
+ * marked with {@link MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW} is
+ * expanded or collapsed.
+ *
+ * @see MenuItem#expandActionView()
+ * @see MenuItem#collapseActionView()
+ * @see MenuItem#setShowAsActionFlags(int)
+ */
+ public interface OnActionExpandListener {
+ /**
+ * Called when a menu item with {@link MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}
+ * is expanded.
+ * @param item Item that was expanded
+ * @return true if the item should expand, false if expansion should be suppressed.
+ */
+ public boolean onMenuItemActionExpand(MenuItem item);
+
+ /**
+ * Called when a menu item with {@link MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}
+ * is collapsed.
+ * @param item Item that was collapsed
+ * @return true if the item should collapse, false if collapsing should be suppressed.
+ */
+ public boolean onMenuItemActionCollapse(MenuItem item);
+ }
+
+ /**
+ * Return the identifier for this menu item. The identifier can not
+ * be changed after the menu is created.
+ *
+ * @return The menu item's identifier.
+ */
+ public int getItemId();
+
+ /**
+ * Return the group identifier that this menu item is part of. The group
+ * identifier can not be changed after the menu is created.
+ *
+ * @return The menu item's group identifier.
+ */
+ public int getGroupId();
+
+ /**
+ * Return the category and order within the category of this item. This
+ * item will be shown before all items (within its category) that have
+ * order greater than this value.
+ *
+ * An order integer contains the item's category (the upper bits of the
+ * integer; set by or/add the category with the order within the
+ * category) and the ordering of the item within that category (the
+ * lower bits). Example categories are {@link Menu#CATEGORY_SYSTEM},
+ * {@link Menu#CATEGORY_SECONDARY}, {@link Menu#CATEGORY_ALTERNATIVE},
+ * {@link Menu#CATEGORY_CONTAINER}. See {@link Menu} for a full list.
+ *
+ * @return The order of this item.
+ */
+ public int getOrder();
+
+ /**
+ * Change the title associated with this item.
+ *
+ * @param title The new text to be displayed.
+ * @return This Item so additional setters can be called.
+ */
+ public MenuItem setTitle(CharSequence title);
+
+ /**
+ * Change the title associated with this item.
+ *
+ * Some menu types do not sufficient space to show the full title, and
+ * instead a condensed title is preferred. See {@link Menu} for more
+ * information.
+ *
+ * @param title The resource id of the new text to be displayed.
+ * @return This Item so additional setters can be called.
+ * @see #setTitleCondensed(CharSequence)
+ */
+
+ public MenuItem setTitle(int title);
+
+ /**
+ * Retrieve the current title of the item.
+ *
+ * @return The title.
+ */
+ public CharSequence getTitle();
+
+ /**
+ * Change the condensed title associated with this item. The condensed
+ * title is used in situations where the normal title may be too long to
+ * be displayed.
+ *
+ * @param title The new text to be displayed as the condensed title.
+ * @return This Item so additional setters can be called.
+ */
+ public MenuItem setTitleCondensed(CharSequence title);
+
+ /**
+ * Retrieve the current condensed title of the item. If a condensed
+ * title was never set, it will return the normal title.
+ *
+ * @return The condensed title, if it exists.
+ * Otherwise the normal title.
+ */
+ public CharSequence getTitleCondensed();
+
+ /**
+ * Change the icon associated with this item. This icon will not always be
+ * shown, so the title should be sufficient in describing this item. See
+ * {@link Menu} for the menu types that support icons.
+ *
+ * @param icon The new icon (as a Drawable) to be displayed.
+ * @return This Item so additional setters can be called.
+ */
+ public MenuItem setIcon(Drawable icon);
+
+ /**
+ * Change the icon associated with this item. This icon will not always be
+ * shown, so the title should be sufficient in describing this item. See
+ * {@link Menu} for the menu types that support icons.
+ *
+ * This method will set the resource ID of the icon which will be used to
+ * lazily get the Drawable when this item is being shown.
+ *
+ * @param iconRes The new icon (as a resource ID) to be displayed.
+ * @return This Item so additional setters can be called.
+ */
+ public MenuItem setIcon(int iconRes);
+
+ /**
+ * Returns the icon for this item as a Drawable (getting it from resources if it hasn't been
+ * loaded before).
+ *
+ * @return The icon as a Drawable.
+ */
+ public Drawable getIcon();
+
+ /**
+ * Change the Intent associated with this item. By default there is no
+ * Intent associated with a menu item. If you set one, and nothing
+ * else handles the item, then the default behavior will be to call
+ * {@link android.content.Context#startActivity} with the given Intent.
+ *
+ *
Note that setIntent() can not be used with the versions of
+ * {@link Menu#add} that take a Runnable, because {@link Runnable#run}
+ * does not return a value so there is no way to tell if it handled the
+ * item. In this case it is assumed that the Runnable always handles
+ * the item, and the intent will never be started.
+ *
+ * @see #getIntent
+ * @param intent The Intent to associated with the item. This Intent
+ * object is not copied, so be careful not to
+ * modify it later.
+ * @return This Item so additional setters can be called.
+ */
+ public MenuItem setIntent(Intent intent);
+
+ /**
+ * Return the Intent associated with this item. This returns a
+ * reference to the Intent which you can change as desired to modify
+ * what the Item is holding.
+ *
+ * @see #setIntent
+ * @return Returns the last value supplied to {@link #setIntent}, or
+ * null.
+ */
+ public Intent getIntent();
+
+ /**
+ * Change both the numeric and alphabetic shortcut associated with this
+ * item. Note that the shortcut will be triggered when the key that
+ * generates the given character is pressed alone or along with with the alt
+ * key. Also note that case is not significant and that alphabetic shortcut
+ * characters will be displayed in lower case.
+ *
+ * See {@link Menu} for the menu types that support shortcuts.
+ *
+ * @param numericChar The numeric shortcut key. This is the shortcut when
+ * using a numeric (e.g., 12-key) keyboard.
+ * @param alphaChar The alphabetic shortcut key. This is the shortcut when
+ * using a keyboard with alphabetic keys.
+ * @return This Item so additional setters can be called.
+ */
+ public MenuItem setShortcut(char numericChar, char alphaChar);
+
+ /**
+ * Change the numeric shortcut associated with this item.
+ *
+ * See {@link Menu} for the menu types that support shortcuts.
+ *
+ * @param numericChar The numeric shortcut key. This is the shortcut when
+ * using a 12-key (numeric) keyboard.
+ * @return This Item so additional setters can be called.
+ */
+ public MenuItem setNumericShortcut(char numericChar);
+
+ /**
+ * Return the char for this menu item's numeric (12-key) shortcut.
+ *
+ * @return Numeric character to use as a shortcut.
+ */
+ public char getNumericShortcut();
+
+ /**
+ * Change the alphabetic shortcut associated with this item. The shortcut
+ * will be triggered when the key that generates the given character is
+ * pressed alone or along with with the alt key. Case is not significant and
+ * shortcut characters will be displayed in lower case. Note that menu items
+ * with the characters '\b' or '\n' as shortcuts will get triggered by the
+ * Delete key or Carriage Return key, respectively.
+ *
+ * See {@link Menu} for the menu types that support shortcuts.
+ *
+ * @param alphaChar The alphabetic shortcut key. This is the shortcut when
+ * using a keyboard with alphabetic keys.
+ * @return This Item so additional setters can be called.
+ */
+ public MenuItem setAlphabeticShortcut(char alphaChar);
+
+ /**
+ * Return the char for this menu item's alphabetic shortcut.
+ *
+ * @return Alphabetic character to use as a shortcut.
+ */
+ public char getAlphabeticShortcut();
+
+ /**
+ * Control whether this item can display a check mark. Setting this does
+ * not actually display a check mark (see {@link #setChecked} for that);
+ * rather, it ensures there is room in the item in which to display a
+ * check mark.
+ *
+ * See {@link Menu} for the menu types that support check marks.
+ *
+ * @param checkable Set to true to allow a check mark, false to
+ * disallow. The default is false.
+ * @see #setChecked
+ * @see #isCheckable
+ * @see Menu#setGroupCheckable
+ * @return This Item so additional setters can be called.
+ */
+ public MenuItem setCheckable(boolean checkable);
+
+ /**
+ * Return whether the item can currently display a check mark.
+ *
+ * @return If a check mark can be displayed, returns true.
+ *
+ * @see #setCheckable
+ */
+ public boolean isCheckable();
+
+ /**
+ * Control whether this item is shown with a check mark. Note that you
+ * must first have enabled checking with {@link #setCheckable} or else
+ * the check mark will not appear. If this item is a member of a group that contains
+ * mutually-exclusive items (set via {@link Menu#setGroupCheckable(int, boolean, boolean)},
+ * the other items in the group will be unchecked.
+ *
+ * See {@link Menu} for the menu types that support check marks.
+ *
+ * @see #setCheckable
+ * @see #isChecked
+ * @see Menu#setGroupCheckable
+ * @param checked Set to true to display a check mark, false to hide
+ * it. The default value is false.
+ * @return This Item so additional setters can be called.
+ */
+ public MenuItem setChecked(boolean checked);
+
+ /**
+ * Return whether the item is currently displaying a check mark.
+ *
+ * @return If a check mark is displayed, returns true.
+ *
+ * @see #setChecked
+ */
+ public boolean isChecked();
+
+ /**
+ * Sets the visibility of the menu item. Even if a menu item is not visible,
+ * it may still be invoked via its shortcut (to completely disable an item,
+ * set it to invisible and {@link #setEnabled(boolean) disabled}).
+ *
+ * @param visible If true then the item will be visible; if false it is
+ * hidden.
+ * @return This Item so additional setters can be called.
+ */
+ public MenuItem setVisible(boolean visible);
+
+ /**
+ * Return the visibility of the menu item.
+ *
+ * @return If true the item is visible; else it is hidden.
+ */
+ public boolean isVisible();
+
+ /**
+ * Sets whether the menu item is enabled. Disabling a menu item will not
+ * allow it to be invoked via its shortcut. The menu item will still be
+ * visible.
+ *
+ * @param enabled If true then the item will be invokable; if false it is
+ * won't be invokable.
+ * @return This Item so additional setters can be called.
+ */
+ public MenuItem setEnabled(boolean enabled);
+
+ /**
+ * Return the enabled state of the menu item.
+ *
+ * @return If true the item is enabled and hence invokable; else it is not.
+ */
+ public boolean isEnabled();
+
+ /**
+ * Check whether this item has an associated sub-menu. I.e. it is a
+ * sub-menu of another menu.
+ *
+ * @return If true this item has a menu; else it is a
+ * normal item.
+ */
+ public boolean hasSubMenu();
+
+ /**
+ * Get the sub-menu to be invoked when this item is selected, if it has
+ * one. See {@link #hasSubMenu()}.
+ *
+ * @return The associated menu if there is one, else null
+ */
+ public SubMenu getSubMenu();
+
+ /**
+ * Set a custom listener for invocation of this menu item. In most
+ * situations, it is more efficient and easier to use
+ * {@link Activity#onOptionsItemSelected(MenuItem)} or
+ * {@link Activity#onContextItemSelected(MenuItem)}.
+ *
+ * @param menuItemClickListener The object to receive invokations.
+ * @return This Item so additional setters can be called.
+ * @see Activity#onOptionsItemSelected(MenuItem)
+ * @see Activity#onContextItemSelected(MenuItem)
+ */
+ public MenuItem setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener menuItemClickListener);
+
+ /**
+ * Gets the extra information linked to this menu item. This extra
+ * information is set by the View that added this menu item to the
+ * menu.
+ *
+ * @see OnCreateContextMenuListener
+ * @return The extra information linked to the View that added this
+ * menu item to the menu. This can be null.
+ */
+ public ContextMenuInfo getMenuInfo();
+
+ /**
+ * Sets how this item should display in the presence of an Action Bar.
+ * The parameter actionEnum is a flag set. One of {@link #SHOW_AS_ACTION_ALWAYS},
+ * {@link #SHOW_AS_ACTION_IF_ROOM}, or {@link #SHOW_AS_ACTION_NEVER} should
+ * be used, and you may optionally OR the value with {@link #SHOW_AS_ACTION_WITH_TEXT}.
+ * SHOW_AS_ACTION_WITH_TEXT requests that when the item is shown as an action,
+ * it should be shown with a text label.
+ *
+ * @param actionEnum How the item should display. One of
+ * {@link #SHOW_AS_ACTION_ALWAYS}, {@link #SHOW_AS_ACTION_IF_ROOM}, or
+ * {@link #SHOW_AS_ACTION_NEVER}. SHOW_AS_ACTION_NEVER is the default.
+ *
+ * @see android.app.ActionBar
+ * @see #setActionView(View)
+ */
+ public void setShowAsAction(int actionEnum);
+
+ /**
+ * Sets how this item should display in the presence of an Action Bar.
+ * The parameter actionEnum is a flag set. One of {@link #SHOW_AS_ACTION_ALWAYS},
+ * {@link #SHOW_AS_ACTION_IF_ROOM}, or {@link #SHOW_AS_ACTION_NEVER} should
+ * be used, and you may optionally OR the value with {@link #SHOW_AS_ACTION_WITH_TEXT}.
+ * SHOW_AS_ACTION_WITH_TEXT requests that when the item is shown as an action,
+ * it should be shown with a text label.
+ *
+ *
Note: This method differs from {@link #setShowAsAction(int)} only in that it
+ * returns the current MenuItem instance for call chaining.
+ *
+ * @param actionEnum How the item should display. One of
+ * {@link #SHOW_AS_ACTION_ALWAYS}, {@link #SHOW_AS_ACTION_IF_ROOM}, or
+ * {@link #SHOW_AS_ACTION_NEVER}. SHOW_AS_ACTION_NEVER is the default.
+ *
+ * @see android.app.ActionBar
+ * @see #setActionView(View)
+ * @return This MenuItem instance for call chaining.
+ */
+ public MenuItem setShowAsActionFlags(int actionEnum);
+
+ /**
+ * Set an action view for this menu item. An action view will be displayed in place
+ * of an automatically generated menu item element in the UI when this item is shown
+ * as an action within a parent.
+ *
+ * Note: Setting an action view overrides the action provider
+ * set via {@link #setActionProvider(ActionProvider)}.
+ *
+ *
+ * @param view View to use for presenting this item to the user.
+ * @return This Item so additional setters can be called.
+ *
+ * @see #setShowAsAction(int)
+ */
+ public MenuItem setActionView(View view);
+
+ /**
+ * Set an action view for this menu item. An action view will be displayed in place
+ * of an automatically generated menu item element in the UI when this item is shown
+ * as an action within a parent.
+ *
+ * Note: Setting an action view overrides the action provider
+ * set via {@link #setActionProvider(ActionProvider)}.
+ *
+ *
+ * @param resId Layout resource to use for presenting this item to the user.
+ * @return This Item so additional setters can be called.
+ *
+ * @see #setShowAsAction(int)
+ */
+ public MenuItem setActionView(int resId);
+
+ /**
+ * Returns the currently set action view for this menu item.
+ *
+ * @return This item's action view
+ *
+ * @see #setActionView(View)
+ * @see #setShowAsAction(int)
+ */
+ public View getActionView();
+
+ /**
+ * Sets the {@link ActionProvider} responsible for creating an action view if
+ * the item is placed on the action bar. The provider also provides a default
+ * action invoked if the item is placed in the overflow menu.
+ *
+ * Note: Setting an action provider overrides the action view
+ * set via {@link #setActionView(int)} or {@link #setActionView(View)}.
+ *
+ *
+ * @param actionProvider The action provider.
+ * @return This Item so additional setters can be called.
+ *
+ * @see ActionProvider
+ */
+ public MenuItem setActionProvider(ActionProvider actionProvider);
+
+ /**
+ * Gets the {@link ActionProvider}.
+ *
+ * @return The action provider.
+ *
+ * @see ActionProvider
+ * @see #setActionProvider(ActionProvider)
+ */
+ public ActionProvider getActionProvider();
+
+ /**
+ * Expand the action view associated with this menu item.
+ * The menu item must have an action view set, as well as
+ * the showAsAction flag {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}.
+ * If a listener has been set using {@link #setOnActionExpandListener(OnActionExpandListener)}
+ * it will have its {@link OnActionExpandListener#onMenuItemActionExpand(MenuItem)}
+ * method invoked. The listener may return false from this method to prevent expanding
+ * the action view.
+ *
+ * @return true if the action view was expanded, false otherwise.
+ */
+ public boolean expandActionView();
+
+ /**
+ * Collapse the action view associated with this menu item.
+ * The menu item must have an action view set, as well as the showAsAction flag
+ * {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}. If a listener has been set using
+ * {@link #setOnActionExpandListener(OnActionExpandListener)} it will have its
+ * {@link OnActionExpandListener#onMenuItemActionCollapse(MenuItem)} method invoked.
+ * The listener may return false from this method to prevent collapsing the action view.
+ *
+ * @return true if the action view was collapsed, false otherwise.
+ */
+ public boolean collapseActionView();
+
+ /**
+ * Returns true if this menu item's action view has been expanded.
+ *
+ * @return true if the item's action view is expanded, false otherwise.
+ *
+ * @see #expandActionView()
+ * @see #collapseActionView()
+ * @see #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW
+ * @see OnActionExpandListener
+ */
+ public boolean isActionViewExpanded();
+
+ /**
+ * Set an {@link OnActionExpandListener} on this menu item to be notified when
+ * the associated action view is expanded or collapsed. The menu item must
+ * be configured to expand or collapse its action view using the flag
+ * {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}.
+ *
+ * @param listener Listener that will respond to expand/collapse events
+ * @return This menu item instance for call chaining
+ */
+ public MenuItem setOnActionExpandListener(OnActionExpandListener listener);
+}
\ No newline at end of file
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/SubMenu.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/SubMenu.java
new file mode 100644
index 0000000000..397fd1c2d7
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/SubMenu.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.view;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+/**
+ * Subclass of {@link Menu} for sub menus.
+ *
+ * Sub menus do not support item icons, or nested sub menus.
+ *
+ *
+ *
Developer Guides
+ *
For information about creating menus, read the
+ * Menus developer guide.
+ *
+ */
+
+public interface SubMenu extends Menu {
+ /**
+ * Sets the submenu header's title to the title given in titleRes
+ * resource identifier.
+ *
+ * @param titleRes The string resource identifier used for the title.
+ * @return This SubMenu so additional setters can be called.
+ */
+ public SubMenu setHeaderTitle(int titleRes);
+
+ /**
+ * Sets the submenu header's title to the title given in title.
+ *
+ * @param title The character sequence used for the title.
+ * @return This SubMenu so additional setters can be called.
+ */
+ public SubMenu setHeaderTitle(CharSequence title);
+
+ /**
+ * Sets the submenu header's icon to the icon given in iconRes
+ * resource id.
+ *
+ * @param iconRes The resource identifier used for the icon.
+ * @return This SubMenu so additional setters can be called.
+ */
+ public SubMenu setHeaderIcon(int iconRes);
+
+ /**
+ * Sets the submenu header's icon to the icon given in icon
+ * {@link Drawable}.
+ *
+ * @param icon The {@link Drawable} used for the icon.
+ * @return This SubMenu so additional setters can be called.
+ */
+ public SubMenu setHeaderIcon(Drawable icon);
+
+ /**
+ * Sets the header of the submenu to the {@link View} given in
+ * view. This replaces the header title and icon (and those
+ * replace this).
+ *
+ * @param view The {@link View} used for the header.
+ * @return This SubMenu so additional setters can be called.
+ */
+ public SubMenu setHeaderView(View view);
+
+ /**
+ * Clears the header of the submenu.
+ */
+ public void clearHeader();
+
+ /**
+ * Change the icon associated with this submenu's item in its parent menu.
+ *
+ * @see MenuItem#setIcon(int)
+ * @param iconRes The new icon (as a resource ID) to be displayed.
+ * @return This SubMenu so additional setters can be called.
+ */
+ public SubMenu setIcon(int iconRes);
+
+ /**
+ * Change the icon associated with this submenu's item in its parent menu.
+ *
+ * @see MenuItem#setIcon(Drawable)
+ * @param icon The new icon (as a Drawable) to be displayed.
+ * @return This SubMenu so additional setters can be called.
+ */
+ public SubMenu setIcon(Drawable icon);
+
+ /**
+ * Gets the {@link MenuItem} that represents this submenu in the parent
+ * menu. Use this for setting additional item attributes.
+ *
+ * @return The {@link MenuItem} that launches the submenu when invoked.
+ */
+ public MenuItem getItem();
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/Window.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/Window.java
new file mode 100644
index 0000000000..a340a4291f
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/view/Window.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2011 Jake Wharton
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.view;
+
+import android.content.Context;
+
+/**
+ *
Abstract base class for a top-level window look and behavior policy. An
+ * instance of this class should be used as the top-level view added to the
+ * window manager. It provides standard UI policies such as a background, title
+ * area, default key processing, etc.
+ *
+ *
The only existing implementation of this abstract class is
+ * android.policy.PhoneWindow, which you should instantiate when needing a
+ * Window. Eventually that class will be refactored and a factory method added
+ * for creating Window instances without knowing about a particular
+ * implementation.
+ */
+public abstract class Window extends android.view.Window {
+ public static final long FEATURE_ACTION_BAR = android.view.Window.FEATURE_ACTION_BAR;
+ public static final long FEATURE_ACTION_BAR_OVERLAY = android.view.Window.FEATURE_ACTION_BAR_OVERLAY;
+ public static final long FEATURE_ACTION_MODE_OVERLAY = android.view.Window.FEATURE_ACTION_MODE_OVERLAY;
+ public static final long FEATURE_NO_TITLE = android.view.Window.FEATURE_NO_TITLE;
+ public static final long FEATURE_PROGRESS = android.view.Window.FEATURE_PROGRESS;
+ public static final long FEATURE_INDETERMINATE_PROGRESS = android.view.Window.FEATURE_INDETERMINATE_PROGRESS;
+
+ /**
+ * Create a new instance for a context.
+ *
+ * @param context Context.
+ */
+ private Window(Context context) {
+ super(context);
+ }
+
+
+ public interface Callback {
+ /**
+ * Called when a panel's menu item has been selected by the user.
+ *
+ * @param featureId The panel that the menu is in.
+ * @param item The menu item that was selected.
+ *
+ * @return boolean Return true to finish processing of selection, or
+ * false to perform the normal menu handling (calling its
+ * Runnable or sending a Message to its target Handler).
+ */
+ public boolean onMenuItemSelected(int featureId, MenuItem item);
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/widget/ActivityChooserModel.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/widget/ActivityChooserModel.java
new file mode 100644
index 0000000000..d7f110fc62
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/widget/ActivityChooserModel.java
@@ -0,0 +1,1104 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.widget;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.database.DataSetObservable;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Xml;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+/**
+ *
+ * This class represents a data model for choosing a component for handing a
+ * given {@link Intent}. The model is responsible for querying the system for
+ * activities that can handle the given intent and order found activities
+ * based on historical data of previous choices. The historical data is stored
+ * in an application private file. If a client does not want to have persistent
+ * choice history the file can be omitted, thus the activities will be ordered
+ * based on historical usage for the current session.
+ *
+ *
+ * For each backing history file there is a singleton instance of this class. Thus,
+ * several clients that specify the same history file will share the same model. Note
+ * that if multiple clients are sharing the same model they should implement semantically
+ * equivalent functionality since setting the model intent will change the found
+ * activities and they may be inconsistent with the functionality of some of the clients.
+ * For example, choosing a share activity can be implemented by a single backing
+ * model and two different views for performing the selection. If however, one of the
+ * views is used for sharing but the other for importing, for example, then each
+ * view should be backed by a separate model.
+ *
+ *
+ * The way clients interact with this class is as follows:
+ *
+ *
+ *
+ *
+ * // Get a model and set it to a couple of clients with semantically similar function.
+ * ActivityChooserModel dataModel =
+ * ActivityChooserModel.get(context, "task_specific_history_file_name.xml");
+ *
+ * ActivityChooserModelClient modelClient1 = getActivityChooserModelClient1();
+ * modelClient1.setActivityChooserModel(dataModel);
+ *
+ * ActivityChooserModelClient modelClient2 = getActivityChooserModelClient2();
+ * modelClient2.setActivityChooserModel(dataModel);
+ *
+ * // Set an intent to choose a an activity for.
+ * dataModel.setIntent(intent);
+ *
+ *
+ *
+ *
+ * Note: This class is thread safe.
+ *
+ *
+ * @hide
+ */
+class ActivityChooserModel extends DataSetObservable {
+
+ /**
+ * Client that utilizes an {@link ActivityChooserModel}.
+ */
+ public interface ActivityChooserModelClient {
+
+ /**
+ * Sets the {@link ActivityChooserModel}.
+ *
+ * @param dataModel The model.
+ */
+ public void setActivityChooserModel(ActivityChooserModel dataModel);
+ }
+
+ /**
+ * Defines a sorter that is responsible for sorting the activities
+ * based on the provided historical choices and an intent.
+ */
+ public interface ActivitySorter {
+
+ /**
+ * Sorts the activities in descending order of relevance
+ * based on previous history and an intent.
+ *
+ * @param intent The {@link Intent}.
+ * @param activities Activities to be sorted.
+ * @param historicalRecords Historical records.
+ */
+ // This cannot be done by a simple comparator since an Activity weight
+ // is computed from history. Note that Activity implements Comparable.
+ public void sort(Intent intent, List activities,
+ List historicalRecords);
+ }
+
+ /**
+ * Listener for choosing an activity.
+ */
+ public interface OnChooseActivityListener {
+
+ /**
+ * Called when an activity has been chosen. The client can decide whether
+ * an activity can be chosen and if so the caller of
+ * {@link ActivityChooserModel#chooseActivity(int)} will receive and {@link Intent}
+ * for launching it.
+ *
+ * Note: Modifying the intent is not permitted and
+ * any changes to the latter will be ignored.
+ *
+ *
+ * @param host The listener's host model.
+ * @param intent The intent for launching the chosen activity.
+ * @return Whether the intent is handled and should not be delivered to clients.
+ *
+ * @see ActivityChooserModel#chooseActivity(int)
+ */
+ public boolean onChooseActivity(ActivityChooserModel host, Intent intent);
+ }
+
+ /**
+ * Flag for selecting debug mode.
+ */
+ private static final boolean DEBUG = false;
+
+ /**
+ * Tag used for logging.
+ */
+ private static final String LOG_TAG = ActivityChooserModel.class.getSimpleName();
+
+ /**
+ * The root tag in the history file.
+ */
+ private static final String TAG_HISTORICAL_RECORDS = "historical-records";
+
+ /**
+ * The tag for a record in the history file.
+ */
+ private static final String TAG_HISTORICAL_RECORD = "historical-record";
+
+ /**
+ * Attribute for the activity.
+ */
+ private static final String ATTRIBUTE_ACTIVITY = "activity";
+
+ /**
+ * Attribute for the choice time.
+ */
+ private static final String ATTRIBUTE_TIME = "time";
+
+ /**
+ * Attribute for the choice weight.
+ */
+ private static final String ATTRIBUTE_WEIGHT = "weight";
+
+ /**
+ * The default name of the choice history file.
+ */
+ public static final String DEFAULT_HISTORY_FILE_NAME =
+ "activity_choser_model_history.xml";
+
+ /**
+ * The default maximal length of the choice history.
+ */
+ public static final int DEFAULT_HISTORY_MAX_LENGTH = 50;
+
+ /**
+ * The amount with which to inflate a chosen activity when set as default.
+ */
+ private static final int DEFAULT_ACTIVITY_INFLATION = 5;
+
+ /**
+ * Default weight for a choice record.
+ */
+ private static final float DEFAULT_HISTORICAL_RECORD_WEIGHT = 1.0f;
+
+ /**
+ * The extension of the history file.
+ */
+ private static final String HISTORY_FILE_EXTENSION = ".xml";
+
+ /**
+ * An invalid item index.
+ */
+ private static final int INVALID_INDEX = -1;
+
+ /**
+ * Lock to guard the model registry.
+ */
+ private static final Object sRegistryLock = new Object();
+
+ /**
+ * This the registry for data models.
+ */
+ private static final Map sDataModelRegistry =
+ new HashMap();
+
+ /**
+ * Lock for synchronizing on this instance.
+ */
+ private final Object mInstanceLock = new Object();
+
+ /**
+ * List of activities that can handle the current intent.
+ */
+ private final List mActivites = new ArrayList();
+
+ /**
+ * List with historical choice records.
+ */
+ private final List mHistoricalRecords = new ArrayList();
+
+ /**
+ * Context for accessing resources.
+ */
+ private final Context mContext;
+
+ /**
+ * The name of the history file that backs this model.
+ */
+ private final String mHistoryFileName;
+
+ /**
+ * The intent for which a activity is being chosen.
+ */
+ private Intent mIntent;
+
+ /**
+ * The sorter for ordering activities based on intent and past choices.
+ */
+ private ActivitySorter mActivitySorter = new DefaultSorter();
+
+ /**
+ * The maximal length of the choice history.
+ */
+ private int mHistoryMaxSize = DEFAULT_HISTORY_MAX_LENGTH;
+
+ /**
+ * Flag whether choice history can be read. In general many clients can
+ * share the same data model and {@link #readHistoricalData()} may be called
+ * by arbitrary of them any number of times. Therefore, this class guarantees
+ * that the very first read succeeds and subsequent reads can be performed
+ * only after a call to {@link #persistHistoricalData()} followed by change
+ * of the share records.
+ */
+ private boolean mCanReadHistoricalData = true;
+
+ /**
+ * Flag whether the choice history was read. This is used to enforce that
+ * before calling {@link #persistHistoricalData()} a call to
+ * {@link #persistHistoricalData()} has been made. This aims to avoid a
+ * scenario in which a choice history file exits, it is not read yet and
+ * it is overwritten. Note that always all historical records are read in
+ * full and the file is rewritten. This is necessary since we need to
+ * purge old records that are outside of the sliding window of past choices.
+ */
+ private boolean mReadShareHistoryCalled = false;
+
+ /**
+ * Flag whether the choice records have changed. In general many clients can
+ * share the same data model and {@link #persistHistoricalData()} may be called
+ * by arbitrary of them any number of times. Therefore, this class guarantees
+ * that choice history will be persisted only if it has changed.
+ */
+ private boolean mHistoricalRecordsChanged = true;
+
+ /**
+ * Hander for scheduling work on client tread.
+ */
+ private final Handler mHandler = new Handler();
+
+ /**
+ * Policy for controlling how the model handles chosen activities.
+ */
+ private OnChooseActivityListener mActivityChoserModelPolicy;
+
+ /**
+ * Gets the data model backed by the contents of the provided file with historical data.
+ * Note that only one data model is backed by a given file, thus multiple calls with
+ * the same file name will return the same model instance. If no such instance is present
+ * it is created.
+ *
+ * Note: To use the default historical data file clients should explicitly
+ * pass as file name {@link #DEFAULT_HISTORY_FILE_NAME}. If no persistence of the choice
+ * history is desired clients should pass null for the file name. In such
+ * case a new model is returned for each invocation.
+ *
+ *
+ *
+ * Always use difference historical data files for semantically different actions.
+ * For example, sharing is different from importing.
+ *
+ *
+ * @param context Context for loading resources.
+ * @param historyFileName File name with choice history, null
+ * if the model should not be backed by a file. In this case the activities
+ * will be ordered only by data from the current session.
+ *
+ * @return The model.
+ */
+ public static ActivityChooserModel get(Context context, String historyFileName) {
+ synchronized (sRegistryLock) {
+ ActivityChooserModel dataModel = sDataModelRegistry.get(historyFileName);
+ if (dataModel == null) {
+ dataModel = new ActivityChooserModel(context, historyFileName);
+ sDataModelRegistry.put(historyFileName, dataModel);
+ }
+ dataModel.readHistoricalData();
+ return dataModel;
+ }
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context Context for loading resources.
+ * @param historyFileName The history XML file.
+ */
+ private ActivityChooserModel(Context context, String historyFileName) {
+ mContext = context.getApplicationContext();
+ if (!TextUtils.isEmpty(historyFileName)
+ && !historyFileName.endsWith(HISTORY_FILE_EXTENSION)) {
+ mHistoryFileName = historyFileName + HISTORY_FILE_EXTENSION;
+ } else {
+ mHistoryFileName = historyFileName;
+ }
+ }
+
+ /**
+ * Sets an intent for which to choose a activity.
+ *
+ * Note: Clients must set only semantically similar
+ * intents for each data model.
+ *
+ *
+ * @param intent The intent.
+ */
+ public void setIntent(Intent intent) {
+ synchronized (mInstanceLock) {
+ if (mIntent == intent) {
+ return;
+ }
+ mIntent = intent;
+ loadActivitiesLocked();
+ }
+ }
+
+ /**
+ * Gets the intent for which a activity is being chosen.
+ *
+ * @return The intent.
+ */
+ public Intent getIntent() {
+ synchronized (mInstanceLock) {
+ return mIntent;
+ }
+ }
+
+ /**
+ * Gets the number of activities that can handle the intent.
+ *
+ * @return The activity count.
+ *
+ * @see #setIntent(Intent)
+ */
+ public int getActivityCount() {
+ synchronized (mInstanceLock) {
+ return mActivites.size();
+ }
+ }
+
+ /**
+ * Gets an activity at a given index.
+ *
+ * @return The activity.
+ *
+ * @see ActivityResolveInfo
+ * @see #setIntent(Intent)
+ */
+ public ResolveInfo getActivity(int index) {
+ synchronized (mInstanceLock) {
+ return mActivites.get(index).resolveInfo;
+ }
+ }
+
+ /**
+ * Gets the index of a the given activity.
+ *
+ * @param activity The activity index.
+ *
+ * @return The index if found, -1 otherwise.
+ */
+ public int getActivityIndex(ResolveInfo activity) {
+ List activities = mActivites;
+ final int activityCount = activities.size();
+ for (int i = 0; i < activityCount; i++) {
+ ActivityResolveInfo currentActivity = activities.get(i);
+ if (currentActivity.resolveInfo == activity) {
+ return i;
+ }
+ }
+ return INVALID_INDEX;
+ }
+
+ /**
+ * Chooses a activity to handle the current intent. This will result in
+ * adding a historical record for that action and construct intent with
+ * its component name set such that it can be immediately started by the
+ * client.
+ *
+ * Note: By calling this method the client guarantees
+ * that the returned intent will be started. This intent is returned to
+ * the client solely to let additional customization before the start.
+ *
+ *
+ * @return An {@link Intent} for launching the activity or null if the
+ * policy has consumed the intent.
+ *
+ * @see HistoricalRecord
+ * @see OnChooseActivityListener
+ */
+ public Intent chooseActivity(int index) {
+ ActivityResolveInfo chosenActivity = mActivites.get(index);
+
+ ComponentName chosenName = new ComponentName(
+ chosenActivity.resolveInfo.activityInfo.packageName,
+ chosenActivity.resolveInfo.activityInfo.name);
+
+ Intent choiceIntent = new Intent(mIntent);
+ choiceIntent.setComponent(chosenName);
+
+ if (mActivityChoserModelPolicy != null) {
+ // Do not allow the policy to change the intent.
+ Intent choiceIntentCopy = new Intent(choiceIntent);
+ final boolean handled = mActivityChoserModelPolicy.onChooseActivity(this,
+ choiceIntentCopy);
+ if (handled) {
+ return null;
+ }
+ }
+
+ HistoricalRecord historicalRecord = new HistoricalRecord(chosenName,
+ System.currentTimeMillis(), DEFAULT_HISTORICAL_RECORD_WEIGHT);
+ addHisoricalRecord(historicalRecord);
+
+ return choiceIntent;
+ }
+
+ /**
+ * Sets the listener for choosing an activity.
+ *
+ * @param listener The listener.
+ */
+ public void setOnChooseActivityListener(OnChooseActivityListener listener) {
+ mActivityChoserModelPolicy = listener;
+ }
+
+ /**
+ * Gets the default activity, The default activity is defined as the one
+ * with highest rank i.e. the first one in the list of activities that can
+ * handle the intent.
+ *
+ * @return The default activity, null id not activities.
+ *
+ * @see #getActivity(int)
+ */
+ public ResolveInfo getDefaultActivity() {
+ synchronized (mInstanceLock) {
+ if (!mActivites.isEmpty()) {
+ return mActivites.get(0).resolveInfo;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets the default activity. The default activity is set by adding a
+ * historical record with weight high enough that this activity will
+ * become the highest ranked. Such a strategy guarantees that the default
+ * will eventually change if not used. Also the weight of the record for
+ * setting a default is inflated with a constant amount to guarantee that
+ * it will stay as default for awhile.
+ *
+ * @param index The index of the activity to set as default.
+ */
+ public void setDefaultActivity(int index) {
+ ActivityResolveInfo newDefaultActivity = mActivites.get(index);
+ ActivityResolveInfo oldDefaultActivity = mActivites.get(0);
+
+ final float weight;
+ if (oldDefaultActivity != null) {
+ // Add a record with weight enough to boost the chosen at the top.
+ weight = oldDefaultActivity.weight - newDefaultActivity.weight
+ + DEFAULT_ACTIVITY_INFLATION;
+ } else {
+ weight = DEFAULT_HISTORICAL_RECORD_WEIGHT;
+ }
+
+ ComponentName defaultName = new ComponentName(
+ newDefaultActivity.resolveInfo.activityInfo.packageName,
+ newDefaultActivity.resolveInfo.activityInfo.name);
+ HistoricalRecord historicalRecord = new HistoricalRecord(defaultName,
+ System.currentTimeMillis(), weight);
+ addHisoricalRecord(historicalRecord);
+ }
+
+ /**
+ * Reads the history data from the backing file if the latter
+ * was provided. Calling this method more than once before a call
+ * to {@link #persistHistoricalData()} has been made has no effect.
+ *
+ * Note: Historical data is read asynchronously and
+ * as soon as the reading is completed any registered
+ * {@link DataSetObserver}s will be notified. Also no historical
+ * data is read until this method is invoked.
+ *
+ */
+ private void readHistoricalData() {
+ synchronized (mInstanceLock) {
+ if (!mCanReadHistoricalData || !mHistoricalRecordsChanged) {
+ return;
+ }
+ mCanReadHistoricalData = false;
+ mReadShareHistoryCalled = true;
+ if (!TextUtils.isEmpty(mHistoryFileName)) {
+ /*AsyncTask.*/SERIAL_EXECUTOR.execute(new HistoryLoader());
+ }
+ }
+ }
+
+ private static final Executor SERIAL_EXECUTOR = Executors.newSingleThreadExecutor();
+
+ /**
+ * Persists the history data to the backing file if the latter
+ * was provided. Calling this method before a call to {@link #readHistoricalData()}
+ * throws an exception. Calling this method more than one without choosing an
+ * activity has not effect.
+ *
+ * @throws IllegalStateException If this method is called before a call to
+ * {@link #readHistoricalData()}.
+ */
+ private void persistHistoricalData() {
+ synchronized (mInstanceLock) {
+ if (!mReadShareHistoryCalled) {
+ throw new IllegalStateException("No preceding call to #readHistoricalData");
+ }
+ if (!mHistoricalRecordsChanged) {
+ return;
+ }
+ mHistoricalRecordsChanged = false;
+ mCanReadHistoricalData = true;
+ if (!TextUtils.isEmpty(mHistoryFileName)) {
+ /*AsyncTask.*/SERIAL_EXECUTOR.execute(new HistoryPersister());
+ }
+ }
+ }
+
+ /**
+ * Sets the sorter for ordering activities based on historical data and an intent.
+ *
+ * @param activitySorter The sorter.
+ *
+ * @see ActivitySorter
+ */
+ public void setActivitySorter(ActivitySorter activitySorter) {
+ synchronized (mInstanceLock) {
+ if (mActivitySorter == activitySorter) {
+ return;
+ }
+ mActivitySorter = activitySorter;
+ sortActivities();
+ }
+ }
+
+ /**
+ * Sorts the activities based on history and an intent. If
+ * a sorter is not specified this a default implementation is used.
+ *
+ * @see #setActivitySorter(ActivitySorter)
+ */
+ private void sortActivities() {
+ synchronized (mInstanceLock) {
+ if (mActivitySorter != null && !mActivites.isEmpty()) {
+ mActivitySorter.sort(mIntent, mActivites,
+ Collections.unmodifiableList(mHistoricalRecords));
+ notifyChanged();
+ }
+ }
+ }
+
+ /**
+ * Sets the maximal size of the historical data. Defaults to
+ * {@link #DEFAULT_HISTORY_MAX_LENGTH}
+ *
+ * Note: Setting this property will immediately
+ * enforce the specified max history size by dropping enough old
+ * historical records to enforce the desired size. Thus, any
+ * records that exceed the history size will be discarded and
+ * irreversibly lost.
+ *
+ *
+ * @param historyMaxSize The max history size.
+ */
+ public void setHistoryMaxSize(int historyMaxSize) {
+ synchronized (mInstanceLock) {
+ if (mHistoryMaxSize == historyMaxSize) {
+ return;
+ }
+ mHistoryMaxSize = historyMaxSize;
+ pruneExcessiveHistoricalRecordsLocked();
+ sortActivities();
+ }
+ }
+
+ /**
+ * Gets the history max size.
+ *
+ * @return The history max size.
+ */
+ public int getHistoryMaxSize() {
+ synchronized (mInstanceLock) {
+ return mHistoryMaxSize;
+ }
+ }
+
+ /**
+ * Gets the history size.
+ *
+ * @return The history size.
+ */
+ public int getHistorySize() {
+ synchronized (mInstanceLock) {
+ return mHistoricalRecords.size();
+ }
+ }
+
+ /**
+ * Adds a historical record.
+ *
+ * @param historicalRecord The record to add.
+ * @return True if the record was added.
+ */
+ private boolean addHisoricalRecord(HistoricalRecord historicalRecord) {
+ synchronized (mInstanceLock) {
+ final boolean added = mHistoricalRecords.add(historicalRecord);
+ if (added) {
+ mHistoricalRecordsChanged = true;
+ pruneExcessiveHistoricalRecordsLocked();
+ persistHistoricalData();
+ sortActivities();
+ }
+ return added;
+ }
+ }
+
+ /**
+ * Prunes older excessive records to guarantee {@link #mHistoryMaxSize}.
+ */
+ private void pruneExcessiveHistoricalRecordsLocked() {
+ List choiceRecords = mHistoricalRecords;
+ final int pruneCount = choiceRecords.size() - mHistoryMaxSize;
+ if (pruneCount <= 0) {
+ return;
+ }
+ mHistoricalRecordsChanged = true;
+ for (int i = 0; i < pruneCount; i++) {
+ HistoricalRecord prunedRecord = choiceRecords.remove(0);
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Pruned: " + prunedRecord);
+ }
+ }
+ }
+
+ /**
+ * Loads the activities.
+ */
+ private void loadActivitiesLocked() {
+ mActivites.clear();
+ if (mIntent != null) {
+ List resolveInfos =
+ mContext.getPackageManager().queryIntentActivities(mIntent, 0);
+ final int resolveInfoCount = resolveInfos.size();
+ for (int i = 0; i < resolveInfoCount; i++) {
+ ResolveInfo resolveInfo = resolveInfos.get(i);
+ mActivites.add(new ActivityResolveInfo(resolveInfo));
+ }
+ sortActivities();
+ } else {
+ notifyChanged();
+ }
+ }
+
+ /**
+ * Represents a record in the history.
+ */
+ public final static class HistoricalRecord {
+
+ /**
+ * The activity name.
+ */
+ public final ComponentName activity;
+
+ /**
+ * The choice time.
+ */
+ public final long time;
+
+ /**
+ * The record weight.
+ */
+ public final float weight;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param activityName The activity component name flattened to string.
+ * @param time The time the activity was chosen.
+ * @param weight The weight of the record.
+ */
+ public HistoricalRecord(String activityName, long time, float weight) {
+ this(ComponentName.unflattenFromString(activityName), time, weight);
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param activityName The activity name.
+ * @param time The time the activity was chosen.
+ * @param weight The weight of the record.
+ */
+ public HistoricalRecord(ComponentName activityName, long time, float weight) {
+ this.activity = activityName;
+ this.time = time;
+ this.weight = weight;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((activity == null) ? 0 : activity.hashCode());
+ result = prime * result + (int) (time ^ (time >>> 32));
+ result = prime * result + Float.floatToIntBits(weight);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ HistoricalRecord other = (HistoricalRecord) obj;
+ if (activity == null) {
+ if (other.activity != null) {
+ return false;
+ }
+ } else if (!activity.equals(other.activity)) {
+ return false;
+ }
+ if (time != other.time) {
+ return false;
+ }
+ if (Float.floatToIntBits(weight) != Float.floatToIntBits(other.weight)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("[");
+ builder.append("; activity:").append(activity);
+ builder.append("; time:").append(time);
+ builder.append("; weight:").append(new BigDecimal(weight));
+ builder.append("]");
+ return builder.toString();
+ }
+ }
+
+ /**
+ * Represents an activity.
+ */
+ public final class ActivityResolveInfo implements Comparable {
+
+ /**
+ * The {@link ResolveInfo} of the activity.
+ */
+ public final ResolveInfo resolveInfo;
+
+ /**
+ * Weight of the activity. Useful for sorting.
+ */
+ public float weight;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param resolveInfo activity {@link ResolveInfo}.
+ */
+ public ActivityResolveInfo(ResolveInfo resolveInfo) {
+ this.resolveInfo = resolveInfo;
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 + Float.floatToIntBits(weight);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ActivityResolveInfo other = (ActivityResolveInfo) obj;
+ if (Float.floatToIntBits(weight) != Float.floatToIntBits(other.weight)) {
+ return false;
+ }
+ return true;
+ }
+
+ public int compareTo(ActivityResolveInfo another) {
+ return Float.floatToIntBits(another.weight) - Float.floatToIntBits(weight);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("[");
+ builder.append("resolveInfo:").append(resolveInfo.toString());
+ builder.append("; weight:").append(new BigDecimal(weight));
+ builder.append("]");
+ return builder.toString();
+ }
+ }
+
+ /**
+ * Default activity sorter implementation.
+ */
+ private final class DefaultSorter implements ActivitySorter {
+ private static final float WEIGHT_DECAY_COEFFICIENT = 0.95f;
+
+ private final Map mPackageNameToActivityMap =
+ new HashMap();
+
+ public void sort(Intent intent, List activities,
+ List historicalRecords) {
+ Map packageNameToActivityMap =
+ mPackageNameToActivityMap;
+ packageNameToActivityMap.clear();
+
+ final int activityCount = activities.size();
+ for (int i = 0; i < activityCount; i++) {
+ ActivityResolveInfo activity = activities.get(i);
+ activity.weight = 0.0f;
+ String packageName = activity.resolveInfo.activityInfo.packageName;
+ packageNameToActivityMap.put(packageName, activity);
+ }
+
+ final int lastShareIndex = historicalRecords.size() - 1;
+ float nextRecordWeight = 1;
+ for (int i = lastShareIndex; i >= 0; i--) {
+ HistoricalRecord historicalRecord = historicalRecords.get(i);
+ String packageName = historicalRecord.activity.getPackageName();
+ ActivityResolveInfo activity = packageNameToActivityMap.get(packageName);
+ if (activity != null) {
+ activity.weight += historicalRecord.weight * nextRecordWeight;
+ nextRecordWeight = nextRecordWeight * WEIGHT_DECAY_COEFFICIENT;
+ }
+ }
+
+ Collections.sort(activities);
+
+ if (DEBUG) {
+ for (int i = 0; i < activityCount; i++) {
+ Log.i(LOG_TAG, "Sorted: " + activities.get(i));
+ }
+ }
+ }
+ }
+
+ /**
+ * Command for reading the historical records from a file off the UI thread.
+ */
+ private final class HistoryLoader implements Runnable {
+
+ public void run() {
+ FileInputStream fis = null;
+ try {
+ fis = mContext.openFileInput(mHistoryFileName);
+ } catch (FileNotFoundException fnfe) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Could not open historical records file: " + mHistoryFileName);
+ }
+ return;
+ }
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(fis, null);
+
+ int type = XmlPullParser.START_DOCUMENT;
+ while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
+ type = parser.next();
+ }
+
+ if (!TAG_HISTORICAL_RECORDS.equals(parser.getName())) {
+ throw new XmlPullParserException("Share records file does not start with "
+ + TAG_HISTORICAL_RECORDS + " tag.");
+ }
+
+ List readRecords = new ArrayList();
+
+ while (true) {
+ type = parser.next();
+ if (type == XmlPullParser.END_DOCUMENT) {
+ break;
+ }
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ String nodeName = parser.getName();
+ if (!TAG_HISTORICAL_RECORD.equals(nodeName)) {
+ throw new XmlPullParserException("Share records file not well-formed.");
+ }
+
+ String activity = parser.getAttributeValue(null, ATTRIBUTE_ACTIVITY);
+ final long time =
+ Long.parseLong(parser.getAttributeValue(null, ATTRIBUTE_TIME));
+ final float weight =
+ Float.parseFloat(parser.getAttributeValue(null, ATTRIBUTE_WEIGHT));
+
+ HistoricalRecord readRecord = new HistoricalRecord(activity, time,
+ weight);
+ readRecords.add(readRecord);
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Read " + readRecord.toString());
+ }
+ }
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Read " + readRecords.size() + " historical records.");
+ }
+
+ synchronized (mInstanceLock) {
+ Set uniqueShareRecords =
+ new LinkedHashSet(readRecords);
+
+ // Make sure no duplicates. Example: Read a file with
+ // one record, add one record, persist the two records,
+ // add a record, read the persisted records - the
+ // read two records should not be added again.
+ List historicalRecords = mHistoricalRecords;
+ final int historicalRecordsCount = historicalRecords.size();
+ for (int i = historicalRecordsCount - 1; i >= 0; i--) {
+ HistoricalRecord historicalRecord = historicalRecords.get(i);
+ uniqueShareRecords.add(historicalRecord);
+ }
+
+ if (historicalRecords.size() == uniqueShareRecords.size()) {
+ return;
+ }
+
+ // Make sure the oldest records go to the end.
+ historicalRecords.clear();
+ historicalRecords.addAll(uniqueShareRecords);
+
+ mHistoricalRecordsChanged = true;
+
+ // Do this on the client thread since the client may be on the UI
+ // thread, wait for data changes which happen during sorting, and
+ // perform UI modification based on the data change.
+ mHandler.post(new Runnable() {
+ public void run() {
+ pruneExcessiveHistoricalRecordsLocked();
+ sortActivities();
+ }
+ });
+ }
+ } catch (XmlPullParserException xppe) {
+ Log.e(LOG_TAG, "Error reading historical recrod file: " + mHistoryFileName, xppe);
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error reading historical recrod file: " + mHistoryFileName, ioe);
+ } finally {
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException ioe) {
+ /* ignore */
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Command for persisting the historical records to a file off the UI thread.
+ */
+ private final class HistoryPersister implements Runnable {
+
+ public void run() {
+ FileOutputStream fos = null;
+ List records = null;
+
+ synchronized (mInstanceLock) {
+ records = new ArrayList(mHistoricalRecords);
+ }
+
+ try {
+ fos = mContext.openFileOutput(mHistoryFileName, Context.MODE_PRIVATE);
+ } catch (FileNotFoundException fnfe) {
+ Log.e(LOG_TAG, "Error writing historical recrod file: " + mHistoryFileName, fnfe);
+ return;
+ }
+
+ XmlSerializer serializer = Xml.newSerializer();
+
+ try {
+ serializer.setOutput(fos, null);
+ serializer.startDocument("UTF-8", true);
+ serializer.startTag(null, TAG_HISTORICAL_RECORDS);
+
+ final int recordCount = records.size();
+ for (int i = 0; i < recordCount; i++) {
+ HistoricalRecord record = records.remove(0);
+ serializer.startTag(null, TAG_HISTORICAL_RECORD);
+ serializer.attribute(null, ATTRIBUTE_ACTIVITY, record.activity.flattenToString());
+ serializer.attribute(null, ATTRIBUTE_TIME, String.valueOf(record.time));
+ serializer.attribute(null, ATTRIBUTE_WEIGHT, String.valueOf(record.weight));
+ serializer.endTag(null, TAG_HISTORICAL_RECORD);
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Wrote " + record.toString());
+ }
+ }
+
+ serializer.endTag(null, TAG_HISTORICAL_RECORDS);
+ serializer.endDocument();
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Wrote " + recordCount + " historical records.");
+ }
+ } catch (IllegalArgumentException iae) {
+ Log.e(LOG_TAG, "Error writing historical recrod file: " + mHistoryFileName, iae);
+ } catch (IllegalStateException ise) {
+ Log.e(LOG_TAG, "Error writing historical recrod file: " + mHistoryFileName, ise);
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error writing historical recrod file: " + mHistoryFileName, ioe);
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException e) {
+ /* ignore */
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/widget/ActivityChooserView.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/widget/ActivityChooserView.java
new file mode 100644
index 0000000000..e19ea9e9e1
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/widget/ActivityChooserView.java
@@ -0,0 +1,827 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.widget;
+
+import android.os.Build;
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.internal.widget.IcsLinearLayout;
+import com.actionbarsherlock.internal.widget.IcsListPopupWindow;
+import com.actionbarsherlock.view.ActionProvider;
+import com.actionbarsherlock.widget.ActivityChooserModel.ActivityChooserModelClient;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.database.DataSetObserver;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+/**
+ * This class is a view for choosing an activity for handling a given {@link Intent}.
+ *
+ * The view is composed of two adjacent buttons:
+ *
+ *
+ * The left button is an immediate action and allows one click activity choosing.
+ * Tapping this button immediately executes the intent without requiring any further
+ * user input. Long press on this button shows a popup for changing the default
+ * activity.
+ *
+ *
+ * The right button is an overflow action and provides an optimized menu
+ * of additional activities. Tapping this button shows a popup anchored to this
+ * view, listing the most frequently used activities. This list is initially
+ * limited to a small number of items in frequency used order. The last item,
+ * "Show all..." serves as an affordance to display all available activities.
+ *
+ *
+ *
+ *
+ * @hide
+ */
+class ActivityChooserView extends ViewGroup implements ActivityChooserModelClient {
+
+ /**
+ * An adapter for displaying the activities in an {@link AdapterView}.
+ */
+ private final ActivityChooserViewAdapter mAdapter;
+
+ /**
+ * Implementation of various interfaces to avoid publishing them in the APIs.
+ */
+ private final Callbacks mCallbacks;
+
+ /**
+ * The content of this view.
+ */
+ private final IcsLinearLayout mActivityChooserContent;
+
+ /**
+ * Stores the background drawable to allow hiding and latter showing.
+ */
+ private final Drawable mActivityChooserContentBackground;
+
+ /**
+ * The expand activities action button;
+ */
+ private final FrameLayout mExpandActivityOverflowButton;
+
+ /**
+ * The image for the expand activities action button;
+ */
+ private final ImageView mExpandActivityOverflowButtonImage;
+
+ /**
+ * The default activities action button;
+ */
+ private final FrameLayout mDefaultActivityButton;
+
+ /**
+ * The image for the default activities action button;
+ */
+ private final ImageView mDefaultActivityButtonImage;
+
+ /**
+ * The maximal width of the list popup.
+ */
+ private final int mListPopupMaxWidth;
+
+ /**
+ * The ActionProvider hosting this view, if applicable.
+ */
+ ActionProvider mProvider;
+
+ /**
+ * Observer for the model data.
+ */
+ private final DataSetObserver mModelDataSetOberver = new DataSetObserver() {
+
+ @Override
+ public void onChanged() {
+ super.onChanged();
+ mAdapter.notifyDataSetChanged();
+ }
+ @Override
+ public void onInvalidated() {
+ super.onInvalidated();
+ mAdapter.notifyDataSetInvalidated();
+ }
+ };
+
+ private final OnGlobalLayoutListener mOnGlobalLayoutListener = new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ if (isShowingPopup()) {
+ if (!isShown()) {
+ getListPopupWindow().dismiss();
+ } else {
+ getListPopupWindow().show();
+ if (mProvider != null) {
+ mProvider.subUiVisibilityChanged(true);
+ }
+ }
+ }
+ }
+ };
+
+ /**
+ * Popup window for showing the activity overflow list.
+ */
+ private IcsListPopupWindow mListPopupWindow;
+
+ /**
+ * Listener for the dismissal of the popup/alert.
+ */
+ private PopupWindow.OnDismissListener mOnDismissListener;
+
+ /**
+ * Flag whether a default activity currently being selected.
+ */
+ private boolean mIsSelectingDefaultActivity;
+
+ /**
+ * The count of activities in the popup.
+ */
+ private int mInitialActivityCount = ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_DEFAULT;
+
+ /**
+ * Flag whether this view is attached to a window.
+ */
+ private boolean mIsAttachedToWindow;
+
+ /**
+ * String resource for formatting content description of the default target.
+ */
+ private int mDefaultActionButtonContentDescription;
+
+ private final Context mContext;
+
+ /**
+ * Create a new instance.
+ *
+ * @param context The application environment.
+ */
+ public ActivityChooserView(Context context) {
+ this(context, null);
+ }
+
+ /**
+ * Create a new instance.
+ *
+ * @param context The application environment.
+ * @param attrs A collection of attributes.
+ */
+ public ActivityChooserView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ /**
+ * Create a new instance.
+ *
+ * @param context The application environment.
+ * @param attrs A collection of attributes.
+ * @param defStyle The default style to apply to this view.
+ */
+ public ActivityChooserView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mContext = context;
+
+ TypedArray attributesArray = context.obtainStyledAttributes(attrs,
+ R.styleable.SherlockActivityChooserView, defStyle, 0);
+
+ mInitialActivityCount = attributesArray.getInt(
+ R.styleable.SherlockActivityChooserView_initialActivityCount,
+ ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_DEFAULT);
+
+ Drawable expandActivityOverflowButtonDrawable = attributesArray.getDrawable(
+ R.styleable.SherlockActivityChooserView_expandActivityOverflowButtonDrawable);
+
+ attributesArray.recycle();
+
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ inflater.inflate(R.layout.abs__activity_chooser_view, this, true);
+
+ mCallbacks = new Callbacks();
+
+ mActivityChooserContent = (IcsLinearLayout) findViewById(R.id.abs__activity_chooser_view_content);
+ mActivityChooserContentBackground = mActivityChooserContent.getBackground();
+
+ mDefaultActivityButton = (FrameLayout) findViewById(R.id.abs__default_activity_button);
+ mDefaultActivityButton.setOnClickListener(mCallbacks);
+ mDefaultActivityButton.setOnLongClickListener(mCallbacks);
+ mDefaultActivityButtonImage = (ImageView) mDefaultActivityButton.findViewById(R.id.abs__image);
+
+ mExpandActivityOverflowButton = (FrameLayout) findViewById(R.id.abs__expand_activities_button);
+ mExpandActivityOverflowButton.setOnClickListener(mCallbacks);
+ mExpandActivityOverflowButtonImage =
+ (ImageView) mExpandActivityOverflowButton.findViewById(R.id.abs__image);
+ mExpandActivityOverflowButtonImage.setImageDrawable(expandActivityOverflowButtonDrawable);
+
+ mAdapter = new ActivityChooserViewAdapter();
+ mAdapter.registerDataSetObserver(new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ super.onChanged();
+ updateAppearance();
+ }
+ });
+
+ Resources resources = context.getResources();
+ mListPopupMaxWidth = Math.max(resources.getDisplayMetrics().widthPixels / 2,
+ resources.getDimensionPixelSize(R.dimen.abs__config_prefDialogWidth));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setActivityChooserModel(ActivityChooserModel dataModel) {
+ mAdapter.setDataModel(dataModel);
+ if (isShowingPopup()) {
+ dismissPopup();
+ showPopup();
+ }
+ }
+
+ /**
+ * Sets the background for the button that expands the activity
+ * overflow list.
+ *
+ * Note: Clients would like to set this drawable
+ * as a clue about the action the chosen activity will perform. For
+ * example, if a share activity is to be chosen the drawable should
+ * give a clue that sharing is to be performed.
+ *
+ * @param drawable The drawable.
+ */
+ public void setExpandActivityOverflowButtonDrawable(Drawable drawable) {
+ mExpandActivityOverflowButtonImage.setImageDrawable(drawable);
+ }
+
+ /**
+ * Sets the content description for the button that expands the activity
+ * overflow list.
+ *
+ * description as a clue about the action performed by the button.
+ * For example, if a share activity is to be chosen the content
+ * description should be something like "Share with".
+ *
+ * @param resourceId The content description resource id.
+ */
+ public void setExpandActivityOverflowButtonContentDescription(int resourceId) {
+ CharSequence contentDescription = mContext.getString(resourceId);
+ mExpandActivityOverflowButtonImage.setContentDescription(contentDescription);
+ }
+
+ /**
+ * Set the provider hosting this view, if applicable.
+ * @hide Internal use only
+ */
+ public void setProvider(ActionProvider provider) {
+ mProvider = provider;
+ }
+
+ /**
+ * Shows the popup window with activities.
+ *
+ * @return True if the popup was shown, false if already showing.
+ */
+ public boolean showPopup() {
+ if (isShowingPopup() || !mIsAttachedToWindow) {
+ return false;
+ }
+ mIsSelectingDefaultActivity = false;
+ showPopupUnchecked(mInitialActivityCount);
+ return true;
+ }
+
+ /**
+ * Shows the popup no matter if it was already showing.
+ *
+ * @param maxActivityCount The max number of activities to display.
+ */
+ private void showPopupUnchecked(int maxActivityCount) {
+ if (mAdapter.getDataModel() == null) {
+ throw new IllegalStateException("No data model. Did you call #setDataModel?");
+ }
+
+ getViewTreeObserver().addOnGlobalLayoutListener(mOnGlobalLayoutListener);
+
+ final boolean defaultActivityButtonShown =
+ mDefaultActivityButton.getVisibility() == VISIBLE;
+
+ final int activityCount = mAdapter.getActivityCount();
+ final int maxActivityCountOffset = defaultActivityButtonShown ? 1 : 0;
+ if (maxActivityCount != ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED
+ && activityCount > maxActivityCount + maxActivityCountOffset) {
+ mAdapter.setShowFooterView(true);
+ mAdapter.setMaxActivityCount(maxActivityCount - 1);
+ } else {
+ mAdapter.setShowFooterView(false);
+ mAdapter.setMaxActivityCount(maxActivityCount);
+ }
+
+ IcsListPopupWindow popupWindow = getListPopupWindow();
+ if (!popupWindow.isShowing()) {
+ if (mIsSelectingDefaultActivity || !defaultActivityButtonShown) {
+ mAdapter.setShowDefaultActivity(true, defaultActivityButtonShown);
+ } else {
+ mAdapter.setShowDefaultActivity(false, false);
+ }
+ final int contentWidth = Math.min(mAdapter.measureContentWidth(), mListPopupMaxWidth);
+ popupWindow.setContentWidth(contentWidth);
+ popupWindow.show();
+ if (mProvider != null) {
+ mProvider.subUiVisibilityChanged(true);
+ }
+ popupWindow.getListView().setContentDescription(mContext.getString(
+ R.string.abs__activitychooserview_choose_application));
+ }
+ }
+
+ /**
+ * Dismisses the popup window with activities.
+ *
+ * @return True if dismissed, false if already dismissed.
+ */
+ public boolean dismissPopup() {
+ if (isShowingPopup()) {
+ getListPopupWindow().dismiss();
+ ViewTreeObserver viewTreeObserver = getViewTreeObserver();
+ if (viewTreeObserver.isAlive()) {
+ viewTreeObserver.removeGlobalOnLayoutListener(mOnGlobalLayoutListener);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Gets whether the popup window with activities is shown.
+ *
+ * @return True if the popup is shown.
+ */
+ public boolean isShowingPopup() {
+ return getListPopupWindow().isShowing();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ ActivityChooserModel dataModel = mAdapter.getDataModel();
+ if (dataModel != null) {
+ dataModel.registerObserver(mModelDataSetOberver);
+ }
+ mIsAttachedToWindow = true;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ ActivityChooserModel dataModel = mAdapter.getDataModel();
+ if (dataModel != null) {
+ try {
+ dataModel.unregisterObserver(mModelDataSetOberver);
+ } catch (IllegalStateException e) {
+ //Oh, well... fixes issue #557
+ }
+ }
+ ViewTreeObserver viewTreeObserver = getViewTreeObserver();
+ if (viewTreeObserver.isAlive()) {
+ viewTreeObserver.removeGlobalOnLayoutListener(mOnGlobalLayoutListener);
+ }
+ mIsAttachedToWindow = false;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ View child = mActivityChooserContent;
+ // If the default action is not visible we want to be as tall as the
+ // ActionBar so if this widget is used in the latter it will look as
+ // a normal action button.
+ if (mDefaultActivityButton.getVisibility() != VISIBLE) {
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
+ MeasureSpec.EXACTLY);
+ }
+ measureChild(child, widthMeasureSpec, heightMeasureSpec);
+ setMeasuredDimension(child.getMeasuredWidth(), child.getMeasuredHeight());
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ mActivityChooserContent.layout(0, 0, right - left, bottom - top);
+ if (getListPopupWindow().isShowing()) {
+ showPopupUnchecked(mAdapter.getMaxActivityCount());
+ } else {
+ dismissPopup();
+ }
+ }
+
+ public ActivityChooserModel getDataModel() {
+ return mAdapter.getDataModel();
+ }
+
+ /**
+ * Sets a listener to receive a callback when the popup is dismissed.
+ *
+ * @param listener The listener to be notified.
+ */
+ public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
+ mOnDismissListener = listener;
+ }
+
+ /**
+ * Sets the initial count of items shown in the activities popup
+ * i.e. the items before the popup is expanded. This is an upper
+ * bound since it is not guaranteed that such number of intent
+ * handlers exist.
+ *
+ * @param itemCount The initial popup item count.
+ */
+ public void setInitialActivityCount(int itemCount) {
+ mInitialActivityCount = itemCount;
+ }
+
+ /**
+ * Sets a content description of the default action button. This
+ * resource should be a string taking one formatting argument and
+ * will be used for formatting the content description of the button
+ * dynamically as the default target changes. For example, a resource
+ * pointing to the string "share with %1$s" will result in a content
+ * description "share with Bluetooth" for the Bluetooth activity.
+ *
+ * @param resourceId The resource id.
+ */
+ public void setDefaultActionButtonContentDescription(int resourceId) {
+ mDefaultActionButtonContentDescription = resourceId;
+ }
+
+ /**
+ * Gets the list popup window which is lazily initialized.
+ *
+ * @return The popup.
+ */
+ private IcsListPopupWindow getListPopupWindow() {
+ if (mListPopupWindow == null) {
+ mListPopupWindow = new IcsListPopupWindow(getContext());
+ mListPopupWindow.setAdapter(mAdapter);
+ mListPopupWindow.setAnchorView(ActivityChooserView.this);
+ mListPopupWindow.setModal(true);
+ mListPopupWindow.setOnItemClickListener(mCallbacks);
+ mListPopupWindow.setOnDismissListener(mCallbacks);
+ }
+ return mListPopupWindow;
+ }
+
+ /**
+ * Updates the buttons state.
+ */
+ private void updateAppearance() {
+ // Expand overflow button.
+ if (mAdapter.getCount() > 0) {
+ mExpandActivityOverflowButton.setEnabled(true);
+ } else {
+ mExpandActivityOverflowButton.setEnabled(false);
+ }
+ // Default activity button.
+ final int activityCount = mAdapter.getActivityCount();
+ final int historySize = mAdapter.getHistorySize();
+ if (activityCount > 0 && historySize > 0) {
+ mDefaultActivityButton.setVisibility(VISIBLE);
+ ResolveInfo activity = mAdapter.getDefaultActivity();
+ PackageManager packageManager = mContext.getPackageManager();
+ mDefaultActivityButtonImage.setImageDrawable(activity.loadIcon(packageManager));
+ if (mDefaultActionButtonContentDescription != 0) {
+ CharSequence label = activity.loadLabel(packageManager);
+ String contentDescription = mContext.getString(
+ mDefaultActionButtonContentDescription, label);
+ mDefaultActivityButton.setContentDescription(contentDescription);
+ }
+ } else {
+ mDefaultActivityButton.setVisibility(View.GONE);
+ }
+ // Activity chooser content.
+ if (mDefaultActivityButton.getVisibility() == VISIBLE) {
+ mActivityChooserContent.setBackgroundDrawable(mActivityChooserContentBackground);
+ } else {
+ mActivityChooserContent.setBackgroundDrawable(null);
+ mActivityChooserContent.setPadding(0, 0, 0, 0);
+ }
+ }
+
+ /**
+ * Interface implementation to avoid publishing them in the APIs.
+ */
+ private class Callbacks implements AdapterView.OnItemClickListener,
+ View.OnClickListener, View.OnLongClickListener, PopupWindow.OnDismissListener {
+
+ // AdapterView#OnItemClickListener
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ ActivityChooserViewAdapter adapter = (ActivityChooserViewAdapter) parent.getAdapter();
+ final int itemViewType = adapter.getItemViewType(position);
+ switch (itemViewType) {
+ case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_FOOTER: {
+ showPopupUnchecked(ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED);
+ } break;
+ case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_ACTIVITY: {
+ dismissPopup();
+ if (mIsSelectingDefaultActivity) {
+ // The item at position zero is the default already.
+ if (position > 0) {
+ mAdapter.getDataModel().setDefaultActivity(position);
+ }
+ } else {
+ // If the default target is not shown in the list, the first
+ // item in the model is default action => adjust index
+ position = mAdapter.getShowDefaultActivity() ? position : position + 1;
+ Intent launchIntent = mAdapter.getDataModel().chooseActivity(position);
+ if (launchIntent != null) {
+ mContext.startActivity(launchIntent);
+ }
+ }
+ } break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ // View.OnClickListener
+ public void onClick(View view) {
+ if (view == mDefaultActivityButton) {
+ dismissPopup();
+ ResolveInfo defaultActivity = mAdapter.getDefaultActivity();
+ final int index = mAdapter.getDataModel().getActivityIndex(defaultActivity);
+ Intent launchIntent = mAdapter.getDataModel().chooseActivity(index);
+ if (launchIntent != null) {
+ mContext.startActivity(launchIntent);
+ }
+ } else if (view == mExpandActivityOverflowButton) {
+ mIsSelectingDefaultActivity = false;
+ showPopupUnchecked(mInitialActivityCount);
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ // OnLongClickListener#onLongClick
+ @Override
+ public boolean onLongClick(View view) {
+ if (view == mDefaultActivityButton) {
+ if (mAdapter.getCount() > 0) {
+ mIsSelectingDefaultActivity = true;
+ showPopupUnchecked(mInitialActivityCount);
+ }
+ } else {
+ throw new IllegalArgumentException();
+ }
+ return true;
+ }
+
+ // PopUpWindow.OnDismissListener#onDismiss
+ public void onDismiss() {
+ notifyOnDismissListener();
+ if (mProvider != null) {
+ mProvider.subUiVisibilityChanged(false);
+ }
+ }
+
+ private void notifyOnDismissListener() {
+ if (mOnDismissListener != null) {
+ mOnDismissListener.onDismiss();
+ }
+ }
+ }
+
+ private static class SetActivated {
+ public static void invoke(View view, boolean activated) {
+ view.setActivated(activated);
+ }
+ }
+
+ private static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
+
+ /**
+ * Adapter for backing the list of activities shown in the popup.
+ */
+ private class ActivityChooserViewAdapter extends BaseAdapter {
+
+ public static final int MAX_ACTIVITY_COUNT_UNLIMITED = Integer.MAX_VALUE;
+
+ public static final int MAX_ACTIVITY_COUNT_DEFAULT = 4;
+
+ private static final int ITEM_VIEW_TYPE_ACTIVITY = 0;
+
+ private static final int ITEM_VIEW_TYPE_FOOTER = 1;
+
+ private static final int ITEM_VIEW_TYPE_COUNT = 3;
+
+ private ActivityChooserModel mDataModel;
+
+ private int mMaxActivityCount = MAX_ACTIVITY_COUNT_DEFAULT;
+
+ private boolean mShowDefaultActivity;
+
+ private boolean mHighlightDefaultActivity;
+
+ private boolean mShowFooterView;
+
+ public void setDataModel(ActivityChooserModel dataModel) {
+ ActivityChooserModel oldDataModel = mAdapter.getDataModel();
+ if (oldDataModel != null && isShown()) {
+ try {
+ oldDataModel.unregisterObserver(mModelDataSetOberver);
+ } catch (IllegalStateException e) {
+ //Oh, well... fixes issue #557
+ }
+ }
+ mDataModel = dataModel;
+ if (dataModel != null && isShown()) {
+ dataModel.registerObserver(mModelDataSetOberver);
+ }
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (mShowFooterView && position == getCount() - 1) {
+ return ITEM_VIEW_TYPE_FOOTER;
+ } else {
+ return ITEM_VIEW_TYPE_ACTIVITY;
+ }
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return ITEM_VIEW_TYPE_COUNT;
+ }
+
+ public int getCount() {
+ int count = 0;
+ int activityCount = mDataModel.getActivityCount();
+ if (!mShowDefaultActivity && mDataModel.getDefaultActivity() != null) {
+ activityCount--;
+ }
+ count = Math.min(activityCount, mMaxActivityCount);
+ if (mShowFooterView) {
+ count++;
+ }
+ return count;
+ }
+
+ public Object getItem(int position) {
+ final int itemViewType = getItemViewType(position);
+ switch (itemViewType) {
+ case ITEM_VIEW_TYPE_FOOTER:
+ return null;
+ case ITEM_VIEW_TYPE_ACTIVITY:
+ if (!mShowDefaultActivity && mDataModel.getDefaultActivity() != null) {
+ position++;
+ }
+ return mDataModel.getActivity(position);
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final int itemViewType = getItemViewType(position);
+ switch (itemViewType) {
+ case ITEM_VIEW_TYPE_FOOTER:
+ if (convertView == null || convertView.getId() != ITEM_VIEW_TYPE_FOOTER) {
+ convertView = LayoutInflater.from(getContext()).inflate(
+ R.layout.abs__activity_chooser_view_list_item, parent, false);
+ convertView.setId(ITEM_VIEW_TYPE_FOOTER);
+ TextView titleView = (TextView) convertView.findViewById(R.id.abs__title);
+ titleView.setText(mContext.getString(
+ R.string.abs__activity_chooser_view_see_all));
+ }
+ return convertView;
+ case ITEM_VIEW_TYPE_ACTIVITY:
+ if (convertView == null || convertView.getId() != R.id.abs__list_item) {
+ convertView = LayoutInflater.from(getContext()).inflate(
+ R.layout.abs__activity_chooser_view_list_item, parent, false);
+ }
+ PackageManager packageManager = mContext.getPackageManager();
+ // Set the icon
+ ImageView iconView = (ImageView) convertView.findViewById(R.id.abs__icon);
+ ResolveInfo activity = (ResolveInfo) getItem(position);
+ iconView.setImageDrawable(activity.loadIcon(packageManager));
+ // Set the title.
+ TextView titleView = (TextView) convertView.findViewById(R.id.abs__title);
+ titleView.setText(activity.loadLabel(packageManager));
+ if (IS_HONEYCOMB) {
+ // Highlight the default.
+ if (mShowDefaultActivity && position == 0 && mHighlightDefaultActivity) {
+ SetActivated.invoke(convertView, true);
+ } else {
+ SetActivated.invoke(convertView, false);
+ }
+ }
+ return convertView;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public int measureContentWidth() {
+ // The user may have specified some of the target not to be shown but we
+ // want to measure all of them since after expansion they should fit.
+ final int oldMaxActivityCount = mMaxActivityCount;
+ mMaxActivityCount = MAX_ACTIVITY_COUNT_UNLIMITED;
+
+ int contentWidth = 0;
+ View itemView = null;
+
+ final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int count = getCount();
+
+ for (int i = 0; i < count; i++) {
+ itemView = getView(i, itemView, null);
+ itemView.measure(widthMeasureSpec, heightMeasureSpec);
+ contentWidth = Math.max(contentWidth, itemView.getMeasuredWidth());
+ }
+
+ mMaxActivityCount = oldMaxActivityCount;
+
+ return contentWidth;
+ }
+
+ public void setMaxActivityCount(int maxActivityCount) {
+ if (mMaxActivityCount != maxActivityCount) {
+ mMaxActivityCount = maxActivityCount;
+ notifyDataSetChanged();
+ }
+ }
+
+ public ResolveInfo getDefaultActivity() {
+ return mDataModel.getDefaultActivity();
+ }
+
+ public void setShowFooterView(boolean showFooterView) {
+ if (mShowFooterView != showFooterView) {
+ mShowFooterView = showFooterView;
+ notifyDataSetChanged();
+ }
+ }
+
+ public int getActivityCount() {
+ return mDataModel.getActivityCount();
+ }
+
+ public int getHistorySize() {
+ return mDataModel.getHistorySize();
+ }
+
+ public int getMaxActivityCount() {
+ return mMaxActivityCount;
+ }
+
+ public ActivityChooserModel getDataModel() {
+ return mDataModel;
+ }
+
+ public void setShowDefaultActivity(boolean showDefaultActivity,
+ boolean highlightDefaultActivity) {
+ if (mShowDefaultActivity != showDefaultActivity
+ || mHighlightDefaultActivity != highlightDefaultActivity) {
+ mShowDefaultActivity = showDefaultActivity;
+ mHighlightDefaultActivity = highlightDefaultActivity;
+ notifyDataSetChanged();
+ }
+ }
+
+ public boolean getShowDefaultActivity() {
+ return mShowDefaultActivity;
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/widget/SearchView.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/widget/SearchView.java
new file mode 100644
index 0000000000..c9e7897d43
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/widget/SearchView.java
@@ -0,0 +1,1811 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.widget;
+
+import android.app.PendingIntent;
+import android.app.SearchManager;
+import android.app.SearchableInfo;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.database.Cursor;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.speech.RecognizerIntent;
+import android.support.v4.view.KeyEventCompat;
+import android.support.v4.widget.CursorAdapter;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.text.style.ImageSpan;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.AutoCompleteTextView;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.view.CollapsibleActionView;
+
+import java.lang.reflect.Method;
+import java.util.WeakHashMap;
+
+import static com.actionbarsherlock.widget.SuggestionsAdapter.getColumnString;
+
+/**
+ * A widget that provides a user interface for the user to enter a search query and submit a request
+ * to a search provider. Shows a list of query suggestions or results, if available, and allows the
+ * user to pick a suggestion or result to launch into.
+ *
+ *
+ * When the SearchView is used in an ActionBar as an action view for a collapsible menu item, it
+ * needs to be set to iconified by default using {@link #setIconifiedByDefault(boolean)
+ * setIconifiedByDefault(true)}. This is the default, so nothing needs to be done.
+ *
+ *
+ * If you want the search field to always be visible, then call setIconifiedByDefault(false).
+ *
+ *
+ *
+ *
Developer Guides
+ *
For information about using {@code SearchView}, read the
+ * Search developer guide.
+ *
+ *
+ * @see android.view.MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW
+ * @attr ref android.R.styleable#SearchView_iconifiedByDefault
+ * @attr ref android.R.styleable#SearchView_imeOptions
+ * @attr ref android.R.styleable#SearchView_inputType
+ * @attr ref android.R.styleable#SearchView_maxWidth
+ * @attr ref android.R.styleable#SearchView_queryHint
+ */
+public class SearchView extends LinearLayout implements CollapsibleActionView {
+
+ private static final boolean DBG = false;
+ private static final String LOG_TAG = "SearchView";
+
+ /**
+ * Private constant for removing the microphone in the keyboard.
+ */
+ private static final String IME_OPTION_NO_MICROPHONE = "nm";
+
+ private OnQueryTextListener mOnQueryChangeListener;
+ private OnCloseListener mOnCloseListener;
+ private OnFocusChangeListener mOnQueryTextFocusChangeListener;
+ private OnSuggestionListener mOnSuggestionListener;
+ private OnClickListener mOnSearchClickListener;
+
+ private boolean mIconifiedByDefault;
+ private boolean mIconified;
+ private CursorAdapter mSuggestionsAdapter;
+ private View mSearchButton;
+ private View mSubmitButton;
+ private View mSearchPlate;
+ private View mSubmitArea;
+ private ImageView mCloseButton;
+ private View mSearchEditFrame;
+ private View mVoiceButton;
+ private SearchAutoComplete mQueryTextView;
+ private View mDropDownAnchor;
+ private ImageView mSearchHintIcon;
+ private boolean mSubmitButtonEnabled;
+ private CharSequence mQueryHint;
+ private boolean mQueryRefinement;
+ private boolean mClearingFocus;
+ private int mMaxWidth;
+ private boolean mVoiceButtonEnabled;
+ private CharSequence mOldQueryText;
+ private CharSequence mUserQuery;
+ private boolean mExpandedInActionView;
+ private int mCollapsedImeOptions;
+
+ private SearchableInfo mSearchable;
+ private Bundle mAppSearchData;
+
+ /*
+ * SearchView can be set expanded before the IME is ready to be shown during
+ * initial UI setup. The show operation is asynchronous to account for this.
+ */
+ private Runnable mShowImeRunnable = new Runnable() {
+ public void run() {
+ InputMethodManager imm = (InputMethodManager)
+ getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ if (imm != null) {
+ showSoftInputUnchecked(SearchView.this, imm, 0);
+ }
+ }
+ };
+
+ private Runnable mUpdateDrawableStateRunnable = new Runnable() {
+ public void run() {
+ updateFocusedState();
+ }
+ };
+
+ private Runnable mReleaseCursorRunnable = new Runnable() {
+ public void run() {
+ if (mSuggestionsAdapter != null && mSuggestionsAdapter instanceof SuggestionsAdapter) {
+ mSuggestionsAdapter.changeCursor(null);
+ }
+ }
+ };
+
+ // For voice searching
+ private final Intent mVoiceWebSearchIntent;
+ private final Intent mVoiceAppSearchIntent;
+
+ // A weak map of drawables we've gotten from other packages, so we don't load them
+ // more than once.
+ private final WeakHashMap mOutsideDrawablesCache =
+ new WeakHashMap();
+
+ /**
+ * Callbacks for changes to the query text.
+ */
+ public interface OnQueryTextListener {
+
+ /**
+ * Called when the user submits the query. This could be due to a key press on the
+ * keyboard or due to pressing a submit button.
+ * The listener can override the standard behavior by returning true
+ * to indicate that it has handled the submit request. Otherwise return false to
+ * let the SearchView handle the submission by launching any associated intent.
+ *
+ * @param query the query text that is to be submitted
+ *
+ * @return true if the query has been handled by the listener, false to let the
+ * SearchView perform the default action.
+ */
+ boolean onQueryTextSubmit(String query);
+
+ /**
+ * Called when the query text is changed by the user.
+ *
+ * @param newText the new content of the query text field.
+ *
+ * @return false if the SearchView should perform the default action of showing any
+ * suggestions if available, true if the action was handled by the listener.
+ */
+ boolean onQueryTextChange(String newText);
+ }
+
+ public interface OnCloseListener {
+
+ /**
+ * The user is attempting to close the SearchView.
+ *
+ * @return true if the listener wants to override the default behavior of clearing the
+ * text field and dismissing it, false otherwise.
+ */
+ boolean onClose();
+ }
+
+ /**
+ * Callback interface for selection events on suggestions. These callbacks
+ * are only relevant when a SearchableInfo has been specified by {@link #setSearchableInfo}.
+ */
+ public interface OnSuggestionListener {
+
+ /**
+ * Called when a suggestion was selected by navigating to it.
+ * @param position the absolute position in the list of suggestions.
+ *
+ * @return true if the listener handles the event and wants to override the default
+ * behavior of possibly rewriting the query based on the selected item, false otherwise.
+ */
+ boolean onSuggestionSelect(int position);
+
+ /**
+ * Called when a suggestion was clicked.
+ * @param position the absolute position of the clicked item in the list of suggestions.
+ *
+ * @return true if the listener handles the event and wants to override the default
+ * behavior of launching any intent or submitting a search query specified on that item.
+ * Return false otherwise.
+ */
+ boolean onSuggestionClick(int position);
+ }
+
+ public SearchView(Context context) {
+ this(context, null);
+ }
+
+ public SearchView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
+ throw new IllegalStateException("SearchView is API 8+ only.");
+ }
+
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.abs__search_view, this, true);
+
+ mSearchButton = findViewById(R.id.abs__search_button);
+ mQueryTextView = (SearchAutoComplete) findViewById(R.id.abs__search_src_text);
+ mQueryTextView.setSearchView(this);
+
+ mSearchEditFrame = findViewById(R.id.abs__search_edit_frame);
+ mSearchPlate = findViewById(R.id.abs__search_plate);
+ mSubmitArea = findViewById(R.id.abs__submit_area);
+ mSubmitButton = findViewById(R.id.abs__search_go_btn);
+ mCloseButton = (ImageView) findViewById(R.id.abs__search_close_btn);
+ mVoiceButton = findViewById(R.id.abs__search_voice_btn);
+ mSearchHintIcon = (ImageView) findViewById(R.id.abs__search_mag_icon);
+
+ mSearchButton.setOnClickListener(mOnClickListener);
+ mCloseButton.setOnClickListener(mOnClickListener);
+ mSubmitButton.setOnClickListener(mOnClickListener);
+ mVoiceButton.setOnClickListener(mOnClickListener);
+ mQueryTextView.setOnClickListener(mOnClickListener);
+
+ mQueryTextView.addTextChangedListener(mTextWatcher);
+ mQueryTextView.setOnEditorActionListener(mOnEditorActionListener);
+ mQueryTextView.setOnItemClickListener(mOnItemClickListener);
+ mQueryTextView.setOnItemSelectedListener(mOnItemSelectedListener);
+ mQueryTextView.setOnKeyListener(mTextKeyListener);
+ // Inform any listener of focus changes
+ mQueryTextView.setOnFocusChangeListener(new OnFocusChangeListener() {
+
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (mOnQueryTextFocusChangeListener != null) {
+ mOnQueryTextFocusChangeListener.onFocusChange(SearchView.this, hasFocus);
+ }
+ }
+ });
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SherlockSearchView, 0, 0);
+ setIconifiedByDefault(a.getBoolean(R.styleable.SherlockSearchView_iconifiedByDefault, true));
+ int maxWidth = a.getDimensionPixelSize(R.styleable.SherlockSearchView_android_maxWidth, -1);
+ if (maxWidth != -1) {
+ setMaxWidth(maxWidth);
+ }
+ CharSequence queryHint = a.getText(R.styleable.SherlockSearchView_queryHint);
+ if (!TextUtils.isEmpty(queryHint)) {
+ setQueryHint(queryHint);
+ }
+ int imeOptions = a.getInt(R.styleable.SherlockSearchView_android_imeOptions, -1);
+ if (imeOptions != -1) {
+ setImeOptions(imeOptions);
+ }
+ int inputType = a.getInt(R.styleable.SherlockSearchView_android_inputType, -1);
+ if (inputType != -1) {
+ setInputType(inputType);
+ }
+
+ a.recycle();
+
+ boolean focusable = true;
+
+ a = context.obtainStyledAttributes(attrs, R.styleable.SherlockView, 0, 0);
+ focusable = a.getBoolean(R.styleable.SherlockView_android_focusable, focusable);
+ a.recycle();
+ setFocusable(focusable);
+
+ // Save voice intent for later queries/launching
+ mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
+ mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
+ RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
+
+ mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+ mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ mDropDownAnchor = findViewById(mQueryTextView.getDropDownAnchor());
+ if (mDropDownAnchor != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ mDropDownAnchor.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ adjustDropDownSizeAndPosition();
+ }
+ });
+ } else {
+ mDropDownAnchor.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override public void onGlobalLayout() {
+ adjustDropDownSizeAndPosition();
+ }
+ });
+ }
+ }
+
+ updateViewsVisibility(mIconifiedByDefault);
+ updateQueryHint();
+ }
+
+ /**
+ * Sets the SearchableInfo for this SearchView. Properties in the SearchableInfo are used
+ * to display labels, hints, suggestions, create intents for launching search results screens
+ * and controlling other affordances such as a voice button.
+ *
+ * @param searchable a SearchableInfo can be retrieved from the SearchManager, for a specific
+ * activity or a global search provider.
+ */
+ public void setSearchableInfo(SearchableInfo searchable) {
+ mSearchable = searchable;
+ if (mSearchable != null) {
+ updateSearchAutoComplete();
+ updateQueryHint();
+ }
+ // Cache the voice search capability
+ mVoiceButtonEnabled = hasVoiceSearch();
+
+ if (mVoiceButtonEnabled) {
+ // Disable the microphone on the keyboard, as a mic is displayed near the text box
+ // TODO: use imeOptions to disable voice input when the new API will be available
+ mQueryTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
+ }
+ updateViewsVisibility(isIconified());
+ }
+
+ /**
+ * Sets the APP_DATA for legacy SearchDialog use.
+ * @param appSearchData bundle provided by the app when launching the search dialog
+ * @hide
+ */
+ public void setAppSearchData(Bundle appSearchData) {
+ mAppSearchData = appSearchData;
+ }
+
+ /**
+ * Sets the IME options on the query text field.
+ *
+ * @see TextView#setImeOptions(int)
+ * @param imeOptions the options to set on the query text field
+ *
+ * @attr ref android.R.styleable#SearchView_imeOptions
+ */
+ public void setImeOptions(int imeOptions) {
+ mQueryTextView.setImeOptions(imeOptions);
+ }
+
+ /**
+ * Returns the IME options set on the query text field.
+ * @return the ime options
+ * @see TextView#setImeOptions(int)
+ *
+ * @attr ref android.R.styleable#SearchView_imeOptions
+ */
+ public int getImeOptions() {
+ return mQueryTextView.getImeOptions();
+ }
+
+ /**
+ * Sets the input type on the query text field.
+ *
+ * @see TextView#setInputType(int)
+ * @param inputType the input type to set on the query text field
+ *
+ * @attr ref android.R.styleable#SearchView_inputType
+ */
+ public void setInputType(int inputType) {
+ mQueryTextView.setInputType(inputType);
+ }
+
+ /**
+ * Returns the input type set on the query text field.
+ * @return the input type
+ *
+ * @attr ref android.R.styleable#SearchView_inputType
+ */
+ public int getInputType() {
+ return mQueryTextView.getInputType();
+ }
+
+ /** @hide */
+ @Override
+ public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
+ // Don't accept focus if in the middle of clearing focus
+ if (mClearingFocus) return false;
+ // Check if SearchView is focusable.
+ if (!isFocusable()) return false;
+ // If it is not iconified, then give the focus to the text field
+ if (!isIconified()) {
+ boolean result = mQueryTextView.requestFocus(direction, previouslyFocusedRect);
+ if (result) {
+ updateViewsVisibility(false);
+ }
+ return result;
+ } else {
+ return super.requestFocus(direction, previouslyFocusedRect);
+ }
+ }
+
+ /** @hide */
+ @Override
+ public void clearFocus() {
+ mClearingFocus = true;
+ setImeVisibility(false);
+ super.clearFocus();
+ mQueryTextView.clearFocus();
+ mClearingFocus = false;
+ }
+
+ /**
+ * Sets a listener for user actions within the SearchView.
+ *
+ * @param listener the listener object that receives callbacks when the user performs
+ * actions in the SearchView such as clicking on buttons or typing a query.
+ */
+ public void setOnQueryTextListener(OnQueryTextListener listener) {
+ mOnQueryChangeListener = listener;
+ }
+
+ /**
+ * Sets a listener to inform when the user closes the SearchView.
+ *
+ * @param listener the listener to call when the user closes the SearchView.
+ */
+ public void setOnCloseListener(OnCloseListener listener) {
+ mOnCloseListener = listener;
+ }
+
+ /**
+ * Sets a listener to inform when the focus of the query text field changes.
+ *
+ * @param listener the listener to inform of focus changes.
+ */
+ public void setOnQueryTextFocusChangeListener(OnFocusChangeListener listener) {
+ mOnQueryTextFocusChangeListener = listener;
+ }
+
+ /**
+ * Sets a listener to inform when a suggestion is focused or clicked.
+ *
+ * @param listener the listener to inform of suggestion selection events.
+ */
+ public void setOnSuggestionListener(OnSuggestionListener listener) {
+ mOnSuggestionListener = listener;
+ }
+
+ /**
+ * Sets a listener to inform when the search button is pressed. This is only
+ * relevant when the text field is not visible by default. Calling {@link #setIconified
+ * setIconified(false)} can also cause this listener to be informed.
+ *
+ * @param listener the listener to inform when the search button is clicked or
+ * the text field is programmatically de-iconified.
+ */
+ public void setOnSearchClickListener(OnClickListener listener) {
+ mOnSearchClickListener = listener;
+ }
+
+ /**
+ * Returns the query string currently in the text field.
+ *
+ * @return the query string
+ */
+ public CharSequence getQuery() {
+ return mQueryTextView.getText();
+ }
+
+ /**
+ * Sets a query string in the text field and optionally submits the query as well.
+ *
+ * @param query the query string. This replaces any query text already present in the
+ * text field.
+ * @param submit whether to submit the query right now or only update the contents of
+ * text field.
+ */
+ public void setQuery(CharSequence query, boolean submit) {
+ mQueryTextView.setText(query);
+ if (query != null) {
+ mQueryTextView.setSelection(mQueryTextView.length());
+ mUserQuery = query;
+ }
+
+ // If the query is not empty and submit is requested, submit the query
+ if (submit && !TextUtils.isEmpty(query)) {
+ onSubmitQuery();
+ }
+ }
+
+ /**
+ * Sets the hint text to display in the query text field. This overrides any hint specified
+ * in the SearchableInfo.
+ *
+ * @param hint the hint text to display
+ *
+ * @attr ref android.R.styleable#SearchView_queryHint
+ */
+ public void setQueryHint(CharSequence hint) {
+ mQueryHint = hint;
+ updateQueryHint();
+ }
+
+ /**
+ * Gets the hint text to display in the query text field.
+ * @return the query hint text, if specified, null otherwise.
+ *
+ * @attr ref android.R.styleable#SearchView_queryHint
+ */
+ public CharSequence getQueryHint() {
+ if (mQueryHint != null) {
+ return mQueryHint;
+ } else if (mSearchable != null) {
+ CharSequence hint = null;
+ int hintId = mSearchable.getHintId();
+ if (hintId != 0) {
+ hint = getContext().getString(hintId);
+ }
+ return hint;
+ }
+ return null;
+ }
+
+ /**
+ * Sets the default or resting state of the search field. If true, a single search icon is
+ * shown by default and expands to show the text field and other buttons when pressed. Also,
+ * if the default state is iconified, then it collapses to that state when the close button
+ * is pressed. Changes to this property will take effect immediately.
+ *
+ *
The default value is true.
+ *
+ * @param iconified whether the search field should be iconified by default
+ *
+ * @attr ref android.R.styleable#SearchView_iconifiedByDefault
+ */
+ public void setIconifiedByDefault(boolean iconified) {
+ if (mIconifiedByDefault == iconified) return;
+ mIconifiedByDefault = iconified;
+ updateViewsVisibility(iconified);
+ updateQueryHint();
+ }
+
+ /**
+ * Returns the default iconified state of the search field.
+ * @return
+ *
+ * @attr ref android.R.styleable#SearchView_iconifiedByDefault
+ */
+ public boolean isIconfiedByDefault() {
+ return mIconifiedByDefault;
+ }
+
+ /**
+ * Iconifies or expands the SearchView. Any query text is cleared when iconified. This is
+ * a temporary state and does not override the default iconified state set by
+ * {@link #setIconifiedByDefault(boolean)}. If the default state is iconified, then
+ * a false here will only be valid until the user closes the field. And if the default
+ * state is expanded, then a true here will only clear the text field and not close it.
+ *
+ * @param iconify a true value will collapse the SearchView to an icon, while a false will
+ * expand it.
+ */
+ public void setIconified(boolean iconify) {
+ if (iconify) {
+ onCloseClicked();
+ } else {
+ onSearchClicked();
+ }
+ }
+
+ /**
+ * Returns the current iconified state of the SearchView.
+ *
+ * @return true if the SearchView is currently iconified, false if the search field is
+ * fully visible.
+ */
+ public boolean isIconified() {
+ return mIconified;
+ }
+
+ /**
+ * Enables showing a submit button when the query is non-empty. In cases where the SearchView
+ * is being used to filter the contents of the current activity and doesn't launch a separate
+ * results activity, then the submit button should be disabled.
+ *
+ * @param enabled true to show a submit button for submitting queries, false if a submit
+ * button is not required.
+ */
+ public void setSubmitButtonEnabled(boolean enabled) {
+ mSubmitButtonEnabled = enabled;
+ updateViewsVisibility(isIconified());
+ }
+
+ /**
+ * Returns whether the submit button is enabled when necessary or never displayed.
+ *
+ * @return whether the submit button is enabled automatically when necessary
+ */
+ public boolean isSubmitButtonEnabled() {
+ return mSubmitButtonEnabled;
+ }
+
+ /**
+ * Specifies if a query refinement button should be displayed alongside each suggestion
+ * or if it should depend on the flags set in the individual items retrieved from the
+ * suggestions provider. Clicking on the query refinement button will replace the text
+ * in the query text field with the text from the suggestion. This flag only takes effect
+ * if a SearchableInfo has been specified with {@link #setSearchableInfo(SearchableInfo)}
+ * and not when using a custom adapter.
+ *
+ * @param enable true if all items should have a query refinement button, false if only
+ * those items that have a query refinement flag set should have the button.
+ *
+ * @see SearchManager#SUGGEST_COLUMN_FLAGS
+ * @see SearchManager#FLAG_QUERY_REFINEMENT
+ */
+ public void setQueryRefinementEnabled(boolean enable) {
+ mQueryRefinement = enable;
+ if (mSuggestionsAdapter instanceof SuggestionsAdapter) {
+ ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement(
+ enable ? SuggestionsAdapter.REFINE_ALL : SuggestionsAdapter.REFINE_BY_ENTRY);
+ }
+ }
+
+ /**
+ * Returns whether query refinement is enabled for all items or only specific ones.
+ * @return true if enabled for all items, false otherwise.
+ */
+ public boolean isQueryRefinementEnabled() {
+ return mQueryRefinement;
+ }
+
+ /**
+ * You can set a custom adapter if you wish. Otherwise the default adapter is used to
+ * display the suggestions from the suggestions provider associated with the SearchableInfo.
+ *
+ * @see #setSearchableInfo(SearchableInfo)
+ */
+ public void setSuggestionsAdapter(CursorAdapter adapter) {
+ mSuggestionsAdapter = adapter;
+
+ mQueryTextView.setAdapter(mSuggestionsAdapter);
+ }
+
+ /**
+ * Returns the adapter used for suggestions, if any.
+ * @return the suggestions adapter
+ */
+ public CursorAdapter getSuggestionsAdapter() {
+ return mSuggestionsAdapter;
+ }
+
+ /**
+ * Makes the view at most this many pixels wide
+ *
+ * @attr ref android.R.styleable#SearchView_maxWidth
+ */
+ public void setMaxWidth(int maxpixels) {
+ mMaxWidth = maxpixels;
+
+ requestLayout();
+ }
+
+ /**
+ * Gets the specified maximum width in pixels, if set. Returns zero if
+ * no maximum width was specified.
+ * @return the maximum width of the view
+ *
+ * @attr ref android.R.styleable#SearchView_maxWidth
+ */
+ public int getMaxWidth() {
+ return mMaxWidth;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // Let the standard measurements take effect in iconified state.
+ if (isIconified()) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
+ }
+
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+
+ switch (widthMode) {
+ case MeasureSpec.AT_MOST:
+ // If there is an upper limit, don't exceed maximum width (explicit or implicit)
+ if (mMaxWidth > 0) {
+ width = Math.min(mMaxWidth, width);
+ } else {
+ width = Math.min(getPreferredWidth(), width);
+ }
+ break;
+ case MeasureSpec.EXACTLY:
+ // If an exact width is specified, still don't exceed any specified maximum width
+ if (mMaxWidth > 0) {
+ width = Math.min(mMaxWidth, width);
+ }
+ break;
+ case MeasureSpec.UNSPECIFIED:
+ // Use maximum width, if specified, else preferred width
+ width = mMaxWidth > 0 ? mMaxWidth : getPreferredWidth();
+ break;
+ }
+ widthMode = MeasureSpec.EXACTLY;
+ super.onMeasure(MeasureSpec.makeMeasureSpec(width, widthMode), heightMeasureSpec);
+ }
+
+ private int getPreferredWidth() {
+ return getContext().getResources()
+ .getDimensionPixelSize(R.dimen.abs__search_view_preferred_width);
+ }
+
+ private void updateViewsVisibility(final boolean collapsed) {
+ mIconified = collapsed;
+ // Visibility of views that are visible when collapsed
+ final int visCollapsed = collapsed ? VISIBLE : GONE;
+ // Is there text in the query
+ final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText());
+
+ mSearchButton.setVisibility(visCollapsed);
+ updateSubmitButton(hasText);
+ mSearchEditFrame.setVisibility(collapsed ? GONE : VISIBLE);
+ mSearchHintIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE);
+ updateCloseButton();
+ updateVoiceButton(!hasText);
+ updateSubmitArea();
+ }
+
+ private boolean hasVoiceSearch() {
+ if (mSearchable != null && mSearchable.getVoiceSearchEnabled()) {
+ Intent testIntent = null;
+ if (mSearchable.getVoiceSearchLaunchWebSearch()) {
+ testIntent = mVoiceWebSearchIntent;
+ } else if (mSearchable.getVoiceSearchLaunchRecognizer()) {
+ testIntent = mVoiceAppSearchIntent;
+ }
+ if (testIntent != null) {
+ ResolveInfo ri = getContext().getPackageManager().resolveActivity(testIntent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ return ri != null;
+ }
+ }
+ return false;
+ }
+
+ private boolean isSubmitAreaEnabled() {
+ return (mSubmitButtonEnabled || mVoiceButtonEnabled) && !isIconified();
+ }
+
+ private void updateSubmitButton(boolean hasText) {
+ int visibility = GONE;
+ if (mSubmitButtonEnabled && isSubmitAreaEnabled() && hasFocus()
+ && (hasText || !mVoiceButtonEnabled)) {
+ visibility = VISIBLE;
+ }
+ mSubmitButton.setVisibility(visibility);
+ }
+
+ private void updateSubmitArea() {
+ int visibility = GONE;
+ if (isSubmitAreaEnabled()
+ && (mSubmitButton.getVisibility() == VISIBLE
+ || mVoiceButton.getVisibility() == VISIBLE)) {
+ visibility = VISIBLE;
+ }
+ mSubmitArea.setVisibility(visibility);
+ }
+
+ private void updateCloseButton() {
+ final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText());
+ // Should we show the close button? It is not shown if there's no focus,
+ // field is not iconified by default and there is no text in it.
+ final boolean showClose = hasText || (mIconifiedByDefault && !mExpandedInActionView);
+ mCloseButton.setVisibility(showClose ? VISIBLE : GONE);
+ mCloseButton.getDrawable().setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET);
+ }
+
+ private void postUpdateFocusedState() {
+ post(mUpdateDrawableStateRunnable);
+ }
+
+ private void updateFocusedState() {
+ boolean focused = mQueryTextView.hasFocus();
+ mSearchPlate.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET);
+ mSubmitArea.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET);
+ invalidate();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ removeCallbacks(mUpdateDrawableStateRunnable);
+ post(mReleaseCursorRunnable);
+ super.onDetachedFromWindow();
+ }
+
+ private void setImeVisibility(final boolean visible) {
+ if (visible) {
+ post(mShowImeRunnable);
+ } else {
+ removeCallbacks(mShowImeRunnable);
+ InputMethodManager imm = (InputMethodManager)
+ getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ if (imm != null) {
+ imm.hideSoftInputFromWindow(getWindowToken(), 0);
+ }
+ }
+ }
+
+ /**
+ * Called by the SuggestionsAdapter
+ * @hide
+ */
+ /* package */void onQueryRefine(CharSequence queryText) {
+ setQuery(queryText);
+ }
+
+ private final OnClickListener mOnClickListener = new OnClickListener() {
+
+ public void onClick(View v) {
+ if (v == mSearchButton) {
+ onSearchClicked();
+ } else if (v == mCloseButton) {
+ onCloseClicked();
+ } else if (v == mSubmitButton) {
+ onSubmitQuery();
+ } else if (v == mVoiceButton) {
+ onVoiceClicked();
+ } else if (v == mQueryTextView) {
+ forceSuggestionQuery();
+ }
+ }
+ };
+
+ /**
+ * Handles the key down event for dealing with action keys.
+ *
+ * @param keyCode This is the keycode of the typed key, and is the same value as
+ * found in the KeyEvent parameter.
+ * @param event The complete event record for the typed key
+ *
+ * @return true if the event was handled here, or false if not.
+ */
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (mSearchable == null) {
+ return false;
+ }
+
+ // if it's an action specified by the searchable activity, launch the
+ // entered query with the action key
+ // TODO SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
+ // TODO if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
+ // TODO launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView.getText()
+ // TODO .toString());
+ // TODO return true;
+ // TODO }
+
+ return super.onKeyDown(keyCode, event);
+ }
+
+ /**
+ * React to the user typing "enter" or other hardwired keys while typing in
+ * the search box. This handles these special keys while the edit box has
+ * focus.
+ */
+ View.OnKeyListener mTextKeyListener = new View.OnKeyListener() {
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ // guard against possible race conditions
+ if (mSearchable == null) {
+ return false;
+ }
+
+ if (DBG) {
+ Log.d(LOG_TAG, "mTextListener.onKey(" + keyCode + "," + event + "), selection: "
+ + mQueryTextView.getListSelection());
+ }
+
+ // If a suggestion is selected, handle enter, search key, and action keys
+ // as presses on the selected suggestion
+ if (mQueryTextView.isPopupShowing()
+ && mQueryTextView.getListSelection() != ListView.INVALID_POSITION) {
+ return onSuggestionsKey(v, keyCode, event);
+ }
+
+ // If there is text in the query box, handle enter, and action keys
+ // The search key is handled by the dialog's onKeyDown().
+ if (!mQueryTextView.isEmpty() && KeyEventCompat.hasNoModifiers(event)) {
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ if (keyCode == KeyEvent.KEYCODE_ENTER) {
+ v.cancelLongPress();
+
+ // Launch as a regular search.
+ launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, mQueryTextView.getText()
+ .toString());
+ return true;
+ }
+ }
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ // TODO SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
+ // TODO if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
+ // TODO launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView
+ // TODO .getText().toString());
+ // TODO return true;
+ // TODO }
+ }
+ }
+ return false;
+ }
+ };
+
+ /**
+ * React to the user typing while in the suggestions list. First, check for
+ * action keys. If not handled, try refocusing regular characters into the
+ * EditText.
+ */
+ private boolean onSuggestionsKey(View v, int keyCode, KeyEvent event) {
+ // guard against possible race conditions (late arrival after dismiss)
+ if (mSearchable == null) {
+ return false;
+ }
+ if (mSuggestionsAdapter == null) {
+ return false;
+ }
+ if (event.getAction() == KeyEvent.ACTION_DOWN && KeyEventCompat.hasNoModifiers(event)) {
+ // First, check for enter or search (both of which we'll treat as a
+ // "click")
+ if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH
+ || keyCode == KeyEvent.KEYCODE_TAB) {
+ int position = mQueryTextView.getListSelection();
+ return onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null);
+ }
+
+ // Next, check for left/right moves, which we use to "return" the
+ // user to the edit view
+ if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
+ // give "focus" to text editor, with cursor at the beginning if
+ // left key, at end if right key
+ // TODO: Reverse left/right for right-to-left languages, e.g.
+ // Arabic
+ int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : mQueryTextView
+ .length();
+ mQueryTextView.setSelection(selPoint);
+ mQueryTextView.setListSelection(0);
+ mQueryTextView.clearListSelection();
+ ensureImeVisible(mQueryTextView, true);
+
+ return true;
+ }
+
+ // Next, check for an "up and out" move
+ if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mQueryTextView.getListSelection()) {
+ // TODO: restoreUserQuery();
+ // let ACTV complete the move
+ return false;
+ }
+
+ // Next, check for an "action key"
+ // TODO SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
+ // TODO if ((actionKey != null)
+ // TODO && ((actionKey.getSuggestActionMsg() != null) || (actionKey
+ // TODO .getSuggestActionMsgColumn() != null))) {
+ // TODO // launch suggestion using action key column
+ // TODO int position = mQueryTextView.getListSelection();
+ // TODO if (position != ListView.INVALID_POSITION) {
+ // TODO Cursor c = mSuggestionsAdapter.getCursor();
+ // TODO if (c.moveToPosition(position)) {
+ // TODO final String actionMsg = getActionKeyMessage(c, actionKey);
+ // TODO if (actionMsg != null && (actionMsg.length() > 0)) {
+ // TODO return onItemClicked(position, keyCode, actionMsg);
+ // TODO }
+ // TODO }
+ // TODO }
+ // TODO }
+ }
+ return false;
+ }
+
+ /**
+ * For a given suggestion and a given cursor row, get the action message. If
+ * not provided by the specific row/column, also check for a single
+ * definition (for the action key).
+ *
+ * @param c The cursor providing suggestions
+ * @param actionKey The actionkey record being examined
+ *
+ * @return Returns a string, or null if no action key message for this
+ * suggestion
+ */
+ // TODO private static String getActionKeyMessage(Cursor c, SearchableInfo.ActionKeyInfo actionKey) {
+ // TODO String result = null;
+ // TODO // check first in the cursor data, for a suggestion-specific message
+ // TODO final String column = actionKey.getSuggestActionMsgColumn();
+ // TODO if (column != null) {
+ // TODO result = SuggestionsAdapter.getColumnString(c, column);
+ // TODO }
+ // TODO // If the cursor didn't give us a message, see if there's a single
+ // TODO // message defined
+ // TODO // for the actionkey (for all suggestions)
+ // TODO if (result == null) {
+ // TODO result = actionKey.getSuggestActionMsg();
+ // TODO }
+ // TODO return result;
+ // TODO }
+
+ private int getSearchIconId() {
+ TypedValue outValue = new TypedValue();
+ getContext().getTheme().resolveAttribute(R.attr.searchViewSearchIcon,
+ outValue, true);
+ return outValue.resourceId;
+ }
+
+ private CharSequence getDecoratedHint(CharSequence hintText) {
+ // If the field is always expanded, then don't add the search icon to the hint
+ if (!mIconifiedByDefault) return hintText;
+
+ SpannableStringBuilder ssb = new SpannableStringBuilder(" "); // for the icon
+ ssb.append(hintText);
+ Drawable searchIcon = getContext().getResources().getDrawable(getSearchIconId());
+ int textSize = (int) (mQueryTextView.getTextSize() * 1.25);
+ searchIcon.setBounds(0, 0, textSize, textSize);
+ ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ return ssb;
+ }
+
+ private void updateQueryHint() {
+ if (mQueryHint != null) {
+ mQueryTextView.setHint(getDecoratedHint(mQueryHint));
+ } else if (mSearchable != null) {
+ CharSequence hint = null;
+ int hintId = mSearchable.getHintId();
+ if (hintId != 0) {
+ hint = getContext().getString(hintId);
+ }
+ if (hint != null) {
+ mQueryTextView.setHint(getDecoratedHint(hint));
+ }
+ } else {
+ mQueryTextView.setHint(getDecoratedHint(""));
+ }
+ }
+
+ /**
+ * Updates the auto-complete text view.
+ */
+ private void updateSearchAutoComplete() {
+ // TODO mQueryTextView.setDropDownAnimationStyle(0); // no animation
+ mQueryTextView.setThreshold(mSearchable.getSuggestThreshold());
+ mQueryTextView.setImeOptions(mSearchable.getImeOptions());
+ int inputType = mSearchable.getInputType();
+ // We only touch this if the input type is set up for text (which it almost certainly
+ // should be, in the case of search!)
+ if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) {
+ // The existence of a suggestions authority is the proxy for "suggestions
+ // are available here"
+ inputType &= ~InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
+ if (mSearchable.getSuggestAuthority() != null) {
+ inputType |= InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
+ // TYPE_TEXT_FLAG_AUTO_COMPLETE means that the text editor is performing
+ // auto-completion based on its own semantics, which it will present to the user
+ // as they type. This generally means that the input method should not show its
+ // own candidates, and the spell checker should not be in action. The text editor
+ // supplies its candidates by calling InputMethodManager.displayCompletions(),
+ // which in turn will call InputMethodSession.displayCompletions().
+ inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
+ }
+ }
+ mQueryTextView.setInputType(inputType);
+ if (mSuggestionsAdapter != null) {
+ mSuggestionsAdapter.changeCursor(null);
+ }
+ // attach the suggestions adapter, if suggestions are available
+ // The existence of a suggestions authority is the proxy for "suggestions available here"
+ if (mSearchable.getSuggestAuthority() != null) {
+ mSuggestionsAdapter = new SuggestionsAdapter(getContext(),
+ this, mSearchable, mOutsideDrawablesCache);
+ mQueryTextView.setAdapter(mSuggestionsAdapter);
+ ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement(
+ mQueryRefinement ? SuggestionsAdapter.REFINE_ALL
+ : SuggestionsAdapter.REFINE_BY_ENTRY);
+ }
+ }
+
+ /**
+ * Update the visibility of the voice button. There are actually two voice search modes,
+ * either of which will activate the button.
+ * @param empty whether the search query text field is empty. If it is, then the other
+ * criteria apply to make the voice button visible.
+ */
+ private void updateVoiceButton(boolean empty) {
+ int visibility = GONE;
+ if (mVoiceButtonEnabled && !isIconified() && empty) {
+ visibility = VISIBLE;
+ mSubmitButton.setVisibility(GONE);
+ }
+ mVoiceButton.setVisibility(visibility);
+ }
+
+ private final OnEditorActionListener mOnEditorActionListener = new OnEditorActionListener() {
+
+ /**
+ * Called when the input method default action key is pressed.
+ */
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ onSubmitQuery();
+ return true;
+ }
+ };
+
+ private void onTextChanged(CharSequence newText) {
+ CharSequence text = mQueryTextView.getText();
+ mUserQuery = text;
+ boolean hasText = !TextUtils.isEmpty(text);
+ updateSubmitButton(hasText);
+ updateVoiceButton(!hasText);
+ updateCloseButton();
+ updateSubmitArea();
+ if (mOnQueryChangeListener != null && !TextUtils.equals(newText, mOldQueryText)) {
+ mOnQueryChangeListener.onQueryTextChange(newText.toString());
+ }
+ mOldQueryText = newText.toString();
+ }
+
+ private void onSubmitQuery() {
+ CharSequence query = mQueryTextView.getText();
+ if (query != null && TextUtils.getTrimmedLength(query) > 0) {
+ if (mOnQueryChangeListener == null
+ || !mOnQueryChangeListener.onQueryTextSubmit(query.toString())) {
+ if (mSearchable != null) {
+ launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, query.toString());
+ setImeVisibility(false);
+ }
+ dismissSuggestions();
+ }
+ }
+ }
+
+ private void dismissSuggestions() {
+ mQueryTextView.dismissDropDown();
+ }
+
+ private void onCloseClicked() {
+ CharSequence text = mQueryTextView.getText();
+ if (TextUtils.isEmpty(text)) {
+ if (mIconifiedByDefault) {
+ // If the app doesn't override the close behavior
+ if (mOnCloseListener == null || !mOnCloseListener.onClose()) {
+ // hide the keyboard and remove focus
+ clearFocus();
+ // collapse the search field
+ updateViewsVisibility(true);
+ }
+ }
+ } else {
+ mQueryTextView.setText("");
+ mQueryTextView.requestFocus();
+ setImeVisibility(true);
+ }
+
+ }
+
+ private void onSearchClicked() {
+ updateViewsVisibility(false);
+ mQueryTextView.requestFocus();
+ setImeVisibility(true);
+ if (mOnSearchClickListener != null) {
+ mOnSearchClickListener.onClick(this);
+ }
+ }
+
+ private void onVoiceClicked() {
+ // guard against possible race conditions
+ if (mSearchable == null) {
+ return;
+ }
+ SearchableInfo searchable = mSearchable;
+ try {
+ if (searchable.getVoiceSearchLaunchWebSearch()) {
+ Intent webSearchIntent = createVoiceWebSearchIntent(mVoiceWebSearchIntent,
+ searchable);
+ getContext().startActivity(webSearchIntent);
+ } else if (searchable.getVoiceSearchLaunchRecognizer()) {
+ Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent,
+ searchable);
+ getContext().startActivity(appSearchIntent);
+ }
+ } catch (ActivityNotFoundException e) {
+ // Should not happen, since we check the availability of
+ // voice search before showing the button. But just in case...
+ Log.w(LOG_TAG, "Could not find voice search activity");
+ }
+ }
+
+ void onTextFocusChanged() {
+ updateViewsVisibility(isIconified());
+ // Delayed update to make sure that the focus has settled down and window focus changes
+ // don't affect it. A synchronous update was not working.
+ postUpdateFocusedState();
+ if (mQueryTextView.hasFocus()) {
+ forceSuggestionQuery();
+ }
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+
+ postUpdateFocusedState();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onActionViewCollapsed() {
+ clearFocus();
+ updateViewsVisibility(true);
+ mQueryTextView.setImeOptions(mCollapsedImeOptions);
+ mExpandedInActionView = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onActionViewExpanded() {
+ if (mExpandedInActionView) return;
+
+ mExpandedInActionView = true;
+ mCollapsedImeOptions = mQueryTextView.getImeOptions();
+ mQueryTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN);
+ mQueryTextView.setText("");
+ setIconified(false);
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(SearchView.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(SearchView.class.getName());
+ }
+
+ private void adjustDropDownSizeAndPosition() {
+ if (mDropDownAnchor.getWidth() > 1) {
+ Resources res = getContext().getResources();
+ int anchorPadding = mSearchPlate.getPaddingLeft();
+ Rect dropDownPadding = new Rect();
+ int iconOffset = mIconifiedByDefault
+ ? res.getDimensionPixelSize(R.dimen.abs__dropdownitem_icon_width)
+ + res.getDimensionPixelSize(R.dimen.abs__dropdownitem_text_padding_left)
+ : 0;
+ mQueryTextView.getDropDownBackground().getPadding(dropDownPadding);
+ mQueryTextView.setDropDownHorizontalOffset(-(dropDownPadding.left + iconOffset)
+ + anchorPadding);
+ mQueryTextView.setDropDownWidth(mDropDownAnchor.getWidth() + dropDownPadding.left
+ + dropDownPadding.right + iconOffset - (anchorPadding));
+ }
+ }
+
+ private boolean onItemClicked(int position, int actionKey, String actionMsg) {
+ if (mOnSuggestionListener == null
+ || !mOnSuggestionListener.onSuggestionClick(position)) {
+ launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null);
+ setImeVisibility(false);
+ dismissSuggestions();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean onItemSelected(int position) {
+ if (mOnSuggestionListener == null
+ || !mOnSuggestionListener.onSuggestionSelect(position)) {
+ rewriteQueryFromSuggestion(position);
+ return true;
+ }
+ return false;
+ }
+
+ private final OnItemClickListener mOnItemClickListener = new OnItemClickListener() {
+
+ /**
+ * Implements OnItemClickListener
+ */
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ if (DBG) Log.d(LOG_TAG, "onItemClick() position " + position);
+ onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null);
+ }
+ };
+
+ private final OnItemSelectedListener mOnItemSelectedListener = new OnItemSelectedListener() {
+
+ /**
+ * Implements OnItemSelectedListener
+ */
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ if (DBG) Log.d(LOG_TAG, "onItemSelected() position " + position);
+ SearchView.this.onItemSelected(position);
+ }
+
+ /**
+ * Implements OnItemSelectedListener
+ */
+ public void onNothingSelected(AdapterView> parent) {
+ if (DBG)
+ Log.d(LOG_TAG, "onNothingSelected()");
+ }
+ };
+
+ /**
+ * Query rewriting.
+ */
+ private void rewriteQueryFromSuggestion(int position) {
+ CharSequence oldQuery = mQueryTextView.getText();
+ Cursor c = mSuggestionsAdapter.getCursor();
+ if (c == null) {
+ return;
+ }
+ if (c.moveToPosition(position)) {
+ // Get the new query from the suggestion.
+ CharSequence newQuery = mSuggestionsAdapter.convertToString(c);
+ if (newQuery != null) {
+ // The suggestion rewrites the query.
+ // Update the text field, without getting new suggestions.
+ setQuery(newQuery);
+ } else {
+ // The suggestion does not rewrite the query, restore the user's query.
+ setQuery(oldQuery);
+ }
+ } else {
+ // We got a bad position, restore the user's query.
+ setQuery(oldQuery);
+ }
+ }
+
+ /**
+ * Launches an intent based on a suggestion.
+ *
+ * @param position The index of the suggestion to create the intent from.
+ * @param actionKey The key code of the action key that was pressed,
+ * or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
+ * @param actionMsg The message for the action key that was pressed,
+ * or null if none.
+ * @return true if a successful launch, false if could not (e.g. bad position).
+ */
+ private boolean launchSuggestion(int position, int actionKey, String actionMsg) {
+ Cursor c = mSuggestionsAdapter.getCursor();
+ if ((c != null) && c.moveToPosition(position)) {
+
+ Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg);
+
+ // launch the intent
+ launchIntent(intent);
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Launches an intent, including any special intent handling.
+ */
+ private void launchIntent(Intent intent) {
+ if (intent == null) {
+ return;
+ }
+ try {
+ // If the intent was created from a suggestion, it will always have an explicit
+ // component here.
+ getContext().startActivity(intent);
+ } catch (RuntimeException ex) {
+ Log.e(LOG_TAG, "Failed launch activity: " + intent, ex);
+ }
+ }
+
+ /**
+ * Sets the text in the query box, without updating the suggestions.
+ */
+ private void setQuery(CharSequence query) {
+ setText(mQueryTextView, query, true);
+ // Move the cursor to the end
+ mQueryTextView.setSelection(TextUtils.isEmpty(query) ? 0 : query.length());
+ }
+
+ private void launchQuerySearch(int actionKey, String actionMsg, String query) {
+ String action = Intent.ACTION_SEARCH;
+ Intent intent = createIntent(action, null, null, query, actionKey, actionMsg);
+ getContext().startActivity(intent);
+ }
+
+ /**
+ * Constructs an intent from the given information and the search dialog state.
+ *
+ * @param action Intent action.
+ * @param data Intent data, or null.
+ * @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or null.
+ * @param query Intent query, or null.
+ * @param actionKey The key code of the action key that was pressed,
+ * or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
+ * @param actionMsg The message for the action key that was pressed,
+ * or null if none.
+ * @return The intent.
+ */
+ private Intent createIntent(String action, Uri data, String extraData, String query,
+ int actionKey, String actionMsg) {
+ // Now build the Intent
+ Intent intent = new Intent(action);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // We need CLEAR_TOP to avoid reusing an old task that has other activities
+ // on top of the one we want. We don't want to do this in in-app search though,
+ // as it can be destructive to the activity stack.
+ if (data != null) {
+ intent.setData(data);
+ }
+ intent.putExtra(SearchManager.USER_QUERY, mUserQuery);
+ if (query != null) {
+ intent.putExtra(SearchManager.QUERY, query);
+ }
+ if (extraData != null) {
+ intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
+ }
+ if (mAppSearchData != null) {
+ intent.putExtra(SearchManager.APP_DATA, mAppSearchData);
+ }
+ if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
+ intent.putExtra(SearchManager.ACTION_KEY, actionKey);
+ intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
+ }
+ intent.setComponent(mSearchable.getSearchActivity());
+ return intent;
+ }
+
+ /**
+ * Create and return an Intent that can launch the voice search activity for web search.
+ */
+ private Intent createVoiceWebSearchIntent(Intent baseIntent, SearchableInfo searchable) {
+ Intent voiceIntent = new Intent(baseIntent);
+ ComponentName searchActivity = searchable.getSearchActivity();
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, searchActivity == null ? null
+ : searchActivity.flattenToShortString());
+ return voiceIntent;
+ }
+
+ /**
+ * Create and return an Intent that can launch the voice search activity, perform a specific
+ * voice transcription, and forward the results to the searchable activity.
+ *
+ * @param baseIntent The voice app search intent to start from
+ * @return A completely-configured intent ready to send to the voice search activity
+ */
+ private Intent createVoiceAppSearchIntent(Intent baseIntent, SearchableInfo searchable) {
+ ComponentName searchActivity = searchable.getSearchActivity();
+
+ // create the necessary intent to set up a search-and-forward operation
+ // in the voice search system. We have to keep the bundle separate,
+ // because it becomes immutable once it enters the PendingIntent
+ Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
+ queryIntent.setComponent(searchActivity);
+ PendingIntent pending = PendingIntent.getActivity(getContext(), 0, queryIntent,
+ PendingIntent.FLAG_ONE_SHOT);
+
+ // Now set up the bundle that will be inserted into the pending intent
+ // when it's time to do the search. We always build it here (even if empty)
+ // because the voice search activity will always need to insert "QUERY" into
+ // it anyway.
+ Bundle queryExtras = new Bundle();
+
+ // Now build the intent to launch the voice search. Add all necessary
+ // extras to launch the voice recognizer, and then all the necessary extras
+ // to forward the results to the searchable activity
+ Intent voiceIntent = new Intent(baseIntent);
+
+ // Add all of the configuration options supplied by the searchable's metadata
+ String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM;
+ String prompt = null;
+ String language = null;
+ int maxResults = 1;
+
+ Resources resources = getResources();
+ if (searchable.getVoiceLanguageModeId() != 0) {
+ languageModel = resources.getString(searchable.getVoiceLanguageModeId());
+ }
+ if (searchable.getVoicePromptTextId() != 0) {
+ prompt = resources.getString(searchable.getVoicePromptTextId());
+ }
+ if (searchable.getVoiceLanguageId() != 0) {
+ language = resources.getString(searchable.getVoiceLanguageId());
+ }
+ if (searchable.getVoiceMaxResults() != 0) {
+ maxResults = searchable.getVoiceMaxResults();
+ }
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel);
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language);
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults);
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, searchActivity == null ? null
+ : searchActivity.flattenToShortString());
+
+ // Add the values that configure forwarding the results
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending);
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras);
+
+ return voiceIntent;
+ }
+
+ /**
+ * When a particular suggestion has been selected, perform the various lookups required
+ * to use the suggestion. This includes checking the cursor for suggestion-specific data,
+ * and/or falling back to the XML for defaults; It also creates REST style Uri data when
+ * the suggestion includes a data id.
+ *
+ * @param c The suggestions cursor, moved to the row of the user's selection
+ * @param actionKey The key code of the action key that was pressed,
+ * or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
+ * @param actionMsg The message for the action key that was pressed,
+ * or null if none.
+ * @return An intent for the suggestion at the cursor's position.
+ */
+ private Intent createIntentFromSuggestion(Cursor c, int actionKey, String actionMsg) {
+ try {
+ // use specific action if supplied, or default action if supplied, or fixed default
+ String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION);
+
+ if (action == null) {
+ action = mSearchable.getSuggestIntentAction();
+ }
+ if (action == null) {
+ action = Intent.ACTION_SEARCH;
+ }
+
+ // use specific data if supplied, or default data if supplied
+ String data = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA);
+ if (data == null) {
+ data = mSearchable.getSuggestIntentData();
+ }
+ // then, if an ID was provided, append it.
+ if (data != null) {
+ String id = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
+ if (id != null) {
+ data = data + "/" + Uri.encode(id);
+ }
+ }
+ Uri dataUri = (data == null) ? null : Uri.parse(data);
+
+ String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY);
+ String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
+
+ return createIntent(action, dataUri, extraData, query, actionKey, actionMsg);
+ } catch (RuntimeException e ) {
+ int rowNum;
+ try { // be really paranoid now
+ rowNum = c.getPosition();
+ } catch (RuntimeException e2 ) {
+ rowNum = -1;
+ }
+ Log.w(LOG_TAG, "Search suggestions cursor at row " + rowNum +
+ " returned exception.", e);
+ return null;
+ }
+ }
+
+ private void forceSuggestionQuery() {
+ try {
+ Method before = SearchAutoComplete.class.getMethod("doBeforeTextChanged");
+ Method after = SearchAutoComplete.class.getMethod("doAfterTextChanged");
+ before.setAccessible(true);
+ after.setAccessible(true);
+ before.invoke(mQueryTextView);
+ after.invoke(mQueryTextView);
+ } catch (Exception e) {
+ // Oh well...
+ }
+ }
+
+ static boolean isLandscapeMode(Context context) {
+ return context.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
+ }
+
+ /**
+ * Callback to watch the text field for empty/non-empty
+ */
+ private TextWatcher mTextWatcher = new TextWatcher() {
+
+ public void beforeTextChanged(CharSequence s, int start, int before, int after) { }
+
+ public void onTextChanged(CharSequence s, int start,
+ int before, int after) {
+ SearchView.this.onTextChanged(s);
+ }
+
+ public void afterTextChanged(Editable s) {
+ }
+ };
+
+ /**
+ * Local subclass for AutoCompleteTextView.
+ * @hide
+ */
+ public static class SearchAutoComplete extends AutoCompleteTextView {
+
+ private int mThreshold;
+ private SearchView mSearchView;
+
+ public SearchAutoComplete(Context context) {
+ super(context);
+ mThreshold = getThreshold();
+ }
+
+ public SearchAutoComplete(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mThreshold = getThreshold();
+ }
+
+ public SearchAutoComplete(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mThreshold = getThreshold();
+ }
+
+ void setSearchView(SearchView searchView) {
+ mSearchView = searchView;
+ }
+
+ @Override
+ public void setThreshold(int threshold) {
+ super.setThreshold(threshold);
+ mThreshold = threshold;
+ }
+
+ /**
+ * Returns true if the text field is empty, or contains only whitespace.
+ */
+ private boolean isEmpty() {
+ return TextUtils.getTrimmedLength(getText()) == 0;
+ }
+
+ /**
+ * We override this method to avoid replacing the query box text when a
+ * suggestion is clicked.
+ */
+ @Override
+ protected void replaceText(CharSequence text) {
+ }
+
+ /**
+ * We override this method to avoid an extra onItemClick being called on
+ * the drop-down's OnItemClickListener by
+ * {@link AutoCompleteTextView#onKeyUp(int, KeyEvent)} when an item is
+ * clicked with the trackball.
+ */
+ @Override
+ public void performCompletion() {
+ }
+
+ /**
+ * We override this method to be sure and show the soft keyboard if
+ * appropriate when the TextView has focus.
+ */
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+
+ if (hasWindowFocus && mSearchView.hasFocus() && getVisibility() == VISIBLE) {
+ InputMethodManager inputManager = (InputMethodManager) getContext()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputManager.showSoftInput(this, 0);
+ // If in landscape mode, then make sure that
+ // the ime is in front of the dropdown.
+ if (isLandscapeMode(getContext())) {
+ ensureImeVisible(this, true);
+ }
+ }
+ }
+
+ @Override
+ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+ super.onFocusChanged(focused, direction, previouslyFocusedRect);
+ mSearchView.onTextFocusChanged();
+ }
+
+ /**
+ * We override this method so that we can allow a threshold of zero,
+ * which ACTV does not.
+ */
+ @Override
+ public boolean enoughToFilter() {
+ return mThreshold <= 0 || super.enoughToFilter();
+ }
+
+ @Override
+ public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ // special case for the back key, we do not even try to send it
+ // to the drop down list but instead, consume it immediately
+ if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
+ KeyEvent.DispatcherState state = getKeyDispatcherState();
+ if (state != null) {
+ state.startTracking(event, this);
+ }
+ return true;
+ } else if (event.getAction() == KeyEvent.ACTION_UP) {
+ KeyEvent.DispatcherState state = getKeyDispatcherState();
+ if (state != null) {
+ state.handleUpEvent(event);
+ }
+ if (event.isTracking() && !event.isCanceled()) {
+ mSearchView.clearFocus();
+ mSearchView.setImeVisibility(false);
+ return true;
+ }
+ }
+ }
+ return super.onKeyPreIme(keyCode, event);
+ }
+
+ }
+
+ private static void ensureImeVisible(AutoCompleteTextView view, boolean visible) {
+ try {
+ Method method = AutoCompleteTextView.class.getMethod("ensureImeVisible", boolean.class);
+ method.setAccessible(true);
+ method.invoke(view, visible);
+ } catch (Exception e) {
+ //Oh well...
+ }
+ }
+
+ private static void showSoftInputUnchecked(View view, InputMethodManager imm, int flags) {
+ try {
+ Method method = imm.getClass().getMethod("showSoftInputUnchecked", int.class, ResultReceiver.class);
+ method.setAccessible(true);
+ method.invoke(imm, flags, null);
+ } catch (Exception e) {
+ //Fallback to public API which hopefully does mostly the same thing
+ imm.showSoftInput(view, flags);
+ }
+ }
+
+ private static void setText(AutoCompleteTextView view, CharSequence text, boolean filter) {
+ try {
+ Method method = AutoCompleteTextView.class.getMethod("setText", CharSequence.class, boolean.class);
+ method.setAccessible(true);
+ method.invoke(view, text, filter);
+ } catch (Exception e) {
+ //Fallback to public API which hopefully does mostly the same thing
+ view.setText(text);
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/widget/ShareActionProvider.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/widget/ShareActionProvider.java
new file mode 100644
index 0000000000..83e9f0ca9f
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/widget/ShareActionProvider.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.widget;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.util.TypedValue;
+import android.view.View;
+
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.view.ActionProvider;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.MenuItem.OnMenuItemClickListener;
+import com.actionbarsherlock.view.SubMenu;
+import com.actionbarsherlock.widget.ActivityChooserModel.OnChooseActivityListener;
+
+/**
+ * This is a provider for a share action. It is responsible for creating views
+ * that enable data sharing and also to show a sub menu with sharing activities
+ * if the hosting item is placed on the overflow menu.
+ *
+ * Here is how to use the action provider with custom backing file in a {@link MenuItem}:
+ *
+ *
+ *
+ *
+ * // In Activity#onCreateOptionsMenu
+ * public boolean onCreateOptionsMenu(Menu menu) {
+ * // Get the menu item.
+ * MenuItem menuItem = menu.findItem(R.id.my_menu_item);
+ * // Get the provider and hold onto it to set/change the share intent.
+ * mShareActionProvider = (ShareActionProvider) menuItem.getActionProvider();
+ * // Set history different from the default before getting the action
+ * // view since a call to {@link MenuItem#getActionView() MenuItem.getActionView()} calls
+ * // {@link ActionProvider#onCreateActionView()} which uses the backing file name. Omit this
+ * // line if using the default share history file is desired.
+ * mShareActionProvider.setShareHistoryFileName("custom_share_history.xml");
+ * . . .
+ * }
+ *
+ * // Somewhere in the application.
+ * public void doShare(Intent shareIntent) {
+ * // When you want to share set the share intent.
+ * mShareActionProvider.setShareIntent(shareIntent);
+ * }
+ *
+ *
+ *
+ *
+ * Note: While the sample snippet demonstrates how to use this provider
+ * in the context of a menu item, the use of the provider is not limited to menu items.
+ *
+ *
+ * @see ActionProvider
+ */
+public class ShareActionProvider extends ActionProvider {
+
+ /**
+ * Listener for the event of selecting a share target.
+ */
+ public interface OnShareTargetSelectedListener {
+
+ /**
+ * Called when a share target has been selected. The client can
+ * decide whether to handle the intent or rely on the default
+ * behavior which is launching it.
+ *
+ * Note: Modifying the intent is not permitted and
+ * any changes to the latter will be ignored.
+ *
+ *
+ * @param source The source of the notification.
+ * @param intent The intent for launching the chosen share target.
+ * @return Whether the client has handled the intent.
+ */
+ public boolean onShareTargetSelected(ShareActionProvider source, Intent intent);
+ }
+
+ /**
+ * The default for the maximal number of activities shown in the sub-menu.
+ */
+ private static final int DEFAULT_INITIAL_ACTIVITY_COUNT = 4;
+
+ /**
+ * The the maximum number activities shown in the sub-menu.
+ */
+ private int mMaxShownActivityCount = DEFAULT_INITIAL_ACTIVITY_COUNT;
+
+ /**
+ * Listener for handling menu item clicks.
+ */
+ private final ShareMenuItemOnMenuItemClickListener mOnMenuItemClickListener =
+ new ShareMenuItemOnMenuItemClickListener();
+
+ /**
+ * The default name for storing share history.
+ */
+ public static final String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
+
+ /**
+ * Context for accessing resources.
+ */
+ private final Context mContext;
+
+ /**
+ * The name of the file with share history data.
+ */
+ private String mShareHistoryFileName = DEFAULT_SHARE_HISTORY_FILE_NAME;
+
+ private OnShareTargetSelectedListener mOnShareTargetSelectedListener;
+
+ private OnChooseActivityListener mOnChooseActivityListener;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context Context for accessing resources.
+ */
+ public ShareActionProvider(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ /**
+ * Sets a listener to be notified when a share target has been selected.
+ * The listener can optionally decide to handle the selection and
+ * not rely on the default behavior which is to launch the activity.
+ *
+ * Note: If you choose the backing share history file
+ * you will still be notified in this callback.
+ *
+ * @param listener The listener.
+ */
+ public void setOnShareTargetSelectedListener(OnShareTargetSelectedListener listener) {
+ mOnShareTargetSelectedListener = listener;
+ setActivityChooserPolicyIfNeeded();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public View onCreateActionView() {
+ // Create the view and set its data model.
+ ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
+ ActivityChooserView activityChooserView = new ActivityChooserView(mContext);
+ activityChooserView.setActivityChooserModel(dataModel);
+
+ // Lookup and set the expand action icon.
+ TypedValue outTypedValue = new TypedValue();
+ mContext.getTheme().resolveAttribute(R.attr.actionModeShareDrawable, outTypedValue, true);
+ Drawable drawable = mContext.getResources().getDrawable(outTypedValue.resourceId);
+ activityChooserView.setExpandActivityOverflowButtonDrawable(drawable);
+ activityChooserView.setProvider(this);
+
+ // Set content description.
+ activityChooserView.setDefaultActionButtonContentDescription(
+ R.string.abs__shareactionprovider_share_with_application);
+ activityChooserView.setExpandActivityOverflowButtonContentDescription(
+ R.string.abs__shareactionprovider_share_with);
+
+ return activityChooserView;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean hasSubMenu() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onPrepareSubMenu(SubMenu subMenu) {
+ // Clear since the order of items may change.
+ subMenu.clear();
+
+ ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
+ PackageManager packageManager = mContext.getPackageManager();
+
+ final int expandedActivityCount = dataModel.getActivityCount();
+ final int collapsedActivityCount = Math.min(expandedActivityCount, mMaxShownActivityCount);
+
+ // Populate the sub-menu with a sub set of the activities.
+ for (int i = 0; i < collapsedActivityCount; i++) {
+ ResolveInfo activity = dataModel.getActivity(i);
+ subMenu.add(0, i, i, activity.loadLabel(packageManager))
+ .setIcon(activity.loadIcon(packageManager))
+ .setOnMenuItemClickListener(mOnMenuItemClickListener);
+ }
+
+ if (collapsedActivityCount < expandedActivityCount) {
+ // Add a sub-menu for showing all activities as a list item.
+ SubMenu expandedSubMenu = subMenu.addSubMenu(Menu.NONE, collapsedActivityCount,
+ collapsedActivityCount,
+ mContext.getString(R.string.abs__activity_chooser_view_see_all));
+ for (int i = 0; i < expandedActivityCount; i++) {
+ ResolveInfo activity = dataModel.getActivity(i);
+ expandedSubMenu.add(0, i, i, activity.loadLabel(packageManager))
+ .setIcon(activity.loadIcon(packageManager))
+ .setOnMenuItemClickListener(mOnMenuItemClickListener);
+ }
+ }
+ }
+
+ /**
+ * Sets the file name of a file for persisting the share history which
+ * history will be used for ordering share targets. This file will be used
+ * for all view created by {@link #onCreateActionView()}. Defaults to
+ * {@link #DEFAULT_SHARE_HISTORY_FILE_NAME}. Set to null
+ * if share history should not be persisted between sessions.
+ *
+ * Note: The history file name can be set any time, however
+ * only the action views created by {@link #onCreateActionView()} after setting
+ * the file name will be backed by the provided file.
+ *
+ *
+ * @param shareHistoryFile The share history file name.
+ */
+ public void setShareHistoryFileName(String shareHistoryFile) {
+ mShareHistoryFileName = shareHistoryFile;
+ setActivityChooserPolicyIfNeeded();
+ }
+
+ /**
+ * Sets an intent with information about the share action. Here is a
+ * sample for constructing a share intent:
+ *
+ *
+ *
+ * Intent shareIntent = new Intent(Intent.ACTION_SEND);
+ * shareIntent.setType("image/*");
+ * Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg"));
+ * shareIntent.putExtra(Intent.EXTRA_STREAM, uri.toString());
+ *
+ *
+ *
+ *
+ * @param shareIntent The share intent.
+ *
+ * @see Intent#ACTION_SEND
+ * @see Intent#ACTION_SEND_MULTIPLE
+ */
+ public void setShareIntent(Intent shareIntent) {
+ ActivityChooserModel dataModel = ActivityChooserModel.get(mContext,
+ mShareHistoryFileName);
+ dataModel.setIntent(shareIntent);
+ }
+
+ /**
+ * Reusable listener for handling share item clicks.
+ */
+ private class ShareMenuItemOnMenuItemClickListener implements OnMenuItemClickListener {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ ActivityChooserModel dataModel = ActivityChooserModel.get(mContext,
+ mShareHistoryFileName);
+ final int itemId = item.getItemId();
+ Intent launchIntent = dataModel.chooseActivity(itemId);
+ if (launchIntent != null) {
+ mContext.startActivity(launchIntent);
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Set the activity chooser policy of the model backed by the current
+ * share history file if needed which is if there is a registered callback.
+ */
+ private void setActivityChooserPolicyIfNeeded() {
+ if (mOnShareTargetSelectedListener == null) {
+ return;
+ }
+ if (mOnChooseActivityListener == null) {
+ mOnChooseActivityListener = new ShareAcitivityChooserModelPolicy();
+ }
+ ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
+ dataModel.setOnChooseActivityListener(mOnChooseActivityListener);
+ }
+
+ /**
+ * Policy that delegates to the {@link OnShareTargetSelectedListener}, if such.
+ */
+ private class ShareAcitivityChooserModelPolicy implements OnChooseActivityListener {
+ @Override
+ public boolean onChooseActivity(ActivityChooserModel host, Intent intent) {
+ if (mOnShareTargetSelectedListener != null) {
+ return mOnShareTargetSelectedListener.onShareTargetSelected(
+ ShareActionProvider.this, intent);
+ }
+ return false;
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/widget/SuggestionsAdapter.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/widget/SuggestionsAdapter.java
new file mode 100644
index 0000000000..bd5cbd718d
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/src/com/actionbarsherlock/widget/SuggestionsAdapter.java
@@ -0,0 +1,733 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.actionbarsherlock.widget;
+
+import android.app.SearchManager;
+import android.app.SearchableInfo;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.widget.ResourceCursorAdapter;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.TextUtils;
+import android.text.style.TextAppearanceSpan;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.actionbarsherlock.R;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.WeakHashMap;
+
+/**
+ * Provides the contents for the suggestion drop-down list.
+ *
+ * @hide
+ */
+class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListener {
+
+ private static final boolean DBG = false;
+ private static final String LOG_TAG = "SuggestionsAdapter";
+ private static final int QUERY_LIMIT = 50;
+
+ static final int REFINE_NONE = 0;
+ static final int REFINE_BY_ENTRY = 1;
+ static final int REFINE_ALL = 2;
+
+ private SearchManager mSearchManager;
+ private SearchView mSearchView;
+ private Context mProviderContext;
+ private WeakHashMap mOutsideDrawablesCache;
+ private boolean mClosed = false;
+ private int mQueryRefinement = REFINE_BY_ENTRY;
+
+ // URL color
+ private ColorStateList mUrlColor;
+
+ static final int INVALID_INDEX = -1;
+
+ // Cached column indexes, updated when the cursor changes.
+ private int mText1Col = INVALID_INDEX;
+ private int mText2Col = INVALID_INDEX;
+ private int mText2UrlCol = INVALID_INDEX;
+ private int mIconName1Col = INVALID_INDEX;
+ private int mIconName2Col = INVALID_INDEX;
+ private int mFlagsCol = INVALID_INDEX;
+
+ // private final Runnable mStartSpinnerRunnable;
+ // private final Runnable mStopSpinnerRunnable;
+
+ /**
+ * The amount of time we delay in the filter when the user presses the delete key.
+ */
+ //private static final long DELETE_KEY_POST_DELAY = 500L;
+
+ public SuggestionsAdapter(Context context, SearchView searchView,
+ SearchableInfo mSearchable, WeakHashMap outsideDrawablesCache) {
+ super(context,
+ R.layout.abs__search_dropdown_item_icons_2line,
+ null, // no initial cursor
+ true); // auto-requery
+ mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
+ mProviderContext = mContext;
+ mSearchView = searchView;
+
+ mOutsideDrawablesCache = outsideDrawablesCache;
+
+ // mStartSpinnerRunnable = new Runnable() {
+ // public void run() {
+ // // mSearchView.setWorking(true); // TODO:
+ // }
+ // };
+ //
+ // mStopSpinnerRunnable = new Runnable() {
+ // public void run() {
+ // // mSearchView.setWorking(false); // TODO:
+ // }
+ // };
+
+ // delay 500ms when deleting
+// TODO getFilter().setDelayer(new Filter.Delayer() {
+//
+// private int mPreviousLength = 0;
+//
+// public long getPostingDelay(CharSequence constraint) {
+// if (constraint == null) return 0;
+//
+// long delay = constraint.length() < mPreviousLength ? DELETE_KEY_POST_DELAY : 0;
+// mPreviousLength = constraint.length();
+// return delay;
+// }
+// });
+ }
+
+ /**
+ * Enables query refinement for all suggestions. This means that an additional icon
+ * will be shown for each entry. When clicked, the suggested text on that line will be
+ * copied to the query text field.
+ *
+ *
+ * @param refineWhat which queries to refine. Possible values are {@link #REFINE_NONE},
+ * {@link #REFINE_BY_ENTRY}, and {@link #REFINE_ALL}.
+ */
+ public void setQueryRefinement(int refineWhat) {
+ mQueryRefinement = refineWhat;
+ }
+
+ /**
+ * Returns the current query refinement preference.
+ * @return value of query refinement preference
+ */
+ public int getQueryRefinement() {
+ return mQueryRefinement;
+ }
+
+ /**
+ * Overridden to always return false, since we cannot be sure that
+ * suggestion sources return stable IDs.
+ */
+ @Override
+ public boolean hasStableIds() {
+ return false;
+ }
+
+ /**
+ * Use the search suggestions provider to obtain a live cursor. This will be called
+ * in a worker thread, so it's OK if the query is slow (e.g. round trip for suggestions).
+ * The results will be processed in the UI thread and changeCursor() will be called.
+ */
+ @Override
+ public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
+ if (DBG) Log.d(LOG_TAG, "runQueryOnBackgroundThread(" + constraint + ")");
+ String query = (constraint == null) ? "" : constraint.toString();
+ /**
+ * for in app search we show the progress spinner until the cursor is returned with
+ * the results.
+ */
+ Cursor cursor = null;
+ if (mSearchView.getVisibility() != View.VISIBLE
+ || mSearchView.getWindowVisibility() != View.VISIBLE) {
+ return null;
+ }
+ //mSearchView.getWindow().getDecorView().post(mStartSpinnerRunnable); // TODO:
+ try {
+ cursor = getSuggestions(query, QUERY_LIMIT);
+ // trigger fill window so the spinner stays up until the results are copied over and
+ // closer to being ready
+ if (cursor != null) {
+ cursor.getCount();
+ return cursor;
+ }
+ } catch (RuntimeException e) {
+ Log.w(LOG_TAG, "Search suggestions query threw an exception.", e);
+ }
+ // If cursor is null or an exception was thrown, stop the spinner and return null.
+ // changeCursor doesn't get called if cursor is null
+ // mSearchView.getWindow().getDecorView().post(mStopSpinnerRunnable); // TODO:
+ return null;
+ }
+
+ public Cursor getSuggestions(String query, int limit) {
+ Uri.Builder uriBuilder = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .query("") // TODO: Remove, workaround for a bug in Uri.writeToParcel()
+ .fragment(""); // TODO: Remove, workaround for a bug in Uri.writeToParcel()
+
+ // append standard suggestion query path
+ uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY);
+
+ // inject query, either as selection args or inline
+ uriBuilder.appendPath(query);
+
+ if (limit > 0) {
+ uriBuilder.appendQueryParameter(SearchManager.SUGGEST_PARAMETER_LIMIT, String.valueOf(limit));
+ }
+
+ Uri uri = uriBuilder.build();
+
+ // finally, make the query
+ return mContext.getContentResolver().query(uri, null, null, null, null);
+ }
+
+ public void close() {
+ if (DBG) Log.d(LOG_TAG, "close()");
+ changeCursor(null);
+ mClosed = true;
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ if (DBG) Log.d(LOG_TAG, "notifyDataSetChanged");
+ super.notifyDataSetChanged();
+
+ // mSearchView.onDataSetChanged(); // TODO:
+
+ updateSpinnerState(getCursor());
+ }
+
+ @Override
+ public void notifyDataSetInvalidated() {
+ if (DBG) Log.d(LOG_TAG, "notifyDataSetInvalidated");
+ super.notifyDataSetInvalidated();
+
+ updateSpinnerState(getCursor());
+ }
+
+ private void updateSpinnerState(Cursor cursor) {
+ Bundle extras = cursor != null ? cursor.getExtras() : null;
+ if (DBG) {
+ Log.d(LOG_TAG, "updateSpinnerState - extra = "
+ + (extras != null
+ ? extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)
+ : null));
+ }
+ // Check if the Cursor indicates that the query is not complete and show the spinner
+ if (extras != null
+ && extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)) {
+ // mSearchView.getWindow().getDecorView().post(mStartSpinnerRunnable); // TODO:
+ return;
+ }
+ // If cursor is null or is done, stop the spinner
+ // mSearchView.getWindow().getDecorView().post(mStopSpinnerRunnable); // TODO:
+ }
+
+ /**
+ * Cache columns.
+ */
+ @Override
+ public void changeCursor(Cursor c) {
+ if (DBG) Log.d(LOG_TAG, "changeCursor(" + c + ")");
+
+ if (mClosed) {
+ Log.w(LOG_TAG, "Tried to change cursor after adapter was closed.");
+ if (c != null) c.close();
+ return;
+ }
+
+ try {
+ super.changeCursor(c);
+
+ if (c != null) {
+ mText1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
+ mText2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2);
+ mText2UrlCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2_URL);
+ mIconName1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
+ mIconName2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2);
+ mFlagsCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_FLAGS);
+ }
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "error changing cursor and caching columns", e);
+ }
+ }
+
+ /**
+ * Tags the view with cached child view look-ups.
+ */
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ View v = super.newView(context, cursor, parent);
+ v.setTag(new ChildViewCache(v));
+ return v;
+ }
+
+ /**
+ * Cache of the child views of drop-drown list items, to avoid looking up the children
+ * each time the contents of a list item are changed.
+ */
+ private final static class ChildViewCache {
+ public final TextView mText1;
+ public final TextView mText2;
+ public final ImageView mIcon1;
+ public final ImageView mIcon2;
+ public final ImageView mIconRefine;
+
+ public ChildViewCache(View v) {
+ mText1 = (TextView) v.findViewById(android.R.id.text1);
+ mText2 = (TextView) v.findViewById(android.R.id.text2);
+ mIcon1 = (ImageView) v.findViewById(android.R.id.icon1);
+ mIcon2 = (ImageView) v.findViewById(android.R.id.icon2);
+ mIconRefine = (ImageView) v.findViewById(R.id.edit_query);
+ }
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ ChildViewCache views = (ChildViewCache) view.getTag();
+
+ int flags = 0;
+ if (mFlagsCol != INVALID_INDEX) {
+ flags = cursor.getInt(mFlagsCol);
+ }
+ if (views.mText1 != null) {
+ String text1 = getStringOrNull(cursor, mText1Col);
+ setViewText(views.mText1, text1);
+ }
+ if (views.mText2 != null) {
+ // First check TEXT_2_URL
+ CharSequence text2 = getStringOrNull(cursor, mText2UrlCol);
+ if (text2 != null) {
+ text2 = formatUrl(text2);
+ } else {
+ text2 = getStringOrNull(cursor, mText2Col);
+ }
+
+ // If no second line of text is indicated, allow the first line of text
+ // to be up to two lines if it wants to be.
+ if (TextUtils.isEmpty(text2)) {
+ if (views.mText1 != null) {
+ views.mText1.setSingleLine(false);
+ views.mText1.setMaxLines(2);
+ }
+ } else {
+ if (views.mText1 != null) {
+ views.mText1.setSingleLine(true);
+ views.mText1.setMaxLines(1);
+ }
+ }
+ setViewText(views.mText2, text2);
+ }
+
+ if (views.mIcon1 != null) {
+ setViewDrawable(views.mIcon1, getIcon1(cursor), View.INVISIBLE);
+ }
+ if (views.mIcon2 != null) {
+ setViewDrawable(views.mIcon2, getIcon2(cursor), View.GONE);
+ }
+ if (mQueryRefinement == REFINE_ALL
+ || (mQueryRefinement == REFINE_BY_ENTRY
+ && (flags & SearchManager.FLAG_QUERY_REFINEMENT) != 0)) {
+ views.mIconRefine.setVisibility(View.VISIBLE);
+ views.mIconRefine.setTag(views.mText1.getText());
+ views.mIconRefine.setOnClickListener(this);
+ } else {
+ views.mIconRefine.setVisibility(View.GONE);
+ }
+ }
+
+ public void onClick(View v) {
+ Object tag = v.getTag();
+ if (tag instanceof CharSequence) {
+ mSearchView.onQueryRefine((CharSequence) tag);
+ }
+ }
+
+ private CharSequence formatUrl(CharSequence url) {
+ if (mUrlColor == null) {
+ // Lazily get the URL color from the current theme.
+ TypedValue colorValue = new TypedValue();
+ mContext.getTheme().resolveAttribute(R.attr.textColorSearchUrl, colorValue, true);
+ mUrlColor = mContext.getResources().getColorStateList(colorValue.resourceId);
+ }
+
+ SpannableString text = new SpannableString(url);
+ text.setSpan(new TextAppearanceSpan(null, 0, 0, mUrlColor, null),
+ 0, url.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ return text;
+ }
+
+ private void setViewText(TextView v, CharSequence text) {
+ // Set the text even if it's null, since we need to clear any previous text.
+ v.setText(text);
+
+ if (TextUtils.isEmpty(text)) {
+ v.setVisibility(View.GONE);
+ } else {
+ v.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private Drawable getIcon1(Cursor cursor) {
+ if (mIconName1Col == INVALID_INDEX) {
+ return null;
+ }
+ String value = cursor.getString(mIconName1Col);
+ Drawable drawable = getDrawableFromResourceValue(value);
+ if (drawable != null) {
+ return drawable;
+ }
+ return getDefaultIcon1(cursor);
+ }
+
+ private Drawable getIcon2(Cursor cursor) {
+ if (mIconName2Col == INVALID_INDEX) {
+ return null;
+ }
+ String value = cursor.getString(mIconName2Col);
+ return getDrawableFromResourceValue(value);
+ }
+
+ /**
+ * Sets the drawable in an image view, makes sure the view is only visible if there
+ * is a drawable.
+ */
+ private void setViewDrawable(ImageView v, Drawable drawable, int nullVisibility) {
+ // Set the icon even if the drawable is null, since we need to clear any
+ // previous icon.
+ v.setImageDrawable(drawable);
+
+ if (drawable == null) {
+ v.setVisibility(nullVisibility);
+ } else {
+ v.setVisibility(View.VISIBLE);
+
+ // This is a hack to get any animated drawables (like a 'working' spinner)
+ // to animate. You have to setVisible true on an AnimationDrawable to get
+ // it to start animating, but it must first have been false or else the
+ // call to setVisible will be ineffective. We need to clear up the story
+ // about animated drawables in the future, see http://b/1878430.
+ drawable.setVisible(false, false);
+ drawable.setVisible(true, false);
+ }
+ }
+
+ /**
+ * Gets the text to show in the query field when a suggestion is selected.
+ *
+ * @param cursor The Cursor to read the suggestion data from. The Cursor should already
+ * be moved to the suggestion that is to be read from.
+ * @return The text to show, or null if the query should not be
+ * changed when selecting this suggestion.
+ */
+ @Override
+ public CharSequence convertToString(Cursor cursor) {
+ if (cursor == null) {
+ return null;
+ }
+
+ String query = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_QUERY);
+ if (query != null) {
+ return query;
+ }
+
+ return null;
+ }
+
+ /**
+ * This method is overridden purely to provide a bit of protection against
+ * flaky content providers.
+ *
+ * @see android.widget.ListAdapter#getView(int, View, ViewGroup)
+ */
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ try {
+ return super.getView(position, convertView, parent);
+ } catch (RuntimeException e) {
+ Log.w(LOG_TAG, "Search suggestions cursor threw exception.", e);
+ // Put exception string in item title
+ View v = newView(mContext, mCursor, parent);
+ if (v != null) {
+ ChildViewCache views = (ChildViewCache) v.getTag();
+ TextView tv = views.mText1;
+ tv.setText(e.toString());
+ }
+ return v;
+ }
+ }
+
+ /**
+ * Gets a drawable given a value provided by a suggestion provider.
+ *
+ * This value could be just the string value of a resource id
+ * (e.g., "2130837524"), in which case we will try to retrieve a drawable from
+ * the provider's resources. If the value is not an integer, it is
+ * treated as a Uri and opened with
+ * {@link ContentResolver#openOutputStream(android.net.Uri, String)}.
+ *
+ * All resources and URIs are read using the suggestion provider's context.
+ *
+ * If the string is not formatted as expected, or no drawable can be found for
+ * the provided value, this method returns null.
+ *
+ * @param drawableId a string like "2130837524",
+ * "android.resource://com.android.alarmclock/2130837524",
+ * or "content://contacts/photos/253".
+ * @return a Drawable, or null if none found
+ */
+ private Drawable getDrawableFromResourceValue(String drawableId) {
+ if (drawableId == null || drawableId.length() == 0 || "0".equals(drawableId)) {
+ return null;
+ }
+ try {
+ // First, see if it's just an integer
+ int resourceId = Integer.parseInt(drawableId);
+ // It's an int, look for it in the cache
+ String drawableUri = ContentResolver.SCHEME_ANDROID_RESOURCE
+ + "://" + mProviderContext.getPackageName() + "/" + resourceId;
+ // Must use URI as cache key, since ints are app-specific
+ Drawable drawable = checkIconCache(drawableUri);
+ if (drawable != null) {
+ return drawable;
+ }
+ // Not cached, find it by resource ID
+ drawable = mProviderContext.getResources().getDrawable(resourceId);
+ // Stick it in the cache, using the URI as key
+ storeInIconCache(drawableUri, drawable);
+ return drawable;
+ } catch (NumberFormatException nfe) {
+ // It's not an integer, use it as a URI
+ Drawable drawable = checkIconCache(drawableId);
+ if (drawable != null) {
+ return drawable;
+ }
+ Uri uri = Uri.parse(drawableId);
+ drawable = getDrawable(uri);
+ storeInIconCache(drawableId, drawable);
+ return drawable;
+ } catch (Resources.NotFoundException nfe) {
+ // It was an integer, but it couldn't be found, bail out
+ Log.w(LOG_TAG, "Icon resource not found: " + drawableId);
+ return null;
+ }
+ }
+
+ /**
+ * Gets a drawable by URI, without using the cache.
+ *
+ * @return A drawable, or {@code null} if the drawable could not be loaded.
+ */
+ private Drawable getDrawable(Uri uri) {
+ try {
+ String scheme = uri.getScheme();
+ if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
+ // Load drawables through Resources, to get the source density information
+ try {
+ return getTheDrawable(uri);
+ } catch (Resources.NotFoundException ex) {
+ throw new FileNotFoundException("Resource does not exist: " + uri);
+ }
+ } else {
+ // Let the ContentResolver handle content and file URIs.
+ InputStream stream = mProviderContext.getContentResolver().openInputStream(uri);
+ if (stream == null) {
+ throw new FileNotFoundException("Failed to open " + uri);
+ }
+ try {
+ return Drawable.createFromStream(stream, null);
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException ex) {
+ Log.e(LOG_TAG, "Error closing icon stream for " + uri, ex);
+ }
+ }
+ }
+ } catch (FileNotFoundException fnfe) {
+ Log.w(LOG_TAG, "Icon not found: " + uri + ", " + fnfe.getMessage());
+ return null;
+ }
+ }
+
+ public Drawable getTheDrawable(Uri uri) throws FileNotFoundException {
+ String authority = uri.getAuthority();
+ Resources r;
+ if (TextUtils.isEmpty(authority)) {
+ throw new FileNotFoundException("No authority: " + uri);
+ } else {
+ try {
+ r = mContext.getPackageManager().getResourcesForApplication(authority);
+ } catch (NameNotFoundException ex) {
+ throw new FileNotFoundException("No package found for authority: " + uri);
+ }
+ }
+ List path = uri.getPathSegments();
+ if (path == null) {
+ throw new FileNotFoundException("No path: " + uri);
+ }
+ int len = path.size();
+ int id;
+ if (len == 1) {
+ try {
+ id = Integer.parseInt(path.get(0));
+ } catch (NumberFormatException e) {
+ throw new FileNotFoundException("Single path segment is not a resource ID: " + uri);
+ }
+ } else if (len == 2) {
+ id = r.getIdentifier(path.get(1), path.get(0), authority);
+ } else {
+ throw new FileNotFoundException("More than two path segments: " + uri);
+ }
+ if (id == 0) {
+ throw new FileNotFoundException("No resource found for: " + uri);
+ }
+ return r.getDrawable(id);
+ }
+
+ private Drawable checkIconCache(String resourceUri) {
+ Drawable.ConstantState cached = mOutsideDrawablesCache.get(resourceUri);
+ if (cached == null) {
+ return null;
+ }
+ if (DBG) Log.d(LOG_TAG, "Found icon in cache: " + resourceUri);
+ return cached.newDrawable();
+ }
+
+ private void storeInIconCache(String resourceUri, Drawable drawable) {
+ if (drawable != null) {
+ mOutsideDrawablesCache.put(resourceUri, drawable.getConstantState());
+ }
+ }
+
+ /**
+ * Gets the left-hand side icon that will be used for the current suggestion
+ * if the suggestion contains an icon column but no icon or a broken icon.
+ *
+ * @param cursor A cursor positioned at the current suggestion.
+ * @return A non-null drawable.
+ */
+ private Drawable getDefaultIcon1(Cursor cursor) {
+ // Fall back to a default icon
+ return mContext.getPackageManager().getDefaultActivityIcon();
+ }
+
+ /**
+ * Gets the activity or application icon for an activity.
+ * Uses the local icon cache for fast repeated lookups.
+ *
+ * @param component Name of an activity.
+ * @return A drawable, or {@code null} if neither the activity nor the application
+ * has an icon set.
+ */
+ private Drawable getActivityIconWithCache(ComponentName component) {
+ // First check the icon cache
+ String componentIconKey = component.flattenToShortString();
+ // Using containsKey() since we also store null values.
+ if (mOutsideDrawablesCache.containsKey(componentIconKey)) {
+ Drawable.ConstantState cached = mOutsideDrawablesCache.get(componentIconKey);
+ return cached == null ? null : cached.newDrawable(mProviderContext.getResources());
+ }
+ // Then try the activity or application icon
+ Drawable drawable = getActivityIcon(component);
+ // Stick it in the cache so we don't do this lookup again.
+ Drawable.ConstantState toCache = drawable == null ? null : drawable.getConstantState();
+ mOutsideDrawablesCache.put(componentIconKey, toCache);
+ return drawable;
+ }
+
+ /**
+ * Gets the activity or application icon for an activity.
+ *
+ * @param component Name of an activity.
+ * @return A drawable, or {@code null} if neither the acitivy or the application
+ * have an icon set.
+ */
+ private Drawable getActivityIcon(ComponentName component) {
+ PackageManager pm = mContext.getPackageManager();
+ final ActivityInfo activityInfo;
+ try {
+ activityInfo = pm.getActivityInfo(component, PackageManager.GET_META_DATA);
+ } catch (NameNotFoundException ex) {
+ Log.w(LOG_TAG, ex.toString());
+ return null;
+ }
+ int iconId = activityInfo.getIconResource();
+ if (iconId == 0) return null;
+ String pkg = component.getPackageName();
+ Drawable drawable = pm.getDrawable(pkg, iconId, activityInfo.applicationInfo);
+ if (drawable == null) {
+ Log.w(LOG_TAG, "Invalid icon resource " + iconId + " for "
+ + component.flattenToShortString());
+ return null;
+ }
+ return drawable;
+ }
+
+ /**
+ * Gets the value of a string column by name.
+ *
+ * @param cursor Cursor to read the value from.
+ * @param columnName The name of the column to read.
+ * @return The value of the given column, or null
+ * if the cursor does not contain the given column.
+ */
+ public static String getColumnString(Cursor cursor, String columnName) {
+ int col = cursor.getColumnIndex(columnName);
+ return getStringOrNull(cursor, col);
+ }
+
+ private static String getStringOrNull(Cursor cursor, int col) {
+ if (col == INVALID_INDEX) {
+ return null;
+ }
+ try {
+ return cursor.getString(col);
+ } catch (Exception e) {
+ Log.e(LOG_TAG,
+ "unexpected error retrieving valid column from cursor, "
+ + "did the remote process die?", e);
+ return null;
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/library/test/com/actionbarsherlock/internal/ManifestParsingTest.java b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/test/com/actionbarsherlock/internal/ManifestParsingTest.java
new file mode 100644
index 0000000000..47475c5746
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/library/test/com/actionbarsherlock/internal/ManifestParsingTest.java
@@ -0,0 +1,37 @@
+package com.actionbarsherlock.internal;
+
+import org.junit.Test;
+
+import static com.actionbarsherlock.internal.ActionBarSherlockCompat.cleanActivityName;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+public class ManifestParsingTest {
+ @Test
+ public void testFullyQualifiedClassName() {
+ String expected = "com.other.package.SomeClass";
+ String actual = cleanActivityName("com.jakewharton.test", "com.other.package.SomeClass");
+ assertThat(expected, equalTo(actual));
+ }
+
+ @Test
+ public void testFullyQualifiedClassNameSamePackage() {
+ String expected = "com.jakewharton.test.SomeClass";
+ String actual = cleanActivityName("com.jakewharton.test", "com.jakewharton.test.SomeClass");
+ assertThat(expected, equalTo(actual));
+ }
+
+ @Test
+ public void testUnqualifiedClassName() {
+ String expected = "com.jakewharton.test.SomeClass";
+ String actual = cleanActivityName("com.jakewharton.test", "SomeClass");
+ assertThat(expected, equalTo(actual));
+ }
+
+ @Test
+ public void testRelativeClassName() {
+ String expected = "com.jakewharton.test.ui.SomeClass";
+ String actual = cleanActivityName("com.jakewharton.test", ".ui.SomeClass");
+ assertThat(expected, equalTo(actual));
+ }
+}
\ No newline at end of file
diff --git a/product/modules/agents/android/client/plugins/ActionBarSherlock/pom.xml b/product/modules/agents/android/client/plugins/ActionBarSherlock/pom.xml
new file mode 100644
index 0000000000..ce4d4fca88
--- /dev/null
+++ b/product/modules/agents/android/client/plugins/ActionBarSherlock/pom.xml
@@ -0,0 +1,191 @@
+
+
+
+ 4.0.0
+
+
+ org.sonatype.oss
+ oss-parent
+ 7
+
+
+ com.actionbarsherlock
+ parent
+ pom
+ 4.2.0
+
+ ActionBarSherlock (Parent)
+ Android library for implementing the action bar design pattern using the backported sources of Ice Cream Sandwich.
+ http://actionbarsherlock.com
+ 2011
+
+
+ library
+ samples
+
+
+
+ https://github.com/JakeWharton/ActionBarSherlock/
+ scm:git:git://github.com/JakeWharton/ActionBarSherlock.git
+ scm:git:git@github.com:JakeWharton/ActionBarSherlock.git
+
+
+
+
+ Jake Wharton
+ jakewharton@gmail.com
+ jakewharton
+ http://jakewharton.com
+ -5
+
+ developer
+
+
+
+
+
+
+ Apache License Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+ Jake Wharton
+ http://jakewharton.com
+
+
+
+ GitHub Issues
+ https://github.com/JakeWharton/ActionBarSherlock/issues
+
+
+
+ UTF-8
+ UTF-8
+
+ 1.6
+ 4.0.1.2
+ 14
+ r99
+
+ 3.3.2
+ 4.10
+
+ JakeWharton
+ ActionBarSherlock
+
+
+
+
+
+ com.google.android
+ android
+ ${android.version}
+
+
+ com.google.android
+ support-v4
+ ${android-support.version}
+ system
+ ${basedir}/libs/android-support-v4.jar
+
+
+ com.nineoldandroids
+ library
+ 2.4.0
+
+
+ com.github.rtyley
+ roboguice-sherlock
+ 1.4
+
+
+ junit
+ junit
+ ${junit.version}
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.5
+
+
+ ${java.version}
+
+
+
+
+ com.jayway.maven.plugins.android.generation2
+ android-maven-plugin
+ 3.3.2
+
+
+ ${android.platform}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.8
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ 2.9.1
+
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-site-plugin
+ 3.0
+
+ true
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.2.2
+
+ true
+
+
+
+
+ com.github.github
+ site-maven-plugin
+ 0.5
+
+
+ site
+
+ site
+
+
+
+
+ Creating site for ${project.version}.
+ website
+
+
+
+
+
diff --git a/product/modules/agents/android/client/proguard-project.txt b/product/modules/agents/android/client/proguard-project.txt
new file mode 100644
index 0000000000..f2fe1559a2
--- /dev/null
+++ b/product/modules/agents/android/client/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/product/modules/agents/android/client/project.properties b/product/modules/agents/android/client/project.properties
new file mode 100644
index 0000000000..f1eace0071
--- /dev/null
+++ b/product/modules/agents/android/client/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
+android.library.reference.1=plugins/ActionBarSherlock/library
diff --git a/product/modules/agents/android/client/res/drawable-hdpi/ic_bookmark.png b/product/modules/agents/android/client/res/drawable-hdpi/ic_bookmark.png
new file mode 100644
index 0000000000..d14f46de0c
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-hdpi/ic_bookmark.png differ
diff --git a/product/modules/agents/android/client/res/drawable-hdpi/ic_check_default.png b/product/modules/agents/android/client/res/drawable-hdpi/ic_check_default.png
new file mode 100644
index 0000000000..ea7db4a7d6
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-hdpi/ic_check_default.png differ
diff --git a/product/modules/agents/android/client/res/drawable-hdpi/ic_check_selected.png b/product/modules/agents/android/client/res/drawable-hdpi/ic_check_selected.png
new file mode 100644
index 0000000000..319b7124de
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-hdpi/ic_check_selected.png differ
diff --git a/product/modules/agents/android/client/res/drawable-hdpi/ic_launcher.png b/product/modules/agents/android/client/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000000..79e263bc29
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-hdpi/ic_launcher.png differ
diff --git a/product/modules/agents/android/client/res/drawable-hdpi/ic_logo.png b/product/modules/agents/android/client/res/drawable-hdpi/ic_logo.png
new file mode 100644
index 0000000000..5ac68d8a29
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-hdpi/ic_logo.png differ
diff --git a/product/modules/agents/android/client/res/drawable-hdpi/ic_logo_dark.png b/product/modules/agents/android/client/res/drawable-hdpi/ic_logo_dark.png
new file mode 100644
index 0000000000..5ac68d8a29
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-hdpi/ic_logo_dark.png differ
diff --git a/product/modules/agents/android/client/res/drawable-hdpi/ic_stat_gcm.png b/product/modules/agents/android/client/res/drawable-hdpi/ic_stat_gcm.png
new file mode 100644
index 0000000000..71ce86772d
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-hdpi/ic_stat_gcm.png differ
diff --git a/product/modules/agents/android/client/res/drawable-hdpi/option_icon.png b/product/modules/agents/android/client/res/drawable-hdpi/option_icon.png
new file mode 100644
index 0000000000..e5c743202f
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-hdpi/option_icon.png differ
diff --git a/product/modules/agents/android/client/res/drawable-hdpi/repeat_bg.png b/product/modules/agents/android/client/res/drawable-hdpi/repeat_bg.png
new file mode 100644
index 0000000000..1282b9cde8
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-hdpi/repeat_bg.png differ
diff --git a/product/modules/agents/android/client/res/drawable-hdpi/top_bar.png b/product/modules/agents/android/client/res/drawable-hdpi/top_bar.png
new file mode 100644
index 0000000000..2934d923c6
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-hdpi/top_bar.png differ
diff --git a/product/modules/agents/android/client/res/drawable-mdpi/ic_bookmark.png b/product/modules/agents/android/client/res/drawable-mdpi/ic_bookmark.png
new file mode 100644
index 0000000000..77598e5c39
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-mdpi/ic_bookmark.png differ
diff --git a/product/modules/agents/android/client/res/drawable-mdpi/ic_check_default.png b/product/modules/agents/android/client/res/drawable-mdpi/ic_check_default.png
new file mode 100644
index 0000000000..18f9aa1ae7
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-mdpi/ic_check_default.png differ
diff --git a/product/modules/agents/android/client/res/drawable-mdpi/ic_check_selected.png b/product/modules/agents/android/client/res/drawable-mdpi/ic_check_selected.png
new file mode 100644
index 0000000000..b44af9284b
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-mdpi/ic_check_selected.png differ
diff --git a/product/modules/agents/android/client/res/drawable-mdpi/ic_launcher.png b/product/modules/agents/android/client/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000000..5219466ac8
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-mdpi/ic_launcher.png differ
diff --git a/product/modules/agents/android/client/res/drawable-mdpi/ic_logo.png b/product/modules/agents/android/client/res/drawable-mdpi/ic_logo.png
new file mode 100644
index 0000000000..943a716e4f
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-mdpi/ic_logo.png differ
diff --git a/product/modules/agents/android/client/res/drawable-mdpi/ic_logo_dark.png b/product/modules/agents/android/client/res/drawable-mdpi/ic_logo_dark.png
new file mode 100644
index 0000000000..943a716e4f
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-mdpi/ic_logo_dark.png differ
diff --git a/product/modules/agents/android/client/res/drawable-mdpi/option_icon.png b/product/modules/agents/android/client/res/drawable-mdpi/option_icon.png
new file mode 100644
index 0000000000..004c41b5bf
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-mdpi/option_icon.png differ
diff --git a/product/modules/agents/android/client/res/drawable-mdpi/top_bar.png b/product/modules/agents/android/client/res/drawable-mdpi/top_bar.png
new file mode 100644
index 0000000000..e4a9ebae11
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-mdpi/top_bar.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/appinstall.png b/product/modules/agents/android/client/res/drawable-xhdpi/appinstall.png
new file mode 100644
index 0000000000..686dfe2ee0
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/appinstall.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/applist.png b/product/modules/agents/android/client/res/drawable-xhdpi/applist.png
new file mode 100644
index 0000000000..81aecad314
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/applist.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/appuninstall.png b/product/modules/agents/android/client/res/drawable-xhdpi/appuninstall.png
new file mode 100644
index 0000000000..711149d214
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/appuninstall.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/camera.png b/product/modules/agents/android/client/res/drawable-xhdpi/camera.png
new file mode 100644
index 0000000000..044758a85d
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/camera.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/changepassword.png b/product/modules/agents/android/client/res/drawable-xhdpi/changepassword.png
new file mode 100644
index 0000000000..dee9bb3f69
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/changepassword.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/encrypt.png b/product/modules/agents/android/client/res/drawable-xhdpi/encrypt.png
new file mode 100644
index 0000000000..ab4a2fa559
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/encrypt.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/ic_bookmark.png b/product/modules/agents/android/client/res/drawable-xhdpi/ic_bookmark.png
new file mode 100644
index 0000000000..82f18a7750
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/ic_bookmark.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/ic_check_default.png b/product/modules/agents/android/client/res/drawable-xhdpi/ic_check_default.png
new file mode 100644
index 0000000000..cf411b4552
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/ic_check_default.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/ic_check_selected.png b/product/modules/agents/android/client/res/drawable-xhdpi/ic_check_selected.png
new file mode 100644
index 0000000000..88b1110261
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/ic_check_selected.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/ic_launcher.png b/product/modules/agents/android/client/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000000..96210dd12f
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/ic_logo.png b/product/modules/agents/android/client/res/drawable-xhdpi/ic_logo.png
new file mode 100644
index 0000000000..ca46dfb347
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/ic_logo.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/ic_logo_dark.png b/product/modules/agents/android/client/res/drawable-xhdpi/ic_logo_dark.png
new file mode 100644
index 0000000000..ca46dfb347
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/ic_logo_dark.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/info.png b/product/modules/agents/android/client/res/drawable-xhdpi/info.png
new file mode 100644
index 0000000000..78b917243f
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/info.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/location.png b/product/modules/agents/android/client/res/drawable-xhdpi/location.png
new file mode 100644
index 0000000000..3c93225bb0
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/location.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/lock.png b/product/modules/agents/android/client/res/drawable-xhdpi/lock.png
new file mode 100644
index 0000000000..5c30af3f8b
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/lock.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/mute.png b/product/modules/agents/android/client/res/drawable-xhdpi/mute.png
new file mode 100644
index 0000000000..04ca709343
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/mute.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/notification.png b/product/modules/agents/android/client/res/drawable-xhdpi/notification.png
new file mode 100644
index 0000000000..c3fc558acb
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/notification.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/repeat_bg.png b/product/modules/agents/android/client/res/drawable-xhdpi/repeat_bg.png
new file mode 100644
index 0000000000..b15eedd675
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/repeat_bg.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/wifi.png b/product/modules/agents/android/client/res/drawable-xhdpi/wifi.png
new file mode 100644
index 0000000000..72b2e70ff5
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/wifi.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xhdpi/wipe.png b/product/modules/agents/android/client/res/drawable-xhdpi/wipe.png
new file mode 100644
index 0000000000..ca047ab527
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xhdpi/wipe.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xxhdpi/ic_bookmark.png b/product/modules/agents/android/client/res/drawable-xxhdpi/ic_bookmark.png
new file mode 100644
index 0000000000..cbdd6b86c0
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xxhdpi/ic_bookmark.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xxhdpi/ic_check_default.png b/product/modules/agents/android/client/res/drawable-xxhdpi/ic_check_default.png
new file mode 100644
index 0000000000..dc687f3889
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xxhdpi/ic_check_default.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xxhdpi/ic_check_selected.png b/product/modules/agents/android/client/res/drawable-xxhdpi/ic_check_selected.png
new file mode 100644
index 0000000000..29355e4587
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xxhdpi/ic_check_selected.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xxhdpi/ic_launcher.png b/product/modules/agents/android/client/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..2c36d39e78
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xxhdpi/ic_logo.png b/product/modules/agents/android/client/res/drawable-xxhdpi/ic_logo.png
new file mode 100644
index 0000000000..993ef9971f
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xxhdpi/ic_logo.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xxhdpi/ic_logo_dark.png b/product/modules/agents/android/client/res/drawable-xxhdpi/ic_logo_dark.png
new file mode 100644
index 0000000000..993ef9971f
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xxhdpi/ic_logo_dark.png differ
diff --git a/product/modules/agents/android/client/res/drawable-xxhdpi/repeat_bg.png b/product/modules/agents/android/client/res/drawable-xxhdpi/repeat_bg.png
new file mode 100644
index 0000000000..9028b134e0
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable-xxhdpi/repeat_bg.png differ
diff --git a/product/modules/agents/android/client/res/drawable/btn_grey.xml b/product/modules/agents/android/client/res/drawable/btn_grey.xml
new file mode 100644
index 0000000000..f773bd2aad
--- /dev/null
+++ b/product/modules/agents/android/client/res/drawable/btn_grey.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/drawable/btn_orange.xml b/product/modules/agents/android/client/res/drawable/btn_orange.xml
new file mode 100644
index 0000000000..c67fa13d1e
--- /dev/null
+++ b/product/modules/agents/android/client/res/drawable/btn_orange.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/drawable/custom_checkbox.xml b/product/modules/agents/android/client/res/drawable/custom_checkbox.xml
new file mode 100644
index 0000000000..a0ad1a1a42
--- /dev/null
+++ b/product/modules/agents/android/client/res/drawable/custom_checkbox.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/drawable/dot.png b/product/modules/agents/android/client/res/drawable/dot.png
new file mode 100644
index 0000000000..7a3361fbec
Binary files /dev/null and b/product/modules/agents/android/client/res/drawable/dot.png differ
diff --git a/product/modules/agents/android/client/res/drawable/mdm_logo.xml b/product/modules/agents/android/client/res/drawable/mdm_logo.xml
new file mode 100644
index 0000000000..01fa30de41
--- /dev/null
+++ b/product/modules/agents/android/client/res/drawable/mdm_logo.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/layout/activity_agent_settings.xml b/product/modules/agents/android/client/res/layout/activity_agent_settings.xml
new file mode 100644
index 0000000000..ad864a9649
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/activity_agent_settings.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/layout/activity_alert.xml b/product/modules/agents/android/client/res/layout/activity_alert.xml
new file mode 100644
index 0000000000..50232ae14c
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/activity_alert.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/layout/activity_already_registered.xml b/product/modules/agents/android/client/res/layout/activity_already_registered.xml
new file mode 100644
index 0000000000..a10e29995e
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/activity_already_registered.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/layout/activity_authentication.xml b/product/modules/agents/android/client/res/layout/activity_authentication.xml
new file mode 100644
index 0000000000..cd459ddba4
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/activity_authentication.xml
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/layout/activity_authentication_error.xml b/product/modules/agents/android/client/res/layout/activity_authentication_error.xml
new file mode 100644
index 0000000000..d742e5d485
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/activity_authentication_error.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/layout/activity_available_operations.xml b/product/modules/agents/android/client/res/layout/activity_available_operations.xml
new file mode 100644
index 0000000000..e1c825d7e3
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/activity_available_operations.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/layout/activity_display_device_info.xml b/product/modules/agents/android/client/res/layout/activity_display_device_info.xml
new file mode 100644
index 0000000000..bc71b330e1
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/activity_display_device_info.xml
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/layout/activity_entry.xml b/product/modules/agents/android/client/res/layout/activity_entry.xml
new file mode 100644
index 0000000000..c5b47b285c
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/activity_entry.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/layout/activity_log.xml b/product/modules/agents/android/client/res/layout/activity_log.xml
new file mode 100644
index 0000000000..ae0aa279a7
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/activity_log.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/layout/activity_main.xml b/product/modules/agents/android/client/res/layout/activity_main.xml
new file mode 100644
index 0000000000..078b90789d
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/activity_main.xml
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/layout/activity_notification.xml b/product/modules/agents/android/client/res/layout/activity_notification.xml
new file mode 100644
index 0000000000..99e0da00d7
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/activity_notification.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/layout/activity_pin_code.xml b/product/modules/agents/android/client/res/layout/activity_pin_code.xml
new file mode 100644
index 0000000000..6b947c5d7f
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/activity_pin_code.xml
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/layout/activity_register_successful.xml b/product/modules/agents/android/client/res/layout/activity_register_successful.xml
new file mode 100644
index 0000000000..a760eb5b67
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/activity_register_successful.xml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/layout/activity_settings.xml b/product/modules/agents/android/client/res/layout/activity_settings.xml
new file mode 100644
index 0000000000..bcb3c17e06
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/activity_settings.xml
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/layout/custom_sherlock_bar.xml b/product/modules/agents/android/client/res/layout/custom_sherlock_bar.xml
new file mode 100644
index 0000000000..dbefe542fc
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/custom_sherlock_bar.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/layout/custom_terms_popup.xml b/product/modules/agents/android/client/res/layout/custom_terms_popup.xml
new file mode 100644
index 0000000000..b085c49dac
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/custom_terms_popup.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/layout/footer_repeat.xml b/product/modules/agents/android/client/res/layout/footer_repeat.xml
new file mode 100644
index 0000000000..e28749e577
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/footer_repeat.xml
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/layout/header_gradient.xml b/product/modules/agents/android/client/res/layout/header_gradient.xml
new file mode 100644
index 0000000000..cc1c99c722
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/header_gradient.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/layout/login.xml b/product/modules/agents/android/client/res/layout/login.xml
new file mode 100644
index 0000000000..66c3cd278a
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/login.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/layout/main.xml b/product/modules/agents/android/client/res/layout/main.xml
new file mode 100644
index 0000000000..4ab85b4973
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/main.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/product/modules/agents/android/client/res/layout/notify.xml b/product/modules/agents/android/client/res/layout/notify.xml
new file mode 100644
index 0000000000..b89045515d
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/notify.xml
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/layout/row_with_icon.xml b/product/modules/agents/android/client/res/layout/row_with_icon.xml
new file mode 100644
index 0000000000..7322a13b0f
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/row_with_icon.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/layout/simplerow.xml b/product/modules/agents/android/client/res/layout/simplerow.xml
new file mode 100644
index 0000000000..ccb77e2733
--- /dev/null
+++ b/product/modules/agents/android/client/res/layout/simplerow.xml
@@ -0,0 +1,8 @@
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/menu/agent_settings.xml b/product/modules/agents/android/client/res/menu/agent_settings.xml
new file mode 100644
index 0000000000..d227c4927c
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/agent_settings.xml
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/menu/alert.xml b/product/modules/agents/android/client/res/menu/alert.xml
new file mode 100644
index 0000000000..c002028230
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/alert.xml
@@ -0,0 +1,9 @@
+
diff --git a/product/modules/agents/android/client/res/menu/all_ready_registered.xml b/product/modules/agents/android/client/res/menu/all_ready_registered.xml
new file mode 100644
index 0000000000..d227c4927c
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/all_ready_registered.xml
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/menu/auth_sherlock_menu.xml b/product/modules/agents/android/client/res/menu/auth_sherlock_menu.xml
new file mode 100644
index 0000000000..e4616aed07
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/auth_sherlock_menu.xml
@@ -0,0 +1,15 @@
+
+
diff --git a/product/modules/agents/android/client/res/menu/authentication.xml b/product/modules/agents/android/client/res/menu/authentication.xml
new file mode 100644
index 0000000000..d227c4927c
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/authentication.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/menu/authentication_error.xml b/product/modules/agents/android/client/res/menu/authentication_error.xml
new file mode 100644
index 0000000000..d227c4927c
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/authentication_error.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/menu/available_operations.xml b/product/modules/agents/android/client/res/menu/available_operations.xml
new file mode 100644
index 0000000000..d227c4927c
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/available_operations.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/menu/display_device_info.xml b/product/modules/agents/android/client/res/menu/display_device_info.xml
new file mode 100644
index 0000000000..d227c4927c
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/display_device_info.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/menu/entry.xml b/product/modules/agents/android/client/res/menu/entry.xml
new file mode 100644
index 0000000000..d227c4927c
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/entry.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/menu/log.xml b/product/modules/agents/android/client/res/menu/log.xml
new file mode 100644
index 0000000000..c002028230
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/log.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/menu/main.xml b/product/modules/agents/android/client/res/menu/main.xml
new file mode 100644
index 0000000000..d227c4927c
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/main.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/menu/notification.xml b/product/modules/agents/android/client/res/menu/notification.xml
new file mode 100644
index 0000000000..c002028230
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/notification.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/menu/notify.xml b/product/modules/agents/android/client/res/menu/notify.xml
new file mode 100644
index 0000000000..d227c4927c
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/notify.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/menu/options_menu.xml b/product/modules/agents/android/client/res/menu/options_menu.xml
new file mode 100644
index 0000000000..99912b4210
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/options_menu.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/product/modules/agents/android/client/res/menu/pin_code.xml b/product/modules/agents/android/client/res/menu/pin_code.xml
new file mode 100644
index 0000000000..d227c4927c
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/pin_code.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/menu/register_successful.xml b/product/modules/agents/android/client/res/menu/register_successful.xml
new file mode 100644
index 0000000000..d227c4927c
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/register_successful.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/menu/settings.xml b/product/modules/agents/android/client/res/menu/settings.xml
new file mode 100644
index 0000000000..d227c4927c
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/settings.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/menu/sherlock_menu.xml b/product/modules/agents/android/client/res/menu/sherlock_menu.xml
new file mode 100644
index 0000000000..61204e5f93
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/sherlock_menu.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/menu/sherlock_menu_debug.xml b/product/modules/agents/android/client/res/menu/sherlock_menu_debug.xml
new file mode 100644
index 0000000000..a5c2738da9
--- /dev/null
+++ b/product/modules/agents/android/client/res/menu/sherlock_menu_debug.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/raw/emm_truststore.bks b/product/modules/agents/android/client/res/raw/emm_truststore.bks
new file mode 100644
index 0000000000..661d26effc
Binary files /dev/null and b/product/modules/agents/android/client/res/raw/emm_truststore.bks differ
diff --git a/product/modules/agents/android/client/res/values-sw600dp/dimens.xml b/product/modules/agents/android/client/res/values-sw600dp/dimens.xml
new file mode 100644
index 0000000000..1ba777d65d
--- /dev/null
+++ b/product/modules/agents/android/client/res/values-sw600dp/dimens.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/values-sw720dp-land/dimens.xml b/product/modules/agents/android/client/res/values-sw720dp-land/dimens.xml
new file mode 100644
index 0000000000..eee741a510
--- /dev/null
+++ b/product/modules/agents/android/client/res/values-sw720dp-land/dimens.xml
@@ -0,0 +1,9 @@
+
+
+
+ 128dp
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/values-v11/styles.xml b/product/modules/agents/android/client/res/values-v11/styles.xml
new file mode 100644
index 0000000000..541752f6ed
--- /dev/null
+++ b/product/modules/agents/android/client/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/values-v14/styles.xml b/product/modules/agents/android/client/res/values-v14/styles.xml
new file mode 100644
index 0000000000..f20e01501d
--- /dev/null
+++ b/product/modules/agents/android/client/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/values/colors.xml b/product/modules/agents/android/client/res/values/colors.xml
new file mode 100644
index 0000000000..f5ae18b41e
--- /dev/null
+++ b/product/modules/agents/android/client/res/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+ #A8A8A8
+ #ffffff
+ #606060
+
+
diff --git a/product/modules/agents/android/client/res/values/dimens.xml b/product/modules/agents/android/client/res/values/dimens.xml
new file mode 100644
index 0000000000..ef19409fe6
--- /dev/null
+++ b/product/modules/agents/android/client/res/values/dimens.xml
@@ -0,0 +1,7 @@
+
+
+
+ 16dp
+ 16dp
+ 44dp
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/values/ids.xml b/product/modules/agents/android/client/res/values/ids.xml
new file mode 100644
index 0000000000..1949d8be52
--- /dev/null
+++ b/product/modules/agents/android/client/res/values/ids.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product/modules/agents/android/client/res/values/strings.xml b/product/modules/agents/android/client/res/values/strings.xml
new file mode 100644
index 0000000000..9f24ae92df
--- /dev/null
+++ b/product/modules/agents/android/client/res/values/strings.xml
@@ -0,0 +1,185 @@
+
+
+
+ NsOFFVXkUsahefJHDC4S2csarPka
+ EnG1MjQxF5tiFE0DDNvfJ_6ou_8a
+
+ Please set the %1$s constant and recompile the app.
+ Device is already registered on server.
+ From GCM: device successfully registered!
+ From GCM: device successfully unregistered!
+ From GCM: you got message!
+ From GCM: error (%1$s).
+ From GCM: recoverable error (%1$s).
+ From GCM: server deleted %1$d pending messages!
+ Trying (attempt %1$d/%2$d) to register device on Demo Server.
+ From Demo Server: successfully added device!
+ From Demo Server: successfully removed device!
+ Could not register device on Demo Server after %1$d attempts.
+ Could not unregister device on Demo Server (%1$s).
+ Register
+ Unregister
+ Image
+ Clear
+ Exit
+ WSO2 Agent
+
+ Settings
+ Hello MDM
+ DisplayDeviceInfo
+ http://192.168.6.6:8080/mdm/register
+ RegisterSuccessful
+ Entry
+ Authentication Failed
+ Registration Failed
+ Connection Failed
+ Notification failed
+ Device Admin Enabled
+ Device Admin Disabled
+ You are not allowed to enroll because your device is rooted and device OS version is older than 4.0.3
+ You are not allowed to enroll because your device is rooted
+ Compatible device
+ You are not allowed to enroll because your device OS version is older than 4.0.3
+ AuthenticationError
+ NotificationActivity
+ NotifyActivity
+ AgentSettingsActivity
+ AvailableOperationsActivity
+ Operations
+ Change PIN
+ Change Server Address
+ Debug Log
+ Message from EMM
+ This will enable device administration
+ BYOD
+ COPE
+ Initialization Failed. Please try again.
+ Initialization Failed
+ Re-type the PIN to Confirm
+
+
+ New PIN Code
+
+
+ PIN Code changed successfully
+ Old pincode you entered is wrong, please try again
+ Please enter Server Address, i.e : www.abc.com
+ Please enter username
+ Please enter password
+
+
+ from_activity_name
+ regid
+ main_activity_name
+ message
+ freshRegFlag
+ username
+ notification
+
+
+ com.mdm
+ policy
+ isAgreed
+ registered
+ ip
+ eula
+ regId
+ username
+ pin
+ 1
+ 0
+ deviceType
+ senderID
+ mode
+ interval
+
+ username
+
+ 0
+ ClientID
+ ClientSecret
+
+
+
+
+ Could not connect to server please check your internet connection and try again
+ Connection Error
+ Registration Failed
+ Unregistration Failed
+ Access Denied
+ Enrollment Failed
+ Enrollment failed, please contact administrator.
+ Authentication failed due to a connection failure please check your network connection and try again.
+ Incorrect login information. Please try again.
+ Network connectivity is unavailable. Please check your network connectivity.
+ Cannot proceed the registration. Please contact administrator.
+ Cannot proceed the authentication. Please contact administrator.
+ Cannot unregister the device. Please contact administrator.
+ Cannot execute the notification. Please contact administrator.
+ Server is unavailable. Please contact administrator.
+ Invalid server Address. Please try again.
+
+
+ Authenticating
+ Please wait
+ Checking Registration Info
+ Retrieving server settings
+ Retrieving policy agreement
+ Enrolling Device
+ Are you sure you want to set
+ as your PIN code?
+ You have set your domain to
+ Your device is a
+ Device type
+ device, are you sure you want to proceed ?
+ are you sure you want to proceed ?
+ You are trying to unregister from EMM. Are you sure you want to proceed ?
+
+
+ Register
+ Device un-registration successful, click to re-register to the MDM
+
+
+ Phone Info
+ Change PIN code
+ Change IP address
+
+
+ Unregistering Device
+ Please wait
+
+
+ IMEI:
+ Device:
+ Model:
+ No Sim
+ Operator:
+ IMSI:
+ OS:
+ Rooted:
+ Yes
+ No
+
+
+ OK
+ Cancel
+
+
+ /Download/
+ update.apk
+ application/vnd.android.package-archive
+ package:
+ com.android.launcher.action.INSTALL_SHORTCUT
+
+
+ POST
+ GET
+ LogActivity
+
+
+ Set your server address here to start registration, i.e : www.abc.com
+
+
+ PINs don\'t match, Please re-enter
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/values/styles.xml b/product/modules/agents/android/client/res/values/styles.xml
new file mode 100644
index 0000000000..ba647c19f0
--- /dev/null
+++ b/product/modules/agents/android/client/res/values/styles.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/res/xml/wso2_device_admin.xml b/product/modules/agents/android/client/res/xml/wso2_device_admin.xml
new file mode 100644
index 0000000000..651a7d991a
--- /dev/null
+++ b/product/modules/agents/android/client/res/xml/wso2_device_admin.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/AlertActivity.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/AlertActivity.java
new file mode 100644
index 0000000000..bc821bbb18
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/AlertActivity.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent;
+
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+import com.actionbarsherlock.app.SherlockActivity;
+import org.wso2.cdm.agent.R;
+
+public class AlertActivity extends SherlockActivity {
+ String message = "";
+ Button btnOK;
+ TextView txtMessage;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_alert);
+
+ btnOK = (Button) findViewById(R.id.btnOK);
+ txtMessage = (TextView) findViewById(R.id.txtMessage);
+
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ if (extras.containsKey(getResources().getString(R.string.intent_extra_message))) {
+ message = extras.getString(getResources().getString(R.string.intent_extra_message));
+ }
+ }
+
+ txtMessage.setText(message);
+
+ btnOK.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ // TODO Auto-generated method stub
+ AlertActivity.this.finish();
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(com.actionbarsherlock.view.Menu menu) {
+ // TODO Auto-generated method stub
+ return true;
+ }
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/AlreadyRegisteredActivity.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/AlreadyRegisteredActivity.java
new file mode 100644
index 0000000000..ec0558d085
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/AlreadyRegisteredActivity.java
@@ -0,0 +1,549 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.wso2.cdm.agent.R;
+import org.wso2.cdm.agent.api.PhoneState;
+import org.wso2.cdm.agent.proxy.APIResultCallBack;
+import org.wso2.cdm.agent.services.LocalNotification;
+import org.wso2.cdm.agent.services.Operation;
+import org.wso2.cdm.agent.services.WSO2DeviceAdminReceiver;
+import org.wso2.cdm.agent.utils.CommonDialogUtils;
+import org.wso2.cdm.agent.utils.CommonUtilities;
+import org.wso2.cdm.agent.utils.ServerUtils;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+
+public class AlreadyRegisteredActivity extends SherlockActivity implements APIResultCallBack {
+
+ private String TAG = AlreadyRegisteredActivity.class.getSimpleName();
+
+ AsyncTask mRegisterTask;
+ AsyncTask mCheckRegisterTask;
+ static final int ACTIVATION_REQUEST = 47; // identifies our request id
+ DevicePolicyManager devicePolicyManager;
+ ComponentName demoDeviceAdmin;
+ String regId = "";
+ Context context = null;
+ boolean state = false;
+ ProgressDialog progressDialog;
+ private Button btnUnregister;
+ private TextView txtRegText;
+ // private ImageView optionBtn;
+ private final int TAG_BTN_UNREGISTER = 0;
+ private final int TAG_BTN_OPTIONS = 1;
+ private final int TAG_BTN_RE_REGISTER = 2;
+ ActionBar actionbar;
+ boolean unregState = false;
+ boolean freshRegFlag = false;
+ boolean isUnregisterBtnClicked = false;
+ Operation operation;
+ AlertDialog.Builder alertDialog;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_already_registered);
+ getSupportActionBar().setDisplayShowCustomEnabled(true);
+ getSupportActionBar().setCustomView(R.layout.custom_sherlock_bar);
+ getSupportActionBar().setTitle(R.string.empty_app_title);
+ View homeIcon = findViewById(
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ? android.R.id.home
+ : R.id.abs__home);
+ ((View) homeIcon.getParent()).setVisibility(View.GONE);
+
+ devicePolicyManager = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
+ demoDeviceAdmin = new ComponentName(this,
+ WSO2DeviceAdminReceiver.class);
+ operation = new Operation(AlreadyRegisteredActivity.this);
+ context = AlreadyRegisteredActivity.this;
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ if (extras.containsKey(getResources().getString(
+ R.string.intent_extra_fresh_reg_flag))) {
+ freshRegFlag = extras.getBoolean(getResources().getString(
+ R.string.intent_extra_fresh_reg_flag));
+ }
+
+ }
+
+ String regIden = CommonUtilities
+ .getPref(context, context.getResources().getString(R.string.shared_pref_regId));
+ if (!regIden.equals("")) {
+ regId = regIden;
+ }
+
+ if (freshRegFlag) {
+ try {
+ SharedPreferences mainPref = AlreadyRegisteredActivity.this
+ .getSharedPreferences(
+ getResources().getString(
+ R.string.shared_pref_package),
+ Context.MODE_PRIVATE
+ );
+ Editor editor = mainPref.edit();
+ Log.e("freshRegFlag", "1");
+ editor.putString(
+ getResources().getString(
+ R.string.shared_pref_registered), "1"
+ );
+ editor.commit();
+
+ if (!devicePolicyManager.isAdminActive(demoDeviceAdmin)) {
+ Intent intent1 = new Intent(
+ DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
+ intent1.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+ demoDeviceAdmin);
+ intent1.putExtra(
+ DevicePolicyManager.EXTRA_ADD_EXPLANATION,
+ getResources().getString(
+ R.string.device_admin_enable_alert)
+ );
+ startActivityForResult(intent1, ACTIVATION_REQUEST);
+ }
+ //operation.executePolicy();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ freshRegFlag = false;
+ }
+ txtRegText = (TextView) findViewById(R.id.txtRegText);
+
+ btnUnregister = (Button) findViewById(R.id.btnUnreg);
+ btnUnregister.setTag(TAG_BTN_UNREGISTER);
+ btnUnregister.setOnClickListener(onClickListenerButtonClicked);
+
+ }
+
+ DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ dialog.dismiss();
+ break;
+
+ case DialogInterface.BUTTON_NEGATIVE:
+ startUnRegistration();
+ break;
+ }
+ }
+ };
+
+ OnClickListener onClickListenerButtonClicked = new OnClickListener() {
+
+ @Override
+ public void onClick(View view) {
+ int iTag = (Integer) view.getTag();
+
+ switch (iTag) {
+
+ case TAG_BTN_UNREGISTER:
+ AlertDialog.Builder builder = new AlertDialog.Builder(
+ AlreadyRegisteredActivity.this);
+ builder.setMessage(getResources().getString(R.string.dialog_unregister))
+ .setNegativeButton(getResources().getString(R.string.yes),
+ dialogClickListener)
+ .setPositiveButton(getResources().getString(R.string.no),
+ dialogClickListener).show();
+ break;
+
+ case TAG_BTN_OPTIONS:
+ break;
+ case TAG_BTN_RE_REGISTER:
+ Intent intent = new Intent(AlreadyRegisteredActivity.this,
+ ServerDetails.class);
+ intent.putExtra(
+ getResources().getString(R.string.intent_extra_regid),
+ regId);
+ startActivity(intent);
+ finish();
+ break;
+
+ default:
+ break;
+ }
+
+ }
+ };
+
+ DialogInterface.OnClickListener isRegisteredFailedOKBtnClickListerner =
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface arg0, int arg1) {
+
+ Intent intent = new Intent(AlreadyRegisteredActivity.this,
+ ServerDetails.class);
+ intent.putExtra(
+ getResources().getString(
+ R.string.intent_extra_regid), regId
+ );
+ intent.putExtra(
+ getResources().getString(
+ R.string.intent_extra_from_activity),
+ AlreadyRegisteredActivity.class.getSimpleName()
+ );
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+
+ }
+ };
+
+ public void startUnRegistration() {
+ final Context context = AlreadyRegisteredActivity.this;
+ isUnregisterBtnClicked = true;
+
+ progressDialog = ProgressDialog
+ .show(AlreadyRegisteredActivity.this,
+ getResources().getString(
+ R.string.dialog_message_unregistering),
+ getResources().getString(
+ R.string.dialog_message_please_wait), true
+ );
+
+ regId = CommonUtilities.getPref(context, context.getResources()
+ .getString(R.string.shared_pref_regId));
+
+ Map requestParams = new HashMap();
+ requestParams.put("regid", regId);
+
+ // Check network connection availability before calling the API.
+ if (PhoneState.isNetworkAvailable(context)) {
+ // Call device unregister API.
+ // ServerUtils.callSecuredAPI(AlreadyRegisteredActivity.this, CommonUtilities.UNREGISTER_ENDPOINT,
+ // CommonUtilities.POST_METHOD, requestParams,
+ // AlreadyRegisteredActivity.this,
+ // CommonUtilities.UNREGISTER_REQUEST_CODE);
+ } else {
+ CommonDialogUtils.stopProgressDialog(progressDialog);
+ CommonDialogUtils
+ .showNetworkUnavailableMessage(AlreadyRegisteredActivity.this);
+ }
+
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ if (CommonUtilities.DEBUG_MODE_ENABLED) {
+ getSupportMenuInflater().inflate(R.menu.sherlock_menu_debug, menu);
+ } else {
+ getSupportMenuInflater().inflate(R.menu.sherlock_menu, menu);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.info_setting:
+ Intent intentIN = new Intent(AlreadyRegisteredActivity.this,
+ DisplayDeviceInfoActivity.class);
+ intentIN.putExtra(
+ getResources().getString(
+ R.string.intent_extra_from_activity),
+ AlreadyRegisteredActivity.class.getSimpleName()
+ );
+ startActivity(intentIN);
+ return true;
+ case R.id.pin_setting:
+ Intent intentPIN = new Intent(AlreadyRegisteredActivity.this,
+ PinCodeActivity.class);
+ intentPIN.putExtra(
+ getResources().getString(
+ R.string.intent_extra_from_activity),
+ AlreadyRegisteredActivity.class.getSimpleName()
+ );
+ startActivity(intentPIN);
+ return true;
+ case R.id.ip_setting:
+ SharedPreferences mainPref = AlreadyRegisteredActivity.this
+ .getSharedPreferences("com.mdm", Context.MODE_PRIVATE);
+ Editor editor = mainPref.edit();
+ editor.putString(getResources().getString(R.string.shared_pref_ip),
+ "");
+ editor.commit();
+
+ Intent intentIP = new Intent(AlreadyRegisteredActivity.this,
+ ServerDetails.class);
+ intentIP.putExtra(
+ getResources().getString(
+ R.string.intent_extra_from_activity),
+ AlreadyRegisteredActivity.class.getSimpleName()
+ );
+ startActivity(intentIP);
+ return true;
+ case R.id.debug_log:
+ Intent intentDebug = new Intent(AlreadyRegisteredActivity.this,
+ LogActivity.class);
+ startActivity(intentDebug);
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ Intent i = new Intent();
+ i.setAction(Intent.ACTION_MAIN);
+ i.addCategory(Intent.CATEGORY_HOME);
+ this.startActivity(i);
+ // finish();
+ super.onBackPressed();
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ Intent i = new Intent();
+ i.setAction(Intent.ACTION_MAIN);
+ i.addCategory(Intent.CATEGORY_HOME);
+ this.startActivity(i);
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_HOME) {
+ Intent i = new Intent();
+ i.setAction(Intent.ACTION_MAIN);
+ i.addCategory(Intent.CATEGORY_HOME);
+ this.startActivity(i);
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (!freshRegFlag && !isUnregisterBtnClicked) {
+ // Check network connection availability before calling the API.
+ if (PhoneState.isNetworkAvailable(context)) {
+ // Call isRegistered API.
+ Map requestParams = new HashMap();
+ requestParams.put("regid", regId);
+ // ServerUtils.callSecuredAPI(AlreadyRegisteredActivity.this,
+ // CommonUtilities.IS_REGISTERED_ENDPOINT,
+ // CommonUtilities.POST_METHOD, requestParams,
+ // AlreadyRegisteredActivity.this,
+ // CommonUtilities.IS_REGISTERED_REQUEST_CODE);
+ } else {
+ CommonDialogUtils
+ .showNetworkUnavailableMessage(AlreadyRegisteredActivity.this);
+ }
+
+ }
+
+ }
+
+ public void showAlert(String message, String title) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setMessage(message);
+ builder.setTitle(title);
+ builder.setCancelable(true);
+ builder.setPositiveButton(getResources().getString(R.string.button_ok),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ //cancelEntry();
+ dialog.cancel();
+ }
+ }
+ );
+
+ AlertDialog alert = builder.create();
+ alert.show();
+ }
+
+ public void cancelEntry() {
+ Intent intentIP = new Intent(AlreadyRegisteredActivity.this,
+ ServerDetails.class);
+ intentIP.putExtra(
+ getResources().getString(R.string.intent_extra_from_activity),
+ AlreadyRegisteredActivity.class.getSimpleName());
+ intentIP.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intentIP);
+
+ }
+
+ @Override
+ public void onReceiveAPIResult(Map result, int requestCode) {
+ String responseStatus = "";
+
+ if (requestCode == CommonUtilities.UNREGISTER_REQUEST_CODE) {
+ stopProgressDialog();
+ if (result != null) {
+ responseStatus = result.get(CommonUtilities.STATUS_KEY);
+ if (responseStatus != null) {
+ if (responseStatus.equals(CommonUtilities.REQUEST_SUCCESSFUL)) {
+ txtRegText
+ .setText(R.string.register_text_view_text_unregister);
+ btnUnregister.setText(R.string.register_button_text);
+ btnUnregister.setTag(TAG_BTN_RE_REGISTER);
+ btnUnregister
+ .setOnClickListener(onClickListenerButtonClicked);
+ ServerUtils.clearAppData(context);
+
+ } else if (responseStatus
+ .equals(CommonUtilities.INTERNAL_SERVER_ERROR)) {
+ Log.e(TAG, "The value of status is : " + responseStatus);
+ alertDialog = CommonDialogUtils
+ .getAlertDialogWithOneButtonAndTitle(
+ context,
+ getResources().getString(
+ R.string.title_head_connection_error),
+ getResources().getString(
+ R.string.error_internal_server),
+ getResources().getString(R.string.button_ok),
+ null
+ );
+ } else {
+ Log.e(TAG, "The result is : " + result);
+ Log.e(TAG, "The responseStatus is : " + responseStatus);
+ loadAuthenticationErrorActivity();
+ }
+ } else {
+ Log.e(TAG, "The result is null in onReceiveAPIResult().");
+ Log.e(TAG, "The responseStatus is : " + responseStatus);
+ loadAuthenticationErrorActivity();
+ }
+
+ } else {
+ Log.e(TAG, "The result is null in onReceiveAPIResult().");
+ Log.e(TAG, "The responseStatus is : " + responseStatus);
+ loadAuthenticationErrorActivity();
+ }
+
+ }
+
+ if (requestCode == CommonUtilities.IS_REGISTERED_REQUEST_CODE) {
+ stopProgressDialog();
+ if (result != null) {
+ responseStatus = result.get(CommonUtilities.STATUS_KEY);
+ if (responseStatus != null) {
+ if (responseStatus
+ .equals(CommonUtilities.INTERNAL_SERVER_ERROR)) {
+ Log.e(TAG, "The value of status is : " + responseStatus);
+ alertDialog = CommonDialogUtils
+ .getAlertDialogWithOneButtonAndTitle(
+ context,
+ getResources().getString(
+ R.string.title_head_connection_error),
+ getResources().getString(
+ R.string.error_internal_server),
+ getResources().getString(R.string.button_ok),
+ null
+ );
+ alertDialog.show();
+ } else if (!responseStatus
+ .equals(CommonUtilities.REQUEST_SUCCESSFUL)) {
+ Log.e(TAG, "The value of status is : " + responseStatus);
+ ServerUtils.clearAppData(context);
+
+ alertDialog = CommonDialogUtils
+ .getAlertDialogWithOneButtonAndTitle(
+ context,
+ getResources().getString(
+ R.string.title_head_registration_error),
+ getResources().getString(
+ R.string.error_for_all_unknown_registration_failures),
+ getResources().getString(R.string.button_ok),
+ isRegisteredFailedOKBtnClickListerner
+ );
+ alertDialog.show();
+ ServerUtils.clearAppData(context);
+ }
+ } else {
+ Log.e(TAG, "The result is null in onReceiveAPIResult()");
+ ServerUtils.clearAppData(context);
+
+ alertDialog = CommonDialogUtils
+ .getAlertDialogWithOneButtonAndTitle(
+ context,
+ getResources().getString(
+ R.string.title_head_registration_error),
+ getResources().getString(
+ R.string.error_for_all_unknown_registration_failures),
+ getResources().getString(R.string.button_ok),
+ isRegisteredFailedOKBtnClickListerner
+ );
+ alertDialog.show();
+ }
+
+ } else {
+ Log.e(TAG, "The responseStatus is null in onReceiveAPIResult()");
+ ServerUtils.clearAppData(context);
+
+ alertDialog = CommonDialogUtils
+ .getAlertDialogWithOneButtonAndTitle(
+ context,
+ getResources().getString(
+ R.string.title_head_registration_error),
+ getResources().getString(
+ R.string.error_for_all_unknown_registration_failures),
+ getResources().getString(R.string.button_ok),
+ null
+ );
+ alertDialog.show();
+ }
+
+ }
+
+ }
+
+ private void loadAuthenticationErrorActivity() {
+ Intent intent = new Intent(AlreadyRegisteredActivity.this,
+ AuthenticationErrorActivity.class);
+ intent.putExtra(
+ getResources().getString(
+ R.string.intent_extra_regid), regId
+ );
+ intent.putExtra(
+ getResources().getString(
+ R.string.intent_extra_from_activity),
+ AlreadyRegisteredActivity.class.getSimpleName()
+ );
+ startActivity(intent);
+ }
+
+ private void stopProgressDialog() {
+ if (progressDialog != null && progressDialog.isShowing()) {
+ progressDialog.dismiss();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/AuthenticationActivity.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/AuthenticationActivity.java
new file mode 100644
index 0000000000..817d5bd7fa
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/AuthenticationActivity.java
@@ -0,0 +1,697 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent;
+
+import java.util.Map;
+
+import org.wso2.cdm.agent.R;
+import org.wso2.cdm.agent.api.PhoneState;
+import org.wso2.cdm.agent.proxy.APIAccessCallBack;
+import org.wso2.cdm.agent.proxy.APIResultCallBack;
+import org.wso2.cdm.agent.proxy.IdentityProxy;
+import org.wso2.cdm.agent.utils.CommonDialogUtils;
+import org.wso2.cdm.agent.utils.CommonUtilities;
+import org.wso2.cdm.agent.utils.Constant;
+import org.wso2.cdm.agent.utils.Preference;
+import org.wso2.cdm.agent.utils.ServerUtils;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.webkit.WebView;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.RadioButton;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.SherlockActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+
+/**
+ * Activity that captures username, password and device ownership details.
+ */
+public class AuthenticationActivity extends SherlockActivity implements APIAccessCallBack,
+ APIResultCallBack {
+
+ private String TAG = AuthenticationActivity.class.getSimpleName();
+
+ Button btnRegister;
+ EditText etUsername;
+ EditText etDomain;
+ EditText etPassword;
+ RadioButton radioBYOD, radioCOPE;
+ String deviceType;
+ Context context;
+ String senderId;
+ String usernameForRegister;
+ String usernameVal;
+ String passwordVal;
+ String domain;
+ ProgressDialog progressDialog;
+ AlertDialog.Builder alertDialog;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_authentication);
+ getSupportActionBar().setDisplayShowCustomEnabled(true);
+ getSupportActionBar().setCustomView(R.layout.custom_sherlock_bar);
+ getSupportActionBar().setTitle(R.string.empty_app_title);
+ View homeIcon =
+ (View) findViewById(
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB
+ ? android.R.id.home
+ : R.id.abs__home
+ ).getParent();
+ homeIcon.setVisibility(View.GONE);
+
+ context = AuthenticationActivity.this;
+ deviceType = getResources().getString(R.string.device_enroll_type_byod);
+ etDomain = (EditText) findViewById(R.id.etDomain);
+ etUsername = (EditText) findViewById(R.id.etUsername);
+ etPassword = (EditText) findViewById(R.id.etPassword);
+ radioBYOD = (RadioButton) findViewById(R.id.radioBYOD);
+ radioCOPE = (RadioButton) findViewById(R.id.radioCOPE);
+ etDomain.setFocusable(true);
+ etDomain.requestFocus();
+ btnRegister = (Button) findViewById(R.id.btnRegister);
+ btnRegister.setEnabled(false);
+ btnRegister.setOnClickListener(onClickAuthenticate);
+ // change button color background till user enters a valid input
+ btnRegister.setBackground(getResources().getDrawable(R.drawable.btn_grey));
+ btnRegister.setTextColor(getResources().getColor(R.color.black));
+
+ etUsername.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ enableSubmitIfReady();
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ enableSubmitIfReady();
+ }
+ });
+
+ etPassword.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ enableSubmitIfReady();
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ enableSubmitIfReady();
+ }
+ });
+
+ }
+
+ OnClickListener onClickAuthenticate = new OnClickListener() {
+
+ @Override
+ public void onClick(View view) {
+ if (etUsername.getText() != null &&
+ !etUsername.getText().toString().trim().equals("") &&
+ etPassword.getText() != null &&
+ !etPassword.getText().toString().trim().equals("")) {
+
+ passwordVal = etPassword.getText().toString().trim();
+ usernameVal = etUsername.getText().toString().trim();
+ if (etDomain.getText() != null &&
+ !etDomain.getText().toString().trim().equals("")) {
+ usernameVal += "@" + etDomain.getText().toString().trim();
+ }
+
+ if (radioBYOD.isChecked()) {
+ deviceType = getResources().getString(R.string.device_enroll_type_byod);
+ } else {
+ deviceType = getResources().getString(R.string.device_enroll_type_cope);
+ }
+ StringBuilder messageBuilder = new StringBuilder();
+ messageBuilder.append(getResources().getString(R.string.dialog_init_middle));
+ messageBuilder.append(" ");
+ messageBuilder.append(deviceType);
+ messageBuilder.append(" ");
+ messageBuilder.append(getResources().getString(R.string.dialog_init_end));
+ alertDialog =
+ CommonDialogUtils.getAlertDialogWithTwoButtonAndTitle(context,
+ getResources()
+ .getString(
+ R.string.dialog_init_device_type),
+ messageBuilder
+ .toString(),
+ getResources()
+ .getString(
+ R.string.yes),
+ getResources()
+ .getString(
+ R.string.no),
+ dialogClickListener,
+ dialogClickListener
+ );
+ alertDialog.show();
+ } else {
+ if (etUsername.getText() != null &&
+ !etUsername.getText().toString().trim().equals("")) {
+ Toast.makeText(context,
+ getResources().getString(R.string.toast_error_password),
+ Toast.LENGTH_LONG).show();
+ } else {
+ Toast.makeText(context,
+ getResources().getString(R.string.toast_error_username),
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ }
+ };
+
+ DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ startAuthentication();
+ dialog.dismiss();
+ break;
+
+ case DialogInterface.BUTTON_NEGATIVE:
+ dialog.dismiss();
+ break;
+ }
+ }
+ };
+
+ /**
+ * Start authentication process.
+ */
+ public void startAuthentication() {
+ Preference
+ .put(context, getResources().getString(R.string.shared_pref_reg_type), deviceType);
+ // Check network connection availability before calling the API.
+ if (PhoneState.isNetworkAvailable(context)) {
+ initializeIDPLib(getResources().getString(R.string.client_id),
+ getResources().getString(R.string.client_secreat));
+ } else {
+ CommonDialogUtils.stopProgressDialog(progressDialog);
+ CommonDialogUtils.showNetworkUnavailableMessage(context);
+ }
+
+ }
+
+ /**
+ * Initialize the Android IDP SDK by passing credentials,client ID and
+ * client secret.
+ *
+ * @param clientKey client id value to access APIs..
+ * @param clientSecret client secret value to access APIs.
+ */
+ private void initializeIDPLib(String clientKey, String clientSecret) {
+ String serverIP =
+ CommonUtilities.getPref(AuthenticationActivity.this,
+ context.getResources()
+ .getString(R.string.shared_pref_ip)
+ );
+ String serverURL =
+ CommonUtilities.SERVER_PROTOCOL + serverIP + ":" +
+ CommonUtilities.SERVER_PORT + CommonUtilities.OAUTH_ENDPOINT;
+ if (etDomain.getText() != null && !etDomain.getText().toString().trim().equals("")) {
+ usernameForRegister =
+ etUsername.getText().toString().trim() + "@" +
+ etDomain.getText().toString().trim();
+
+ } else {
+ usernameForRegister = etUsername.getText().toString().trim();
+ }
+ IdentityProxy.getInstance().init(clientKey, clientSecret, usernameForRegister, passwordVal,
+ serverURL, AuthenticationActivity.this,
+ this.getApplicationContext());
+ }
+
+ @Override
+ public void onAPIAccessRecive(String status) {
+ if (status != null) {
+ if (status.trim().equals(CommonUtilities.REQUEST_SUCCESSFUL)) {
+
+ SharedPreferences mainPref =
+ this.getSharedPreferences(
+ getResources().getString(R.string.shared_pref_package),
+ Context.MODE_PRIVATE);
+ Editor editor = mainPref.edit();
+ editor.putString(getResources().getString(R.string.shared_pref_username),
+ usernameForRegister);
+ editor.commit();
+
+ // Check network connection availability before calling the API.
+ if (PhoneState.isNetworkAvailable(context)) {
+ getLicense();
+ } else {
+ CommonDialogUtils.stopProgressDialog(progressDialog);
+ CommonDialogUtils.showNetworkUnavailableMessage(AuthenticationActivity.this);
+ }
+
+ } else if (status.trim().equals(CommonUtilities.AUTHENTICATION_FAILED)) {
+ CommonDialogUtils.stopProgressDialog(progressDialog);
+ alertDialog =
+ CommonDialogUtils.getAlertDialogWithOneButtonAndTitle(context,
+ getResources()
+ .getString(
+ R.string.title_head_authentication_error),
+ getResources()
+ .getString(
+ R.string.error_authentication_failed),
+ getResources()
+ .getString(
+ R.string.button_ok),
+ dialogClickListener
+ );
+ } else if (status.trim().equals(CommonUtilities.INTERNAL_SERVER_ERROR)) {
+ showInternalServerErrorMessage();
+
+ } else {
+ Log.e(TAG, "Status: " + status);
+ showAuthCommonErrorMessage();
+ }
+
+ } else {
+ Log.e(TAG, "The value of status is null in onAPIAccessRecive()");
+ showAuthCommonErrorMessage();
+ }
+
+ }
+
+ /**
+ * Initialize get device license agreement. Check if the user has already
+ * agreed to license agreement
+ */
+ private void getLicense() {
+ String licenseAgreedResponse =
+ Preference.get(context,
+ getResources().getString(R.string.shared_pref_isagreed));
+ String type =
+ Preference.get(context,
+ getResources().getString(R.string.shared_pref_reg_type));
+
+ // No need to display license for COPE devices
+ if (type.trim().equals(getResources().getString(R.string.device_enroll_type_byod))) {
+ if (licenseAgreedResponse == null) {
+
+ // Get License
+ OnCancelListener cancelListener = new OnCancelListener() {
+
+ @Override
+ public void onCancel(DialogInterface arg0) {
+ CommonDialogUtils.getAlertDialogWithOneButtonAndTitle(context,
+ getResources()
+ .getString(
+ R.string.error_enrollment_failed_detail),
+ getResources()
+ .getString(
+ R.string.error_enrollment_failed),
+ getResources()
+ .getString(
+ R.string.button_ok),
+ null
+ );
+ }
+ };
+
+ progressDialog =
+ CommonDialogUtils.showPrgressDialog(context,
+ getResources().getString(
+ R.string.dialog_license_agreement),
+ getResources().getString(
+ R.string.dialog_please_wait),
+ cancelListener
+ );
+
+ // Check network connection availability before calling the API.
+ if (PhoneState.isNetworkAvailable(context)) {
+ getLicenseFromServer();
+ } else {
+ CommonDialogUtils.stopProgressDialog(progressDialog);
+ CommonDialogUtils.showNetworkUnavailableMessage(context);
+ }
+
+ } else {
+ loadPincodeAcitvity();
+ }
+ } else {
+ loadPincodeAcitvity();
+ }
+
+ }
+
+ /**
+ * Retriever license agreement details from the server.
+ */
+ private void getLicenseFromServer() {
+ ServerUtils.callSecuredAPI(AuthenticationActivity.this,
+ CommonUtilities.API_SERVER_URL +
+ CommonUtilities.LICENSE_ENDPOINT,
+ CommonUtilities.GET_METHOD, null,
+ AuthenticationActivity.this,
+ CommonUtilities.LICENSE_REQUEST_CODE
+ );
+ }
+
+ @Override
+ public void onReceiveAPIResult(Map result, int requestCode) {
+ if (requestCode == CommonUtilities.SENDER_ID_REQUEST_CODE) {
+ //manipulateSenderIdResponse(result);
+ } else if (requestCode == CommonUtilities.LICENSE_REQUEST_CODE) {
+ manipulateLicenseResponse(result);
+ }
+ }
+
+ /**
+ * Manipulates the License agreement response received from server.
+ *
+ * @param result the result of the license agreement request
+ */
+ private void manipulateLicenseResponse(Map result) {
+ String responseStatus;
+ CommonDialogUtils.stopProgressDialog(progressDialog);
+
+ if (result != null) {
+ responseStatus = result.get(CommonUtilities.STATUS_KEY);
+ if (responseStatus.equals(CommonUtilities.REQUEST_SUCCESSFUL)) {
+ String licenseAgreement = result.get(Constant.RESPONSE);
+
+ if (licenseAgreement != null) {
+ Preference.put(context, getResources().getString(R.string.shared_pref_eula),
+ licenseAgreement);
+ showAgreement(licenseAgreement, CommonUtilities.EULA_TITLE);
+ } else {
+ showErrorMessage(
+ getResources().getString(R.string.error_enrollment_failed_detail),
+ getResources().getString(R.string.error_enrollment_failed));
+ }
+
+ } else if (responseStatus.equals(CommonUtilities.INTERNAL_SERVER_ERROR)) {
+ Log.e(TAG, "The result is : " + result);
+ showInternalServerErrorMessage();
+ } else {
+ showEnrollementFailedErrorMessage();
+ }
+
+ } else {
+ Log.e(TAG, "The result is null in manipulateLicenseResponse()");
+ showEnrollementFailedErrorMessage();
+ }
+ }
+
+ public void showErrorMessage(String message, String title) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setMessage(message);
+ builder.setTitle(title);
+ builder.setCancelable(true);
+ builder.setPositiveButton(getResources().getString(R.string.button_ok),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ cancelEntry();
+ dialog.dismiss();
+ }
+ }
+ );
+ AlertDialog alert = builder.create();
+ alert.show();
+ }
+
+ /**
+ * Show the license text retrieved from the server.
+ *
+ * @param message Message text to be shown as the license.
+ * @param title Title of the license.
+ */
+ public void showAgreement(String message, String title) {
+ final Dialog dialog = new Dialog(context);
+ dialog.setContentView(R.layout.custom_terms_popup);
+ dialog.setTitle(CommonUtilities.EULA_TITLE);
+ dialog.setCancelable(false);
+
+ WebView web = (WebView) dialog.findViewById(R.id.webview);
+ String html = "" + message + "";
+ String mime = "text/html";
+ String encoding = "utf-8";
+ web.loadDataWithBaseURL(null, html, mime, encoding, null);
+
+ Button dialogButton = (Button) dialog.findViewById(R.id.dialogButtonOK);
+ Button cancelButton = (Button) dialog.findViewById(R.id.dialogButtonCancel);
+
+ dialogButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Preference.put(context, getResources().getString(R.string.shared_pref_isagreed),
+ "1");
+ dialog.dismiss();
+ loadPincodeAcitvity();
+ }
+ });
+
+ cancelButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dialog.dismiss();
+ cancelEntry();
+ }
+ });
+
+ dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
+
+ @Override
+ public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_SEARCH && event.getRepeatCount() == 0) {
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
+ return true;
+ }
+ return false;
+ }
+ });
+
+ dialog.show();
+ }
+
+ private void loadPincodeAcitvity() {
+ Intent intent = new Intent(AuthenticationActivity.this, PinCodeActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.putExtra(getResources().getString(R.string.intent_extra_username), usernameVal);
+ startActivity(intent);
+ }
+
+ public void cancelEntry() {
+ SharedPreferences mainPref =
+ context.getSharedPreferences(getResources().getString(R.string.shared_pref_package),
+ Context.MODE_PRIVATE);
+ Editor editor = mainPref.edit();
+ editor.putString(getResources().getString(R.string.shared_pref_policy), "");
+ editor.putString(getResources().getString(R.string.shared_pref_isagreed), "0");
+ editor.putString(getResources().getString(R.string.shared_pref_registered), "0");
+ editor.putString(getResources().getString(R.string.shared_pref_ip), "");
+ editor.commit();
+
+ Intent intentIP = new Intent(AuthenticationActivity.this, ServerDetails.class);
+ intentIP.putExtra(getResources().getString(R.string.intent_extra_from_activity),
+ AuthenticationActivity.class.getSimpleName());
+ intentIP.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intentIP);
+
+ }
+
+ public void showAlertSingle(String message, String title) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setMessage(message);
+ builder.setTitle(title);
+ builder.setCancelable(true);
+ builder.setPositiveButton(getResources().getString(R.string.button_ok),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ // cancelEntry();
+ dialog.cancel();
+ }
+ }
+ );
+ AlertDialog alert = builder.create();
+ alert.show();
+ }
+
+ public void showAuthErrorMessage(String message, String title) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setMessage(message);
+ builder.setTitle(title);
+ builder.setCancelable(true);
+ builder.setPositiveButton(getResources().getString(R.string.button_ok),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.dismiss();
+ }
+ }
+ );
+ AlertDialog alert = builder.create();
+ alert.show();
+ }
+
+ /**
+ * Validation done to see if the username and password fields are properly entered.
+ */
+ public void enableSubmitIfReady() {
+
+ boolean isReady = false;
+
+ if (etUsername.getText().toString().length() >= 1 &&
+ etPassword.getText().toString().length() >= 1) {
+ isReady = true;
+ }
+
+ if (isReady) {
+ btnRegister.setBackground(getResources().getDrawable(R.drawable.btn_orange));
+ btnRegister.setTextColor(getResources().getColor(R.color.white));
+ btnRegister.setEnabled(true);
+ } else {
+ btnRegister.setBackground(getResources().getDrawable(R.drawable.btn_grey));
+ btnRegister.setTextColor(getResources().getColor(R.color.black));
+ btnRegister.setEnabled(false);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getSupportMenuInflater().inflate(R.menu.auth_sherlock_menu, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.ip_setting:
+ SharedPreferences mainPref =
+ AuthenticationActivity.this.getSharedPreferences(
+ getResources().getString(R.string.shared_pref_package),
+ Context.MODE_PRIVATE);
+ Editor editor = mainPref.edit();
+ editor.putString(getResources().getString(R.string.shared_pref_ip), "");
+ editor.commit();
+
+ Intent intentIP = new Intent(AuthenticationActivity.this, ServerDetails.class);
+ intentIP.putExtra(getResources().getString(R.string.intent_extra_from_activity),
+ AuthenticationActivity.class.getSimpleName());
+ startActivity(intentIP);
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ Intent i = new Intent();
+ i.setAction(Intent.ACTION_MAIN);
+ i.addCategory(Intent.CATEGORY_HOME);
+ this.startActivity(i);
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_HOME) {
+ this.finish();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ DialogInterface.OnClickListener senderIdFailedClickListener =
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+ etUsername.setText(CommonUtilities.EMPTY_STRING);
+ etPassword.setText(CommonUtilities.EMPTY_STRING);
+ etDomain.setText(CommonUtilities.EMPTY_STRING);
+ btnRegister.setEnabled(false);
+ btnRegister.setBackground(getResources().getDrawable(R.drawable.btn_grey));
+ btnRegister.setTextColor(getResources().getColor(R.color.black));
+ }
+ };
+
+ private void showEnrollementFailedErrorMessage() {
+ CommonDialogUtils.stopProgressDialog(progressDialog);
+ alertDialog =
+ CommonDialogUtils.getAlertDialogWithOneButtonAndTitle(context,
+ getResources().getString(
+ R.string.error_enrollment_failed),
+ getResources().getString(
+ R.string.error_enrollment_failed_detail),
+ getResources().getString(
+ R.string.button_ok),
+ senderIdFailedClickListener
+ );
+ }
+
+ private void showInternalServerErrorMessage() {
+ CommonDialogUtils.stopProgressDialog(progressDialog);
+ alertDialog =
+ CommonDialogUtils.getAlertDialogWithOneButtonAndTitle(context,
+ getResources().getString(
+ R.string.title_head_connection_error),
+ getResources().getString(
+ R.string.error_internal_server),
+ getResources().getString(
+ R.string.button_ok),
+ null
+ );
+ }
+
+ /**
+ * Shows common error message for authentication.
+ */
+ private void showAuthCommonErrorMessage() {
+ CommonDialogUtils.stopProgressDialog(progressDialog);
+ alertDialog =
+ CommonDialogUtils.getAlertDialogWithOneButtonAndTitle(context,
+ getResources().getString(
+ R.string.title_head_authentication_error),
+ getResources().getString(
+ R.string.error_for_all_unknown_authentication_failures),
+ getResources().getString(
+ R.string.button_ok),
+ null
+ );
+
+ }
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/AuthenticationErrorActivity.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/AuthenticationErrorActivity.java
new file mode 100644
index 0000000000..f5df4fc266
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/AuthenticationErrorActivity.java
@@ -0,0 +1,130 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+import com.google.android.gcm.GCMRegistrar;
+import org.wso2.cdm.agent.R;
+
+public class AuthenticationErrorActivity extends Activity {
+ String regId = "";
+ private Button btnTryAgain;
+ private final int TAG_BTN_TRY_AGAIN = 0;
+ private final int TAG_BTN_UNREGISTER = 1;
+ private String FROM_ACTIVITY=null;
+ private TextView txtMsg;
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_authentication_error);
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ if(extras.containsKey(getResources().getString(R.string.intent_extra_regid))){
+ regId = extras.getString(getResources().getString(R.string.intent_extra_regid));
+ }
+
+ if(extras.containsKey(getResources().getString(R.string.intent_extra_from_activity))){
+ FROM_ACTIVITY = extras.getString(getResources().getString(R.string.intent_extra_from_activity));
+ }
+ }
+ if(regId == null || regId.equals("")){
+ regId = GCMRegistrar.getRegistrationId(this);
+ }
+ txtMsg = (TextView)findViewById(R.id.error);
+
+ btnTryAgain = (Button)findViewById(R.id.btnTryAgain);
+ btnTryAgain.setTag(TAG_BTN_TRY_AGAIN);
+ btnTryAgain.setOnClickListener(onClickListener_BUTTON_CLICKED);
+
+ if(FROM_ACTIVITY.equals(RegistrationActivity.class.getSimpleName())){
+ txtMsg.setText(getResources().getString(R.string.error_registration_failed));
+ }else if(FROM_ACTIVITY.equals(AlreadyRegisteredActivity.class.getSimpleName())){
+ txtMsg.setText(getResources().getString(R.string.error_unregistration_failed));
+ btnTryAgain.setTag(TAG_BTN_UNREGISTER);
+ }
+
+ }
+
+ OnClickListener onClickListener_BUTTON_CLICKED = new OnClickListener() {
+
+ @Override
+ public void onClick(View view) {
+ // TODO Auto-generated method stub
+
+ int iTag = (Integer) view.getTag();
+
+ switch (iTag) {
+
+ case TAG_BTN_TRY_AGAIN:
+ tryAgain();
+ break;
+ case TAG_BTN_UNREGISTER:
+ finish();
+ break;
+
+ default:
+ break;
+ }
+
+ }
+ };
+
+ public void tryAgain(){
+ Intent intent = new Intent(AuthenticationErrorActivity.this,AuthenticationActivity.class);
+ intent.putExtra(getResources().getString(R.string.intent_extra_from_activity), AuthenticationActivity.class.getSimpleName());
+ intent.putExtra(getResources().getString(R.string.intent_extra_regid), regId);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.authentication_error, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ Intent intent2 = new Intent(AuthenticationErrorActivity.this,AuthenticationActivity.class);
+ intent2.putExtra(getResources().getString(R.string.intent_extra_from_activity), AuthenticationActivity.class.getSimpleName());
+ intent2.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent2.putExtra(getResources().getString(R.string.intent_extra_regid), regId);
+ startActivity(intent2);
+ finish();
+ return true;
+ }
+ else if (keyCode == KeyEvent.KEYCODE_HOME) {
+ /*Intent i = new Intent();
+ i.setAction(Intent.ACTION_MAIN);
+ i.addCategory(Intent.CATEGORY_HOME);
+ this.startActivity(i);*/
+ finish();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/DisplayDeviceInfoActivity.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/DisplayDeviceInfoActivity.java
new file mode 100644
index 0000000000..edc7a39ce8
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/DisplayDeviceInfoActivity.java
@@ -0,0 +1,138 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.wso2.cdm.agent.R;
+import org.wso2.cdm.agent.api.DeviceInfo;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.TextView;
+
+public class DisplayDeviceInfoActivity extends Activity {
+ private String FROM_ACTIVITY = null;
+ private String REG_ID = "";
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_display_device_info);
+ DeviceInfo deviceInfo = new DeviceInfo(DisplayDeviceInfoActivity.this);
+ TextView device_id = (TextView)findViewById(R.id.txtId);
+ TextView device = (TextView)findViewById(R.id.txtDevice);
+ TextView model = (TextView)findViewById(R.id.txtModel);
+ TextView operator = (TextView)findViewById(R.id.txtOperator);
+ TextView sdk = (TextView)findViewById(R.id.txtSDK);
+ TextView os = (TextView)findViewById(R.id.txtOS);
+ TextView root = (TextView)findViewById(R.id.txtRoot);
+
+ device_id.setText(getResources().getString(R.string.info_label_imei)+" "+deviceInfo.getDeviceId());
+ device.setText(getResources().getString(R.string.info_label_device)+" "+deviceInfo.getDevice());
+ model.setText(getResources().getString(R.string.info_label_model)+" "+deviceInfo.getDeviceModel());
+ JSONArray jsonArray = null;
+ String operators = "";
+ if(deviceInfo.getNetworkOperatorName()!= null){
+ jsonArray = deviceInfo.getNetworkOperatorName();
+ }
+
+ for (int i = 0; i < jsonArray.length(); i++) {
+ try {
+ if(jsonArray.getString(i) != null){
+ if(i==(jsonArray.length()-1)){
+ operators += jsonArray.getString(i);
+ }else{
+ operators += jsonArray.getString(i)+", ";
+ }
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ if(operators.equals(null)){
+ operators = getResources().getString(R.string.info_label_no_sim);
+ }
+ operator.setText(getResources().getString(R.string.info_label_operator)+" "+operators);
+ if(deviceInfo.getIMSINumber() != null){
+ sdk.setText(getResources().getString(R.string.info_label_imsi)+" "+deviceInfo.getIMSINumber());
+ }else{
+ sdk.setText(getResources().getString(R.string.info_label_imsi)+" "+operators);
+ }
+ os.setText(getResources().getString(R.string.info_label_os)+" "+deviceInfo.getOsVersion());
+ root.setText(getResources().getString(R.string.info_label_rooted)+" "+(deviceInfo.isRooted()?getResources().getString(R.string.yes):getResources().getString(R.string.no)));
+
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ if(extras.containsKey(getResources().getString(R.string.intent_extra_from_activity))){
+ FROM_ACTIVITY = extras.getString(getResources().getString(R.string.intent_extra_from_activity));
+ }
+
+ if(extras.containsKey(getResources().getString(R.string.intent_extra_regid))){
+ REG_ID = extras.getString(getResources().getString(R.string.intent_extra_regid));
+ }
+ }
+
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ //MenuItem menu1 = menu.add(0,1,1,"Settings");
+ //getMenuInflater().inflate(R.menu.display_device_info, menu);
+ return true;
+ }
+
+
+ public boolean onOptionsItemSelected(MenuItem menu){
+ /*switch(menu.getItemId()){
+ case 1:
+ Intent intent = new Intent(DisplayDeviceInfo.this,SettingsActivity.class);
+ startActivity(intent);
+ return true;
+ default:
+ return this.onOptionsItemSelected(menu);
+ }*/
+ return true;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK && FROM_ACTIVITY != null && FROM_ACTIVITY.equals(AlreadyRegisteredActivity.class.getSimpleName())) {
+ Intent intent = new Intent(DisplayDeviceInfoActivity.this,AlreadyRegisteredActivity.class);
+ intent.putExtra(getResources().getString(R.string.intent_extra_from_activity), DisplayDeviceInfoActivity.class.getSimpleName());
+ intent.putExtra(getResources().getString(R.string.intent_extra_regid), REG_ID);
+ startActivity(intent);
+ return true;
+ }else if (keyCode == KeyEvent.KEYCODE_BACK) {
+ Intent i = new Intent();
+ i.setAction(Intent.ACTION_MAIN);
+ i.addCategory(Intent.CATEGORY_HOME);
+ this.startActivity(i);
+ this.finish();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ /**/
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/GCMIntentService.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/GCMIntentService.java
new file mode 100644
index 0000000000..c4a20257d3
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/GCMIntentService.java
@@ -0,0 +1,146 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent;
+
+
+import org.wso2.cdm.agent.R;
+import org.wso2.cdm.agent.api.ApplicationManager;
+import org.wso2.cdm.agent.services.Config;
+import org.wso2.cdm.agent.services.MessageProcessor;
+import org.wso2.cdm.agent.utils.CommonUtilities;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.util.Log;
+
+import com.google.android.gcm.GCMBaseIntentService;
+import com.google.android.gcm.GCMRegistrar;
+
+/**
+ * IntentService responsible for handling GCM messages.
+ */
+public class GCMIntentService extends GCMBaseIntentService {
+ DevicePolicyManager devicePolicyManager;
+ ApplicationManager appList;
+ static final int ACTIVATION_REQUEST = 47;
+ MessageProcessor processMsg = null;
+
+ @SuppressWarnings("hiding")
+ private static final String TAG = "GCMIntentService";
+
+ public GCMIntentService() {
+ super(CommonUtilities.SENDER_ID);
+ }
+
+
+ @Override
+ protected void onRegistered(Context context, String registrationId) {
+ if (CommonUtilities.DEBUG_MODE_ENABLED) {
+ Log.i(TAG, "Device registered: regId = " + registrationId);
+ }
+ SharedPreferences preferences = context.getSharedPreferences(
+ getResources().getString(R.string.shared_pref_package),
+ Context.MODE_PRIVATE);
+ //if (preferences.getString(getResources().getString(R.string.shared_pref_message_mode), "").trim().toUpperCase().contains("GCM")) {
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.putString(getResources().getString(R.string.shared_pref_regId),
+ registrationId);
+ editor.commit();
+ //}
+ }
+
+ @Override
+ protected void onUnregistered(Context context, String registrationId) {
+ if(CommonUtilities.DEBUG_MODE_ENABLED){Log.i(TAG, "Device unregistered");}
+
+ if (GCMRegistrar.isRegisteredOnServer(context)) {
+
+
+ } else {
+ // This callback results from the call to unregister made on
+ // ServerUtilities when the registration to the server failed.
+ if(CommonUtilities.DEBUG_MODE_ENABLED){Log.i(TAG, "Ignoring unregister callback");}
+ }
+ }
+
+ @Override
+ protected void onMessage(Context context, Intent intent) {
+ Log.e("onmsg","onmsg");
+
+ Config.context = this;
+ String mode=CommonUtilities.getPref(context, context.getResources().getString(R.string.shared_pref_message_mode));
+ if(mode.trim().toUpperCase().equals("GCM")){
+ Log.e("onmsg","GCM");
+ MessageProcessor msg=new MessageProcessor(context);
+ msg.getMessages();
+ }
+ else{
+ Log.e("onmsg","mode");
+ }
+ //processMsg = new ProcessMessage(Config.context, CommonUtilities.MESSAGE_MODE_GCM, intent);
+ }
+
+
+ @Override
+ protected void onDeletedMessages(Context context, int total) {
+ if(CommonUtilities.DEBUG_MODE_ENABLED){Log.i(TAG, "Received deleted messages notification");}
+ String message = getString(R.string.gcm_deleted, total);
+ // displayMessage(context, message);
+ // notifies user
+ generateNotification(context, message);
+ }
+
+ @Override
+ public void onError(Context context, String errorId) {
+ if(CommonUtilities.DEBUG_MODE_ENABLED){Log.i(TAG, "Received error: " + errorId);}
+
+ }
+
+ @Override
+ protected boolean onRecoverableError(Context context, String errorId) {
+ // log message
+ if(CommonUtilities.DEBUG_MODE_ENABLED){Log.i(TAG, "Received recoverable error: " + errorId);}
+
+ return super.onRecoverableError(context, errorId);
+ }
+
+ /**
+ * Issues a notification to inform the user that server has sent a message.
+ */
+ private static void generateNotification(Context context, String message) {
+ int icon = R.drawable.ic_stat_gcm;
+ long when = System.currentTimeMillis();
+ NotificationManager notificationManager = (NotificationManager)
+ context.getSystemService(Context.NOTIFICATION_SERVICE);
+ Notification notification = new Notification(icon, message, when);
+ String title = context.getString(R.string.app_name);
+ Intent notificationIntent = new Intent(context, NotifyActivity.class);
+ notificationIntent.putExtra(context.getResources().getString(R.string.intent_extra_notification), message);
+ // set intent so it does not start a new activity
+ notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
+ Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ PendingIntent intent =
+ PendingIntent.getActivity(context, 0, notificationIntent, 0);
+ notification.setLatestEventInfo(context, title, message, intent);
+ notification.flags |= Notification.FLAG_AUTO_CANCEL;
+ notificationManager.notify(0, notification);
+ }
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/LogActivity.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/LogActivity.java
new file mode 100644
index 0000000000..9440fd4ca1
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/LogActivity.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent;
+
+import org.wso2.cdm.agent.R;
+import org.wso2.cdm.agent.utils.LoggerCustom;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.text.Html;
+import android.view.Menu;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+
+public class LogActivity extends Activity {
+ TextView txtLog;
+ Button btnRefresh, btnReset;
+ LoggerCustom logger = null;
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_log);
+ txtLog = (TextView)findViewById(R.id.txtLog);
+ btnRefresh = (Button)findViewById(R.id.btnRefresh);
+ btnReset = (Button)findViewById(R.id.btnReset);
+ logger = new LoggerCustom(this);
+ String log_in = logger.readFileAsString("wso2log.txt");
+
+ txtLog.setText(Html.fromHtml(log_in));
+
+ btnRefresh.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ // TODO Auto-generated method stub
+ String log_in = logger.readFileAsString("wso2log.txt");
+
+ txtLog.setText(Html.fromHtml(log_in));
+ }
+ });
+
+ btnReset.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ // TODO Auto-generated method stub
+ logger.writeStringAsFile("", "wso2log.txt");
+
+ txtLog.setText("");
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ //getMenuInflater().inflate(R.menu.log, menu);
+ return true;
+ }
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/NotifyActivity.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/NotifyActivity.java
new file mode 100644
index 0000000000..a723e836d8
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/NotifyActivity.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent;
+
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.widget.TextView;
+import org.wso2.cdm.agent.R;
+
+public class NotifyActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.notify);
+ TextView notifyTV = (TextView) findViewById(R.id.notify);
+ Intent intent = getIntent();
+ String notification = intent.getStringExtra(getResources().getString(R.string.intent_extra_notification));
+ notifyTV.setText(notification);
+
+
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.notify, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ Intent i = new Intent();
+ i.setAction(Intent.ACTION_MAIN);
+ i.addCategory(Intent.CATEGORY_HOME);
+ this.startActivity(i);
+ this.finish();
+ return true;
+ }
+ else if (keyCode == KeyEvent.KEYCODE_HOME) {
+ /*Intent i = new Intent();
+ i.setAction(Intent.ACTION_MAIN);
+ i.addCategory(Intent.CATEGORY_HOME);
+ this.startActivity(i);*/
+ this.finish();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/PinCodeActivity.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/PinCodeActivity.java
new file mode 100644
index 0000000000..6ea13b09fd
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/PinCodeActivity.java
@@ -0,0 +1,323 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.text.InputType;
+import android.text.method.PasswordTransformationMethod;
+import android.util.Log;
+
+import org.wso2.cdm.agent.R;
+import org.wso2.cdm.agent.utils.CommonDialogUtils;
+import org.wso2.cdm.agent.utils.Preference;
+
+public class PinCodeActivity extends Activity {
+ private TextView lblPin;
+ private EditText txtPin;
+ private EditText txtOldPin;
+ private Button btnPin;
+ private String username = null;
+ private String regId = "";
+ private final int TAG_BTN_SET_PIN = 0;
+ private String fromActivity = null;
+ Context context;
+ AlertDialog.Builder alertDialog;
+ EditText input;
+
+ @SuppressLint("NewApi")
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_pin_code);
+ context = PinCodeActivity.this;
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ if (extras.containsKey(getResources().getString(R.string.intent_extra_username))) {
+ username =
+ extras.getString(getResources().getString(R.string.intent_extra_username));
+ }
+
+ if (extras.containsKey(getResources().getString(R.string.intent_extra_regid))) {
+ regId = extras.getString(getResources().getString(R.string.intent_extra_regid));
+ }
+
+ if (extras.containsKey(getResources().getString(R.string.intent_extra_from_activity))) {
+ fromActivity = extras.getString(
+ getResources().getString(R.string.intent_extra_from_activity));
+ }
+ }
+
+ lblPin = (TextView) findViewById(R.id.lblPin);
+ txtPin = (EditText) findViewById(R.id.txtPinCode);
+ txtOldPin = (EditText) findViewById(R.id.txtOldPinCode);
+ btnPin = (Button) findViewById(R.id.btnSetPin);
+ btnPin.setTag(TAG_BTN_SET_PIN);
+ btnPin.setOnClickListener(onClickListenerButtonClicked);
+ btnPin.setEnabled(false);
+ btnPin.setBackground(getResources().getDrawable(R.drawable.btn_grey));
+ btnPin.setTextColor(getResources().getColor(R.color.black));
+
+ if (fromActivity != null &&
+ fromActivity.equals(AlreadyRegisteredActivity.class.getSimpleName())) {
+ lblPin.setVisibility(View.GONE);
+ txtOldPin.setVisibility(View.VISIBLE);
+ txtPin.setHint(getResources().getString(R.string.hint_new_pin));
+ txtPin.setEnabled(true);
+
+ txtPin.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {
+ enableNewPINSubmitIfReady();
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ enableSubmitIfReady();
+ }
+ });
+
+ txtOldPin.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {
+ enableNewPINSubmitIfReady();
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ enableSubmitIfReady();
+ }
+ });
+ } else {
+ txtPin.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {
+ enableSubmitIfReady();
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ enableSubmitIfReady();
+ }
+ });
+ }
+ }
+
+ OnClickListener onClickListenerButtonClicked = new OnClickListener() {
+
+ @Override
+ public void onClick(View view) {
+ int viewTag = (Integer) view.getTag();
+
+ switch (viewTag) {
+
+ case TAG_BTN_SET_PIN:
+ input = new EditText(PinCodeActivity.this);
+ alertDialog = CommonDialogUtils
+ .getAlertDialogWithTwoButtonAndEditView(PinCodeActivity.this,
+ getResources().getString(
+ R.string.title_head_confirm_pin)
+ , getResources().getString(R.string.button_ok),
+ getResources().getString(
+ R.string.button_cancel),
+ dialogClickListener,
+ dialogClickListener, input);
+
+ final AlertDialog dialog = alertDialog.create();
+ dialog.show();
+ //Overriding default positive button behavior to keep the dialog open, if PINS don't match
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE)
+ .setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (txtPin.getText().toString()
+ .equals(input.getText().toString())) {
+ savePin();
+ dialog.dismiss();
+ } else {
+ input.setError(getResources().getString(
+ R.string.validation_pin_confirm));
+ }
+ }
+ });
+ input.setInputType(InputType.TYPE_CLASS_NUMBER);
+ input.setTransformationMethod(new PasswordTransformationMethod());
+ break;
+ default:
+ break;
+ }
+
+ }
+ };
+
+ DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ savePin();
+ break;
+
+ case DialogInterface.BUTTON_NEGATIVE:
+ dialog.dismiss();
+ break;
+ }
+ }
+ };
+
+ public void savePin() {
+ Preference.put(context, getResources().getString(R.string.shared_pref_pin),
+ txtPin.getText().toString().trim());
+
+ if (fromActivity != null &&
+ (fromActivity.equals(AlreadyRegisteredActivity.class.getSimpleName()))) {
+ Toast.makeText(getApplicationContext(),
+ getResources().getString(R.string.toast_message_pin_change_success),
+ Toast.LENGTH_SHORT).show();
+ Intent intent = new Intent(PinCodeActivity.this, AlreadyRegisteredActivity.class);
+ intent.putExtra(getResources().getString(R.string.intent_extra_from_activity),
+ PinCodeActivity.class.getSimpleName());
+ intent.putExtra(getResources().getString(R.string.intent_extra_regid), regId);
+ startActivity(intent);
+ } else {
+ Intent intent = new Intent(PinCodeActivity.this, RegistrationActivity.class);
+ intent.putExtra(getResources().getString(R.string.intent_extra_regid), regId);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.putExtra(getResources().getString(R.string.intent_extra_username), username);
+ startActivity(intent);
+ }
+ }
+
+ @SuppressLint("NewApi")
+ public void enableSubmitIfReady() {
+
+ boolean isReady = false;
+
+ if (txtPin.getText().toString().length() >= 4) {
+ isReady = true;
+ }
+
+ if (isReady) {
+ btnPin.setBackground(getResources().getDrawable(R.drawable.btn_orange));
+ btnPin.setTextColor(getResources().getColor(R.color.white));
+ btnPin.setEnabled(true);
+ } else {
+ btnPin.setBackground(getResources().getDrawable(R.drawable.btn_grey));
+ btnPin.setTextColor(getResources().getColor(R.color.black));
+ btnPin.setEnabled(false);
+ }
+ }
+
+ @SuppressLint("NewApi")
+ public void enableNewPINSubmitIfReady() {
+
+ boolean isReady = false;
+ SharedPreferences mainPref =
+ this.getSharedPreferences(getResources().getString(R.string.shared_pref_package),
+ Context.MODE_PRIVATE);
+ String pin = mainPref.getString(getResources().getString(R.string.shared_pref_pin), "");
+ if (txtOldPin.getText().toString().trim().length() >= 4 &&
+ txtOldPin.getText().toString().trim().equals(pin.trim())) {
+ txtPin.setEnabled(true);
+ } else {
+ txtPin.setEnabled(false);
+ }
+
+ if (txtPin.getText().toString().trim().length() >= 4 &&
+ txtOldPin.getText().toString().trim().length() >= 4) {
+ if (txtOldPin.getText().toString().trim().equals(pin.trim())) {
+ isReady = true;
+ } else {
+ isReady = false;
+ Toast.makeText(getApplicationContext(),
+ getResources().getString(R.string.toast_message_pin_change_failed),
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ if (isReady) {
+ btnPin.setBackground(getResources().getDrawable(R.drawable.btn_orange));
+ btnPin.setTextColor(getResources().getColor(R.color.white));
+ btnPin.setEnabled(true);
+ } else {
+ btnPin.setBackground(getResources().getDrawable(R.drawable.btn_grey));
+ btnPin.setTextColor(getResources().getColor(R.color.black));
+ btnPin.setEnabled(false);
+ }
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK && fromActivity != null &&
+ fromActivity.equals(AlreadyRegisteredActivity.class.getSimpleName())) {
+ Intent intent = new Intent(PinCodeActivity.this, AlreadyRegisteredActivity.class);
+ intent.putExtra(getResources().getString(R.string.intent_extra_from_activity),
+ PinCodeActivity.class.getSimpleName());
+ intent.putExtra(getResources().getString(R.string.intent_extra_regid), regId);
+ startActivity(intent);
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_BACK) {
+ Intent i = new Intent();
+ i.setAction(Intent.ACTION_MAIN);
+ i.addCategory(Intent.CATEGORY_HOME);
+ this.startActivity(i);
+ this.finish();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ return true;
+ }
+
+}
+
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/RegistrationActivity.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/RegistrationActivity.java
new file mode 100644
index 0000000000..07d92fae08
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/RegistrationActivity.java
@@ -0,0 +1,241 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent;
+
+import java.util.Map;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.wso2.cdm.agent.R;
+import org.wso2.cdm.agent.api.DeviceInfo;
+import org.wso2.cdm.agent.api.PhoneState;
+import org.wso2.cdm.agent.proxy.APIResultCallBack;
+import org.wso2.cdm.agent.utils.CommonDialogUtils;
+import org.wso2.cdm.agent.utils.CommonUtilities;
+import org.wso2.cdm.agent.utils.Preference;
+import org.wso2.cdm.agent.utils.ServerUtils;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+
+public class RegistrationActivity extends Activity implements APIResultCallBack {
+
+ private String TAG = RegistrationActivity.class.getSimpleName();
+
+ String regId = "";
+ String username = "";
+ Context context;
+ boolean regState = false;
+ ProgressDialog progressDialog;
+ AlertDialog.Builder alertDialog;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ context = this;
+ regId = Preference.get(context,getResources().getString(R.string.shared_pref_regId));
+ registerDevice();
+ }
+
+ private void registerDevice() {
+ progressDialog =
+ CommonDialogUtils.showPrgressDialog(RegistrationActivity.this,
+ getResources().getString(R.string.dialog_enrolling),
+ getResources().getString(R.string.dialog_please_wait),
+ null);
+ progressDialog.show();
+
+ DeviceInfo deviceInfo = new DeviceInfo(RegistrationActivity.this);
+ JSONObject jsObject = new JSONObject();
+ SharedPreferences mainPref =
+ RegistrationActivity.this.getSharedPreferences(RegistrationActivity.this.getResources()
+ .getString(R.string.shared_pref_package),
+ Context.MODE_PRIVATE);
+ String type =
+ mainPref.getString(RegistrationActivity.this.getResources()
+ .getString(R.string.shared_pref_reg_type),
+ "");
+ String username =
+ mainPref.getString(RegistrationActivity.this.getResources()
+ .getString(R.string.username),
+ "");
+
+ try {
+ jsObject.put("deviceIdentifier", deviceInfo.getMACAddress());
+ jsObject.put("description", deviceInfo.getDevice());
+ jsObject.put("ownership", type);
+ JSONArray propertiesArray=new JSONArray();
+ JSONObject property= new JSONObject();
+ property.put("name", "username");
+ property.put("value", username);
+ propertiesArray.put(property);
+ property= new JSONObject();
+ property.put("name", "device");
+ property.put("value", deviceInfo.getDevice());
+ propertiesArray.put(property);
+ property= new JSONObject();
+ property.put("name", "imei");
+ property.put("value", deviceInfo.getIMSINumber());
+ propertiesArray.put(property);
+ property= new JSONObject();
+ property.put("name", "imei");
+ property.put("value", deviceInfo.getIMSINumber());
+ propertiesArray.put(property);
+ property= new JSONObject();
+ property.put("name", "model");
+ property.put("value", deviceInfo.getDeviceModel());
+ propertiesArray.put(property);
+ property= new JSONObject();
+ property.put("name", "vendor");
+ property.put("value", deviceInfo.getOsVersion());
+ propertiesArray.put(property);
+ property= new JSONObject();
+ property.put("name", "vendor");
+ property.put("value", deviceInfo.getDeviceManufacturer());
+ propertiesArray.put(property);
+ property= new JSONObject();
+ property.put("name", "osVersion");
+ property.put("value", deviceInfo.getOsVersion());
+ propertiesArray.put(property);
+ jsObject.put("properties", propertiesArray);
+
+ // Check network connection availability before calling the API.
+ if (PhoneState.isNetworkAvailable(context)) {
+ // Call device registration API.
+ ServerUtils.callSecuredAPI(RegistrationActivity.this,
+ CommonUtilities.API_SERVER_URL + CommonUtilities.REGISTER_ENDPOINT,
+ CommonUtilities.POST_METHOD, jsObject,
+ RegistrationActivity.this,
+ CommonUtilities.REGISTER_REQUEST_CODE);
+ } else {
+ CommonDialogUtils.stopProgressDialog(progressDialog);
+ CommonDialogUtils.showNetworkUnavailableMessage(RegistrationActivity.this);
+ }
+
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ Intent i = new Intent();
+ i.setAction(Intent.ACTION_MAIN);
+ i.addCategory(Intent.CATEGORY_HOME);
+ this.startActivity(i);
+ finish();
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_HOME) {
+ finish();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ return true;
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return super.onOptionsItemSelected(item);
+ }
+
+ DialogInterface.OnClickListener registrationFailedOKBtnClickListerner =
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface arg0,
+ int arg1) {
+ loadAuthenticationErrorActivity();
+ }
+ };
+
+ @Override
+ public void onReceiveAPIResult(Map result, int requestCode) {
+ CommonDialogUtils.stopProgressDialog(progressDialog);
+ String responseStatus = "";
+ if (result != null) {
+ responseStatus = result.get(CommonUtilities.STATUS_KEY);
+
+ if (responseStatus.equals(CommonUtilities.REQUEST_SUCCESSFUL)) {
+ Intent intent =
+ new Intent(RegistrationActivity.this,
+ AlreadyRegisteredActivity.class);
+ intent.putExtra(getResources().getString(R.string.intent_extra_fresh_reg_flag),
+ true);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ // finish();
+ } else if (responseStatus.equals(CommonUtilities.INTERNAL_SERVER_ERROR)) {
+ Log.e(TAG, "The value of status is : " + responseStatus);
+ alertDialog =
+ CommonDialogUtils.getAlertDialogWithOneButtonAndTitle(context,
+ getResources().getString(R.string.title_head_connection_error),
+ getResources().getString(R.string.error_internal_server),
+ getResources().getString(R.string.button_ok),
+ registrationFailedOKBtnClickListerner);
+ alertDialog.show();
+ } else {
+ alertDialog =
+ CommonDialogUtils.getAlertDialogWithOneButtonAndTitle(context,
+ getResources().getString(R.string.title_head_registration_error),
+ getResources().getString(R.string.error_for_all_unknown_registration_failures),
+ getResources().getString(R.string.button_ok),
+ registrationFailedOKBtnClickListerner);
+ }
+
+ } else {
+ Log.e(TAG, "The result is null in onReceiveAPIResult(). ");
+ Log.e(TAG, "The responseStatus is : " + responseStatus);
+ alertDialog =
+ CommonDialogUtils.getAlertDialogWithOneButtonAndTitle(context,
+ getResources().getString(R.string.title_head_registration_error),
+ getResources().getString(R.string.error_for_all_unknown_registration_failures),
+ getResources().getString(R.string.button_ok),
+ registrationFailedOKBtnClickListerner);
+ alertDialog.show();
+
+ }
+ }
+
+ /**
+ * Loads Authentication error activity.
+ *
+ */
+ private void loadAuthenticationErrorActivity() {
+ Intent intent = new Intent(RegistrationActivity.this, AuthenticationErrorActivity.class);
+ intent.putExtra(getResources().getString(R.string.intent_extra_from_activity),
+ RegistrationActivity.class.getSimpleName());
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ }
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/ServerDetails.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/ServerDetails.java
new file mode 100644
index 0000000000..13aee37ee5
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/ServerDetails.java
@@ -0,0 +1,188 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import org.wso2.cdm.agent.api.DeviceInfo;
+import org.wso2.cdm.agent.services.MessageProcessor;
+import org.wso2.cdm.agent.utils.CommonDialogUtils;
+import org.wso2.cdm.agent.utils.CommonUtilities;
+import org.wso2.cdm.agent.utils.Preference;
+import org.wso2.cdm.agent.utils.Responce;
+
+/**
+ * This the the activity that is used to capture the server's host name.
+ */
+public class ServerDetails extends Activity {
+
+ TextView evServerIP;
+ Button btnStartRegistration;
+ Context context;
+ DialogInterface.OnClickListener dialogClickListener;
+ DeviceInfo info;
+ TextView tvSeverAddress;
+
+ String senderID = null;
+ ProgressDialog progressDialog;
+ String regId;
+ AlertDialog.Builder alertDialog;
+
+ boolean alreadyRegisteredActivityFlag = false;
+ boolean authenticationActivityFlag = false;
+ private String TAG = ServerDetails.class.getSimpleName();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_settings);
+ context = ServerDetails.this;
+ info = new DeviceInfo(ServerDetails.this);
+ evServerIP = (TextView) findViewById(R.id.evServerIP);
+ tvSeverAddress = (TextView) findViewById(R.id.tvSeverAddress);
+ btnStartRegistration = (Button) findViewById(R.id.btnStartRegistration);
+
+ // Checking if the device meets minimum requirements
+ Responce compatibility = info.isCompatible();
+ if (!compatibility.getCode()) {
+ btnStartRegistration.setVisibility(View.GONE);
+ tvSeverAddress.setVisibility(View.GONE);
+ evServerIP.setVisibility(View.GONE);
+ alertDialog =
+ CommonDialogUtils.getAlertDialogWithOneButtonAndTitle(context,
+ getResources().getString(
+ R.string.error_authorization_failed),
+ getResources().getString(
+ compatibility
+ .getDescriptionResourceID()),
+ getResources().getString(
+ R.string.button_ok),
+ onRootedClickListner);
+ } else {
+ btnStartRegistration.setVisibility(View.VISIBLE);
+ evServerIP.setVisibility(View.VISIBLE);
+ String ipSaved =
+ Preference.get(context.getApplicationContext(),
+ getResources().getString(R.string.shared_pref_ip));
+ regId = Preference.get(context.getApplicationContext(),
+ getResources().getString(R.string.shared_pref_regId));
+
+ //check if we have the IP saved previously.
+ if (ipSaved != null) {
+ evServerIP.setText(ipSaved);
+ CommonUtilities.setServerURL(ipSaved);
+ startAuthenticationActivity();
+ } else {
+ evServerIP.setText(CommonUtilities.SERVER_IP);
+ }
+
+ String deviceActive=Preference.get(context, context.getResources().getString(R.string.shared_pref_device_active));
+ if(deviceActive!=null && deviceActive.equals("1")){
+ Intent intent = new Intent(ServerDetails.this, AlreadyRegisteredActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ }
+ // on click handler for start registration
+ btnStartRegistration.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ AlertDialog.Builder alertBuilder = new AlertDialog.Builder(ServerDetails.this);
+ StringBuilder messageBuilder = new StringBuilder();
+ messageBuilder
+ .append(getResources().getString(R.string.dialog_init_confirmation));
+ messageBuilder.append(" ");
+ messageBuilder.append(evServerIP.getText().toString());
+ messageBuilder.append(" ");
+ messageBuilder
+ .append(getResources().getString(R.string.dialog_init_end_general));
+ alertBuilder.setMessage(messageBuilder.toString())
+ .setPositiveButton(getResources().getString(R.string.yes),
+ dialogClickListener)
+ .setNegativeButton(getResources().getString(R.string.no),
+ dialogClickListener).show();
+ }
+ });
+
+ dialogClickListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ if (!evServerIP.getText().toString().trim().equals("")) {
+ CommonUtilities
+ .setServerURL(evServerIP.getText().toString().trim());
+ Preference.put(context.getApplicationContext(),
+ getResources().getString(R.string.shared_pref_ip),
+ evServerIP.getText().toString().trim());
+ startAuthenticationActivity();
+
+ } else {
+ Toast.makeText(context.getApplicationContext(),
+ getResources().getString(
+ R.string.toast_message_enter_server_address),
+ Toast.LENGTH_LONG).show();
+ }
+ break;
+
+ case DialogInterface.BUTTON_NEGATIVE:
+ dialog.dismiss();
+ break;
+ }
+ }
+ };
+ }
+ Log.d(TAG, "Server details activity started");
+ }
+
+ DialogInterface.OnClickListener onRootedClickListner = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+ };
+
+ /**
+ * This method is called to open AuthenticationActivity.
+ */
+ private void startAuthenticationActivity() {
+ Intent intent = new Intent(ServerDetails.this, AuthenticationActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see android.app.Activity#onDestroy()
+ */
+ @Override
+ protected void onDestroy() {
+ context = null;
+ super.onDestroy();
+ }
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/ApplicationManager.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/ApplicationManager.java
new file mode 100644
index 0000000000..0eedc2a6e7
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/ApplicationManager.java
@@ -0,0 +1,243 @@
+/*
+ ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+*/
+package org.wso2.cdm.agent.api;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.wso2.cdm.agent.R;
+import org.wso2.cdm.agent.models.PInfo;
+import org.wso2.cdm.agent.utils.CommonUtilities;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.provider.Browser;
+import android.util.Base64;
+import android.util.Log;
+
+public class ApplicationManager {
+ Context context = null;
+
+ public ApplicationManager(Context context) {
+ this.context = context;
+ }
+
+ /**
+ * Returns a list of all the applications installed on the device
+ */
+ public String[] getApplicationListasArray() {
+ PackageManager pm = context.getPackageManager();
+ List apps = pm.getInstalledApplications(0);
+ String applicationNames[] = new String[apps.size()];
+ for (int j = 0; j < apps.size(); j++) {
+ applicationNames[j] = apps.get(j).packageName;
+ }
+ return applicationNames;
+ }
+
+ public ArrayList getInstalledApps(boolean getSysPackages) {
+ ArrayList res = new ArrayList();
+ List packs = context.getPackageManager()
+ .getInstalledPackages(0);
+ for (int i = 0; i < packs.size(); i++) {
+ PackageInfo p = packs.get(i);
+ ApplicationInfo a = p.applicationInfo;
+ //if ((!getSysPackages) && (p.versionName == null)) {
+ if ((!getSysPackages) && ((a.flags & ApplicationInfo.FLAG_SYSTEM) == 1)) {
+ continue;
+ }
+ PInfo newInfo = new PInfo();
+ newInfo.appname = p.applicationInfo.loadLabel(
+ context.getPackageManager()).toString();
+ newInfo.pname = p.packageName;
+ //newInfo.pname = "";
+ newInfo.versionName = p.versionName;
+ newInfo.versionCode = p.versionCode;
+ newInfo.icon = "";
+ // newInfo.icon =
+ // encodeImage(p.applicationInfo.loadIcon(context.getPackageManager()));
+ res.add(newInfo);
+ }
+ return res;
+ }
+
+ public String getAppNameFromPackage(String packageName) {
+ boolean getSysPackages = true;
+ String appName = "";
+ List packs = context.getPackageManager()
+ .getInstalledPackages(0);
+ for (int i = 0; i < packs.size(); i++) {
+ PackageInfo p = packs.get(i);
+ if ((!getSysPackages) && (p.versionName == null)) {
+ continue;
+ }
+
+ if(packageName.equals(p.packageName)){
+ appName = p.applicationInfo.loadLabel(
+ context.getPackageManager()).toString();
+ }
+ }
+ return appName;
+ }
+
+ public String encodeImage(Drawable drawable) {
+ Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
+ // Bitmap bitmap =
+ // ((BitmapDrawable)context.getResources().getDrawable(R.drawable.dot)).getBitmap();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); // bm is the
+ // bitmap object
+ byte[] b = baos.toByteArray();
+ String encodedImage = Base64.encodeToString(b, Base64.NO_WRAP);
+ /*
+ * ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ * bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream); byte[]
+ * bitMapData = stream.toByteArray();
+ */
+ // Log.e("BEFORE JSON : ", encodedImage);
+ return encodedImage;
+ }
+
+ /**
+ * Installs an application to the device
+ *
+ * @param url
+ * - APK Url should be passed in as a String
+ */
+ public void installApp(String url) {
+ UpdateApp updator = new UpdateApp();
+ updator.setContext(context);
+ updator.execute(url);
+ }
+
+ /**
+ * Uninstalls an application from the device
+ *
+ * @param url
+ * - Application package name should be passed in as a String
+ */
+ public void unInstallApplication(String packageName)// Specific package Name
+ // Uninstall.
+ {
+ // Uri packageURI = Uri.parse("package:com.CheckInstallApp");
+ if (!packageName.contains(context.getResources().getString(R.string.application_package_prefix))) {
+ packageName = context.getResources().getString(R.string.application_package_prefix) + packageName;
+ }
+ Uri packageURI = Uri.parse(packageName.toString());
+ Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
+ uninstallIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(uninstallIntent);
+ }
+
+ /**
+ * Creates a webclip on the device home screen
+ *
+ * @param url
+ * - Url should be passed in as a String
+ * - Title(Web app title) should be passed in as a String
+ */
+ public void createWebAppBookmark(String url, String title){
+ final Intent in = new Intent();
+ final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ long urlHash = url.hashCode();
+ long uniqueId = (urlHash << 32) | shortcutIntent.hashCode();
+ shortcutIntent.putExtra(Browser.EXTRA_APPLICATION_ID, Long.toString(uniqueId));
+ in.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
+ in.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
+ in.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
+ Intent.ShortcutIconResource.fromContext(
+ context,
+ R.drawable.ic_bookmark));
+ in.setAction(context.getResources().getString(R.string.application_package_launcher_action));
+ //or in.setAction(Intent.ACTION_CREATE_SHORTCUT);
+
+ context.sendBroadcast(in);
+ }
+
+ /**
+ * Installs or updates an application to the device
+ *
+ * @param url
+ * - APK Url should be passed in as a String
+ */
+ public class UpdateApp extends AsyncTask {
+ private Context context;
+
+ public void setContext(Context contextf) {
+ context = contextf;
+ }
+
+ @Override
+ protected Void doInBackground(String... arg0) {
+ try {
+ URL url = new URL(arg0[0]);
+ HttpURLConnection c = (HttpURLConnection) url.openConnection();
+ c.setRequestMethod(context.getResources().getString(R.string.server_util_req_type_get));
+ c.setDoOutput(true);
+ c.connect();
+
+ String PATH = Environment.getExternalStorageDirectory()
+ .getPath() + context.getResources().getString(R.string.application_mgr_download_location);
+ File file = new File(PATH);
+ file.mkdirs();
+ File outputFile = new File(file, context.getResources().getString(R.string.application_mgr_download_file_name));
+ if (outputFile.exists()) {
+ outputFile.delete();
+ }
+ FileOutputStream fos = new FileOutputStream(outputFile);
+
+ InputStream is = c.getInputStream();
+
+ byte[] buffer = new byte[1024];
+ int len1 = 0;
+ while ((len1 = is.read(buffer)) != -1) {
+ fos.write(buffer, 0, len1);
+ }
+ fos.close();
+ is.close();
+
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(
+ Uri.fromFile(new File(PATH + context.getResources().getString(R.string.application_mgr_download_file_name))),
+ context.getResources().getString(R.string.application_mgr_mime));
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+
+ } catch (Exception e) {
+ if(CommonUtilities.DEBUG_MODE_ENABLED){
+ Log.e("UpdateAPP", "Update error! " + e.getMessage());
+ }
+ }
+ return null;
+ }
+ };
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/Battery.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/Battery.java
new file mode 100644
index 0000000000..ffac7ca685
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/Battery.java
@@ -0,0 +1,59 @@
+/*
+ ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+*/
+package org.wso2.cdm.agent.api;
+
+public class Battery {
+ int scale = -1;
+ int level = -1;
+ int voltage = -1;
+ int temp = -1;
+ private static Battery battery = null;
+ private Battery(){
+
+ }
+ public static Battery getInstance(){
+ if(battery==null){
+ battery = new Battery();
+ }
+ return battery;
+ }
+
+ public int getScale() {
+ return scale;
+ }
+ public void setScale(int scale) {
+ this.scale = scale;
+ }
+ public int getLevel() {
+ return level;
+ }
+ public void setLevel(int level) {
+ this.level = level;
+ }
+ public int getVoltage() {
+ return voltage;
+ }
+ public void setVoltage(int voltage) {
+ this.voltage = voltage;
+ }
+ public int getTemp() {
+ return temp;
+ }
+ public void setTemp(int temp) {
+ this.temp = temp;
+ }
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/DeviceInfo.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/DeviceInfo.java
new file mode 100644
index 0000000000..140474944a
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/DeviceInfo.java
@@ -0,0 +1,348 @@
+/*
+ * ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ * ~
+ * ~ Licensed under the Apache License, Version 2.0 (the "License");
+ * ~ you may not use this file except in compliance with the License.
+ * ~ You may obtain a copy of the License at
+ * ~
+ * ~ http://www.apache.org/licenses/LICENSE-2.0
+ * ~
+ * ~ Unless required by applicable law or agreed to in writing, software
+ * ~ distributed under the License is distributed on an "AS IS" BASIS,
+ * ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * ~ See the License for the specific language governing permissions and
+ * ~ limitations under the License.
+ */
+package org.wso2.cdm.agent.api;
+
+import java.io.File;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.List;
+
+import org.json.JSONArray;
+import org.wso2.cdm.agent.R;
+import org.wso2.cdm.agent.utils.CommonUtilities;
+import org.wso2.cdm.agent.utils.Responce;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Build;
+import android.os.Environment;
+import android.os.StatFs;
+import android.provider.Settings.Secure;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+public class DeviceInfo {
+ String deviceModel = null;
+ String osVersion = null;
+ int sdkVersion = 0;
+ String device = null;
+ String imsi = null;
+ String mac = null;
+ String deviceId = null;
+ String manufacturer = null;
+ String networkOperatorName = "No Sim";
+ Root rootChecker = null;
+ Context context = null;
+ double gbDivider = 1073741824;
+ double mbDivider = 1048576;
+ long ERROR = 0;
+
+ // private static DeviceInfo deviceInfo = null;
+
+ public DeviceInfo(Context context) {
+ this.context = context;
+ }
+
+ /**
+ * Returns the network operator name
+ */
+ public JSONArray getNetworkOperatorName() {
+ JSONArray jsonArray = null;
+ final TelephonyManager tm =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ if (CommonUtilities.DEBUG_MODE_ENABLED) {
+ Log.e("Network OP", tm.getSimOperatorName());
+ }
+ if (tm.getSimOperatorName() != null && tm.getSimOperatorName() != "") {
+ networkOperatorName = tm.getSimOperatorName();
+ } else {
+ networkOperatorName = "No Sim";
+ }
+
+ SharedPreferences mainPref = context.getSharedPreferences("com.mdm", Context.MODE_PRIVATE);
+ try {
+ jsonArray = new JSONArray(mainPref.getString("operators", "[]"));
+ boolean simstatus = false;
+ if (jsonArray.length() > 0) {
+ for (int i = 0; i < jsonArray.length(); i++) {
+ if ((jsonArray.getString(i) != null) &&
+ jsonArray.getString(i).trim().equals(tm.getSimOperatorName())) {
+ simstatus = true;
+ }
+ }
+ if (!simstatus) {
+ jsonArray.put(tm.getSimOperatorName());
+ }
+ } else {
+ jsonArray.put(tm.getSimOperatorName());
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ Editor editor = mainPref.edit();
+ editor.putString("operators", jsonArray.toString());
+ editor.commit();
+
+ return jsonArray;
+ }
+
+ /**
+ * Returns the device model
+ */
+ public String getDeviceModel() {
+ deviceModel = android.os.Build.MODEL;
+ return deviceModel;
+ }
+
+ public String getDeviceManufacturer() {
+ manufacturer = Build.MANUFACTURER;
+ return manufacturer;
+ }
+
+ /**
+ * Returns the OS Version
+ */
+ public String getOsVersion() {
+ osVersion = android.os.Build.VERSION.RELEASE;
+ return osVersion;
+ }
+
+ /**
+ * Returns the SDK Version number
+ */
+ public int getSdkVersion() {
+ sdkVersion = android.os.Build.VERSION.SDK_INT;
+ return sdkVersion;
+ }
+
+ /**
+ * Returns the device name
+ */
+ public String getDevice() {
+ device = android.os.Build.DEVICE;
+ return device;
+ }
+
+ /**
+ * Returns the IMEI Number
+ */
+ public String getDeviceId() {
+ final TelephonyManager tm =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ try {
+ deviceId = tm.getDeviceId();
+ if (deviceId == null || deviceId.length() == 0)
+ deviceId = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ return deviceId;
+ }
+
+ /**
+ * Returns the IMSI Number
+ */
+ public String getIMSINumber() {
+ final TelephonyManager tm =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ imsi = tm.getSubscriberId();
+ return imsi;
+ }
+
+ /**
+ * Returns the device WiFi MAC
+ */
+ public String getMACAddress() {
+ WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ WifiInfo wInfo = wifiManager.getConnectionInfo();
+ mac = wInfo.getMacAddress();
+ return mac;
+ }
+
+ /**
+ * Returns the Email address of the device owner
+ */
+ public String getEmail() {
+ /*
+ * AccountManager manager = AccountManager.get(context);
+ * Account[] accounts = manager.getAccountsByType("com.google");
+ * Account account = accounts[0];
+ * Log.v("My Email",account.name.toString());
+ * return account.name.toString();
+ */
+ SharedPreferences example = context.getSharedPreferences("com.mdm", Context.MODE_PRIVATE);
+ String userString = example.getString("username", "");
+ return userString;
+ }
+
+ /**
+ * Returns true if the device is a Rooted device
+ */
+ public boolean isRooted() {
+ rootChecker = new Root();
+ return rootChecker.isDeviceRooted();
+ }
+
+ /**
+ * Returns the SIM serial number
+ */
+ public String getSimSerialNumber() {
+ TelephonyManager telemamanger =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ String getSimSerialNumber = telemamanger.getSimSerialNumber();
+ return getSimSerialNumber;
+ }
+
+ /**
+ * Returns all the sensors available on the device as a List
+ */
+ public void getAllSensors() {
+ SensorManager sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ List list = sm.getSensorList(Sensor.TYPE_ALL);
+
+ if (CommonUtilities.DEBUG_MODE_ENABLED) {
+ for (Sensor s : list) {
+ Log.d("SENSORS", s.getName());
+ }
+ }
+ }
+
+ /**
+ * Returns whether the external memory is available or not
+ */
+ public boolean externalMemoryAvailable() {
+ return android.os.Environment.getExternalStorageState()
+ .equals(android.os.Environment.MEDIA_MOUNTED);
+ }
+
+ /**
+ * Returns the available internal memory size
+ */
+ public double getAvailableInternalMemorySize() {
+ File path = Environment.getDataDirectory();
+ StatFs stat = new StatFs(path.getPath());
+ double blockSize = stat.getBlockSize();
+ double availableBlocks = stat.getAvailableBlocks();
+ return formatSizeGB(availableBlocks * blockSize);
+ }
+
+ /**
+ * Returns the total internal memory size
+ */
+ public double getTotalInternalMemorySize() {
+ File path = Environment.getDataDirectory();
+ StatFs stat = new StatFs(path.getPath());
+ double blockSize = stat.getBlockSize();
+ double totalBlocks = stat.getBlockCount();
+ return formatSizeGB(totalBlocks * blockSize);
+ }
+
+ /**
+ * Returns the available external memory size
+ */
+ public double getAvailableExternalMemorySize() {
+ if (externalMemoryAvailable()) {
+ File path = Environment.getExternalStorageDirectory();
+ StatFs stat = new StatFs(path.getPath());
+ double blockSize = stat.getBlockSize();
+ double availableBlocks = stat.getAvailableBlocks();
+ return formatSizeGB(availableBlocks * blockSize);
+ } else {
+ return ERROR;
+ }
+ }
+
+ /**
+ * Returns the total external memory size
+ */
+ public double getTotalExternalMemorySize() {
+ if (externalMemoryAvailable()) {
+ File path = Environment.getExternalStorageDirectory();
+ StatFs stat = new StatFs(path.getPath());
+ double blockSize = stat.getBlockSize();
+ double totalBlocks = stat.getBlockCount();
+ return formatSizeGB(totalBlocks * blockSize);
+ } else {
+ return ERROR;
+ }
+ }
+
+ /**
+ * Returns the string formatted value for the size
+ */
+ public double formatSizeGB(double total) {
+ double amount = (total / gbDivider);
+ BigDecimal bd = new BigDecimal(amount).setScale(2, RoundingMode.HALF_EVEN);
+ amount = bd.doubleValue();
+ return amount;
+ }
+
+ public double formatSizeMB(double total) {
+ double amount = (total / mbDivider);
+ BigDecimal bd = new BigDecimal(amount).setScale(2, RoundingMode.HALF_EVEN);
+ amount = bd.doubleValue();
+ return amount;
+ }
+
+ public Responce isCompatible() {
+ if (!(getSdkVersion() >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) &&
+ isRooted()) {
+ return Responce.INCOMPATIBLE;
+
+ } else if (getSdkVersion() < android.os.Build.VERSION_CODES.FROYO) {
+ return Responce.INCOMPATIBLE;
+ } else if (isRooted()) {
+ return Responce.INCOMPATIBLE;
+ }
+ return Responce.COMPATIBLE;
+ }
+
+ /*
+ * public static String formatSize(long size) {
+ * String suffix = null;
+ *
+ * if (size >= 1024) {
+ * suffix = "KB";
+ * size /= 1024;
+ * if (size >= 1024) {
+ * suffix = "MB";
+ * size /= 1024;
+ * if(size >=1024){
+ * suffix = "GB";
+ * }
+ * }
+ * }
+ *
+ * StringBuilder resultBuffer = new StringBuilder(Long.toString(size));
+ *
+ * int commaOffset = resultBuffer.length() - 3;
+ * while (commaOffset > 0) {
+ * resultBuffer.insert(commaOffset, ',');
+ * commaOffset -= 3;
+ * }
+ *
+ * if (suffix != null) resultBuffer.append(suffix);
+ * return resultBuffer.toString();
+ * }
+ */
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/ExecShell.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/ExecShell.java
new file mode 100644
index 0000000000..0d2f38d263
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/ExecShell.java
@@ -0,0 +1,74 @@
+/*
+ ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+*/
+package org.wso2.cdm.agent.api;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+
+import android.util.Log;
+
+/**
+ * @author Kasun Dananjaya
+ *
+ */
+public class ExecShell {
+
+ private static String LOG_TAG = ExecShell.class.getName();
+
+ public static enum SHELL_CMD {
+ check_su_binary(new String[] {"/system/xbin/which","su"}),
+ ;
+
+ String[] command;
+
+ SHELL_CMD(String[] command){
+ this.command = command;
+ }
+ }
+
+ public ArrayList executeCommand(SHELL_CMD shellCmd){
+ String line = null;
+ ArrayList fullResponse = new ArrayList();
+ Process localProcess = null;
+
+ try {
+ localProcess = Runtime.getRuntime().exec(shellCmd.command);
+ } catch (Exception e) {
+ return null;
+ //e.printStackTrace();
+ }
+
+ BufferedWriter out = new BufferedWriter(new OutputStreamWriter(localProcess.getOutputStream()));
+ BufferedReader in = new BufferedReader(new InputStreamReader(localProcess.getInputStream()));
+
+ try {
+ while ((line = in.readLine()) != null) {
+ Log.d(LOG_TAG, "--> Line received: " + line);
+ fullResponse.add(line);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ Log.d(LOG_TAG, "--> Full response was: " + fullResponse);
+
+ return fullResponse;
+ }
+
+}
\ No newline at end of file
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/GPSTracker.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/GPSTracker.java
new file mode 100644
index 0000000000..e193ce45e0
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/GPSTracker.java
@@ -0,0 +1,213 @@
+/*
+ ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+*/
+package org.wso2.cdm.agent.api;
+
+import android.app.AlertDialog;
+import android.app.Service;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.provider.Settings;
+import android.util.Log;
+
+public class GPSTracker extends Service implements LocationListener {
+
+private final Context mContext;
+
+// flag for GPS status
+boolean isGPSEnabled = false;
+
+// flag for network status
+boolean isNetworkEnabled = false;
+
+// flag for GPS status
+boolean canGetLocation = false;
+
+Location location; // location
+double latitude; // latitude
+double longitude; // longitude
+
+// The minimum distance to change Updates in meters
+private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters
+
+// The minimum time between updates in milliseconds
+private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 1 minute
+
+// Declaring a Location Manager
+protected LocationManager locationManager;
+
+public GPSTracker(Context context) {
+ this.mContext = context;
+ getLocation();
+}
+
+public Location getLocation() {
+ try {
+ locationManager = (LocationManager) mContext
+ .getSystemService(LOCATION_SERVICE);
+
+ // getting GPS status
+ isGPSEnabled = locationManager
+ .isProviderEnabled(LocationManager.GPS_PROVIDER);
+
+ // getting network status
+ isNetworkEnabled = locationManager
+ .isProviderEnabled(LocationManager.NETWORK_PROVIDER);
+
+ if (!isGPSEnabled && !isNetworkEnabled) {
+ // no network provider is enabled
+ } else {
+ this.canGetLocation = true;
+ if (isNetworkEnabled) {
+ locationManager.requestLocationUpdates(
+ LocationManager.NETWORK_PROVIDER,
+ MIN_TIME_BW_UPDATES,
+ MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
+ Log.d("Network", "Network");
+ if (locationManager != null) {
+ location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
+ if (location != null) {
+ latitude = location.getLatitude();
+ longitude = location.getLongitude();
+ }
+ }
+ }
+ // if GPS Enabled get lat/long using GPS Services
+ if (isGPSEnabled) {
+ if (location == null) {
+ locationManager.requestLocationUpdates(
+ LocationManager.GPS_PROVIDER,
+ MIN_TIME_BW_UPDATES,
+ MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
+ Log.d("GPS Enabled", "GPS Enabled");
+ if (locationManager != null) {
+ location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
+ if (location != null) {
+ latitude = location.getLatitude();
+ longitude = location.getLongitude();
+ }
+ }
+ }
+ }
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return location;
+}
+
+/**
+ * Stop using GPS listener
+ * Calling this function will stop using GPS in your app
+ * */
+public void stopUsingGPS(){
+ if(locationManager != null){
+ locationManager.removeUpdates(GPSTracker.this);
+ }
+}
+
+/**
+ * Function to get latitude
+ * */
+public double getLatitude(){
+ if(location != null){
+ latitude = location.getLatitude();
+ }
+
+ // return latitude
+ return latitude;
+}
+
+/**
+ * Function to get longitude
+ * */
+public double getLongitude(){
+ if(location != null){
+ longitude = location.getLongitude();
+ }
+
+ // return longitude
+ return longitude;
+}
+
+/**
+ * Function to check GPS/wifi enabled
+ * @return boolean
+ * */
+public boolean canGetLocation() {
+ return this.canGetLocation;
+}
+
+/**
+ * Function to show settings alert dialog
+ * On pressing Settings button will lauch Settings Options
+ * */
+public void showSettingsAlert(){
+ AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
+
+ // Setting Dialog Title
+ alertDialog.setTitle("GPS is settings");
+
+ // Setting Dialog Message
+ alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?");
+
+ // On pressing Settings button
+ alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,int which) {
+ Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
+ mContext.startActivity(intent);
+ }
+ });
+
+ // on pressing cancel button
+ alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ });
+
+ // Showing Alert Message
+ alertDialog.show();
+}
+
+@Override
+public void onLocationChanged(Location location) {
+}
+
+@Override
+public void onProviderDisabled(String provider) {
+}
+
+@Override
+public void onProviderEnabled(String provider) {
+}
+
+@Override
+public void onStatusChanged(String provider, int status, Bundle extras) {
+}
+
+@Override
+public IBinder onBind(Intent arg0) {
+ return null;
+}
+}
\ No newline at end of file
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/LocationServices.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/LocationServices.java
new file mode 100644
index 0000000000..bac8b95081
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/LocationServices.java
@@ -0,0 +1,44 @@
+/*
+ ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+*/
+package org.wso2.cdm.agent.api;
+
+import android.content.Context;
+import android.location.Location;
+import android.location.LocationManager;
+
+public class LocationServices {
+ Context context = null;
+ LocationManager lm = null;
+ Location location = null;
+ String latitude = null;
+ String longitude = null;
+
+ public LocationServices(Context context){
+ this.context = context;
+ lm = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
+ location = lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
+
+
+ }
+ public String getLatitude(){
+ return location.getLatitude()+"";
+ }
+ public String getLongitude(){
+ return location.getLongitude()+"";
+ }
+}
+
+
\ No newline at end of file
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/PhoneState.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/PhoneState.java
new file mode 100644
index 0000000000..2ebf5c6e48
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/PhoneState.java
@@ -0,0 +1,252 @@
+/*
+ ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+*/
+package org.wso2.cdm.agent.api;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.simple.JSONArray;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.MemoryInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.BatteryManager;
+import android.util.Log;
+
+public class PhoneState {
+ Context context = null;
+ private long mStartRX = 0;
+ private long mStartTX = 0;
+ double mbDivider = 1048576;
+ //Data Usage API Init
+ TrafficSnapshot latest=null;
+ TrafficSnapshot previous=null;
+ ApplicationManager appList;
+
+ public PhoneState(Context context){
+ this.context = context;
+ appList = new ApplicationManager(this.context);
+ }
+ /**
+ *Returns the device IP address
+ */
+ public String getIpAddress(){
+ WifiManager wifiManager = (WifiManager) context.getSystemService(context.WIFI_SERVICE);
+ WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+ int ipAddress = wifiInfo.getIpAddress();
+ String ip = intToIp(ipAddress);
+ return ip;
+ }
+ /**
+ *Format the integer IP address and return it as a String
+ *@param i - IP address should be passed in as an Integer
+ */
+ public String intToIp(int i) {
+ /* return ((i >> 24 ) & 0xFF ) + "." +
+ ((i >> 16 ) & 0xFF) + "." +
+ ((i >> 8 ) & 0xFF) + "." +
+ ( i & 0xFF) ;*/
+ return (i & 0xFF) + "." +
+ ((i >> 8 ) & 0xFF) + "." +
+ ((i >> 16 ) & 0xFF) + "." +
+ ((i >> 24 ) & 0xFF );
+ }
+ /**
+ *Returns the available amount of memory in the device
+ */
+ public String getAvailableMemory(){
+ ActivityManager activityManager = (ActivityManager)context.getSystemService(context.ACTIVITY_SERVICE);
+ MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
+ activityManager.getMemoryInfo(memoryInfo);
+ Log.v("Available Memory", memoryInfo.availMem+"");
+ String availMemory = memoryInfo.availMem+"";
+ return availMemory;
+ }
+ /**
+ *Returns the amount of uploaded data in bytes
+ *//*
+ public long getDataUploadUsage(){
+ mStartTX = TrafficStats.getTotalTxBytes();
+ return mStartTX;
+ }
+ *//**
+ *Returns the amount of downloaded data in bytes
+ *//*
+ public long getDataDownloadUsage(){
+ mStartRX = TrafficStats.getTotalRxBytes();
+ return mStartRX;
+ }*/
+ /**
+ *Returns the device battery information
+ */
+ public float getBatteryLevel(){
+ Intent batteryIntent = context.getApplicationContext().registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+ int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+ int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+
+ // Error checking that probably isn't needed but I added just in case.
+ if(level == -1 || scale == -1) {
+ return 50.0f;
+ }
+
+ return ((float)level / (float)scale) * 100.0f;
+ /*BroadcastReceiver batteryReceiver = new BroadcastReceiver() {
+ int scale = -1;
+ int level = -1;
+ int voltage = -1;
+ int temp = -1;
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+ scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+ temp = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1);
+ voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1);
+ Log.v("Battery Level", "level is "+level+"/"+scale+", temp is "+temp+", voltage is "+voltage);
+
+ Battery battery = Battery.getInstance();
+ battery.setLevel(level);
+ battery.setScale(scale);
+ battery.setTemp(temp);
+ battery.setVoltage(voltage);
+
+ }
+ };
+ IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ context.registerReceiver(batteryReceiver, filter);
+ return Battery.getInstance();*/
+ }
+
+ /**
+ * Generate Data Usage Report
+ */
+ public JSONObject takeDataUsageSnapShot() {
+ previous=latest;
+ latest=new TrafficSnapshot(context);
+ JSONObject dataObj = new JSONObject();
+ JSONArray jsonArray = new JSONArray();
+ String latestRX, latestTX, previousRX, previousTX, deltaRX, deltaTX;
+ latestRX = String.valueOf(formatSizeMB(latest.device.rx));
+ latestTX = String.valueOf(formatSizeMB(latest.device.tx));
+ try {
+ dataObj.put("totalupload", latestRX);
+ dataObj.put("totaldownload", latestTX);
+
+ if (previous!=null) {
+ previousRX = String.valueOf(previous.device.rx);
+ previousTX = String.valueOf(previous.device.tx);
+
+ deltaRX = String.valueOf(latest.device.rx-previous.device.rx);
+ deltaTX = String.valueOf(latest.device.tx-previous.device.tx);
+ }
+
+ ArrayList log=new ArrayList();
+ HashSet intersection=new HashSet(latest.apps.keySet());
+
+ if (previous!=null) {
+ intersection.retainAll(previous.apps.keySet());
+ }
+
+ for (Integer uid : intersection) {
+ TrafficRecord latest_rec=latest.apps.get(uid);
+ TrafficRecord previous_rec=
+ (previous==null ? null : previous.apps.get(uid));
+
+ jsonArray.add(getDataUseReport(latest_rec.tag, latest_rec, previous_rec, log));
+ }
+
+ dataObj.put("appdata", jsonArray);
+
+ Collections.sort(log);
+
+ for (String row : log) {
+ Log.d("TrafficMonitor", row);
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return dataObj;
+ }
+
+ private JSONObject getDataUseReport(CharSequence name, TrafficRecord latest_rec,
+ TrafficRecord previous_rec,
+ ArrayList rows) {
+ JSONObject jsonObj = new JSONObject();
+ if (latest_rec.rx>-1 || latest_rec.tx>-1) {
+ StringBuilder buf=new StringBuilder(name);
+ try {
+ jsonObj.put("package", name);
+ jsonObj.put("name", appList.getAppNameFromPackage(name.toString()));
+ jsonObj.put("upload", formatSizeMB(latest_rec.tx));
+ jsonObj.put("download", formatSizeMB(latest_rec.rx));
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ buf.append("=");
+ buf.append(String.valueOf(latest_rec.rx));
+ buf.append(" received");
+
+ if (previous_rec!=null) {
+ buf.append(" (delta=");
+ buf.append(String.valueOf(latest_rec.rx-previous_rec.rx));
+ buf.append(")");
+ }
+
+ buf.append(", ");
+ buf.append(String.valueOf(latest_rec.tx));
+ buf.append(" sent");
+
+ if (previous_rec!=null) {
+ buf.append(" (delta=");
+ buf.append(String.valueOf(latest_rec.tx-previous_rec.tx));
+ buf.append(")");
+ }
+
+ rows.add(buf.toString());
+ }
+ return jsonObj;
+ }
+
+ public double formatSizeMB(double total){
+ double amount = (total/mbDivider);
+ BigDecimal bd = new BigDecimal(amount).setScale(2, RoundingMode.HALF_EVEN);
+ amount = bd.doubleValue();
+ return amount;
+ }
+
+ public static boolean isNetworkAvailable(Context context) {
+ ConnectivityManager mConnMan = (ConnectivityManager) context
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo info = mConnMan.getActiveNetworkInfo();
+ if (info == null) {
+ return false;
+ }
+ return info.isConnected();
+ }
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/Root.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/Root.java
new file mode 100644
index 0000000000..f00d114c9a
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/Root.java
@@ -0,0 +1,80 @@
+/*
+ ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+*/
+package org.wso2.cdm.agent.api;
+
+import java.io.File;
+
+import android.util.Log;
+
+/**
+ * @author Kasun Dananjaya
+ *
+ */
+public class Root {
+
+ private static String LOG_TAG = Root.class.getName();
+
+ /**
+ *Returns true if the device is rooted (if any of the root methods returns true)
+ */
+ public boolean isDeviceRooted() {
+ if (checkRootMethod3()){return true;}
+ if (checkRootMethod2()){return true;}
+ if (checkRootMethod1()){return true;}
+
+
+ return false;
+ }
+
+ /**
+ *Returns true if the OS build tags contains "test-keys"
+ */
+ public boolean checkRootMethod1(){
+ String buildTags = android.os.Build.TAGS;
+
+ if (buildTags != null && buildTags.contains("test-keys")) {
+ Log.e("ROOT CHECKER", "ROOT METHOD 1");
+ return true;
+ }
+ return false;
+ }
+ /**
+ *Returns true if the device contains SuperUser.apk which is stored into the device in the rooting process
+ */
+ public boolean checkRootMethod2(){
+ try {
+ File file = new File("/system/app/Superuser.apk");
+ if (file.exists()) {
+ Log.e("ROOT CHECKER", "ROOT METHOD 2");
+ return true;
+
+ }
+ } catch (Exception e) { }
+
+ return false;
+ }
+ /**
+ *Executes a shell command (superuser access with su binary) and returns true if the command succeeds
+ */
+ public boolean checkRootMethod3() {
+ if (new ExecShell().executeCommand(ExecShell.SHELL_CMD.check_su_binary) != null){
+ Log.e("ROOT CHECKER", "ROOT METHOD 3");
+ return true;
+ }else{
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/TrackCallSMS.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/TrackCallSMS.java
new file mode 100644
index 0000000000..b3cb86f743
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/TrackCallSMS.java
@@ -0,0 +1,138 @@
+/*
+ ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+*/
+package org.wso2.cdm.agent.api;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+import org.json.JSONObject;
+import org.json.simple.JSONArray;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.CallLog;
+
+public class TrackCallSMS {
+ Context context = null;
+ ContentResolver cr = null;
+
+ public TrackCallSMS(Context context) {
+ this.context = context;
+ cr = this.context.getContentResolver();
+ }
+
+ /**
+ * Returns a JSONArray of call detail objects Ex: [{number:"0112345666", type:"INCOMING", date:"dd/MM/yyyy hh:mm:ss.SSS", duration:"90"}]
+ */
+ public JSONArray getCallDetails() {
+ JSONArray jsonArray = null;
+ try {
+ Cursor managedCursor = cr.query(CallLog.Calls.CONTENT_URI, null,
+ null, null, null);
+ int number = managedCursor.getColumnIndex(CallLog.Calls.NUMBER);
+ int type = managedCursor.getColumnIndex(CallLog.Calls.TYPE);
+ int date = managedCursor.getColumnIndex(CallLog.Calls.DATE);
+ int duration = managedCursor.getColumnIndex(CallLog.Calls.DURATION);
+ jsonArray = new JSONArray();
+
+ while (managedCursor.moveToNext()) {
+ JSONObject jsonObj = new JSONObject();
+ String phNumber = managedCursor.getString(number);
+ String callType = managedCursor.getString(type);
+ String callDate = managedCursor.getString(date);
+ Date callDayTime = new Date(Long.valueOf(callDate));
+ String callDuration = managedCursor.getString(duration);
+ String dir = null;
+ int dircode = Integer.parseInt(callType);
+ switch (dircode) {
+ case CallLog.Calls.OUTGOING_TYPE:
+ dir = "OUTGOING";
+ break;
+
+ case CallLog.Calls.INCOMING_TYPE:
+ dir = "INCOMING";
+ break;
+
+ case CallLog.Calls.MISSED_TYPE:
+ dir = "MISSED";
+ break;
+ }
+ jsonObj.put("number", phNumber);
+ jsonObj.put("type", dir);
+ jsonObj.put("date", callDayTime);
+ jsonObj.put("duration", callDuration);
+ jsonArray.add(jsonObj);
+ }
+
+ managedCursor.close();
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ return jsonArray;
+ }
+
+ /**
+ * Returns a JSONArray of SMS objects Ex: [{number:"0772345666", date:"dd/MM/yyyy hh:mm:ss.SSS", content:"Hello"}]
+ *
+ * @param type
+ * - Folder type should be passed in (1 for Inbox, 2 for Sent box)
+ */
+ public JSONArray getSMS(int type) {
+ JSONArray jsonArray = null;
+ try {
+ Uri uriSms = Uri.parse("content://sms");
+
+ Cursor cursor = cr.query(uriSms, new String[] { "_id", "address",
+ "date", "body", "type", "read" }, "type=" + type, null,
+ "date" + " COLLATE LOCALIZED ASC");
+
+ if (cursor != null) {
+ cursor.moveToLast();
+ if (cursor.getCount() > 0) {
+ jsonArray = new JSONArray();
+ do {
+ JSONObject jsonObj = new JSONObject();
+ String date = cursor.getString(cursor
+ .getColumnIndex("date"));
+ Long timestamp = Long.parseLong(date);
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(timestamp);
+ DateFormat formatter = new SimpleDateFormat(
+ "dd/MM/yyyy hh:mm:ss.SSS");
+ jsonObj.put("number", cursor.getString(cursor
+ .getColumnIndex("address")));
+ jsonObj.put("date",
+ formatter.format(calendar.getTime()));
+ /*jsonObj.put("content",
+ cursor.getString(cursor.getColumnIndex("body")));*/
+ //jsonObj.put("content","Testing SMS");
+ jsonArray.add(jsonObj);
+ } while (cursor.moveToPrevious());
+ }
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ return jsonArray;
+
+ }
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/TrafficRecord.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/TrafficRecord.java
new file mode 100644
index 0000000000..8e2dc3450c
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/TrafficRecord.java
@@ -0,0 +1,36 @@
+/*
+ ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+*/
+
+package org.wso2.cdm.agent.api;
+
+import android.net.TrafficStats;
+
+public class TrafficRecord {
+ public long tx=0;
+ public long rx=0;
+ public String tag=null;
+
+ public TrafficRecord() {
+ tx=TrafficStats.getTotalTxBytes();
+ rx=TrafficStats.getTotalRxBytes();
+ }
+
+ public TrafficRecord(int uid, String tag) {
+ tx=TrafficStats.getUidTxBytes(uid);
+ rx=TrafficStats.getUidRxBytes(uid);
+ this.tag=tag;
+ }
+}
\ No newline at end of file
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/TrafficSnapshot.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/TrafficSnapshot.java
new file mode 100644
index 0000000000..5b973bc36f
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/TrafficSnapshot.java
@@ -0,0 +1,43 @@
+/*
+ ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+*/
+
+package org.wso2.cdm.agent.api;
+
+import java.util.HashMap;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+
+public class TrafficSnapshot {
+ public TrafficRecord device=null;
+ public HashMap apps=
+ new HashMap();
+
+ public TrafficSnapshot(Context ctxt) {
+ device=new TrafficRecord();
+
+ HashMap appNames=new HashMap();
+
+ for (ApplicationInfo app :
+ ctxt.getPackageManager().getInstalledApplications(0)) {
+ appNames.put(app.uid, app.packageName);
+ }
+
+ for (Integer uid : appNames.keySet()) {
+ apps.put(uid, new TrafficRecord(uid, appNames.get(uid)));
+ }
+ }
+}
\ No newline at end of file
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/WiFiConfig.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/WiFiConfig.java
new file mode 100644
index 0000000000..f18ac46e67
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/api/WiFiConfig.java
@@ -0,0 +1,798 @@
+/*
+ ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+*/
+package org.wso2.cdm.agent.api;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Iterator;
+import java.util.List;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.AuthAlgorithm;
+import android.net.wifi.WifiConfiguration.GroupCipher;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiConfiguration.PairwiseCipher;
+import android.net.wifi.WifiConfiguration.Protocol;
+import android.net.wifi.WifiManager;
+import android.os.Environment;
+import android.util.Log;
+
+public class WiFiConfig {
+ private Context context;
+ private static ConnectivityManager connectivityManager;
+ private WifiManager wifi_open_config;
+ private static final String INT_PRIVATE_KEY = "private_key";
+ private static final String INT_PHASE2 = "phase2";
+ private static final String INT_PASSWORD = "password";
+ private static final String INT_IDENTITY = "identity";
+ private static final String INT_EAP = "eap";
+ private static final String INT_CLIENT_CERT = "client_cert";
+ private static final String INT_CA_CERT = "ca_cert";
+ private static final String INT_ANONYMOUS_IDENTITY = "anonymous_identity";
+ final String INT_ENTERPRISEFIELD_NAME = "android.net.wifi.WifiConfiguration$EnterpriseField";
+
+ public WiFiConfig(Context context) {
+ this.context = context;
+ wifi_open_config = (WifiManager) context
+ .getSystemService(Context.WIFI_SERVICE);
+ connectivityManager = (ConnectivityManager) context
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ if (!isConnected(context, ConnectivityManager.TYPE_WIFI)) {
+ wifi_open_config.setWifiEnabled(true);
+ }
+ }
+
+ /**
+ * Checks wether the WIFI is switched on
+ */
+ private static boolean isConnected(Context context, int networkType) {
+ NetworkInfo networkInfo = null;
+ if (connectivityManager != null) {
+ networkInfo = connectivityManager.getNetworkInfo(networkType);
+ }
+ return networkInfo == null ? false : networkInfo.isConnected();
+ }
+
+ /**
+ * Saves a WEP WIFI Configuration Profile
+ * @params SSID, PASSWORD
+ * - WiFi SSID and PASSWORD should be passed in.
+ */
+ public boolean saveWEPConfig(String SSID, String PASSWORD) {
+ WifiManager wifi = (WifiManager) this.context
+ .getSystemService(Context.WIFI_SERVICE);
+ WifiConfiguration wc = new WifiConfiguration();
+ wc.SSID = "\"" + SSID + "\""; // IMP! This should be in Quotes!!
+ wc.hiddenSSID = true;
+ wc.status = WifiConfiguration.Status.DISABLED;
+ wc.priority = 40;
+ wc.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ wc.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
+ wc.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
+ wc.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
+ wc.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
+ wc.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+ wc.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
+ wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
+ wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
+
+ wc.wepKeys[0] = "\"" + PASSWORD + "\""; // This is the WEP Password
+ wc.wepTxKeyIndex = 0;
+
+ WifiManager wifiManag = (WifiManager) this.context
+ .getSystemService(this.context.WIFI_SERVICE);
+ boolean res1 = wifiManag.setWifiEnabled(true);
+ int res = wifi.addNetwork(wc);
+ Log.d("WifiPreference", "add Network returned " + res);
+ boolean saved = wifi.saveConfiguration();
+ Log.d("WifiPreference", "saveConfiguration returned " + saved);
+ boolean b = wifi.enableNetwork(res, true);
+ Log.d("WifiPreference", "enableNetwork returned " + b);
+ return saved;
+ }
+
+ public boolean removeWiFiConfigurationBySSID(String ssid){
+ WifiManager wifi = (WifiManager) this.context.getSystemService(Context.WIFI_SERVICE);
+ List item = wifi.getConfiguredNetworks();
+
+ for(int i=0; i item = wifi.getConfiguredNetworks();
+ int i = item.size();
+ Log.d("WifiPreference", "NO OF CONFIG " + i);
+ Iterator iter = item.iterator();
+ WifiConfiguration config = item.get(0);
+ Log.d("WifiPreference", "SSID" + config.SSID);
+ Log.d("WifiPreference", "PASSWORD" + config.preSharedKey);
+ Log.d("WifiPreference", "ALLOWED ALGORITHMS");
+ Log.d("WifiPreference",
+ "LEAP" + config.allowedAuthAlgorithms.get(AuthAlgorithm.LEAP));
+ Log.d("WifiPreference",
+ "OPEN" + config.allowedAuthAlgorithms.get(AuthAlgorithm.OPEN));
+ Log.d("WifiPreference",
+ "SHARED"
+ + config.allowedAuthAlgorithms
+ .get(AuthAlgorithm.SHARED));
+ Log.d("WifiPreference", "GROUP CIPHERS");
+ Log.d("WifiPreference",
+ "CCMP" + config.allowedGroupCiphers.get(GroupCipher.CCMP));
+ Log.d("WifiPreference",
+ "TKIP" + config.allowedGroupCiphers.get(GroupCipher.TKIP));
+ Log.d("WifiPreference",
+ "WEP104" + config.allowedGroupCiphers.get(GroupCipher.WEP104));
+ Log.d("WifiPreference",
+ "WEP40" + config.allowedGroupCiphers.get(GroupCipher.WEP40));
+ Log.d("WifiPreference", "KEYMGMT");
+ Log.d("WifiPreference",
+ "IEEE8021X"
+ + config.allowedKeyManagement.get(KeyMgmt.IEEE8021X));
+ Log.d("WifiPreference",
+ "NONE" + config.allowedKeyManagement.get(KeyMgmt.NONE));
+ Log.d("WifiPreference",
+ "WPA_EAP" + config.allowedKeyManagement.get(KeyMgmt.WPA_EAP));
+ Log.d("WifiPreference",
+ "WPA_PSK" + config.allowedKeyManagement.get(KeyMgmt.WPA_PSK));
+ Log.d("WifiPreference", "PairWiseCipher");
+ Log.d("WifiPreference",
+ "CCMP" + config.allowedPairwiseCiphers.get(PairwiseCipher.CCMP));
+ Log.d("WifiPreference",
+ "NONE" + config.allowedPairwiseCiphers.get(PairwiseCipher.NONE));
+ Log.d("WifiPreference",
+ "TKIP" + config.allowedPairwiseCiphers.get(PairwiseCipher.TKIP));
+ Log.d("WifiPreference", "Protocols");
+ Log.d("WifiPreference",
+ "RSN" + config.allowedProtocols.get(Protocol.RSN));
+ Log.d("WifiPreference",
+ "WPA" + config.allowedProtocols.get(Protocol.WPA));
+ Log.d("WifiPreference", "WEP Key Strings");
+ String[] wepKeys = config.wepKeys;
+ Log.d("WifiPreference", "WEP KEY 0" + wepKeys[0]);
+ Log.d("WifiPreference", "WEP KEY 1" + wepKeys[1]);
+ Log.d("WifiPreference", "WEP KEY 2" + wepKeys[2]);
+ Log.d("WifiPreference", "WEP KEY 3" + wepKeys[3]);
+ if(config.SSID.equals(ssid)){
+ return true;
+ }else{
+ return false;
+ }
+ }
+
+ /**
+ * Saves a EAP WIFI Configuration Profile
+ * @params userName, passString, eapMethod, phase2AuthMethod
+ * - WiFi User Name, Password, EAP Method and Phase2 Authentication(Optional) Method should be passed in.
+ */
+ public void saveEAPConfig(String userName, String passString, String eapMethod, String phase2AuthMethod) {
+ /******************************** Configuration Strings ****************************************************/
+ String ENTERPRISE_EAP = "TTLS";//Should be one of - TLS/TTLS/PEAP/PWD/SIM/AKA/FAST/LEAP
+ final String ENTERPRISE_CLIENT_CERT = "keystore://USRCERT_CertificateName";
+ final String ENTERPRISE_PRIV_KEY = "keystore://USRPKEY_CertificateName";
+ // CertificateName = Name given to the certificate while installing it
+
+ /* Optional Params- My wireless Doesn't use these */
+ String ENTERPRISE_PHASE2 = "PAP";//Should be one of - PAP/MSCHAP/MSCHAPV2/GTC
+ final String ENTERPRISE_ANON_IDENT = "ABC";
+ final String ENTERPRISE_CA_CERT = "";
+ /******************************** Configuration Strings ****************************************************/
+
+ if(eapMethod!= null && !eapMethod.equals("")){
+ ENTERPRISE_EAP = eapMethod;
+ }
+
+ if(phase2AuthMethod!= null && !phase2AuthMethod.equals("")){
+ ENTERPRISE_PHASE2 = phase2AuthMethod;
+ }
+ /* Create a WifiConfig */
+ WifiConfiguration selectedConfig = new WifiConfiguration();
+
+ /* AP Name */
+ selectedConfig.SSID = "\"SSID_Name\"";
+
+ /* Priority */
+ selectedConfig.priority = 40;
+
+ /* Enable Hidden SSID */
+ selectedConfig.hiddenSSID = true;
+
+ /* Key Mgmnt */
+ selectedConfig.allowedKeyManagement.clear();
+ selectedConfig.allowedKeyManagement
+ .set(WifiConfiguration.KeyMgmt.IEEE8021X);
+ selectedConfig.allowedKeyManagement
+ .set(WifiConfiguration.KeyMgmt.WPA_EAP);
+
+ /* Group Ciphers */
+ selectedConfig.allowedGroupCiphers.clear();
+ selectedConfig.allowedGroupCiphers
+ .set(WifiConfiguration.GroupCipher.CCMP);
+ selectedConfig.allowedGroupCiphers
+ .set(WifiConfiguration.GroupCipher.TKIP);
+ selectedConfig.allowedGroupCiphers
+ .set(WifiConfiguration.GroupCipher.WEP104);
+ selectedConfig.allowedGroupCiphers
+ .set(WifiConfiguration.GroupCipher.WEP40);
+
+ /* Pairwise ciphers */
+ selectedConfig.allowedPairwiseCiphers.clear();
+ selectedConfig.allowedPairwiseCiphers
+ .set(WifiConfiguration.PairwiseCipher.CCMP);
+ selectedConfig.allowedPairwiseCiphers
+ .set(WifiConfiguration.PairwiseCipher.TKIP);
+
+ /* Protocols */
+ selectedConfig.allowedProtocols.clear();
+ selectedConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
+ selectedConfig.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
+
+ // Enterprise Settings
+ // Reflection magic here too, need access to non-public APIs
+ try {
+ // Let the magic start
+ Class[] wcClasses = WifiConfiguration.class.getClasses();
+ // null for overzealous java compiler
+ Class wcEnterpriseField = null;
+
+ for (Class wcClass : wcClasses)
+ if (wcClass.getName().equals(INT_ENTERPRISEFIELD_NAME)) {
+ wcEnterpriseField = wcClass;
+ break;
+ }
+ boolean noEnterpriseFieldType = false;
+ if (wcEnterpriseField == null)
+ noEnterpriseFieldType = true; // Cupcake/Donut access enterprise
+ // settings directly
+
+ Field wcefAnonymousId = null, wcefCaCert = null, wcefClientCert = null, wcefEap = null, wcefIdentity = null, wcefPassword = null, wcefPhase2 = null, wcefPrivateKey = null;
+ Field[] wcefFields = WifiConfiguration.class.getFields();
+ // Dispatching Field vars
+ for (Field wcefField : wcefFields) {
+ if (wcefField.getName().equals(INT_ANONYMOUS_IDENTITY))
+ wcefAnonymousId = wcefField;
+ else if (wcefField.getName().equals(INT_CA_CERT))
+ wcefCaCert = wcefField;
+ else if (wcefField.getName().equals(INT_CLIENT_CERT))
+ wcefClientCert = wcefField;
+ else if (wcefField.getName().equals(INT_EAP))
+ wcefEap = wcefField;
+ else if (wcefField.getName().equals(INT_IDENTITY))
+ wcefIdentity = wcefField;
+ else if (wcefField.getName().equals(INT_PASSWORD))
+ wcefPassword = wcefField;
+ else if (wcefField.getName().equals(INT_PHASE2))
+ wcefPhase2 = wcefField;
+ else if (wcefField.getName().equals(INT_PRIVATE_KEY))
+ wcefPrivateKey = wcefField;
+ }
+
+ Method wcefSetValue = null;
+ if (!noEnterpriseFieldType) {
+ for (Method m : wcEnterpriseField.getMethods())
+ // System.out.println(m.getName());
+ if (m.getName().trim().equals("setValue"))
+ wcefSetValue = m;
+ }
+
+ /* EAP Method */
+ if (!noEnterpriseFieldType) {
+ wcefSetValue
+ .invoke(wcefEap.get(selectedConfig), ENTERPRISE_EAP);
+ } else {
+ wcefEap.set(selectedConfig, ENTERPRISE_EAP);
+ }
+ /* EAP Phase 2 Authentication */
+ if (!noEnterpriseFieldType) {
+ wcefSetValue.invoke(wcefPhase2.get(selectedConfig),
+ ENTERPRISE_PHASE2);
+ } else {
+ wcefPhase2.set(selectedConfig, ENTERPRISE_PHASE2);
+ }
+ /* EAP Anonymous Identity */
+ if (!noEnterpriseFieldType) {
+ wcefSetValue.invoke(wcefAnonymousId.get(selectedConfig),
+ ENTERPRISE_ANON_IDENT);
+ } else {
+ wcefAnonymousId.set(selectedConfig, ENTERPRISE_ANON_IDENT);
+ }
+ /* EAP CA Certificate */
+ if (!noEnterpriseFieldType) {
+ wcefSetValue.invoke(wcefCaCert.get(selectedConfig),
+ ENTERPRISE_CA_CERT);
+ } else {
+ wcefCaCert.set(selectedConfig, ENTERPRISE_CA_CERT);
+ }
+ /* EAP Private key */
+ if (!noEnterpriseFieldType) {
+ wcefSetValue.invoke(wcefPrivateKey.get(selectedConfig),
+ ENTERPRISE_PRIV_KEY);
+ } else {
+ wcefPrivateKey.set(selectedConfig, ENTERPRISE_PRIV_KEY);
+ }
+ /* EAP Identity */
+ if (!noEnterpriseFieldType) {
+ wcefSetValue.invoke(wcefIdentity.get(selectedConfig), userName);
+ } else {
+ wcefIdentity.set(selectedConfig, userName);
+ }
+ /* EAP Password */
+ if (!noEnterpriseFieldType) {
+ wcefSetValue.invoke(wcefPassword.get(selectedConfig),
+ passString);
+ } else {
+ wcefPassword.set(selectedConfig, passString);
+ }
+ /* EAp Client certificate */
+ if (!noEnterpriseFieldType) {
+ wcefSetValue.invoke(wcefClientCert.get(selectedConfig),
+ ENTERPRISE_CLIENT_CERT);
+ } else {
+ wcefClientCert.set(selectedConfig, ENTERPRISE_CLIENT_CERT);
+ }
+ // Adhoc for CM6
+ // if non-CM6 fails gracefully thanks to nested try-catch
+
+ try {
+ Field wcAdhoc = WifiConfiguration.class.getField("adhocSSID");
+ Field wcAdhocFreq = WifiConfiguration.class
+ .getField("frequency");
+ // wcAdhoc.setBoolean(selectedConfig,
+ // prefs.getBoolean(PREF_ADHOC,
+ // false));
+ wcAdhoc.setBoolean(selectedConfig, false);
+ int freq = 2462; // default to channel 11
+ // int freq =
+ // Integer.parseInt(prefs.getString(PREF_ADHOC_FREQUENCY,
+ // "2462")); // default to channel 11
+ // System.err.println(freq);
+ wcAdhocFreq.setInt(selectedConfig, freq);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ // FIXME As above, what should I do here?
+ e.printStackTrace();
+ }
+
+ WifiManager wifiManag = (WifiManager) this.context
+ .getSystemService(Context.WIFI_SERVICE);
+ boolean res1 = wifiManag.setWifiEnabled(true);
+ int res = wifiManag.addNetwork(selectedConfig);
+ Log.d("WifiPreference", "add Network returned " + res);
+ boolean b = wifiManag.enableNetwork(selectedConfig.networkId, false);
+ Log.d("WifiPreference", "enableNetwork returned " + b);
+ boolean c = wifiManag.saveConfiguration();
+ Log.d("WifiPreference", "Save configuration returned " + c);
+ boolean d = wifiManag.enableNetwork(res, true);
+ Log.d("WifiPreference", "enableNetwork returned " + d);
+ }
+
+ /**
+ * Create a Log File to Log WiFi Profile info
+ */
+ public void createLogFile() {
+ BufferedWriter out = null;
+ try {
+ File root = Environment.getExternalStorageDirectory();
+ Log.e("SD CARD WRITE ERROR : ", "SD CARD mounted and writable? "
+ + root.canWrite());
+ if (root.canWrite()) {
+ File gpxfile = new File(root, "ReadConfigLog.txt");
+ FileWriter gpxwriter = new FileWriter(gpxfile);
+ out = new BufferedWriter(gpxwriter);
+ out.write("Hello world");
+ // out.close();
+ }
+ } catch (IOException e) {
+ Log.e("SD CARD READ ERROR : ", "Problem reading SD CARD");
+ Log.e("SD CARD LOG ERROR : ", "Please take logs using Logcat");
+ Log.e("<<<<<<<<<>>>>>>>>>>>",
+ "Could not write file " + e.getMessage());
+ }
+ }
+
+ /**
+ * Read EAP Configuration Profile
+ */
+ public void readEAPConfig(BufferedWriter out) {
+ createLogFile();
+ /* Get the WifiService */
+ WifiManager wifi = (WifiManager) this.context
+ .getSystemService(context.WIFI_SERVICE);
+ /* Get All WIfi configurations */
+ List configList = wifi.getConfiguredNetworks();
+ /*
+ * Now we need to search appropriate configuration i.e. with name
+ * SSID_Name
+ */
+ for (int i = 0; i < configList.size(); i++) {
+ if (configList.get(i).SSID.contentEquals("\"SSID_NAME\"")) {
+ /* We found the appropriate config now read all config details */
+ Iterator iter = configList.iterator();
+ WifiConfiguration config = configList.get(i);
+
+ /*
+ * I dont think these fields have anything to do with EAP config
+ * but still will print these to be on safe side
+ */
+ try {
+ Log.d("<<<<<<<<<>>>>>>>>>>>", "[SSID]"
+ + config.SSID);
+ out.write("<<<<<<<<<>>>>>>>>>>>" + "[SSID]"
+ + config.SSID);
+ Log.d("<<<<<<<<<>>>>>>>>>>>", "[BSSID]"
+ + config.BSSID);
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[BSSID]" + config.BSSID);
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[HIDDEN SSID]" + config.hiddenSSID);
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[HIDDEN SSID]" + config.hiddenSSID);
+ Log.d("<<<<<<<<<>>>>>>>>>>>", "[PASSWORD]"
+ + config.preSharedKey);
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[PASSWORD]" + config.preSharedKey);
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[ALLOWED ALGORITHMS]");
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[ALLOWED ALGORITHMS]");
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[LEAP]"
+ + config.allowedAuthAlgorithms
+ .get(AuthAlgorithm.LEAP));
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[LEAP]"
+ + config.allowedAuthAlgorithms
+ .get(AuthAlgorithm.LEAP));
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[OPEN]"
+ + config.allowedAuthAlgorithms
+ .get(AuthAlgorithm.OPEN));
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[OPEN]"
+ + config.allowedAuthAlgorithms
+ .get(AuthAlgorithm.OPEN));
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[SHARED]"
+ + config.allowedAuthAlgorithms
+ .get(AuthAlgorithm.SHARED));
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[SHARED]"
+ + config.allowedAuthAlgorithms
+ .get(AuthAlgorithm.SHARED));
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[GROUP CIPHERS]");
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[GROUP CIPHERS]");
+ Log.d("<<<<<<<<<>>>>>>>>>>>", "[CCMP]"
+ + config.allowedGroupCiphers.get(GroupCipher.CCMP));
+ out.write("<<<<<<<<<>>>>>>>>>>>" + "[CCMP]"
+ + config.allowedGroupCiphers.get(GroupCipher.CCMP));
+ Log.d("<<<<<<<<<>>>>>>>>>>>", "[TKIP]"
+ + config.allowedGroupCiphers.get(GroupCipher.TKIP));
+ out.write("<<<<<<<<<>>>>>>>>>>>" + "[TKIP]"
+ + config.allowedGroupCiphers.get(GroupCipher.TKIP));
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[WEP104]"
+ + config.allowedGroupCiphers
+ .get(GroupCipher.WEP104));
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[WEP104]"
+ + config.allowedGroupCiphers
+ .get(GroupCipher.WEP104));
+ Log.d("<<<<<<<<<>>>>>>>>>>>", "[WEP40]"
+ + config.allowedGroupCiphers.get(GroupCipher.WEP40));
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[WEP40]"
+ + config.allowedGroupCiphers.get(GroupCipher.WEP40));
+ Log.d("<<<<<<<<<>>>>>>>>>>>", "[KEYMGMT]");
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[KEYMGMT]");
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[IEEE8021X]"
+ + config.allowedKeyManagement
+ .get(KeyMgmt.IEEE8021X));
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[IEEE8021X]"
+ + config.allowedKeyManagement
+ .get(KeyMgmt.IEEE8021X));
+ Log.d("<<<<<<<<<>>>>>>>>>>>", "[NONE]"
+ + config.allowedKeyManagement.get(KeyMgmt.NONE));
+ out.write("<<<<<<<<<>>>>>>>>>>>" + "[NONE]"
+ + config.allowedKeyManagement.get(KeyMgmt.NONE));
+ Log.d("<<<<<<<<<>>>>>>>>>>>", "[WPA_EAP]"
+ + config.allowedKeyManagement.get(KeyMgmt.WPA_EAP));
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[WPA_EAP]"
+ + config.allowedKeyManagement.get(KeyMgmt.WPA_EAP));
+ Log.d("<<<<<<<<<>>>>>>>>>>>", "[WPA_PSK]"
+ + config.allowedKeyManagement.get(KeyMgmt.WPA_PSK));
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[WPA_PSK]"
+ + config.allowedKeyManagement.get(KeyMgmt.WPA_PSK));
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[PairWiseCipher]");
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[PairWiseCipher]");
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[CCMP]"
+ + config.allowedPairwiseCiphers
+ .get(PairwiseCipher.CCMP));
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[CCMP]"
+ + config.allowedPairwiseCiphers
+ .get(PairwiseCipher.CCMP));
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[NONE]"
+ + config.allowedPairwiseCiphers
+ .get(PairwiseCipher.NONE));
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[NONE]"
+ + config.allowedPairwiseCiphers
+ .get(PairwiseCipher.NONE));
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[TKIP]"
+ + config.allowedPairwiseCiphers
+ .get(PairwiseCipher.TKIP));
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[TKIP]"
+ + config.allowedPairwiseCiphers
+ .get(PairwiseCipher.TKIP));
+ Log.d("<<<<<<<<<>>>>>>>>>>>", "[Protocols]");
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[Protocols]");
+ Log.d("<<<<<<<<<>>>>>>>>>>>", "[RSN]"
+ + config.allowedProtocols.get(Protocol.RSN));
+ out.write("<<<<<<<<<>>>>>>>>>>>" + "[RSN]"
+ + config.allowedProtocols.get(Protocol.RSN));
+ Log.d("<<<<<<<<<>>>>>>>>>>>", "[WPA]"
+ + config.allowedProtocols.get(Protocol.WPA));
+ out.write("<<<<<<<<<>>>>>>>>>>>" + "[WPA]"
+ + config.allowedProtocols.get(Protocol.WPA));
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[PRE_SHARED_KEY]" + config.preSharedKey);
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[PRE_SHARED_KEY]" + config.preSharedKey);
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[WEP Key Strings]");
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[WEP Key Strings]");
+ String[] wepKeys = config.wepKeys;
+ Log.d("<<<<<<<<<>>>>>>>>>>>", "[WEP KEY 0]"
+ + wepKeys[0]);
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[WEP KEY 0]" + wepKeys[0]);
+ Log.d("<<<<<<<<<>>>>>>>>>>>", "[WEP KEY 1]"
+ + wepKeys[1]);
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[WEP KEY 1]" + wepKeys[1]);
+ Log.d("<<<<<<<<<>>>>>>>>>>>", "[WEP KEY 2]"
+ + wepKeys[2]);
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[WEP KEY 2]" + wepKeys[2]);
+ Log.d("<<<<<<<<<>>>>>>>>>>>", "[WEP KEY 3]"
+ + wepKeys[3]);
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[WEP KEY 3]" + wepKeys[3]);
+
+ } catch (IOException e) {
+ Log.e("WRITE ERROR : ",
+ "Failed to write Logs to ReadConfigLog.txt");
+ Log.e("WRITE ERROR : ", "Please take logs using Logcat");
+ Log.e("<<<<<<<<<>>>>>>>>>>>",
+ "Could not write to ReadConfigLog.txt"
+ + e.getMessage());
+ }
+ /* reflection magic */
+ /* These are the fields we are really interested in */
+ try {
+ // Let the magic start
+ Class[] wcClasses = WifiConfiguration.class.getClasses();
+ // null for overzealous java compiler
+ Class wcEnterpriseField = null;
+
+ for (Class wcClass : wcClasses)
+ if (wcClass.getName().equals(INT_ENTERPRISEFIELD_NAME)) {
+ wcEnterpriseField = wcClass;
+ break;
+ }
+ boolean noEnterpriseFieldType = false;
+ if (wcEnterpriseField == null)
+ noEnterpriseFieldType = true; // Cupcake/Donut access
+ // enterprise settings
+ // directly
+
+ Field wcefAnonymousId = null, wcefCaCert = null, wcefClientCert = null, wcefEap = null, wcefIdentity = null, wcefPassword = null, wcefPhase2 = null, wcefPrivateKey = null;
+ Field[] wcefFields = WifiConfiguration.class.getFields();
+ // Dispatching Field vars
+ for (Field wcefField : wcefFields) {
+ if (wcefField.getName().trim()
+ .equals(INT_ANONYMOUS_IDENTITY))
+ wcefAnonymousId = wcefField;
+ else if (wcefField.getName().trim().equals(INT_CA_CERT))
+ wcefCaCert = wcefField;
+ else if (wcefField.getName().trim()
+ .equals(INT_CLIENT_CERT))
+ wcefClientCert = wcefField;
+ else if (wcefField.getName().trim().equals(INT_EAP))
+ wcefEap = wcefField;
+ else if (wcefField.getName().trim()
+ .equals(INT_IDENTITY))
+ wcefIdentity = wcefField;
+ else if (wcefField.getName().trim()
+ .equals(INT_PASSWORD))
+ wcefPassword = wcefField;
+ else if (wcefField.getName().trim().equals(INT_PHASE2))
+ wcefPhase2 = wcefField;
+ else if (wcefField.getName().trim()
+ .equals(INT_PRIVATE_KEY))
+ wcefPrivateKey = wcefField;
+ }
+ Method wcefValue = null;
+ if (!noEnterpriseFieldType) {
+ for (Method m : wcEnterpriseField.getMethods())
+ // System.out.println(m.getName());
+ if (m.getName().trim().equals("value")) {
+ wcefValue = m;
+ break;
+ }
+ }
+
+ /* EAP Method */
+ String result = null;
+ Object obj = null;
+ if (!noEnterpriseFieldType) {
+ obj = wcefValue.invoke(wcefEap.get(config), null);
+ String retval = (String) obj;
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[EAP METHOD]" + retval);
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[EAP METHOD]" + retval);
+ } else {
+ obj = wcefEap.get(config);
+ String retval = (String) obj;
+ }
+
+ /* phase 2 */
+ if (!noEnterpriseFieldType) {
+ result = (String) wcefValue.invoke(
+ wcefPhase2.get(config), null);
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[EAP PHASE 2 AUTHENTICATION]" + result);
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[EAP PHASE 2 AUTHENTICATION]" + result);
+ } else {
+ result = (String) wcefPhase2.get(config);
+ }
+
+ /* Anonymous Identity */
+ if (!noEnterpriseFieldType) {
+ result = (String) wcefValue.invoke(
+ wcefAnonymousId.get(config), null);
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[EAP ANONYMOUS IDENTITY]" + result);
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[EAP ANONYMOUS IDENTITY]" + result);
+ } else {
+ result = (String) wcefAnonymousId.get(config);
+ }
+
+ /* CA certificate */
+ if (!noEnterpriseFieldType) {
+ result = (String) wcefValue.invoke(
+ wcefCaCert.get(config), null);
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[EAP CA CERTIFICATE]" + result);
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[EAP CA CERTIFICATE]" + result);
+ } else {
+ result = (String) wcefCaCert.get(config);
+
+ }
+
+ /* private key */
+ if (!noEnterpriseFieldType) {
+ result = (String) wcefValue.invoke(
+ wcefPrivateKey.get(config), null);
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[EAP PRIVATE KEY]" + result);
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[EAP PRIVATE KEY]" + result);
+ } else {
+ result = (String) wcefPrivateKey.get(config);
+ }
+
+ /* Identity */
+ if (!noEnterpriseFieldType) {
+ result = (String) wcefValue.invoke(
+ wcefIdentity.get(config), null);
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[EAP IDENTITY]" + result);
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[EAP IDENTITY]" + result);
+ } else {
+ result = (String) wcefIdentity.get(config);
+ }
+
+ /* Password */
+ if (!noEnterpriseFieldType) {
+ result = (String) wcefValue.invoke(
+ wcefPassword.get(config), null);
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[EAP PASSWORD]" + result);
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[EAP PASSWORD]" + result);
+ } else {
+ result = (String) wcefPassword.get(config);
+ }
+
+ /* client certificate */
+ if (!noEnterpriseFieldType) {
+ result = (String) wcefValue.invoke(
+ wcefClientCert.get(config), null);
+ Log.d("<<<<<<<<<>>>>>>>>>>>",
+ "[EAP CLIENT CERT]" + result);
+ out.write("<<<<<<<<<>>>>>>>>>>>"
+ + "[EAP CLIENT CERT]" + result);
+ Log.e("READ EROR : ",
+ "All config data logged to ReadConfigLog.txt");
+ Log.e("READ EROR : ",
+ "Extract ReadConfigLog.txt from SD CARD");
+ } else {
+ result = (String) wcefClientCert.get(config);
+ }
+
+ out.close();
+
+ } catch (IOException e) {
+ Log.e("LOGGING EROR : ",
+ "Failed to write Logs to ReadConfigLog.txt");
+ Log.e("LOGGING EROR : ", "Please take logs using Logcat");
+ Log.e("<<<<<<<<<>>>>>>>>>>>",
+ "Could not write to ReadConfigLog.txt"
+ + e.getMessage());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+ }
+ }
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/models/PInfo.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/models/PInfo.java
new file mode 100644
index 0000000000..4d209ae4be
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/models/PInfo.java
@@ -0,0 +1,30 @@
+/*
+ ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+*/
+package org.wso2.cdm.agent.models;
+
+import android.util.Log;
+
+public class PInfo {
+ public String appname = "";
+ public String pname = "";
+ public String versionName = "";
+ public int versionCode = 0;
+ public String icon;
+ public void prettyPrint() {
+ Log.v("APPLICATION INFO : ", appname + "\t" + pname + "\t" + versionName + "\t" + versionCode);
+ }
+}
+
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/parser/PayloadParser.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/parser/PayloadParser.java
new file mode 100644
index 0000000000..1e7d53ac62
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/parser/PayloadParser.java
@@ -0,0 +1,74 @@
+/*
+ ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+*/
+package org.wso2.cdm.agent.parser;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.util.Log;
+
+public class PayloadParser {
+
+ public String generateReply(JSONArray inputArray, String regId) {
+ JSONObject outerJson = new JSONObject();
+ JSONArray outerArr = new JSONArray();
+ try {
+ outerJson.put("regId", regId);
+ outerJson.put("data",outerArr);
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+
+
+ for (int i = 0; i < inputArray.length(); i++) {
+ try {
+ String code=inputArray.getJSONObject(i).getString("code");
+ String messageId=inputArray.getJSONObject(i).getString("messageId");
+ JSONArray data=inputArray.getJSONObject(i).getJSONArray("data");
+
+ JSONObject dataArrContents=new JSONObject();
+
+ dataArrContents.put("code", code);
+ JSONArray innerDataArr = new JSONArray();
+
+ JSONObject innerDataOb=new JSONObject();
+ innerDataOb.put("messageId", messageId);
+ innerDataOb.put("data", data);
+ innerDataArr.put(innerDataOb);
+
+
+ dataArrContents.put("data", innerDataArr);
+
+ outerArr.put(dataArrContents);
+
+
+
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+
+
+ }
+
+ return outerJson.toString();
+
+ }
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/APIAccessCallBack.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/APIAccessCallBack.java
new file mode 100644
index 0000000000..4dced9321e
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/APIAccessCallBack.java
@@ -0,0 +1,12 @@
+package org.wso2.cdm.agent.proxy;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: gayan
+ * Date: 4/28/14
+ * Time: 6:10 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public interface APIAccessCallBack {
+ public void onAPIAccessRecive(String status);
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/APIController.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/APIController.java
new file mode 100644
index 0000000000..12662dfaf6
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/APIController.java
@@ -0,0 +1,108 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent.proxy;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+import org.wso2.cdm.agent.R;
+import org.wso2.cdm.agent.utils.CommonUtilities;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.AsyncTask;
+import android.util.Log;
+
+public class APIController implements TokenCallBack {
+ private static String TAG = "APIController";
+ private static Token token;
+ private APIResultCallBack apiResultCall;
+ private APIUtilities apiUtilitiesCurrent;
+ private static String clientKey, clientSecret;
+
+ public void setClientDetails(String clientKey, String clientSecret) {
+ APIController.clientKey = clientKey;
+ APIController.clientSecret = clientSecret;
+ }
+
+ public void invokeAPI(APIUtilities apiUtilities, APIResultCallBack apiResultCallBack,
+ int requestCode, Context context) {
+ apiResultCall = apiResultCallBack;
+ apiUtilitiesCurrent = apiUtilities;
+ if (IdentityProxy.getInstance().getContext() == null) {
+ IdentityProxy.getInstance().setContext(context);
+ }
+ IdentityProxy.getInstance().setRequestCode(requestCode);
+
+ try {
+ IdentityProxy.getInstance().getToken(IdentityProxy.getInstance().getContext(), this,
+ APIController.clientKey,
+ APIController.clientSecret);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } catch (ExecutionException e) {
+ e.printStackTrace();
+ } catch (TimeoutException e) {
+ e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ private class NetworkCallTask extends AsyncTask> {
+ APIResultCallBack apiResultCallBack;
+
+ public NetworkCallTask(APIResultCallBack apiResultCallBack) {
+ this.apiResultCallBack = apiResultCallBack;
+ }
+
+ @Override
+ protected Map doInBackground(APIUtilities... params) {
+ APIUtilities apiUtilities = params[0];
+
+ Map responseParams = null;
+ try {
+ String accessToken = token.getAccessToken();
+ Map headers = new HashMap();
+ headers.put("Content-Type", "application/json");
+ headers.put("Accept", "*/*");
+ headers.put("User-Agent", "Mozilla/5.0 ( compatible ), Android");
+ headers.put("Authorization", "Bearer " + accessToken);
+ responseParams = ServerApiAccess.postData(apiUtilities, headers);
+ return responseParams;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Map result) {
+ apiResultCallBack.onReceiveAPIResult(result, IdentityProxy.getInstance()
+ .getRequestCode());
+ }
+ }
+
+ @Override
+ public void onReceiveTokenResult(Token token, String status) {
+ APIController.token = token;
+ new NetworkCallTask(apiResultCall).execute(apiUtilitiesCurrent);
+
+ }
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/APIResultCallBack.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/APIResultCallBack.java
new file mode 100644
index 0000000000..fb40efac6c
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/APIResultCallBack.java
@@ -0,0 +1,8 @@
+package org.wso2.cdm.agent.proxy;
+
+import java.util.Map;
+
+public interface APIResultCallBack {
+
+ public void onReceiveAPIResult(Map result, int requestCode);
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/APIUtilities.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/APIUtilities.java
new file mode 100644
index 0000000000..fe518d50a7
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/APIUtilities.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent.proxy;
+
+import java.util.Map;
+
+import org.json.JSONObject;
+
+public class APIUtilities {
+ private String httpMethod;
+ private String endPoint;
+ private JSONObject requestParams;
+ private Map requestParamsMap;
+ public Map getRequestParamsMap() {
+ return requestParamsMap;
+ }
+ public void setRequestParamsMap(Map requestParams) {
+ this.requestParamsMap = requestParams;
+ }
+ public JSONObject getRequestParams() {
+ return requestParams;
+ }
+ public void setRequestParams(JSONObject requestParams) {
+ this.requestParams = requestParams;
+ }
+ public String getHttpMethod() {
+ return httpMethod;
+ }
+ public void setHttpMethod(String httpMethod) {
+ this.httpMethod = httpMethod;
+ }
+ public String getEndPoint() {
+ return endPoint;
+ }
+ public void setEndPoint(String endPoint) {
+ this.endPoint = endPoint;
+ }
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/AccessTokenHandler.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/AccessTokenHandler.java
new file mode 100644
index 0000000000..af46c60b8e
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/AccessTokenHandler.java
@@ -0,0 +1,136 @@
+package org.wso2.cdm.agent.proxy;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.os.AsyncTask;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.http.HttpStatus;
+import org.json.JSONException;
+import org.json.JSONObject;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * After receiving authorization code client application can use this class to obtain access token
+ */
+public class AccessTokenHandler extends Activity {
+ private static final String TAG = "AccessTokenHandler";
+ private String username = null;
+ private String password = null;
+ private String clientID = null;
+ private String clientSecret = null;
+ private String tokenEndPoint = null;
+
+ public AccessTokenHandler(String clientID, String clientSecret, String username, String password, String tokenEndPoint, CallBack callBack) {
+ this.username = username;
+ this.password = password;
+ this.clientID = clientID;
+ this.clientSecret = clientSecret;
+ this.tokenEndPoint = tokenEndPoint;
+ }
+
+ public void obtainAccessToken() {
+ new NetworkCallTask().execute();
+ }
+
+ /**
+ * AsyncTask to contact authorization server and get access token, refresh token as a result
+ */
+ private class NetworkCallTask extends AsyncTask {
+ private String response = null;
+ private String responseCode = null;
+ public NetworkCallTask() {
+
+ }
+
+ @Override
+ protected String doInBackground(Void... arg0) {
+ Map request_params = new HashMap();
+ request_params.put("grant_type", "password");
+ request_params.put("username", username);
+ request_params.put("password", password);
+ APIUtilities apiUtilities = new APIUtilities();
+ apiUtilities.setEndPoint(tokenEndPoint);
+ apiUtilities.setHttpMethod("POST");
+ apiUtilities.setRequestParamsMap(request_params);
+
+ Map headers = new HashMap();
+ String authorizationString = "Basic " + new String(Base64.encodeBase64((clientID + ":" + clientSecret).getBytes()));
+ headers.put("Authorization", authorizationString);
+ headers.put("Content-Type", "application/x-www-form-urlencoded");
+
+ Map response_params = ServerApiAccess.postDataAPI(apiUtilities,headers);
+ response = response_params.get("response");
+ responseCode = response_params.get("status");
+ return response;
+ }
+
+ @SuppressLint("SimpleDateFormat")
+ @Override
+ protected void onPostExecute(String result) {
+ String refreshToken = null;
+ String accessToken = null;
+ int timeToExpireSecond=3000;
+ try {
+ IdentityProxy identityProxy = IdentityProxy.getInstance();
+
+ if (responseCode != null && responseCode.equals("200")) {
+ JSONObject response = new JSONObject(result);
+ Log.d("sdf",response.toString());
+ try{
+ accessToken = response.getString("access_token");
+ refreshToken = response.getString("refresh_token");
+ timeToExpireSecond = Integer.parseInt(response.getString("expires_in"));
+ Token token = new Token();
+ token.setDate();
+ token.setRefreshToken(refreshToken);
+ token.setAccessToken(accessToken);
+ token.setExpired(false);
+
+ SharedPreferences mainPref = IdentityProxy.getInstance().getContext().getSharedPreferences("com.mdm",Context.MODE_PRIVATE);
+ Editor editor = mainPref.edit();
+ editor.putString("access_token", accessToken);
+ editor.putString("refresh_token",refreshToken);
+ editor.putString("username", username);
+ DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
+ Date date = new Date();
+ long expiresIN=date.getTime()+(timeToExpireSecond*1000);
+ Date expireDate=new Date(expiresIN);
+ String strDate = dateFormat.format(expireDate);
+ token.setDate(strDate);
+ editor.putString("date",strDate);
+ editor.commit();
+
+ identityProxy.receiveAccessToken(responseCode, "success", token);
+ } catch (JSONException e) {//admin user
+
+ }
+
+
+
+ } else if (responseCode != null) {
+ if("500".equals(responseCode)){
+ identityProxy.receiveAccessToken(responseCode, result, null);
+ }else{
+ JSONObject mainObject = new JSONObject(result);
+ String error = mainObject.getString("error");
+ String errorDescription = mainObject.getString("error_description");
+ Log.d(TAG, error);
+ Log.d(TAG, errorDescription);
+ identityProxy.receiveAccessToken(responseCode, errorDescription, null);
+ }
+ }
+ } catch (JSONException e) {
+ Log.d(TAG,e.toString());
+ }
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/CallBack.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/CallBack.java
new file mode 100644
index 0000000000..415dd2a8b0
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/CallBack.java
@@ -0,0 +1,26 @@
+package org.wso2.cdm.agent.proxy;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: gayan
+ * Date: 3/25/14
+ * Time: 8:40 AM
+ * To change this template use File | Settings | File Templates.
+ */
+public interface CallBack {
+
+ /**
+ * @param tokens
+ * @param status
+ */
+ void receiveAccessToken(String status, String message, Token token);
+
+ /**
+ * @param status
+ * @param message
+ */
+ void receiveNewAccessToken(String status, String message, Token token);
+
+
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/IdentityProxy.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/IdentityProxy.java
new file mode 100644
index 0000000000..85fb7d8d73
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/IdentityProxy.java
@@ -0,0 +1,148 @@
+package org.wso2.cdm.agent.proxy;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.util.Log;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * client application specific data
+ */
+public class IdentityProxy implements CallBack {
+ public final static boolean initiated = false;
+ private static String TAG = "IdentityProxy";
+ private static Token token = null;
+ private static IdentityProxy identityProxy = new IdentityProxy();
+ private static Context context;
+ public static String clientID;
+ public static String clientSecret;
+ private static String accessTokenURL;
+ private APIAccessCallBack apiAccessCallBack;
+ private TokenCallBack callback;
+ int requestCode = 0;
+
+ public int getRequestCode() {
+ return requestCode;
+ }
+
+ public void setRequestCode(int requestCode) {
+ this.requestCode = requestCode;
+ }
+
+ private IdentityProxy() {
+
+ }
+
+ public String getAccessTokenURL() {
+ return accessTokenURL;
+ }
+
+ public void setAccessTokenURL(String accessTokenURL) {
+ IdentityProxy.accessTokenURL = accessTokenURL;
+ }
+
+ public void receiveAccessToken(String status, String message, Token token) {
+ if (token != null) {
+ Log.d(TAG, token.getAccessToken());
+ Log.d(TAG, token.getRefreshToken());
+ }
+ IdentityProxy.token = token;
+ apiAccessCallBack.onAPIAccessRecive(status);
+ }
+
+ public void receiveNewAccessToken(String status, String message, Token token) {
+ IdentityProxy.token = token;
+ callback.onReceiveTokenResult(token, status);
+ }
+
+ public static synchronized IdentityProxy getInstance() {
+ return identityProxy;
+ }
+
+ //initial point of contact
+ public void init(String clientID, String clientSecret, String username, String password,
+ String tokenEndPoint, APIAccessCallBack apiAccessCallBack, Context contextt) {
+ IdentityProxy.clientID = clientID;
+ IdentityProxy.clientSecret = clientSecret;
+ this.apiAccessCallBack = apiAccessCallBack;
+ context = contextt;
+ SharedPreferences mainPref = context.getSharedPreferences("com.mdm", Context.MODE_PRIVATE);
+ Editor editor = mainPref.edit();
+ editor.putString("client_id", clientID);
+ editor.putString("client_secret", clientSecret);
+ editor.putString("token_endpoint", tokenEndPoint);
+ editor.commit();
+ setAccessTokenURL(tokenEndPoint);
+ AccessTokenHandler accessTokenHandler =
+ new AccessTokenHandler(clientID, clientSecret,
+ username, password,
+ tokenEndPoint, this);
+ accessTokenHandler.obtainAccessToken();
+ }
+
+ public void getToken(Context contextt, TokenCallBack call, String clientID, String clientSecret)
+ throws Exception,
+ InterruptedException,
+ ExecutionException,
+ TimeoutException {
+ context = contextt;
+ callback = call;
+ IdentityProxy.clientID = clientID;
+ IdentityProxy.clientSecret = clientSecret;
+ if (token == null) {
+ getStoredToken();
+ } else {
+ boolean isExpired = Token.isValid(token.getDate());
+ if (!isExpired) {// not expired
+ IdentityProxy.getInstance().receiveNewAccessToken("200", "success", token);
+ } else {
+ getStoredToken();
+ }
+ }
+ }
+
+ private void getStoredToken() throws InterruptedException, ExecutionException, TimeoutException {
+ SharedPreferences mainPref = context.getSharedPreferences("com.mdm", Context.MODE_PRIVATE);
+ String refreshToken = mainPref.getString("refresh_token", "").toString();
+ String accessToken = mainPref.getString("access_token", "").toString();
+ String date = mainPref.getString("date", "").toString();
+ String endPoint = mainPref.getString("token_endpoint", "").toString();
+ setAccessTokenURL(endPoint);
+
+ if (!refreshToken.equals("")) {
+ token = new Token();
+ token.setDate(date);
+ token.setRefreshToken(refreshToken);
+ token.setAccessToken(accessToken);
+ boolean isExpired = Token.isValid(token.getDate());
+ if (!isExpired) {// not expired
+ IdentityProxy.getInstance().receiveNewAccessToken("200", "success", token);
+ } else {
+ refreshToken();
+ }
+ } else {
+ IdentityProxy.getInstance().receiveNewAccessToken("400", "fail", token);
+ }
+ }
+
+ public void refreshToken() throws InterruptedException, ExecutionException, TimeoutException {
+ RefreshTokenHandler refreshTokenHandler = new RefreshTokenHandler(token);
+ refreshTokenHandler.obtainNewAccessToken();
+ }
+
+ public Context getContext() {
+ return context;
+ }
+
+ public void setContext(Context context) {
+ IdentityProxy.context = context;
+ }
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/RefreshTokenHandler.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/RefreshTokenHandler.java
new file mode 100644
index 0000000000..6d96f15c21
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/RefreshTokenHandler.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent.proxy;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.os.AsyncTask;
+import android.util.Log;
+import org.apache.commons.codec.binary.Base64;
+import org.json.JSONException;
+import org.json.JSONObject;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Getting new access token and refresh token after access token expiration
+ */
+public class RefreshTokenHandler {
+ private static final String TAG = "RefreshTokenHandler";
+ private static Token token;
+
+ public RefreshTokenHandler(Token token) {
+ RefreshTokenHandler.token = token;
+ }
+
+ public void obtainNewAccessToken() throws InterruptedException, ExecutionException, TimeoutException {
+ new NetworkCallTask().execute();
+ }
+
+ private class NetworkCallTask extends AsyncTask {
+
+ private String responseCode = null;
+
+ public NetworkCallTask() {
+
+ }
+
+ @Override
+ protected String doInBackground(String... params) {
+ String response = "";
+
+ Map request_params = new HashMap();
+ request_params.put("grant_type", "refresh_token");
+ request_params.put("refresh_token", token.getRefreshToken());
+ request_params.put("scope", "PRODUCTION");
+ APIUtilities apiUtilities = new APIUtilities();
+ apiUtilities.setEndPoint(IdentityProxy.getInstance()
+ .getAccessTokenURL());
+ apiUtilities.setHttpMethod("POST");
+ apiUtilities.setRequestParamsMap(request_params);
+
+ Map headers = new HashMap();
+ Log.e("proxy",IdentityProxy.clientID + ":" + IdentityProxy.clientSecret);
+ String authorizationString = "Basic " + new String(Base64.encodeBase64((IdentityProxy.clientID + ":" + IdentityProxy.clientSecret).getBytes()));
+ headers.put("Authorization", authorizationString);
+ headers.put("Content-Type", "application/x-www-form-urlencoded");
+
+ Map response_params = ServerApiAccess.postDataAPI(apiUtilities,headers);
+
+ response = response_params.get("response");
+ responseCode = response_params.get("status");
+ Log.d(TAG, response);
+ return response;
+ }
+
+ @SuppressLint("SimpleDateFormat")
+ @Override
+ protected void onPostExecute(String result) {
+
+ String refreshToken = null;
+ String accessToken = null;
+ int timeToExpireSecond=3000;
+ IdentityProxy identityProxy = IdentityProxy.getInstance();
+ try {
+ JSONObject response = new JSONObject(result);
+ Log.e("refresh Token Post",result.toString());
+
+ if (responseCode != null && responseCode.equals("200")) {
+ refreshToken = response.getString("refresh_token");
+ accessToken = response.getString("access_token");
+ timeToExpireSecond = Integer.parseInt(response.getString("expires_in"));
+
+ token.setRefreshToken(refreshToken);
+ token.setAccessToken(accessToken);
+
+ SharedPreferences mainPref = IdentityProxy.getInstance().getContext().getSharedPreferences("com.mdm",Context.MODE_PRIVATE);
+ Editor editor = mainPref.edit();
+ editor.putString("refresh_token",refreshToken);
+ editor.putString("access_token",accessToken);
+
+
+ DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
+ Date date = new Date();
+ long expiresIN=date.getTime()+(timeToExpireSecond*1000);
+ Date expireDate=new Date(expiresIN);
+ String strDate = dateFormat.format(expireDate);
+ token.setDate(strDate);
+ editor.putString("date",strDate);
+ editor.commit();
+
+ identityProxy.receiveNewAccessToken(responseCode, "success", token);
+
+ } else if (responseCode != null) {
+ JSONObject mainObject = new JSONObject(result);
+ String error = mainObject.getString("error");
+ String errorDescription = mainObject.getString("error_description");
+ Log.d(TAG, error);
+ Log.d(TAG, errorDescription);
+ identityProxy.receiveNewAccessToken(responseCode, errorDescription, token);
+ }
+ } catch (JSONException e) {
+ identityProxy.receiveNewAccessToken(responseCode, "", token);
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/ServerApiAccess.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/ServerApiAccess.java
new file mode 100644
index 0000000000..675f887f61
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/ServerApiAccess.java
@@ -0,0 +1,330 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent.proxy;
+
+import android.util.Log;
+
+import org.apache.http.*;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HTTP;
+import org.json.JSONObject;
+import org.wso2.cdm.agent.R;
+import org.wso2.cdm.agent.utils.CommonUtilities;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.security.KeyStore;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Handle network communication between SDK and authorization server
+ */
+public class ServerApiAccess {
+ private final static String TAG = "ServerUtilities";
+ private static boolean isSSLEnable = false;
+ private static InputStream inputStream;
+ private static String trustStorePassword;
+
+ /**
+ * Enable SSL communication between client application and authorization server (if you have selfish sign certificate)
+ *
+ * @param in
+ * @param myTrustStorePassword
+ */
+ public static void enableSSL(InputStream in, String myTrustStorePassword) {
+ inputStream = in;
+ isSSLEnable = true;
+ trustStorePassword = myTrustStorePassword;
+ }
+
+ public static String buildPayload(Map params){
+ if(params==null){
+ return null;
+ }
+ StringBuilder bodyBuilder = new StringBuilder();
+ Iterator> iterator = params.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Entry param = iterator.next();
+ bodyBuilder.append(param.getKey()).append('=').append(param.getValue());
+ if (iterator.hasNext()) {
+ bodyBuilder.append('&');
+ }
+ }
+ return bodyBuilder.toString();
+ }
+ public static HttpRequestBase buildHeaders(HttpRequestBase httpRequestBase, Map headers,String httpMethod){
+ Iterator> iterator = headers.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Entry header = iterator.next();
+ httpRequestBase.setHeader(header.getKey(), header.getValue());
+ }
+ return httpRequestBase;
+ }
+ public static Map postData(APIUtilities apiUtilities, Map headers) {
+ String httpMethod = apiUtilities.getHttpMethod();
+ String url = apiUtilities.getEndPoint();
+ JSONObject params = apiUtilities.getRequestParams();
+ Map responseParams = new HashMap();
+ HttpClient httpclient = getCertifiedHttpClient();
+
+
+ if(httpMethod.equals("POST")){
+ HttpPost httpPost = new HttpPost(url);
+ if(params!=null){
+ try {
+ httpPost.setEntity(new StringEntity(params.toString()));
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ }
+ Log.e("url",""+url);
+ HttpPost httpPostWithHeaders = (HttpPost)buildHeaders(httpPost,headers,httpMethod);
+ try {
+ HttpResponse response = httpclient.execute(httpPostWithHeaders);
+ String status = String.valueOf(response.getStatusLine().getStatusCode());
+ Log.d(TAG,status);
+ responseParams.put("response", getResponseBody(response));
+ responseParams.put("status", status);
+ return responseParams;
+ } catch (ClientProtocolException e) {
+ Log.d(TAG, "ClientProtocolException :"+e.toString());
+ return null;
+ } catch (IOException e) {
+ Log.d(TAG, e.toString());
+ responseParams.put("response", "Internal Server Error");
+ responseParams.put("status", "500");
+ return responseParams;
+ }
+ }
+ else if(httpMethod.equals("GET")){
+// if(payload!=null){
+// url = url+"?"+payload;
+// }
+ HttpGet httpGet = new HttpGet(url);
+ HttpGet httpGetWithHeaders = (HttpGet) buildHeaders(httpGet,headers,httpMethod);
+ Log.d(TAG,httpGetWithHeaders.toString());
+ try {
+ HttpResponse response = httpclient.execute(httpGetWithHeaders);
+ responseParams.put("response", getResponseBody(response));
+ responseParams.put("status", String.valueOf(response.getStatusLine().getStatusCode()));
+ return responseParams;
+ } catch (ClientProtocolException e) {
+ Log.d(TAG, "ClientProtocolException :"+e.toString());
+ return null;
+ } catch (IOException e) {
+ Log.d(TAG, e.toString());
+ responseParams.put("response", "Internal Server Error");
+ responseParams.put("status", "500");
+ return responseParams;
+ }
+ }
+ return null;
+ }
+
+ public static Map postDataAPI(APIUtilities apiUtilities, Map headers) {
+ String httpMethod = apiUtilities.getHttpMethod();
+ String url = apiUtilities.getEndPoint();
+ Map params = apiUtilities.getRequestParamsMap();
+
+ Map response_params = new HashMap();
+ HttpClient httpclient = getCertifiedHttpClient();
+ String payload = buildPayload(params);
+
+ if(httpMethod.equals("POST")){
+ HttpPost httpPost = new HttpPost(url);
+ Log.e("url",""+url);
+ HttpPost httpPostWithHeaders = (HttpPost)buildHeaders(httpPost,headers,httpMethod);
+ byte[] postData = payload.getBytes();
+ try {
+ httpPostWithHeaders.setEntity(new ByteArrayEntity(postData));
+ HttpResponse response = httpclient.execute(httpPostWithHeaders);
+ String status = String.valueOf(response.getStatusLine().getStatusCode());
+ Log.d(TAG,status);
+ response_params.put("response", getResponseBody(response));
+ response_params.put("status", status);
+ return response_params;
+ } catch (ClientProtocolException e) {
+ Log.d(TAG, "ClientProtocolException :"+e.toString());
+ return null;
+ } catch (IOException e) {
+ Log.d(TAG, e.toString());
+ response_params.put("response", "Internal Server Error");
+ response_params.put("status", "500");
+ return response_params;
+ }
+ }
+ return null;
+ }
+
+ public static HttpClient getCertifiedHttpClient() {
+ try {
+ HttpClient client = null;
+ if(CommonUtilities.SERVER_PROTOCOL.equalsIgnoreCase("https://")){
+ KeyStore localTrustStore = KeyStore.getInstance("BKS");
+ InputStream in = IdentityProxy.getInstance().getContext()
+ .getResources().openRawResource(R.raw.emm_truststore);
+ localTrustStore.load(in, CommonUtilities.TRUSTSTORE_PASSWORD.toCharArray());
+
+
+ SchemeRegistry schemeRegistry = new SchemeRegistry();
+ schemeRegistry.register(new Scheme("http", PlainSocketFactory
+ .getSocketFactory(), 80));
+ SSLSocketFactory sslSocketFactory = new SSLSocketFactory(localTrustStore);
+ sslSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+ schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));
+ HttpParams params = new BasicHttpParams();
+ ClientConnectionManager cm =
+ new ThreadSafeClientConnManager(params, schemeRegistry);
+
+ client = new DefaultHttpClient(cm, params);
+
+ } else {
+ client = new DefaultHttpClient();
+ }
+ return client;
+ } catch (Exception e) {
+ Log.d(TAG, e.toString());
+ return null;
+ }
+ }
+
+ public static String getResponseBody(HttpResponse response) {
+
+ String response_text = null;
+ HttpEntity entity = null;
+ try {
+ entity = response.getEntity();
+ response_text = getResponseBodyContent(entity);
+ } catch (ParseException e) {
+ Log.d(TAG, e.toString());
+ } catch (IOException e) {
+ if (entity != null) {
+ try {
+ entity.consumeContent();
+ } catch (IOException e1) {
+ Log.d(TAG, e1.toString());
+ }
+ }
+ }
+ return response_text;
+ }
+
+ public static String getResponseBodyContent(final HttpEntity entity) throws IOException, ParseException {
+
+ if (entity == null) {
+ throw new IllegalArgumentException("HTTP entity may not be null");
+ }
+
+ InputStream instream = entity.getContent();
+
+ if (instream == null) {
+ return "";
+ }
+
+ if (entity.getContentLength() > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException(
+
+ "HTTP entity too large to be buffered in memory");
+ }
+
+ String charset = getContentCharSet(entity);
+
+ if (charset == null) {
+
+ charset = HTTP.DEFAULT_CONTENT_CHARSET;
+
+ }
+
+ Reader reader = new InputStreamReader(instream, charset);
+
+ StringBuilder buffer = new StringBuilder();
+
+ try {
+
+ char[] tmp = new char[1024];
+
+ int l;
+
+ while ((l = reader.read(tmp)) != -1) {
+
+ buffer.append(tmp, 0, l);
+
+ }
+
+ } finally {
+
+ reader.close();
+
+ }
+
+ return buffer.toString();
+
+ }
+
+ public static String getContentCharSet(final HttpEntity entity) throws ParseException {
+
+ if (entity == null) {
+ throw new IllegalArgumentException("HTTP entity may not be null");
+ }
+
+ String charset = null;
+
+ if (entity.getContentType() != null) {
+
+ HeaderElement values[] = entity.getContentType().getElements();
+
+ if (values.length > 0) {
+
+ NameValuePair param = values[0].getParameterByName("charset");
+
+ if (param != null) {
+
+ charset = param.getValue();
+
+ }
+
+ }
+
+ }
+
+ return charset;
+
+ }
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/ServerUtilities.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/ServerUtilities.java
new file mode 100644
index 0000000000..adfee5fe8e
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/ServerUtilities.java
@@ -0,0 +1,235 @@
+package org.wso2.cdm.agent.proxy;
+
+import android.content.Context;
+import android.util.Log;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.http.*;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HTTP;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.security.KeyStore;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Handle network communication between SDK and authorization server
+ */
+public class ServerUtilities {
+ private final static String TAG = "ServerUtilities";
+ private static boolean isSSLEnable = false;
+ private static InputStream inputStream;
+ private static String trustStorePassword;
+
+ /**
+ * Enable SSL communication between client application and authorization server (if you have selfish sign certificate)
+ *
+ * @param in
+ * @param myTrustStorePassword
+ */
+ public static void enableSSL(InputStream in, String myTrustStorePassword) {
+ inputStream = in;
+ isSSLEnable = true;
+ trustStorePassword = myTrustStorePassword;
+ }
+
+
+ public static synchronized Map postData(Context context, String url, Map params, String clientID, String clientSecret) {
+ // Create a new HttpClient and Post Header
+ Map response_params = new HashMap();
+ HttpClient httpclient = getCertifiedHttpClient(context);
+ Log.d(TAG, "Posting '" + params.toString() + "' to " + url);
+ StringBuilder bodyBuilder = new StringBuilder();
+ Iterator> iterator = params.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Entry param = iterator.next();
+ bodyBuilder.append(param.getKey()).append('=')
+ .append(param.getValue());
+ if (iterator.hasNext()) {
+ bodyBuilder.append('&');
+ }
+ }
+
+ String body = bodyBuilder.toString();
+ Log.d(TAG, "Posting '" + body + "' to " + url);
+
+ byte[] postData = body.getBytes();
+
+ HttpPost httppost = new HttpPost(url);
+
+ httppost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
+ httppost.setHeader("Accept", "*/*");
+ httppost.setHeader("User-Agent", "Mozilla/5.0 ( compatible ), Android");
+
+ IdentityProxy clientCredentials = IdentityProxy.getInstance();
+
+ String authorizationString = "Basic " + new String(Base64.encodeBase64((clientID + ":" + clientSecret).getBytes())); //this line is diffe
+ httppost.setHeader("Authorization", authorizationString);
+
+ try {
+ httppost.setEntity(new ByteArrayEntity(postData));
+ HttpResponse response = httpclient.execute(httppost);
+ Log.d(TAG, response.getStatusLine().getStatusCode() + "");
+ response_params.put("response", getResponseBody(response));
+ response_params.put("status", String.valueOf(response.getStatusLine().getStatusCode()));
+ Log.d(TAG, response_params.get("response"));
+ return response_params;
+ } catch (ClientProtocolException e) {
+ Log.d(TAG, e.toString());
+ return null;
+ } catch (IOException e) {
+ Log.d(TAG, e.toString());
+ return null;
+ }
+ }
+
+ public static HttpClient getCertifiedHttpClient(Context context) {
+ try {
+ HttpClient client = null;
+ if (isSSLEnable) {
+ KeyStore localTrustStore = KeyStore.getInstance("BKS");
+ // InputStream in = context.getResources().openRawResource(R.raw.emm_truststore);
+ localTrustStore.load(inputStream, trustStorePassword.toCharArray());
+
+ SchemeRegistry schemeRegistry = new SchemeRegistry();
+ schemeRegistry.register(new Scheme("http", PlainSocketFactory
+ .getSocketFactory(), 80));
+ SSLSocketFactory sslSocketFactory = new SSLSocketFactory(localTrustStore);
+ sslSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+ schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));
+ HttpParams params = new BasicHttpParams();
+ ClientConnectionManager cm =
+ new ThreadSafeClientConnManager(params, schemeRegistry);
+
+ client = new DefaultHttpClient(cm, params);
+ } else {
+ client = new DefaultHttpClient();
+ }
+ return client;
+ } catch (Exception e) {
+ Log.d(TAG, e.toString());
+ return null;
+ }
+ }
+
+ public static String getResponseBody(HttpResponse response) {
+
+ String response_text = null;
+ HttpEntity entity = null;
+ try {
+ entity = response.getEntity();
+ response_text = _getResponseBody(entity);
+ } catch (ParseException e) {
+ Log.d(TAG, e.toString());
+ } catch (IOException e) {
+ if (entity != null) {
+ try {
+ entity.consumeContent();
+ } catch (IOException e1) {
+ Log.d(TAG, e1.toString());
+ }
+ }
+ }
+ return response_text;
+ }
+
+ public static String _getResponseBody(final HttpEntity entity) throws IOException, ParseException {
+
+ if (entity == null) {
+ throw new IllegalArgumentException("HTTP entity may not be null");
+ }
+
+ InputStream instream = entity.getContent();
+
+ if (instream == null) {
+ return "";
+ }
+
+ if (entity.getContentLength() > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException(
+
+ "HTTP entity too large to be buffered in memory");
+ }
+
+ String charset = getContentCharSet(entity);
+
+ if (charset == null) {
+
+ charset = HTTP.DEFAULT_CONTENT_CHARSET;
+
+ }
+
+ Reader reader = new InputStreamReader(instream, charset);
+
+ StringBuilder buffer = new StringBuilder();
+
+ try {
+
+ char[] tmp = new char[1024];
+
+ int l;
+
+ while ((l = reader.read(tmp)) != -1) {
+
+ buffer.append(tmp, 0, l);
+
+ }
+
+ } finally {
+
+ reader.close();
+
+ }
+
+ return buffer.toString();
+
+ }
+
+ public static String getContentCharSet(final HttpEntity entity) throws ParseException {
+
+ if (entity == null) {
+ throw new IllegalArgumentException("HTTP entity may not be null");
+ }
+
+ String charset = null;
+
+ if (entity.getContentType() != null) {
+
+ HeaderElement values[] = entity.getContentType().getElements();
+
+ if (values.length > 0) {
+
+ NameValuePair param = values[0].getParameterByName("charset");
+
+ if (param != null) {
+
+ charset = param.getValue();
+
+ }
+
+ }
+
+ }
+
+ return charset;
+
+ }
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/Token.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/Token.java
new file mode 100644
index 0000000000..13e137b1ac
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/Token.java
@@ -0,0 +1,94 @@
+package org.wso2.cdm.agent.proxy;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import android.annotation.SuppressLint;
+import android.util.Log;
+
+/**
+ * Persists refresh token to obtain new access token and id token to retrieve login user claims
+ */
+public final class Token {
+ private String refreshToken = null;
+ private String idToken = null;
+ private String accessToken = null;
+ private Date receivedDate = null;
+ private boolean expired =false;
+
+ public Date getDate() {
+ return receivedDate;
+ }
+
+ public void setDate() {
+ DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
+ Date date = new Date();
+ String strDate = dateFormat.format(date);
+ SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
+ try {
+ receivedDate = format.parse(strDate);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void setDate(String date) {
+ SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
+ try {
+ receivedDate = format.parse(date);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+ public void setAccessToken(String accessToken) {
+ this.accessToken = accessToken;
+ }
+
+ public String getIdToken() {
+ return idToken;
+ }
+
+ public void setIdToken(String id_Token) {
+ idToken = id_Token;
+ }
+
+ public String getRefreshToken() {
+ return refreshToken;
+ }
+
+ public void setRefreshToken(String refreshToken) {
+ this.refreshToken = refreshToken;
+ }
+
+
+ public void setExpired(boolean expired) {
+ this.expired = expired;
+ }
+
+ @SuppressLint("SimpleDateFormat")
+ public static boolean isValid(Date expirationDate) {
+ DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
+ Date currentDate = new Date();
+ String strDate = dateFormat.format(currentDate);
+ SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
+ try {
+ currentDate = format.parse(strDate);
+ } catch (ParseException e1) {
+ e1.printStackTrace();
+ }
+ boolean expired = currentDate.after(expirationDate);
+ boolean equalDates = currentDate.equals(expirationDate);
+ if (expired == true || equalDates == true) {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/TokenCallBack.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/TokenCallBack.java
new file mode 100644
index 0000000000..d7dc3b984b
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/proxy/TokenCallBack.java
@@ -0,0 +1,6 @@
+package org.wso2.cdm.agent.proxy;
+
+
+public interface TokenCallBack {
+ public void onReceiveTokenResult(Token token,String status);
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/security/APIResultCallBackImpl.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/security/APIResultCallBackImpl.java
new file mode 100644
index 0000000000..5fc11ae34e
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/security/APIResultCallBackImpl.java
@@ -0,0 +1,100 @@
+package org.wso2.cdm.agent.security;
+
+import java.util.Map;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+import org.wso2.cdm.agent.proxy.APIResultCallBack;
+import org.wso2.cdm.agent.utils.CommonUtilities;
+
+public class APIResultCallBackImpl implements APIResultCallBack {
+
+ @Override
+ public void onReceiveAPIResult(Map result, int requestCode) {
+ String senderId = "";
+ String mode = "";
+ String interval = "";
+ JSONObject response = null;
+ if (result != null) {
+ String responseStatus = result.get("status");
+ // validate status
+ // {"sender_id" : "853689113861", "notifier" : "LOCAL", "notifierInterval" : 5}
+ try {
+ response = (JSONObject)new JSONParser().parse(result.get("response"));
+ senderId = response.getString("sender_id");
+ mode = response.getString("notifier");
+ interval = response.getString("notifierInterval");
+ } catch (ParseException e) {
+ e.printStackTrace();
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+
+ if (requestCode == CommonUtilities.SENDER_ID_REQUEST_CODE) {/*
+ if (responseStatus.equals(CommonUtilities.REQUEST_SUCCESSFUL)) {
+ if(!senderId.equals("")){
+ CommonUtilities.setSENDER_ID(senderId);
+ }
+ SharedPreferences mainPref = context.getSharedPreferences(
+ getResources().getString(R.string.shared_pref_package), Context.MODE_PRIVATE);
+ Editor editor = mainPref.edit();
+ editor.putString(getResources().getString(R.string.shared_pref_sender_id), senderId);
+ editor.putString(getResources().getString(R.string.shared_pref_message_mode), mode);
+ editor.putString(getResources().getString(R.string.shared_pref_monitor_interval), interval);
+ editor.commit();
+
+ if (progressDialog != null && progressDialog.isShowing()) {
+ progressDialog.dismiss();
+ }
+ getLicense();
+
+ } else {
+ if (progressDialog != null && progressDialog.isShowing()) {
+ progressDialog.dismiss();
+ }
+
+ alertDialog = CommonDialogUtils.getAlertDialogWithOneButton(AuthenticationActivity.this, getResources().getString(R.string.title_init_msg_error), getResources().getString(R.string.button_ok), senderIdFailedClickListener);
+ alertDialog.show();
+
+ }
+
+ }
+
+ if (requestCode == CommonUtilities.LICENSE_REQUEST_CODE) {
+ if (responseStatus.equals(CommonUtilities.REQUEST_SUCCESSFUL)) {
+ showAlert(eula, CommonUtilities.EULA_TITLE);
+ // fetchLicense();
+ // NEED TO ADD PIN CODE IMPLEMENTATION. REGISTRATION HAS TO BE DONE IN PINCODE ACTIVITY
+ Intent intent = new Intent(AuthenticationActivity.this,
+ PinCodeActivity.class);
+ intent.putExtra(
+ getResources().getString(R.string.intent_extra_regid),
+ regId);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ if (txtDomain.getText() != null
+ && txtDomain.getText().toString().trim() != "") {
+ intent.putExtra(
+ getResources().getString(R.string.intent_extra_email),
+ username.getText().toString().trim() + "@"
+ + txtDomain.getText().toString().trim());
+ } else {
+ intent.putExtra(
+ getResources().getString(R.string.intent_extra_email),
+ username.getText().toString().trim());
+ }
+ startActivity(intent);
+
+ } else {
+ // NEED TO IMPLEMENT
+ }
+
+ */}
+
+ }
+
+
+ }
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/AlarmReceiver.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/AlarmReceiver.java
new file mode 100644
index 0000000000..b36452abbc
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/AlarmReceiver.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent.services;
+
+import org.wso2.cdm.agent.R;
+import org.wso2.cdm.agent.utils.CommonUtilities;
+import org.wso2.cdm.agent.utils.Constant;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class AlarmReceiver extends BroadcastReceiver {
+
+ private static final String DEBUG_TAG = "AlarmReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(DEBUG_TAG, "Recurring alarm; requesting download service.");
+ String mode=CommonUtilities.getPref(context, context.getResources().getString(R.string.shared_pref_message_mode));
+ if(mode.trim().toUpperCase().equals(Constant.LOCAL)){
+ MessageProcessor messageProcessor=new MessageProcessor(context);
+ messageProcessor.getMessages();
+ }
+ }
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/Config.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/Config.java
new file mode 100644
index 0000000000..d77bc655e7
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/Config.java
@@ -0,0 +1,22 @@
+/*
+ ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+*/
+package org.wso2.cdm.agent.services;
+
+import org.wso2.cdm.agent.GCMIntentService;
+
+public final class Config {
+ public static GCMIntentService context = null;
+}
\ No newline at end of file
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/DeviceStartupIntentReceiver.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/DeviceStartupIntentReceiver.java
new file mode 100644
index 0000000000..ef750dc683
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/DeviceStartupIntentReceiver.java
@@ -0,0 +1,74 @@
+/*
+ ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+*/
+package org.wso2.cdm.agent.services;
+
+import org.wso2.cdm.agent.R;
+import org.wso2.cdm.agent.utils.CommonUtilities;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.SystemClock;
+
+public class DeviceStartupIntentReceiver extends BroadcastReceiver{
+
+ @Override
+ public void onReceive(final Context context, Intent intent1) {
+ setRecurringAlarm(context);
+ }
+
+ private void setRecurringAlarm(Context context) {
+ String mode=CommonUtilities.getPref(context, context.getResources().getString(R.string.shared_pref_message_mode));
+ SharedPreferences mainPref = context.getSharedPreferences(
+ context.getResources().getString(R.string.shared_pref_package), Context.MODE_PRIVATE);
+ Float interval=(Float)mainPref.getFloat(context.getResources().getString(R.string.shared_pref_interval), 1.0f);
+
+ String clientKey = mainPref.getString(context.getResources().getString(R.string.shared_pref_client_id), "");
+ String clientSecret = mainPref.getString(context.getResources().getString(R.string.shared_pref_client_secret), "");
+ if(!clientKey.equals("") && !clientSecret.equals("")){
+ CommonUtilities.CLIENT_ID=clientKey;
+ CommonUtilities.CLIENT_SECRET=clientSecret;
+ }
+
+ if(mode.trim().toUpperCase().equals("LOCAL")){
+ long firstTime = SystemClock.elapsedRealtime();
+ firstTime += 1 * 1000;
+
+ Intent downloader = new Intent(context, AlarmReceiver.class);
+ PendingIntent recurringDownload = PendingIntent.getBroadcast(context,
+ 0, downloader, PendingIntent.FLAG_CANCEL_CURRENT);
+ AlarmManager alarms = (AlarmManager) context
+ .getSystemService(Context.ALARM_SERVICE);
+ /*
+ * alarms.setInexactRepeating(AlarmManager.RTC_WAKEUP,
+ * updateTime.getTimeInMillis(), AlarmManager.INTERVAL_DAY,
+ * recurringDownload);
+ */
+ Float seconds=interval;
+ if(interval<1.0){
+
+ alarms.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, firstTime,
+ seconds.intValue(), recurringDownload);
+ }else{
+ alarms.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, firstTime,
+ seconds.intValue(), recurringDownload);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/LocalNotification.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/LocalNotification.java
new file mode 100644
index 0000000000..fe79aa677a
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/LocalNotification.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent.services;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.SystemClock;
+
+/**
+ * Local notification is a communication mechanism that essentially,
+ * polls to server based on a predefined to retrieve pending data.
+ */
+public class LocalNotification {
+ public static void startPolling(Context context) {
+ int interval=10000;
+
+ long firstTime = SystemClock.elapsedRealtime();
+ firstTime += 1000;
+
+ Intent downloader = new Intent(context, AlarmReceiver.class);
+ PendingIntent recurringDownload = PendingIntent.getBroadcast(context,
+ 0, downloader, PendingIntent.FLAG_CANCEL_CURRENT);
+ AlarmManager alarms = (AlarmManager) context
+ .getSystemService(Context.ALARM_SERVICE);
+ alarms.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, firstTime,
+ interval, recurringDownload);
+ }
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/MessageProcessor.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/MessageProcessor.java
new file mode 100644
index 0000000000..4b4b68fe64
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/MessageProcessor.java
@@ -0,0 +1,114 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent.services;
+
+import java.io.File;
+import java.util.Map;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.wso2.cdm.agent.RegistrationActivity;
+import org.wso2.cdm.agent.api.DeviceInfo;
+import org.wso2.cdm.agent.proxy.APIResultCallBack;
+import org.wso2.cdm.agent.utils.CommonUtilities;
+import org.wso2.cdm.agent.utils.Constant;
+import org.wso2.cdm.agent.utils.Preference;
+import org.wso2.cdm.agent.utils.ServerUtils;
+
+import android.content.Context;
+import android.util.Log;
+
+/**
+ * Used to coordinate the retrieval and processing of messages from the server.
+ */
+public class MessageProcessor implements APIResultCallBack {
+
+ private String TAG = MessageProcessor.class.getSimpleName();
+ private Context context;
+ String deviceId;
+
+ /**
+ * Local notification message handler.
+ *
+ * @param context
+ * Context of the application.
+ */
+ public MessageProcessor(Context context) {
+ this.context = context;
+
+ deviceId=Preference.get(context, "deviceId");
+ if(deviceId ==null){
+ DeviceInfo deviceInfo = new DeviceInfo(context);
+ deviceId=deviceInfo.getMACAddress();
+ Preference.put(context, "deviceId", deviceId);
+ }
+ }
+
+ /**
+ * @param response
+ * Response received from the server that needs to be processed
+ * and applied to the device
+ */
+ public void performOperation(String response) {
+ try {
+ JSONArray operations = new JSONArray(response);
+ for (int x = 0; x < operations.length(); x++) {
+ String featureCode = operations.getJSONObject(x).getString(Constant.CODE);
+ String properties = operations.getJSONObject(x).getString(Constant.PROPERTIES);
+ Operation operation = new Operation(context);
+ operation.doTask(featureCode, properties, 0);
+ }
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ /**
+ * Call the message retrieval end point of the server to get messages
+ * pending.
+ */
+ public void getMessages() {
+
+ ServerUtils.callSecuredAPI(context, CommonUtilities.SERVER_URL + CommonUtilities.NOTIFICATION_ENDPOINT+File.separator+deviceId,
+ CommonUtilities.POST_METHOD, null, MessageProcessor.this,
+ CommonUtilities.NOTIFICATION_REQUEST_CODE);
+ Log.e("sadfs",CommonUtilities.SERVER_URL + CommonUtilities.NOTIFICATION_ENDPOINT);
+ }
+
+ @Override
+ public void onReceiveAPIResult(Map result, int requestCode) {
+ String responseStatus;
+ String response;
+ if (requestCode == CommonUtilities.NOTIFICATION_REQUEST_CODE) {
+ if (result != null) {
+ responseStatus = result.get(CommonUtilities.STATUS_KEY);
+ if (responseStatus != null &&
+ responseStatus.equals(CommonUtilities.REQUEST_SUCCESSFUL)) {
+ response = result.get(Constant.RESPONSE);
+ if (response != null && !response.equals("")) {
+ if (CommonUtilities.DEBUG_MODE_ENABLED) {
+ Log.e(TAG, "onReceiveAPIResult- " + response);
+ }
+ performOperation(response);
+ }
+ }
+
+ }
+ }
+
+ }
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/Operation.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/Operation.java
new file mode 100644
index 0000000000..f7d7fbc368
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/Operation.java
@@ -0,0 +1,1857 @@
+/*
+ ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+*/
+package org.wso2.cdm.agent.services;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.wso2.cdm.agent.AlertActivity;
+import org.wso2.cdm.agent.ServerDetails;
+import org.wso2.cdm.agent.api.ApplicationManager;
+import org.wso2.cdm.agent.api.DeviceInfo;
+import org.wso2.cdm.agent.api.GPSTracker;
+import org.wso2.cdm.agent.api.LocationServices;
+import org.wso2.cdm.agent.api.PhoneState;
+import org.wso2.cdm.agent.api.TrackCallSMS;
+import org.wso2.cdm.agent.api.WiFiConfig;
+import org.wso2.cdm.agent.models.PInfo;
+import org.wso2.cdm.agent.utils.CommonUtilities;
+import org.wso2.cdm.agent.utils.LoggerCustom;
+import org.wso2.cdm.agent.utils.ServerUtils;
+
+import android.annotation.TargetApi;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.telephony.SmsManager;
+import android.text.format.Time;
+import android.util.Log;
+import android.widget.Toast;
+
+public class Operation {
+
+ Context context = null;
+ DevicePolicyManager devicePolicyManager;
+ ApplicationManager appList;
+ DeviceInfo deviceInfo;
+ TrackCallSMS conversations;
+ PhoneState deviceState;
+ String code = null;
+ String policy_code = "";
+ String token = "";
+ String policy_token = "";
+ int policy_count = 0;
+ String data = "";
+ GPSTracker gps;
+ String recepient = "";
+ int mode = 1;
+ LoggerCustom logger = null;
+ private static final String TAG = "Operation Handler";
+ static final int ACTIVATION_REQUEST = 47;
+ static final int REQUEST_CODE_START_ENCRYPTION = 1;
+ AsyncTask mRegisterTask;
+ static final int REQUEST_MODE_BUNDLE = 0;
+ static final int REQUEST_MODE_NORMAL = 1;
+ public static boolean enterpriseWipe=false;
+ Intent intent;
+ Map params = new HashMap();
+ Map bundle_params = new HashMap();
+ SmsManager smsManager;
+
+ public Operation(Context context) {
+ this.context = context;
+ }
+
+ public Operation(Context context, int mode, Intent intent) {
+ this.context = context;
+ this.intent = intent;
+ this.mode = mode;
+
+ if(CommonUtilities.DEBUG_MODE_ENABLED){
+ logger = new LoggerCustom(context);
+ Time now = new Time();
+ now.setToNow();
+ String log_in = logger.readFileAsString("wso2log.txt");
+ String to_write="";
+
+ if(log_in!=null && !log_in.equals("") && !log_in.equals("null")){
+ to_write=" SERVER TO AGENT AT "+now.hour+":"+now.minute+" : CODE : "+intent.getStringExtra("message").trim()+" MSG ID : "+intent.getStringExtra("token").trim()+" DATA : "+intent.getStringExtra("data")+" ========================================================== "+log_in;
+ }else{
+ to_write=" SERVER TO AGENT AT "+now.hour+":"+now.minute+": CODE : "+intent.getStringExtra("message").trim()+" MSG ID : "+intent.getStringExtra("token").trim()+" DATA : "+intent.getStringExtra("data")+" ========================================================== ";
+ }
+ logger.writeStringAsFile(to_write, "wso2log.txt");
+ }
+
+ if(intent.getStringExtra("message").trim().equals(CommonUtilities.OPERATION_POLICY_MONITOR)){
+ policy_token = intent.getStringExtra("token").trim();
+ policy_code = intent.getStringExtra("message").trim();
+ Log.v("Token", policy_token);
+ }else{
+ token = intent.getStringExtra("token").trim();
+ code = intent.getStringExtra("message").trim();
+ Log.v("Code", code);
+ Log.v("Token", token);
+ }
+
+
+ if (intent.getStringExtra("data") != null) {
+ data = intent.getStringExtra("data");
+ Log.v("Data", data);
+ }
+
+ if (intent.getStringExtra("message").trim().equals(CommonUtilities.OPERATION_POLICY_BUNDLE)) {
+ try {
+ SharedPreferences mainPrefp = context.getSharedPreferences(
+ "com.mdm", Context.MODE_PRIVATE);
+ Editor editorp = mainPrefp.edit();
+ editorp.putString("policy", "");
+ editorp.commit();
+
+ SharedPreferences mainPref = context.getSharedPreferences(
+ "com.mdm", Context.MODE_PRIVATE);
+ Editor editor = mainPref.edit();
+ editor.putString("policy", data);
+ editor.commit();
+
+ /*if (mainPref.getString("policy_applied", "") == null
+ || mainPref.getString("policy_applied", "").trim()
+ .equals("0")
+ || mainPref.getString("policy_applied", "").trim()
+ .equals("")) {*/
+ //executePolicy();
+ //}
+ /*
+ * JSONArray jArray = new JSONArray(data); for(int i = 0;
+ * i params,
+ String recepient) {
+ this.context = context;
+ this.mode = mode;
+ this.params = params;
+ this.recepient = recepient;
+
+ if (params.get("data") != null) {
+ data = params.get("data");
+ Log.v("Data", data);
+ }
+
+ if(params.get("code").trim().equals(CommonUtilities.OPERATION_POLICY_MONITOR)){
+ policy_token = params.get("token").trim();
+ policy_code = params.get("code").trim();
+ Log.v("Token", policy_token);
+ }else{
+ token = params.get("token").trim();
+ code = params.get("code").trim();
+ Log.v("Code", code);
+ Log.v("Token", token);
+ }
+
+
+
+ if (params.get("code").trim().equals(CommonUtilities.OPERATION_POLICY_BUNDLE)) {
+ try {
+ SharedPreferences mainPrefp = context.getSharedPreferences(
+ "com.mdm", Context.MODE_PRIVATE);
+ Editor editorp = mainPrefp.edit();
+ editorp.putString("policy", "");
+ editorp.commit();
+
+ SharedPreferences mainPref = context.getSharedPreferences(
+ "com.mdm", Context.MODE_PRIVATE);
+ Editor editor = mainPref.edit();
+ editor.putString("policy", data);
+ editor.commit();
+
+ executePolicy();
+
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }else if(params.get("code").trim().equals(CommonUtilities.OPERATION_POLICY_MONITOR)) {
+ doTask(policy_code, data, REQUEST_MODE_NORMAL);
+ }else{
+ doTask(code, data, REQUEST_MODE_NORMAL);
+ }
+
+ }
+
+
+ public Operation(Context context, int mode, String json) {
+ this.context = context;
+ this.mode = mode;
+ this.params = params;
+ this.recepient = recepient;
+
+ if (params.get("data") != null) {
+ data = params.get("data");
+ Log.v("Data", data);
+ }
+
+ if (params.get("code").trim()
+ .equals(CommonUtilities.OPERATION_POLICY_MONITOR)) {
+ policy_token = params.get("token").trim();
+ policy_code = params.get("code").trim();
+ Log.v("Token", policy_token);
+ } else {
+ token = params.get("token").trim();
+ code = params.get("code").trim();
+ Log.v("Code", code);
+ Log.v("Token", token);
+ }
+
+ if (params.get("code").trim()
+ .equals(CommonUtilities.OPERATION_POLICY_BUNDLE)) {
+ try {
+ SharedPreferences mainPrefp = context.getSharedPreferences(
+ "com.mdm", Context.MODE_PRIVATE);
+ Editor editorp = mainPrefp.edit();
+ editorp.putString("policy", "");
+ editorp.commit();
+
+ SharedPreferences mainPref = context.getSharedPreferences(
+ "com.mdm", Context.MODE_PRIVATE);
+ Editor editor = mainPref.edit();
+ editor.putString("policy", data);
+ editor.commit();
+
+ executePolicy();
+
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ } else if (params.get("code").trim()
+ .equals(CommonUtilities.OPERATION_POLICY_MONITOR)) {
+ doTask(policy_code, data, REQUEST_MODE_NORMAL);
+ } else {
+ doTask(code, data, REQUEST_MODE_NORMAL);
+ }
+
+ }
+
+ public JSONArray operate(String featureCode, JSONObject json) {
+ this.mode = mode;
+ data=json.toString();
+ String featureCodeOp = featureCode;
+ Log.e("featureCodeOp","featureCodeOp"+featureCodeOp);
+ String data = json.toString();
+ Log.e("datadatadatadata","data"+data);
+ return doTask(featureCodeOp, data, REQUEST_MODE_NORMAL);
+
+ //
+ }
+
+ public void executePolicy() {
+ String policy;
+ JSONArray jArray = null;
+ SharedPreferences mainPref = context.getSharedPreferences("com.mdm",
+ Context.MODE_PRIVATE);
+// [{"code" : "500P", "data" : [{"messageId" : "178", "data" : [{"code" : "508A", "data" : {"function" : "Enable"}}, {"code" : "526A", "data" : {"password" : "1234"}}]}]}]
+ try {
+
+ policy = mainPref.getString("policy", "");
+ if(policy!=null && !policy.equals("")){
+ Log.e("INCOMING POLICY : ",policy);
+ jArray = new JSONArray(policy);
+ for (int i = 0; i < jArray.length(); i++) {
+ if(jArray.getJSONObject(i)!=null){
+ JSONObject policyObj = (JSONObject) jArray.getJSONObject(i);
+ if (policyObj.getString("data") != null
+ && policyObj.getString("data") != "") {
+ doTask(policyObj.getString("code"),
+ policyObj.getString("data"), REQUEST_MODE_BUNDLE);
+ }
+ }
+ }
+
+ Editor editor = mainPref.edit();
+ editor.putString("policy_applied", "1");
+ editor.commit();
+ this.data = policy;
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ Editor editor = mainPref.edit();
+ editor.putString("policy_applied", "0");
+ editor.commit();
+ }
+ }
+ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ public void revokePolicy() {
+
+ JSONArray jArray = null;
+ devicePolicyManager = (DevicePolicyManager) context
+ .getSystemService(Context.DEVICE_POLICY_SERVICE);
+ ComponentName cameraAdmin = new ComponentName(context,
+ WSO2DeviceAdminReceiver.class);
+ SharedPreferences mainPref = context.getSharedPreferences("com.mdm",
+ Context.MODE_PRIVATE);
+
+ try {
+
+ String policy = mainPref.getString("policy", "");
+ Log.e("policy revoke() ",policy);
+// [{"data":{"function":"Enable"},"code":"508A"},{"data":{"password":"12345"},"code":"526A"}]
+
+ jArray = new JSONArray(policy.trim());
+ for (int i = 0; i < jArray.length(); i++) {
+ if(jArray.getJSONObject(i)!=null){
+ JSONObject policyObj = (JSONObject) jArray.getJSONObject(i);
+
+
+ if(policyObj.getString("code").trim().equals(CommonUtilities.OPERATION_WIFI)){
+ JSONObject jobj = new JSONObject(policyObj.getString("data"));
+ if (!jobj.isNull("ssid")) {
+ String rev_ssid = (String) jobj.get("ssid");
+ WiFiConfig config = new WiFiConfig(context);
+ config.removeWiFiConfigurationBySSID(rev_ssid);
+ }
+ }else if(policyObj.getString("code").trim().equals(CommonUtilities.OPERATION_DISABLE_CAMERA)){
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ devicePolicyManager.setCameraDisabled(cameraAdmin, false);
+ }
+ }else if(policyObj.getString("code").trim().equals(CommonUtilities.OPERATION_ENCRYPT_STORAGE)){
+ if (policyObj.getString("data") != null
+ && policyObj.getString("data") != "") {
+ JSONObject jobj = new JSONObject(policyObj.getString("data"));
+ boolean encryptFunc=false;
+ if (!jobj.isNull("function")
+ && jobj.get("function").toString()
+ .equalsIgnoreCase("encrypt")) {
+ encryptFunc = true;
+ } else if (!jobj.isNull("function")
+ && jobj.get("function").toString()
+ .equalsIgnoreCase("decrypt")) {
+ encryptFunc = false;
+ } else if (!jobj.isNull("function")) {
+ encryptFunc = Boolean.parseBoolean(jobj.get("function")
+ .toString());
+ }
+ if(encryptFunc){
+ if (devicePolicyManager.getStorageEncryptionStatus() != devicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED) {
+ if (devicePolicyManager.getStorageEncryptionStatus() == devicePolicyManager.ENCRYPTION_STATUS_ACTIVE
+ || devicePolicyManager.getStorageEncryptionStatus() == devicePolicyManager.ENCRYPTION_STATUS_ACTIVATING) {
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ devicePolicyManager.setStorageEncryption(cameraAdmin,
+ false);
+ }
+ }
+ }
+ }
+ }
+ }else if(policyObj.getString("code").trim().equals(CommonUtilities.OPERATION_PASSWORD_POLICY)){
+ devicePolicyManager.setPasswordQuality(cameraAdmin,
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+ }
+// policyObj.getString("code").trim().equals(CommonUtilities.OPERATION_CHANGE_LOCK_CODE)
+ }
+ }
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ @SuppressWarnings("static-access")
+ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ public JSONArray doTask(String codeIn, String dataIn, int req_mode) {
+
+ Log.e("doTask","code:"+codeIn+"\n"+dataIn);
+ String dataInput=dataIn;
+ String codeInput = codeIn;
+ String notification = "";
+ String ssid = "";
+ String password = "";
+
+ devicePolicyManager = (DevicePolicyManager) context.getApplicationContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
+ appList = new ApplicationManager(context.getApplicationContext());
+ deviceInfo = new DeviceInfo(context);
+ gps = new GPSTracker(context);
+ smsManager = SmsManager.getDefault();
+ conversations = new TrackCallSMS(context);
+ deviceState = new PhoneState(context);
+
+ JSONArray resultArr= new JSONArray();
+ JSONObject result= new JSONObject();
+ if (codeInput.equals(CommonUtilities.OPERATION_DEVICE_INFO)) {
+
+ PhoneState phoneState = new PhoneState(context);
+ JSONObject obj = new JSONObject();
+ JSONObject battery_obj = new JSONObject();
+ JSONObject inmemory_obj = new JSONObject();
+ JSONObject exmemory_obj = new JSONObject();
+ JSONObject location_obj = new JSONObject();
+ double latitude = 0;
+ double longitude = 0;
+ try {
+ latitude = gps.getLatitude();
+ longitude = gps.getLongitude();
+ int batteryLevel = (int)Math.floor(phoneState.getBatteryLevel());
+
+// int batteryLevel = 40;
+ battery_obj.put("level", batteryLevel);
+
+ inmemory_obj.put("total",
+ deviceInfo.getTotalInternalMemorySize());
+ inmemory_obj.put("available",
+ deviceInfo.getAvailableInternalMemorySize());
+ exmemory_obj.put("total",
+ deviceInfo.getTotalExternalMemorySize());
+ exmemory_obj.put("available",
+ deviceInfo.getAvailableExternalMemorySize());
+ location_obj.put("latitude", latitude);
+ location_obj.put("longitude", longitude);
+
+ obj.put("battery", battery_obj);
+ obj.put("internal_memory", inmemory_obj);
+ obj.put("external_memory", exmemory_obj);
+ if(latitude!=0 && longitude!=0){
+ obj.put("location_obj", location_obj);
+ }
+ obj.put("operator", deviceInfo.getNetworkOperatorName());
+
+ Map params = new HashMap();
+ params.put("code", codeInput);
+ params.put("msgID", token);
+ params.put("status", "200");
+ params.put("data", obj.toString());
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+ result.put("data", obj);
+
+ Map as = new HashMap();
+ as.put("all", params.toString());
+
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+// ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager
+ .sendTextMessage(
+ recepient,
+ null,
+ "Battery Level : "
+ + phoneState.getBatteryLevel()
+ + ", Total Memory : "
+ + deviceInfo.formatSizeGB(deviceInfo
+ .getTotalInternalMemorySize()
+ + deviceInfo
+ .getTotalExternalMemorySize())
+ + ", Available Memory : "
+ + deviceInfo.formatSizeGB(deviceInfo
+ .getAvailableInternalMemorySize()
+ + deviceInfo
+ .getAvailableExternalMemorySize()),
+ null, null);
+ }
+
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ } else if (codeInput.equals(CommonUtilities.OPERATION_DEVICE_LOCATION)) {
+
+ LocationServices ls = new LocationServices(context);
+ Log.v("Latitude", ls.getLatitude());
+ double latitude = 0;
+ double longitude = 0;
+ JSONObject obj = new JSONObject();
+ try {
+ latitude = gps.getLatitude();
+ longitude = gps.getLongitude();
+ obj.put("latitude", latitude);
+ obj.put("longitude", longitude);
+
+ Map params = new HashMap();
+ params.put("code", CommonUtilities.OPERATION_DEVICE_LOCATION);
+ params.put("msgID", token);
+ params.put("status", "200");
+ params.put("data", obj.toString());
+
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+ result.put("data", obj);
+
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager
+ .sendTextMessage(recepient, null, "Longitude : "
+ + longitude + ",Latitude : " + latitude,
+ null, null);
+ }
+
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ } else if (codeInput
+ .equals(CommonUtilities.OPERATION_GET_APPLICATION_LIST)) {
+ ArrayList apps = appList.getInstalledApps(false); /*
+ * false =
+ * no system
+ * packages
+ */
+
+ JSONArray jsonArray = new JSONArray();
+ int max = apps.size();
+
+ String apz = "";
+ Log.e("APP TOTAL : ", "" + max);
+ for (int i = 0; i < max; i++) {
+ JSONObject jsonObj = new JSONObject();
+ try {
+ jsonObj.put("name", Uri.encode(apps.get(i).appname));
+ jsonObj.put("package", apps.get(i).pname);
+ jsonObj.put("icon", apps.get(i).icon);
+ apz += apps.get(i).appname + " ,";
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ jsonArray.put(jsonObj);
+ }
+
+ JSONObject appsObj = new JSONObject();
+ try {
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+ result.put("data", jsonArray);
+
+
+
+
+ appsObj.put("apps", jsonArray);
+
+ Map params = new HashMap();
+
+ params.put("code",
+ CommonUtilities.OPERATION_GET_APPLICATION_LIST);
+ params.put("msgID", token);
+ params.put("status", "200");
+ //params.put("data", Uri.encode(jsonArray.toString()));
+ Log.e("PASSING MSG ID : ",token);
+ Log.e("PASSING CODE : ",codeInput);
+
+
+
+
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager
+ .sendTextMessage(recepient, null, apz, null, null);
+ }
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ } else if (codeInput.equals(CommonUtilities.OPERATION_LOCK_DEVICE)) {
+
+ Log.d(TAG, "Locking device now");
+ try {
+ Map params = new HashMap();
+ params.put("code", codeInput);
+ params.put("msgID", token);
+ params.put("status", "200");
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+
+ if (req_mode == REQUEST_MODE_NORMAL) {
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ "Device Locked Successfully", null, null);
+ }
+ } else {
+ if (policy_count != 0) {
+ policy_count++;
+ }
+ bundle_params.put("" + policy_count, params.toString());
+
+ }
+ devicePolicyManager.lockNow();
+
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ } else if (codeInput.equals(CommonUtilities.OPERATION_WIPE_DATA)) {
+
+
+ Log.d(TAG,
+ "RESETing device now - all user data will be ERASED to factory settings");
+ String pin = null;
+ SharedPreferences mainPref = context.getSharedPreferences(
+ "com.mdm", Context.MODE_PRIVATE);
+ String pinSaved = mainPref.getString("pin", "");
+
+ try {
+ JSONObject jobj = new JSONObject(dataInput);
+ pin = (String) jobj.get("pin");
+ Map params = new HashMap();
+ params.put("code", codeInput);
+ params.put("msgID", token);
+
+
+ //for local notification
+ resultArr.put(result);
+
+
+ if (pin.trim().equals(pinSaved.trim())) {
+ params.put("status", "200");
+ result.put("status", "true");
+ } else {
+ params.put("status", "400");
+ result.put("status", "false");
+ }
+
+
+ result.put("code", codeInput);
+
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ if (pin.trim().equals(pinSaved.trim())) {
+ smsManager.sendTextMessage(recepient, null,
+ "Device Wiped Successfully", null, null);
+ } else {
+ smsManager.sendTextMessage(recepient, null,
+ "Wrong PIN", null, null);
+ }
+ }
+ if (pin.trim().equals(pinSaved.trim())) {
+ Toast.makeText(context, "Device is being wiped",
+ Toast.LENGTH_LONG).show();
+ try {
+ Thread.sleep(4000);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ devicePolicyManager.wipeData(ACTIVATION_REQUEST);
+ } else {
+ Toast.makeText(context,
+ "Device wipe failed due to wrong PIN",
+ Toast.LENGTH_LONG).show();
+ }
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ } else if (codeInput.equals(CommonUtilities.OPERATION_CLEAR_PASSWORD)) {
+ ComponentName demoDeviceAdmin = new ComponentName(context,
+ WSO2DeviceAdminReceiver.class);
+
+ try {
+ Map params = new HashMap();
+ params.put("code", codeInput);
+ params.put("msgID", token);
+ params.put("status", "200");
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+
+ if (req_mode == REQUEST_MODE_NORMAL) {
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ "Lock code cleared Successfully", null, null);
+ }
+ } else {
+ if (policy_count != 0) {
+ policy_count++;
+ }
+ bundle_params.put("" + policy_count, params.toString());
+ }
+ devicePolicyManager.setPasswordQuality(demoDeviceAdmin,
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+ devicePolicyManager
+ .setPasswordMinimumLength(demoDeviceAdmin, 0);
+ devicePolicyManager.resetPassword("",
+ DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY);
+ devicePolicyManager.lockNow();
+ devicePolicyManager.setPasswordQuality(demoDeviceAdmin,
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ } else if (codeInput.equals(CommonUtilities.OPERATION_NOTIFICATION)) {
+
+ JSONParser jp = new JSONParser();
+ try {
+ JSONObject jobj = new JSONObject(dataInput);
+ if (jobj.get("notification").toString() != null
+ || jobj.get("notification").toString().equals("")) {
+ notification = jobj.get("notification").toString();
+ } else if (jobj.get("Notification").toString() != null
+ || jobj.get("Notification").toString().equals("")) {
+ notification = jobj.get("Notification").toString();
+ } else {
+ notification = "";
+ }
+
+ Log.v("Notification", notification);
+ Map params = new HashMap();
+ params.put("code", codeInput);
+ params.put("msgID", token);
+ params.put("status", "200");
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ "Notification Receieved Successfully", null, null);
+ }
+
+ Intent intent = new Intent(context, AlertActivity.class);
+ intent.putExtra("message", notification);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ } else if (codeInput.equals(CommonUtilities.OPERATION_WIFI)) {
+ boolean wifistatus = false;
+
+ JSONParser jp = new JSONParser();
+ try {
+ JSONObject jobj = new JSONObject(dataInput);
+ if (!jobj.isNull("ssid")) {
+ ssid = (String) jobj.get("ssid");
+ }
+ if (!jobj.isNull("password")) {
+ password = (String) jobj.get("password");
+ }
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ Map inparams = new HashMap();
+ inparams.put("code", codeInput);
+ inparams.put("msgID", token);
+ WiFiConfig config = new WiFiConfig(context);
+
+
+ try {
+ //for local notification
+ resultArr.put(result);
+ result.put("code", codeInput);
+
+ wifistatus = config.saveWEPConfig(ssid, password);
+ if (wifistatus) {
+ inparams.put("status", "200");
+ result.put("status", "true");
+ } else {
+ inparams.put("status", "400");
+ result.put("status", "false");
+ }
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } finally {
+ if (req_mode == REQUEST_MODE_NORMAL) {
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(inparams, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ "WiFi Configured Successfully", null, null);
+ }
+ } else {
+ if (policy_count != 0) {
+ policy_count++;
+ }
+ bundle_params.put("" + policy_count, inparams.toString());
+ }
+ }
+
+ } else if (codeInput.equals(CommonUtilities.OPERATION_DISABLE_CAMERA)) {
+
+ boolean camFunc = false;
+ JSONParser jp = new JSONParser();
+ try {
+ JSONObject jobj = new JSONObject(dataInput);
+ if (!jobj.isNull("function")
+ && jobj.get("function").toString()
+ .equalsIgnoreCase("enable")) {
+ camFunc = false;
+ } else if (!jobj.isNull("function")
+ && jobj.get("function").toString()
+ .equalsIgnoreCase("disable")) {
+ camFunc = true;
+ } else if (!jobj.isNull("function")) {
+ camFunc = Boolean.parseBoolean(jobj.get("function")
+ .toString());
+ }
+
+ ComponentName cameraAdmin = new ComponentName(context,
+ WSO2DeviceAdminReceiver.class);
+ Map params = new HashMap();
+ params.put("code", codeInput);
+ params.put("msgID", token);
+ params.put("status", "200");
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+
+
+ String cammode = "Disabled";
+ if (camFunc) {
+ cammode = "Disabled";
+ } else {
+ cammode = "Enabled";
+ }
+
+
+
+ if (req_mode == REQUEST_MODE_NORMAL) {
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null, "Camera "
+ + cammode + " Successfully", null, null);
+ }
+ } else {
+ if (policy_count != 0) {
+ policy_count++;
+ }
+ bundle_params.put("" + policy_count, params.toString());
+ }
+
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ devicePolicyManager.setCameraDisabled(cameraAdmin, camFunc);
+ }
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ } else if (codeInput
+ .equals(CommonUtilities.OPERATION_INSTALL_APPLICATION)
+ || codeInput
+ .equals(CommonUtilities.OPERATION_INSTALL_APPLICATION_BUNDLE)) {
+
+ try {
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+
+ if (codeInput
+ .equals(CommonUtilities.OPERATION_INSTALL_APPLICATION)) {
+ JSONObject jobj = new JSONObject(dataInput);
+ installApplication(jobj, codeInput);
+ } else if (codeInput
+ .equals(CommonUtilities.OPERATION_INSTALL_APPLICATION_BUNDLE)) {
+ JSONArray jArray = null;
+ jArray = new JSONArray(dataInput);
+ for (int i = 0; i < jArray.length(); i++) {
+ JSONObject appObj = (JSONObject) jArray
+ .getJSONObject(i);
+ installApplication(appObj, codeInput);
+ }
+ }
+
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ } else if (codeInput
+ .equals(CommonUtilities.OPERATION_UNINSTALL_APPLICATION)) {
+
+ String packageName = "";
+ JSONParser jp = new JSONParser();
+ try {
+ JSONObject jobj = new JSONObject(dataInput);
+ packageName = (String) jobj.get("identity");
+
+ Log.v("Package Name : ", packageName);
+ Map params = new HashMap();
+ params.put("code", codeInput);
+ params.put("msgID", token);
+ params.put("status", "200");
+
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ "Application uninstalled Successfully", null, null);
+ }
+ appList.unInstallApplication(packageName);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ } else if (codeInput.equals(CommonUtilities.OPERATION_ENCRYPT_STORAGE)) {
+ boolean encryptFunc = true;
+ String pass = "";
+
+ JSONParser jp = new JSONParser();
+ try {
+ JSONObject jobj = new JSONObject(dataInput);
+ if (!jobj.isNull("function")
+ && jobj.get("function").toString()
+ .equalsIgnoreCase("encrypt")) {
+ encryptFunc = true;
+ } else if (!jobj.isNull("function")
+ && jobj.get("function").toString()
+ .equalsIgnoreCase("decrypt")) {
+ encryptFunc = false;
+ } else if (!jobj.isNull("function")) {
+ encryptFunc = Boolean.parseBoolean(jobj.get("function")
+ .toString());
+ }
+
+ ComponentName admin = new ComponentName(context,
+ WSO2DeviceAdminReceiver.class);
+ Map params = new HashMap();
+ params.put("code", codeInput);
+ params.put("msgID", token);
+
+ if (encryptFunc
+ && devicePolicyManager.getStorageEncryptionStatus() != devicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED) {
+ if (devicePolicyManager.getStorageEncryptionStatus() == devicePolicyManager.ENCRYPTION_STATUS_INACTIVE) {
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ devicePolicyManager.setStorageEncryption(admin,
+ encryptFunc);
+ Intent intent = new Intent(
+ DevicePolicyManager.ACTION_START_ENCRYPTION);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+ }
+ } else if (!encryptFunc
+ && devicePolicyManager.getStorageEncryptionStatus() != devicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED) {
+ if (devicePolicyManager.getStorageEncryptionStatus() == devicePolicyManager.ENCRYPTION_STATUS_ACTIVE
+ || devicePolicyManager.getStorageEncryptionStatus() == devicePolicyManager.ENCRYPTION_STATUS_ACTIVATING) {
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ devicePolicyManager.setStorageEncryption(admin,
+ encryptFunc);
+ }
+ }
+ }
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+ if (devicePolicyManager.getStorageEncryptionStatus() != devicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED) {
+ params.put("status", "200");
+ result.put("status", "true");
+ } else {
+ params.put("status", "400");
+ result.put("status", "false");
+ }
+ if (req_mode == REQUEST_MODE_NORMAL) {
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+// //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ "Storage Encrypted Successfully", null, null);
+ }
+ } else {
+ if (policy_count != 0) {
+ policy_count++;
+ }
+ bundle_params.put("" + policy_count, params.toString());
+ }
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ } else if (codeInput.equals(CommonUtilities.OPERATION_MUTE)) {
+
+ Log.d(TAG, "Muting Device");
+ try {
+ Map params = new HashMap();
+ params.put("code", codeInput);
+ params.put("msgID", token);
+ params.put("status", "200");
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+
+ if (req_mode == REQUEST_MODE_NORMAL) {
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+// //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ "Device Muted Successfully", null, null);
+ }
+ } else {
+ if (policy_count != 0) {
+ policy_count++;
+ }
+ bundle_params.put("" + policy_count, params.toString());
+ }
+
+ muteDevice();
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ } else if (codeInput.equals(CommonUtilities.OPERATION_TRACK_CALLS)) {
+ try {
+ Map params = new HashMap();
+
+ params.put("code", CommonUtilities.OPERATION_TRACK_CALLS);
+ params.put("msgID", token);
+ params.put("status", "200");
+ params.put("data", conversations.getCallDetails().toString());
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+ result.put("data", new JSONObject(conversations.getCallDetails().toString()));
+
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null, conversations
+ .getCallDetails().toString(), null, null);
+ }
+ } catch (Exception e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+ } else if (codeInput.equals(CommonUtilities.OPERATION_TRACK_SMS)) {
+ int MESSAGE_TYPE_INBOX = 1;
+ int MESSAGE_TYPE_SENT = 2;
+ JSONObject smsObj = new JSONObject();
+
+ try {
+ smsObj.put("inbox", conversations.getSMS(MESSAGE_TYPE_INBOX));
+ smsObj.put("sent", conversations.getSMS(MESSAGE_TYPE_SENT));
+
+ Map params = new HashMap();
+
+ params.put("code", CommonUtilities.OPERATION_TRACK_SMS);
+ params.put("msgID", token);
+ params.put("status", "200");
+ params.put("data", smsObj.toString());
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+ result.put("data", smsObj);
+
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ smsObj.toString(), null, null);
+ }
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+ } else if (codeInput.equals(CommonUtilities.OPERATION_DATA_USAGE)) {
+ JSONObject dataObj = new JSONObject();
+
+ try {
+
+ Map params = new HashMap();
+
+ params.put("code", CommonUtilities.OPERATION_DATA_USAGE);
+ params.put("msgID", token);
+ params.put("status", "200");
+ params.put("data", deviceState.takeDataUsageSnapShot()
+ .toString());
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+ result.put("data", new JSONObject(deviceState.takeDataUsageSnapShot()
+ .toString()));
+
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ dataObj.toString(), null, null);
+ }
+ } catch (Exception e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+ } else if (codeInput.equals(CommonUtilities.OPERATION_STATUS)) {
+ boolean encryptStatus = false;
+ boolean passCodeStatus = false;
+ try {
+ if (devicePolicyManager.getStorageEncryptionStatus() != devicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED) {
+ if (devicePolicyManager.getStorageEncryptionStatus() == devicePolicyManager.ENCRYPTION_STATUS_ACTIVE
+ || devicePolicyManager.getStorageEncryptionStatus() == devicePolicyManager.ENCRYPTION_STATUS_ACTIVATING) {
+ encryptStatus = true;
+ } else {
+ encryptStatus = false;
+ }
+ }
+ if (devicePolicyManager.isActivePasswordSufficient()) {
+ passCodeStatus = true;
+ } else {
+ passCodeStatus = false;
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ passCodeStatus = false;
+ }
+ JSONObject dataObj = new JSONObject();
+
+ try {
+ dataObj.put("encryption", encryptStatus);
+ dataObj.put("passcode", passCodeStatus);
+
+ Map params = new HashMap();
+
+ params.put("code", codeInput);
+ params.put("msgID", token);
+ params.put("status", "200");
+ params.put("data", dataObj.toString());
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+ result.put("data", dataObj);
+
+ if (req_mode == REQUEST_MODE_NORMAL) {
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ dataObj.toString(), null, null);
+ }
+ } else {
+ if (policy_count != 0) {
+ policy_count++;
+ }
+ bundle_params.put("" + policy_count, params.toString());
+ }
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ } else if (codeInput.equals(CommonUtilities.OPERATION_WEBCLIP)) {
+ String appUrl = "";
+ String title = "";
+
+ JSONParser jp = new JSONParser();
+ try {
+ JSONObject jobj = new JSONObject(dataInput);
+ Log.v("WEBCLIP DATA : ", data.toString());
+ appUrl = (String) jobj.get("identity");
+ title = (String) jobj.get("title");
+ Log.v("Web App URL : ", appUrl);
+ Map params = new HashMap();
+ params.put("code", codeInput);
+ params.put("msgID", token);
+ params.put("status", "200");
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ "WebClip created Successfully", null, null);
+ }
+ appList.createWebAppBookmark(appUrl, title);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ } else if (codeInput.equals(CommonUtilities.OPERATION_PASSWORD_POLICY)) {
+ ComponentName demoDeviceAdmin = new ComponentName(context,
+ WSO2DeviceAdminReceiver.class);
+
+ int attempts, length, history, specialChars;
+ String alphanumeric, complex;
+ boolean b_alphanumeric, b_complex;
+ long timout;
+ Map inparams = new HashMap();
+
+ JSONParser jp = new JSONParser();
+
+ //for local notification
+ resultArr.put(result);
+
+
+ try {
+ result.put("code", codeInput);
+ JSONObject jobj = new JSONObject(dataInput);
+ if (!jobj.isNull("maxFailedAttempts")
+ && jobj.get("maxFailedAttempts") != null) {
+ attempts = Integer.parseInt((String) jobj
+ .get("maxFailedAttempts"));
+ devicePolicyManager.setMaximumFailedPasswordsForWipe(
+ demoDeviceAdmin, attempts);
+ }
+
+ if (!jobj.isNull("minLength") && jobj.get("minLength") != null) {
+ length = Integer.parseInt((String) jobj.get("minLength"));
+ devicePolicyManager.setPasswordMinimumLength(
+ demoDeviceAdmin, length);
+ }
+
+ if (!jobj.isNull("pinHistory")
+ && jobj.get("pinHistory") != null) {
+ history = Integer.parseInt((String) jobj.get("pinHistory"));
+ devicePolicyManager.setPasswordHistoryLength(
+ demoDeviceAdmin, history);
+ }
+
+ if (!jobj.isNull("minComplexChars")
+ && jobj.get("minComplexChars") != null) {
+ specialChars = Integer.parseInt((String) jobj
+ .get("minComplexChars"));
+ devicePolicyManager.setPasswordMinimumSymbols(
+ demoDeviceAdmin, specialChars);
+ }
+
+ if (!jobj.isNull("requireAlphanumeric")
+ && jobj.get("requireAlphanumeric") != null) {
+ if(jobj.get("requireAlphanumeric") instanceof String){
+ alphanumeric = (String) jobj.get("requireAlphanumeric");
+ if (alphanumeric.equals("true")) {
+ devicePolicyManager
+ .setPasswordQuality(
+ demoDeviceAdmin,
+ DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC);
+ }
+ }else if(jobj.get("requireAlphanumeric") instanceof Boolean){
+ b_alphanumeric = jobj.getBoolean("requireAlphanumeric");
+ if (b_alphanumeric) {
+ devicePolicyManager
+ .setPasswordQuality(
+ demoDeviceAdmin,
+ DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC);
+ }
+ }
+ }
+
+ if (!jobj.isNull("allowSimple")
+ && jobj.get("allowSimple") != null) {
+ if(jobj.get("allowSimple") instanceof String){
+ complex = (String) jobj.get("allowSimple");
+ if (!complex.equals("true")) {
+ devicePolicyManager.setPasswordQuality(demoDeviceAdmin,
+ DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
+ }
+ }else if(jobj.get("allowSimple") instanceof Boolean){
+ b_complex = jobj.getBoolean("allowSimple");
+ if (!b_complex) {
+ devicePolicyManager.setPasswordQuality(demoDeviceAdmin,
+ DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
+ }
+ }
+ }
+
+ if (!jobj.isNull("maxPINAgeInDays")
+ && jobj.get("maxPINAgeInDays") != null) {
+ int daysOfExp = Integer.parseInt((String) jobj
+ .get("maxPINAgeInDays"));
+ timout = (long) (daysOfExp * 24 * 60 * 60 * 1000);
+ devicePolicyManager.setPasswordExpirationTimeout(
+ demoDeviceAdmin, timout);
+ }
+
+ SharedPreferences mainPref = context.getSharedPreferences("com.mdm",
+ Context.MODE_PRIVATE);
+ String policy = mainPref.getString("policy", "");
+
+
+ inparams.put("code", codeInput);
+ inparams.put("msgID", token);
+ inparams.put("status", "200");
+ result.put("status", "true");
+
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ params.put("status", "400");
+ try {
+ result.put("status", "false");
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+ e.printStackTrace();
+ } finally {
+ try {
+ if (req_mode == REQUEST_MODE_NORMAL) {
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(inparams, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ "Password Policies Successfully Set", null,
+ null);
+ }
+ } else {
+ if (policy_count != 0) {
+ policy_count++;
+ }
+ bundle_params.put("" + policy_count,
+ inparams.toString());
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ } else if (codeInput.equals(CommonUtilities.OPERATION_EMAIL_CONFIGURATION)) {
+ String emailname="", emailtype="", ic_username="", ic_password="", ic_hostname="";
+ long timout;
+ Map inparams = new HashMap();
+ //for local notification
+ resultArr.put(result);
+
+
+
+ JSONParser jp = new JSONParser();
+ try {
+ result.put("code", codeInput);
+ JSONObject jobj = new JSONObject(dataInput);
+ if (!jobj.isNull("type")
+ && jobj.get("type") != null) {
+ emailtype = (String) jobj
+ .get("type");
+ }
+
+ if (!jobj.isNull("displayname")
+ && jobj.get("displayname") != null) {
+ emailname = (String) jobj
+ .get("displayname");
+ }
+
+ if (!jobj.isNull("username")
+ && jobj.get("username") != null) {
+ ic_username = (String) jobj
+ .get("username");
+ }
+
+ if (!jobj.isNull("password")
+ && jobj.get("password") != null) {
+ ic_password = (String) jobj
+ .get("password");
+ }
+
+ if(emailtype.trim().equals("GMAIL")){
+ ic_hostname = "imap.googlemail.com";
+ }else if(emailtype.equals("YAHOO")){
+ ic_hostname = "";
+ }else if(emailtype.equals("HOTMAIL")){
+ ic_hostname = "";
+ }
+
+ inparams.put("code", codeInput);
+ inparams.put("msgID", token);
+ inparams.put("status", "200");
+ result.put("status", "true");
+
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ params.put("status", "400");
+ try {
+ result.put("status", "false");
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+ e.printStackTrace();
+ } finally {
+ try {
+ if (req_mode == REQUEST_MODE_NORMAL) {
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(inparams, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ "Email Configured Successfully Set", null,
+ null);
+ }
+ } else {
+ if (policy_count != 0) {
+ policy_count++;
+ }
+ bundle_params.put("" + policy_count,
+ inparams.toString());
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ }else if (codeInput
+ .equals(CommonUtilities.OPERATION_INSTALL_GOOGLE_APP)) {
+
+ String packageName = "";
+ JSONParser jp = new JSONParser();
+ try {
+ JSONObject jobj = new JSONObject(dataInput);
+ packageName = (String) jobj.get("package");
+
+ Log.v("Package Name : ", packageName);
+ Map params = new HashMap();
+ params.put("code", codeInput);
+ params.put("msgID", token);
+ params.put("status", "200");
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ "Application installed Successfully", null, null);
+ }
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setData(Uri.parse("market://details?id=" + packageName));
+ context.startActivity(intent);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ } else if (codeInput
+ .equals(CommonUtilities.OPERATION_CHANGE_LOCK_CODE)) {
+ ComponentName demoDeviceAdmin = new ComponentName(context,
+ WSO2DeviceAdminReceiver.class);
+ devicePolicyManager.setPasswordMinimumLength(demoDeviceAdmin, 3);
+ String pass = "";
+ Map inparams = new HashMap();
+
+ JSONParser jp = new JSONParser();
+ try {
+ JSONObject jobj = new JSONObject(dataInput);
+ if (!jobj.isNull("password")) {
+ pass = (String) jobj.get("password");
+ }
+
+ inparams.put("code", codeInput);
+ inparams.put("msgID", token);
+ inparams.put("status", "200");
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(inparams, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ "Lock code changed Successfully", null, null);
+ }
+
+ if(!pass.equals("")){
+ devicePolicyManager.resetPassword(pass,
+ DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY);
+ devicePolicyManager.lockNow();
+ }
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } finally {
+ try {
+ if (req_mode == REQUEST_MODE_NORMAL) {
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(inparams, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ "Lock code changed Successfully", null,
+ null);
+ }
+ } else {
+ if (policy_count != 0) {
+ policy_count++;
+ }
+ bundle_params.put("" + policy_count,
+ inparams.toString());
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ } else if (codeInput.equals(CommonUtilities.OPERATION_POLICY_BUNDLE)) {
+ Map params = new HashMap();
+ try {
+ params.put("code", code);
+ params.put("msgID", policy_token);
+ params.put("status", "200");
+ params.put("data", bundle_params.toString());
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+ result.put("data", new JSONObject(bundle_params.toString()));
+
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ "Bundle Executed Successfully", null, null);
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ } else if (codeInput.equals(CommonUtilities.OPERATION_POLICY_MONITOR)) {
+ JSONArray sendjArray;
+ JSONObject jobj=null;
+ try {
+ if(this.data!=null && !this.data.trim().equals(""))
+ jobj = new JSONObject(this.data);
+
+ sendjArray = jobj.getJSONArray("policies");
+
+ int type = Integer.parseInt((String) jobj.get("type")
+ .toString().trim());
+
+ if(type!=1 && type!=2 && type!=3){
+ type = 1;
+ }
+
+ Log.e("PASSING MSG ID : ",policy_token);
+ Log.e("PASSING CODE : ",codeInput);
+ Log.e("PASSING TYPE : ",String.valueOf(type));
+ PolicyTester tester = new PolicyTester(context, sendjArray,
+ type, policy_token);
+ //for local notification
+ resultArr=tester.finalArray;
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ } else if (codeInput.equals(CommonUtilities.OPERATION_POLICY_REVOKE)) {
+ try {
+ Map inparams = new HashMap();
+
+
+ inparams.put("code", codeInput);
+ inparams.put("msgID", token);
+ inparams.put("status", "200");
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(inparams, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ "Lock code changed Successfully", null, null);
+ }
+ revokePolicy();
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }else if (codeInput.equals(CommonUtilities.OPERATION_ENTERPRISE_WIPE_DATA)) {
+ try {
+ Map inparams = new HashMap();
+
+
+ inparams.put("code", codeInput);
+ inparams.put("msgID", token);
+ inparams.put("status", "200");
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(inparams, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+// smsManager.sendTextMessage(recepient, null,
+// "Lock code changed Successfully", null, null);
+ }
+ enterpriseWipe=true;
+ ServerUtils.clearAppData(context);
+ Intent intent = new Intent(context, ServerDetails.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ else if (codeInput
+ .equals(CommonUtilities.OPERATION_BLACKLIST_APPS)) {
+ ArrayList apps = appList.getInstalledApps(false); /*
+ * false =
+ * no system
+ * packages
+ */
+
+ JSONArray jsonArray = new JSONArray();
+ int max = apps.size();
+ if (max > 10) {
+ //max = 10;
+ }
+ String apz = "";
+
+
+ try{
+
+ JSONObject appsObj = new JSONObject(dataInput);
+ if (!appsObj.isNull("data")) {
+ appsObj = (JSONObject) appsObj.get("data");
+ }
+// JSONObject appObj = (JSONObject) appsObj.get("data");
+ String identity = (String) appsObj.get("identity");
+
+ for (int j = 0; j < max; j++) {
+ JSONObject jsonObj = new JSONObject();
+ try {
+ jsonObj.put("name", apps.get(j).appname);
+ jsonObj.put("package", apps.get(j).pname);
+ if(identity.trim().equals(apps.get(j).pname)){
+ jsonObj.put("notviolated", false);
+ jsonObj.put("package", apps.get(j).pname);
+ }else{
+ jsonObj.put("notviolated", true);
+ }
+
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ jsonArray.put(jsonObj);
+ }
+ }catch(Exception ex){
+ ex.printStackTrace();
+ }
+
+
+ JSONObject appsObj = new JSONObject();
+ try {
+ appsObj.put("apps", jsonArray);
+
+ Map params = new HashMap();
+
+ params.put("code",
+ CommonUtilities.OPERATION_GET_APPLICATION_LIST);
+ params.put("msgID", token);
+ params.put("status", "200");
+ params.put("data", jsonArray.toString());
+
+ //for local notification
+ resultArr.put(result);
+ result.put("status", "true");
+ result.put("code", codeInput);
+ result.put("data", jsonArray);
+
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager
+ .sendTextMessage(recepient, null, apz, null, null);
+ }
+ SharedPreferences mainPref = context.getSharedPreferences("com.mdm",
+ Context.MODE_PRIVATE);
+ String policy = mainPref.getString("policy", "");
+ if(policy!=null && policy!=""){
+ if(apz!=null || !apz.trim().equals("")){
+
+ }
+ }
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ }
+ return resultArr;
+ }
+
+
+ /**
+ * Install an Application
+ */
+ private void installApplication(JSONObject data_input, String code_input) {
+ String appUrl = "";
+ String type = "enterprise";
+ String os = null;
+
+ JSONParser jp = new JSONParser();
+ try {
+ JSONObject jobj = data_input;
+ appUrl = (String) jobj.get("identity");
+ if (!jobj.isNull("type")) {
+ type = (String) jobj.get("type");
+ }
+
+ if (!jobj.isNull("platform_id")) {
+ os = (String) jobj.get("platform_id");
+ } else if (!jobj.isNull("os")) {
+ os = (String) jobj.get("os");
+ }
+
+ Log.v("App URL : ", appUrl);
+ Map params = new HashMap();
+ params.put("code", code_input);
+ params.put("msgID", token);
+ params.put("status", "200");
+ if (mode == CommonUtilities.MESSAGE_MODE_GCM) {
+ //ServerUtilities.pushData(params, context);
+ } else if (mode == CommonUtilities.MESSAGE_MODE_SMS) {
+ smsManager.sendTextMessage(recepient, null,
+ "Application installed Successfully", null, null);
+ }
+
+ if (type.equalsIgnoreCase("Enterprise")) {
+ if(os != null){
+ if(os.equalsIgnoreCase("android")){
+ Log.e("Enterprise","android");
+ appList.installApp(appUrl);
+ }
+ }else{
+ appList.installApp(appUrl);
+ }
+ } else if (type.equalsIgnoreCase("Market")) {
+ if(os != null){
+ if(os.equalsIgnoreCase("android")){
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse("market://details?id=" + appUrl));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+ }else{
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse("market://details?id=" + appUrl));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+
+ } else {
+ if(os != null){
+ if(os.equalsIgnoreCase("android")){
+ appList.installApp(appUrl);
+ }
+ }else{
+ appList.installApp(appUrl);
+ }
+ }
+
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+
+ /**
+ * Mute the device
+ */
+ private void muteDevice() {
+ Log.v("MUTING THE DEVICE : ", "MUTING");
+ AudioManager audioManager = (AudioManager) context
+ .getSystemService(Context.AUDIO_SERVICE);
+ Log.v("VOLUME : ",
+ "" + audioManager.getStreamVolume(AudioManager.STREAM_RING));
+ audioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0);
+ Log.v("VOLUME AFTER: ",
+ "" + audioManager.getStreamVolume(AudioManager.STREAM_RING));
+
+ }
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/PolicyTester.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/PolicyTester.java
new file mode 100644
index 0000000000..7e4f9bf57a
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/services/PolicyTester.java
@@ -0,0 +1,740 @@
+/*
+ ~ Copyright (c) 2014, WSO2 Inc. (http://wso2.com/) All Rights Reserved.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+*/
+package org.wso2.cdm.agent.services;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.wso2.cdm.agent.AlertActivity;
+import org.wso2.cdm.agent.api.ApplicationManager;
+import org.wso2.cdm.agent.api.DeviceInfo;
+import org.wso2.cdm.agent.api.PhoneState;
+import org.wso2.cdm.agent.api.WiFiConfig;
+import org.wso2.cdm.agent.models.PInfo;
+import org.wso2.cdm.agent.utils.CommonUtilities;
+
+import android.annotation.TargetApi;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.media.AudioManager;
+import android.os.Build;
+import android.util.Log;
+
+public class PolicyTester {
+
+ Context context;
+ DevicePolicyManager devicePolicyManager;
+ ApplicationManager appList;
+ DeviceInfo deviceInfo;
+ PhoneState deviceState;
+ String policy;
+ String usermessage = "";
+ String apz = "";
+ int appcount = 0;
+ JSONObject returnJSON = new JSONObject();
+ JSONArray finalArray = new JSONArray();
+ boolean IS_ENFORCE = false;
+ String ssid, password;
+ int POLICY_MONITOR_TYPE_NO_ENFORCE_RETURN = 1;
+ int POLICY_MONITOR_TYPE_NO_ENFORCE_MESSAGE_RETURN = 2;
+ int POLICY_MONITOR_TYPE_ENFORCE_RETURN = 3;
+
+ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ public PolicyTester(Context context, JSONArray recJArray, int type, String msgID) {
+ this.context = context;
+ devicePolicyManager = (DevicePolicyManager) context
+ .getSystemService(Context.DEVICE_POLICY_SERVICE);
+ appList = new ApplicationManager(context);
+ deviceInfo = new DeviceInfo(context);
+ deviceState = new PhoneState(context);
+
+ if(type == POLICY_MONITOR_TYPE_NO_ENFORCE_RETURN){
+ IS_ENFORCE = false;
+ }else if(type == POLICY_MONITOR_TYPE_NO_ENFORCE_MESSAGE_RETURN){
+ IS_ENFORCE = false;
+ }else if(type == POLICY_MONITOR_TYPE_ENFORCE_RETURN){
+ IS_ENFORCE = true;
+ }else{
+ IS_ENFORCE = false;
+ type = POLICY_MONITOR_TYPE_NO_ENFORCE_MESSAGE_RETURN;
+ }
+
+ SharedPreferences mainPref = context.getSharedPreferences("com.mdm",
+ Context.MODE_PRIVATE);
+ policy = mainPref.getString("policy", "");
+
+ try {
+ JSONArray jArray = null;
+ if(recJArray!=null){
+ jArray = recJArray;
+ }else{
+ jArray = new JSONArray(policy);
+ }
+ Log.e("POLICY ARAY : ", jArray.toString());
+ for (int i = 0; i < jArray.length(); i++) {
+ JSONObject policyObj = (JSONObject) jArray.getJSONObject(i);
+ if (policyObj.getString("data") != null
+ && policyObj.getString("data") != "") {
+ testPolicy(policyObj.getString("code"),
+ policyObj.getString("data"));
+ }
+ }
+
+ JSONObject rootObj = new JSONObject();
+ try {
+ if(deviceInfo.isRooted()){
+ rootObj.put("status", false);
+ }else{
+ rootObj.put("status", true);
+ }
+ rootObj.put("code", "notrooted");
+ finalArray.put(rootObj);
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+ Log.e("MONITOR POLICY : ",policy);
+ Log.e("MONITOR USER MESSAGE : ",usermessage);
+ //Display an alert to the user about policy violation
+ if(policy!=null && policy !=""){
+ if(usermessage!=null && usermessage!="" && type == POLICY_MONITOR_TYPE_NO_ENFORCE_MESSAGE_RETURN){
+ Intent intent = new Intent(context, AlertActivity.class);
+ intent.putExtra("message", usermessage);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+ }
+
+ returnJSON.put("code", CommonUtilities.OPERATION_POLICY_MONITOR);
+ returnJSON.put("data", finalArray);
+
+ Map params = new HashMap();
+ params.put("code", CommonUtilities.OPERATION_POLICY_MONITOR);
+ params.put("msgID", msgID);
+ params.put("status", "200");
+ params.put("data", finalArray.toString());
+
+ //ServerUtilities.pushData(params, context);
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ @SuppressWarnings("static-access")
+ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ public boolean testPolicy(String code, String data) {
+ if (code.equals(CommonUtilities.OPERATION_CLEAR_PASSWORD)) {
+ ComponentName demoDeviceAdmin = new ComponentName(context,
+ WSO2DeviceAdminReceiver.class);
+ JSONObject jobj = new JSONObject();
+ // data = intent.getStringExtra("data");
+ try {
+ Map params = new HashMap();
+ params.put("code", code);
+ params.put("status", "200");
+
+ if(IS_ENFORCE){
+ devicePolicyManager.setPasswordQuality(demoDeviceAdmin,
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+ devicePolicyManager.resetPassword("",
+ DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY);
+ devicePolicyManager.lockNow();
+ devicePolicyManager.setPasswordQuality(demoDeviceAdmin,
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+ jobj.put("status", true);
+ }else{
+ if(devicePolicyManager.getPasswordQuality(demoDeviceAdmin) != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED){
+ jobj.put("status", false);
+ }else{
+ jobj.put("status", true);
+ }
+ }
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ try {
+ jobj.put("code", code);
+
+ //finalArray.put(jobj);
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+
+ } else if (code.equals(CommonUtilities.OPERATION_WIFI)) {
+ boolean wifistatus = false;
+ JSONObject jobjc = new JSONObject();
+ WiFiConfig config = new WiFiConfig(context);
+ // data = intent.getStringExtra("data");
+ JSONParser jp = new JSONParser();
+ try {
+ JSONObject jobj = new JSONObject(data);
+ if(!jobj.isNull("ssid")){
+ ssid = (String) jobj.get("ssid");
+ }
+ if(!jobj.isNull("password")){
+ password = (String) jobj.get("password");
+ }
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ Map inparams = new HashMap();
+ inparams.put("code", code);
+ if(IS_ENFORCE){
+ try {
+ wifistatus = config.saveWEPConfig(ssid, password);
+ jobjc.put("status", true);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ try {
+ if(config.readWEPConfig(ssid)){
+ jobjc.put("status", true);
+ }else{
+ jobjc.put("status", false);
+ if(usermessage!=null && usermessage!=""){
+ usermessage+="\nYou are not using company WIFI account, please change your WIFI configuration \n";
+ }else{
+ usermessage+="You are not using company WIFI account, please change your WIFI configuration \n";
+ }
+
+ }
+ jobjc.put("code", code);
+
+ finalArray.put(jobjc);
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ } else if (code.equals(CommonUtilities.OPERATION_DISABLE_CAMERA)) {
+ ComponentName cameraAdmin = new ComponentName(context,
+ WSO2DeviceAdminReceiver.class);
+ boolean camFunc = false;
+ // data = intent.getStringExtra("data");
+ JSONParser jp = new JSONParser();
+ try {
+ JSONObject jobj = new JSONObject(data);
+
+ if (!jobj.isNull("function")
+ && jobj.get("function").toString()
+ .equalsIgnoreCase("enable")) {
+ camFunc = false;
+ } else if (!jobj.isNull("function")
+ && jobj.get("function").toString()
+ .equalsIgnoreCase("disable")) {
+ camFunc = true;
+ } else if (!jobj.isNull("function")) {
+ camFunc = Boolean.parseBoolean(jobj.get("function")
+ .toString());
+ }
+
+
+ Map params = new HashMap();
+ params.put("code", code);
+ params.put("status", "200");
+ String cammode = "Disabled";
+ if (camFunc) {
+ cammode = "Disabled";
+ } else {
+ cammode = "Enabled";
+ }
+
+ if (IS_ENFORCE && (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)) {
+ devicePolicyManager.setCameraDisabled(cameraAdmin, camFunc);
+ }
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ JSONObject jobj = new JSONObject();
+ try {
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ if(!camFunc){
+ if(!devicePolicyManager.getCameraDisabled(cameraAdmin)){
+ jobj.put("status", true);
+ }else{
+ jobj.put("status", false);
+ }
+ }else{
+ if(devicePolicyManager.getCameraDisabled(cameraAdmin)){
+ jobj.put("status", true);
+ }else{
+ jobj.put("status", false);
+ /*if(usermessage!=null && usermessage!=""){
+ usermessage+="\nYour camera should be deactivated according to the policy, please deactivate your camera\n";
+ }else{
+ usermessage+="Your camera should be deactivated according to the policy, please deactivate your camera \n";
+ }*/
+ }
+ }
+ }else{
+ jobj.put("status", false);
+ }
+ jobj.put("code", code);
+
+ finalArray.put(jobj);
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ } else if (code.equals(CommonUtilities.OPERATION_ENCRYPT_STORAGE)) {
+ boolean encryptFunc = true;
+ String pass = "";
+
+ JSONParser jp = new JSONParser();
+ try {
+ JSONObject jobj = new JSONObject(data);
+
+ if (!jobj.isNull("function")
+ && jobj.get("function").toString()
+ .equalsIgnoreCase("encrypt")) {
+ encryptFunc = true;
+ } else if (!jobj.isNull("function")
+ && jobj.get("function").toString()
+ .equalsIgnoreCase("decrypt")) {
+ encryptFunc = false;
+ } else if (!jobj.isNull("function")) {
+ encryptFunc = Boolean.parseBoolean(jobj.get("function")
+ .toString());
+ }
+
+ ComponentName admin = new ComponentName(context,
+ WSO2DeviceAdminReceiver.class);
+ Map params = new HashMap();
+ params.put("code", code);
+
+ if(IS_ENFORCE){
+ if (encryptFunc
+ && devicePolicyManager.getStorageEncryptionStatus() != devicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED) {
+ if (devicePolicyManager.getStorageEncryptionStatus() == devicePolicyManager.ENCRYPTION_STATUS_INACTIVE) {
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ devicePolicyManager.setStorageEncryption(admin,
+ encryptFunc);
+ Intent intent = new Intent(
+ DevicePolicyManager.ACTION_START_ENCRYPTION);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+ }
+ } else if (!encryptFunc
+ && devicePolicyManager.getStorageEncryptionStatus() != devicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED) {
+ if (devicePolicyManager.getStorageEncryptionStatus() == devicePolicyManager.ENCRYPTION_STATUS_ACTIVE
+ || devicePolicyManager.getStorageEncryptionStatus() == devicePolicyManager.ENCRYPTION_STATUS_ACTIVATING) {
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ devicePolicyManager.setStorageEncryption(admin,
+ encryptFunc);
+ }
+ }
+ }
+ }
+ if (devicePolicyManager.getStorageEncryptionStatus() != devicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED) {
+ params.put("status", "200");
+ } else {
+ params.put("status", "400");
+ }
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ JSONObject jobj = new JSONObject();
+ try {
+ jobj.put("code", code);
+ if(encryptFunc){
+ if(devicePolicyManager.getStorageEncryptionStatus()!= devicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED && devicePolicyManager.getStorageEncryptionStatus() != devicePolicyManager.ENCRYPTION_STATUS_INACTIVE){
+ jobj.put("status", true);
+ }else{
+ jobj.put("status", false);
+ if(usermessage!=null && usermessage!=""){
+ usermessage+="\nYour device should be encrypted according to the policy, please enable device encryption through device settings\n";
+ }else{
+ usermessage+="Your device should be encrypted according to the policy, please enable device encryption through device settings \n";
+ }
+ }
+ }else{
+ if(devicePolicyManager.getStorageEncryptionStatus()== devicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED || devicePolicyManager.getStorageEncryptionStatus() == devicePolicyManager.ENCRYPTION_STATUS_INACTIVE){
+ jobj.put("status", true);
+ }else{
+ jobj.put("status", false);
+ }
+ }
+ finalArray.put(jobj);
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+ } else if (code.equals(CommonUtilities.OPERATION_MUTE)) {
+
+ try {
+ Map params = new HashMap();
+ params.put("code", code);
+ params.put("status", "200");
+ if(IS_ENFORCE){
+ muteDevice();
+ }
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ JSONObject jobj = new JSONObject();
+ try {
+ jobj.put("code", code);
+ if(isMuted()){
+ jobj.put("status", true);
+ }else{
+ jobj.put("status", false);
+ if(usermessage!=null && usermessage!=""){
+ usermessage+="\nYour phone should be muted according to the policy, please mute your phone \n";
+ }else{
+ usermessage+="Your phone should be muted according to the policy, please mute your phone \n";
+ }
+ }
+ finalArray.put(jobj);
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ } else if (code.equals(CommonUtilities.OPERATION_PASSWORD_POLICY)) {
+
+ ComponentName demoDeviceAdmin = new ComponentName(context,
+ WSO2DeviceAdminReceiver.class);
+ JSONObject jobjx = new JSONObject();
+ int attempts, length, history, specialChars;
+ String alphanumeric, complex;
+ boolean b_alphanumeric=false, b_complex=false, is_comply=true, comply_fac1=true, comply_fac2=true, comply_fac3=true, comply_fac4=true, comply_fac5=true, comply_fac6=true, comply_fac7=true;
+ long timout;
+ Map inparams = new HashMap();
+ // data = intent.getStringExtra("data");
+ JSONParser jp = new JSONParser();
+ try {
+ JSONObject jobjpass = new JSONObject();
+ jobjpass.put("code", CommonUtilities.OPERATION_CHANGE_LOCK_CODE);
+
+ if(devicePolicyManager.isActivePasswordSufficient()){
+ is_comply=true;
+ //finalArray.put(jobjpass);
+ }else{
+ is_comply=false;
+ }
+
+ JSONObject jobj = new JSONObject(data);
+ if (!jobj.isNull("maxFailedAttempts")
+ && jobj.get("maxFailedAttempts") != null) {
+ attempts = Integer.parseInt((String) jobj
+ .get("maxFailedAttempts"));
+ if(IS_ENFORCE){
+ devicePolicyManager.setMaximumFailedPasswordsForWipe(
+ demoDeviceAdmin, attempts);
+ comply_fac1=true;
+ }else{
+ if(devicePolicyManager.getMaximumFailedPasswordsForWipe(demoDeviceAdmin) != attempts){
+ comply_fac1=false;
+ }else{
+ comply_fac1=true;
+ }
+ }
+ }
+
+ if (!jobj.isNull("minLength") && jobj.get("minLength") != null) {
+ length = Integer.parseInt((String) jobj.get("minLength"));
+ if(IS_ENFORCE){
+ devicePolicyManager.setPasswordMinimumLength(
+ demoDeviceAdmin, length);
+ comply_fac2=true;
+ }else{
+ if(devicePolicyManager.getPasswordMinimumLength(demoDeviceAdmin) != length){
+ comply_fac2=false;
+ }else{
+ comply_fac2=true;
+ }
+ }
+ }
+
+ if (!jobj.isNull("pinHistory")
+ && jobj.get("pinHistory") != null) {
+ history = Integer.parseInt((String) jobj.get("pinHistory"));
+ if(IS_ENFORCE){
+ devicePolicyManager.setPasswordHistoryLength(
+ demoDeviceAdmin, history);
+ comply_fac3=true;
+ }else{
+ if(devicePolicyManager.getPasswordHistoryLength(demoDeviceAdmin) != history){
+ comply_fac3=false;
+ }else{
+ comply_fac3=true;
+ }
+ }
+ }
+
+ if (!jobj.isNull("minComplexChars")
+ && jobj.get("minComplexChars") != null) {
+ specialChars = Integer.parseInt((String) jobj
+ .get("minComplexChars"));
+ if(IS_ENFORCE){
+ devicePolicyManager.setPasswordMinimumSymbols(
+ demoDeviceAdmin, specialChars);
+ comply_fac4=true;
+ }else{
+ if(devicePolicyManager.getPasswordMinimumSymbols(demoDeviceAdmin) != specialChars){
+ comply_fac4=false;
+ }else{
+ comply_fac4=true;
+ }
+ }
+ }
+
+ if (!jobj.isNull("requireAlphanumeric")
+ && jobj.get("requireAlphanumeric") != null) {
+
+ if(jobj.get("requireAlphanumeric") instanceof String){
+ alphanumeric = (String) jobj.get("requireAlphanumeric").toString();
+ if (alphanumeric.equals("true")) {
+ b_alphanumeric=true;
+ }else{
+ b_alphanumeric=false;
+ }
+ }else if(jobj.get("requireAlphanumeric") instanceof Boolean){
+ b_alphanumeric = jobj.getBoolean("requireAlphanumeric");
+ }
+ if (b_alphanumeric) {
+ if(IS_ENFORCE){
+ devicePolicyManager
+ .setPasswordQuality(
+ demoDeviceAdmin,
+ DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC);
+ comply_fac5=true;
+ }else{
+ if(devicePolicyManager.getPasswordQuality(demoDeviceAdmin) != DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC){
+ comply_fac5=false;
+ }else{
+ comply_fac5=true;
+ }
+ }
+ }else{
+ if(devicePolicyManager.getPasswordQuality(demoDeviceAdmin) == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC){
+ comply_fac5=false;
+ }else{
+ comply_fac5=true;
+ }
+ }
+ }
+
+ if (!jobj.isNull("allowSimple")
+ && jobj.get("allowSimple") != null) {
+
+ if(jobj.get("allowSimple") instanceof String){
+ complex = (String) jobj.get("allowSimple").toString();
+ if (complex.equals("true")) {
+ b_complex=true;
+ }else{
+ b_complex=false;
+ }
+ }else if(jobj.get("allowSimple") instanceof Boolean){
+ b_complex = jobj.getBoolean("allowSimple");
+ }
+
+ if (!b_complex) {
+ if(IS_ENFORCE){
+ devicePolicyManager.setPasswordQuality(demoDeviceAdmin,
+ DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
+ comply_fac6=true;
+ }else{
+ if(devicePolicyManager.getPasswordQuality(demoDeviceAdmin) != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX){
+ comply_fac6=false;
+ }else{
+ comply_fac6=true;
+ }
+ }
+ }else{
+ if(devicePolicyManager.getPasswordQuality(demoDeviceAdmin) == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX){
+ comply_fac6=false;
+ }else{
+ comply_fac6=true;
+ }
+ }
+ }
+
+ if (!jobj.isNull("maxPINAgeInDays")
+ && jobj.get("maxPINAgeInDays") != null) {
+ int daysOfExp = Integer.parseInt((String) jobj
+ .get("maxPINAgeInDays"));
+ timout = (long) (daysOfExp * 24 * 60 * 60 * 1000);
+ if(IS_ENFORCE){
+ devicePolicyManager.setPasswordExpirationTimeout(
+ demoDeviceAdmin, timout);
+ comply_fac7=true;
+ }else{
+ if(devicePolicyManager.getPasswordExpirationTimeout(demoDeviceAdmin) != timout){
+ comply_fac7=false;
+ }else{
+ comply_fac7=true;
+ }
+ }
+ }
+
+ if(!is_comply || !comply_fac1 || !comply_fac2 || !comply_fac3 || !comply_fac4 || !comply_fac5 || !comply_fac6 || !comply_fac7){
+ jobjx.put("status", false);
+ if(usermessage!=null && usermessage!=""){
+ usermessage+="\nYour screen lock password doesn't meet current policy requirement. Please reset your passcode \n";
+ }else{
+ usermessage+="Your screen lock password doesn't meet current policy requirement. Please reset your passcode \n";
+ }
+ }else{
+ jobjx.put("status", true);
+ }
+
+
+ inparams.put("code", code);
+ inparams.put("status", "200");
+
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+
+ e.printStackTrace();
+ }
+
+ try {
+ jobjx.put("code", code);
+
+ finalArray.put(jobjx);
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ }else if (code
+ .equals(CommonUtilities.OPERATION_BLACKLIST_APPS)) {
+ ArrayList apps = appList.getInstalledApps(false); /*
+ * false =
+ * no system
+ * packages
+ */
+ JSONArray jsonArray = new JSONArray();
+ int max = apps.size();
+
+ Boolean flag = true;
+
+ try{
+ JSONObject appObj = new JSONObject(data);
+ String identity = (String) appObj.get("identity");
+ for (int j = 0; j < max; j++) {
+ JSONObject jsonObj = new JSONObject();
+ try {
+ jsonObj.put("name", apps.get(j).appname);
+ jsonObj.put("package", apps.get(j).pname);
+ if(identity.trim().equals(apps.get(j).pname)){
+ jsonObj.put("notviolated", false);
+ flag = false;
+ jsonObj.put("package", apps.get(j).pname);
+ if(apps.get(j).appname!=null){
+ appcount++;
+ apz = appcount+". "+apps.get(j).appname;
+ }
+
+ if(apz!=null || !apz.trim().equals("")){
+ if(usermessage!=null && usermessage!=""){
+ if(appcount>1){
+ usermessage+="\n"+apz;
+ }else{
+ usermessage+="\nFollowing apps are blacklisted by your MDM Admin, please remove them \n\n"+apz;
+ }
+ }else{
+ if(appcount>1){
+ usermessage+="\n"+apz;
+ }else{
+ usermessage+="Following apps are blacklisted by your MDM Admin, please remove them \n\n"+apz;
+ }
+
+ }
+ }
+
+ }else{
+ jsonObj.put("notviolated", true);
+ }
+
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ jsonArray.put(jsonObj);
+ }
+ }catch(Exception ex){
+ ex.printStackTrace();
+ }
+
+ /*
+ * for(int i=0;i mRegisterTask;
+ String regId="";
+ Operation operation;
+ boolean unregState=false;
+
+ /** Called when this application is approved to be a device administrator. */
+ @Override
+ public void onEnabled(Context context, Intent intent) {
+ super.onEnabled(context, intent);
+ String policy;
+ operation = new Operation(context);
+ SharedPreferences mainPref = context.getSharedPreferences("com.mdm",
+ Context.MODE_PRIVATE);
+ Editor editor = mainPref.edit();
+ editor.putString(context.getResources().getString(R.string.shared_pref_device_active), "1");
+ editor.commit();
+
+ MessageProcessor pm=new MessageProcessor(context);
+ pm.getMessages();
+ try {
+ policy = mainPref.getString("policy", "");
+ if(policy!=null && !policy.equals("")){
+ //operation.executePolicy();
+ }
+ }catch(Exception ex){
+
+ }
+ Toast.makeText(context, R.string.device_admin_enabled,
+ Toast.LENGTH_LONG).show();
+ Log.d(TAG, "onEnabled");
+ LocalNotification.startPolling(context);
+
+ }
+
+ /** Called when this application is no longer the device administrator. */
+ @Override
+ public void onDisabled(Context context, Intent intent) {
+ super.onDisabled(context, intent);
+ Toast.makeText(context, R.string.device_admin_disabled,
+ Toast.LENGTH_LONG).show();
+ Log.d(TAG, "onDisabled");
+ if(regId == null || regId.equals("")){
+ regId = GCMRegistrar.getRegistrationId(context);
+ }
+
+ if(regId != null || !regId.equals("")){
+ startUnRegistration(context);
+ }
+
+ }
+
+ public void startUnRegistration(Context app_context) {
+ String regId = CommonUtilities.getPref(app_context, app_context
+ .getResources().getString(R.string.shared_pref_regId));
+
+ JSONObject requestParams = new JSONObject();
+ try {
+ requestParams.put("regid", regId);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ ServerUtils.clearAppData(app_context);
+ ServerUtils.callSecuredAPI(app_context,
+ CommonUtilities.UNREGISTER_ENDPOINT,
+ CommonUtilities.POST_METHOD, requestParams,
+ WSO2DeviceAdminReceiver.this,
+ CommonUtilities.UNREGISTER_REQUEST_CODE);
+ }
+
+ @Override
+ public void onPasswordChanged(Context context, Intent intent) {
+ super.onPasswordChanged(context, intent);
+ Log.d(TAG, "onPasswordChanged");
+ }
+
+ @Override
+ public void onPasswordFailed(Context context, Intent intent) {
+ super.onPasswordFailed(context, intent);
+ Log.d(TAG, "onPasswordFailed");
+ }
+
+ @Override
+ public void onPasswordSucceeded(Context context, Intent intent) {
+ super.onPasswordSucceeded(context, intent);
+ Log.d(TAG, "onPasswordSucceeded");
+ }
+
+ @Override
+ public void onReceiveAPIResult(Map arg0, int arg1) {
+ // We have already cleaned data from device.
+
+ }
+
+
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/utils/CommonDialogUtils.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/utils/CommonDialogUtils.java
new file mode 100644
index 0000000000..91d369cda9
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/utils/CommonDialogUtils.java
@@ -0,0 +1,208 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent.utils;
+
+import org.wso2.cdm.agent.R;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+
+/**
+ *
+ * The CommonDialogUtils class contains the all dialog templates.
+ *
+ */
+public abstract class CommonDialogUtils {
+
+ /**
+ * Return an Alert Dialog with one button.
+ *
+ * @param context
+ * the Activity which needs this alert dialog
+ * @param message
+ * the message in the alert
+ * @param positiveBtnLabel
+ * the label of the positive button
+ * @param positiveClickListener
+ * the onClickListener of the positive button
+ *
+ * @return the generated Alert Dialog
+ */
+ public static AlertDialog.Builder getAlertDialogWithOneButton(Context context,
+ String message,
+ String positiveBtnLabel,
+ DialogInterface.OnClickListener positiveClickListener) {
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setMessage(message).setPositiveButton(positiveBtnLabel, positiveClickListener);
+
+ return builder;
+ }
+
+ /**
+ * Return an Alert Dialog with two buttons.
+ *
+ * @param context
+ * @param context
+ * the Activity which needs this alert dialog
+ * @param message
+ * the message in the alert
+ * @param positiveBtnLabel
+ * the label of the positive button
+ * @param negetiveBtnLabel
+ * the label of the negative button
+ * @param positiveClickListener
+ * the onClickListener of the positive button
+ * @param negativeClickListener
+ * the onClickListener of the negative button
+ *
+ * @return the generated Alert Dialog.
+ */
+ public static AlertDialog.Builder getAlertDialogWithTwoButton(Context context,
+ String message,
+ String positiveBtnLabel,
+ String negetiveBtnLabel,
+ DialogInterface.OnClickListener positiveClickListener,
+ DialogInterface.OnClickListener negativeClickListener) {
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setMessage(message).setPositiveButton(positiveBtnLabel, positiveClickListener)
+ .setNegativeButton(negetiveBtnLabel, negativeClickListener);
+
+ return builder;
+ }
+
+ /**
+ * Shows the Network unavailable message.
+ *
+ * @param context
+ * the Activity where checking the network availability.
+ */
+ public static void showNetworkUnavailableMessage(Context context) {
+ AlertDialog.Builder builder =
+ CommonDialogUtils.getAlertDialogWithOneButton(context,
+ context.getResources()
+ .getString(R.string.error_network_unavailable),
+ context.getResources()
+ .getString(R.string.button_ok),
+ null);
+ builder.show();
+ }
+
+ public static AlertDialog.Builder getAlertDialogWithTwoButtonAndTitle(Context context,
+ String title,
+ String message,
+ String positiveBtnLabel,
+ String negetiveBtnLabel,
+ DialogInterface.OnClickListener positiveClickListener,
+ DialogInterface.OnClickListener negativeClickListener) {
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(title);
+ builder.setMessage(message).setPositiveButton(positiveBtnLabel, positiveClickListener)
+ .setNegativeButton(negetiveBtnLabel, negativeClickListener);
+
+ return builder;
+ }
+
+ /**
+ * Returns an Alert Dialog with one button and title.
+ *
+ * @param context
+ * the activity which need this alert.
+ * @param title
+ * the alert title
+ * @param message
+ * the alert message
+ * @param positiveBtnLabel
+ * the positive button label
+ * @param positiveClickListener
+ * the positive button listener
+ *
+ * @return an alert dialog
+ */
+ public static AlertDialog.Builder getAlertDialogWithOneButtonAndTitle(Context context,
+ String title,
+ String message,
+ String positiveBtnLabel,
+ DialogInterface.OnClickListener positiveClickListener) {
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(title);
+ builder.setMessage(message).setPositiveButton(positiveBtnLabel, positiveClickListener);
+ builder.show();
+ return builder;
+ }
+
+ /**
+ * Shows the ProgressDialog.
+ *
+ * @param context
+ * the Activity which needs the ProgressDialog
+ * @param title
+ * the title
+ * @param message
+ * the message
+ * @param cancelListener
+ * the OnCancelListener
+ */
+ public static ProgressDialog showPrgressDialog(Context context, String title, String message,
+ OnCancelListener cancelListener) {
+ ProgressDialog progressDialog = ProgressDialog.show(context, title, message, true);
+ progressDialog.setCancelable(true);
+ progressDialog.setOnCancelListener(cancelListener);
+
+ return progressDialog;
+ }
+
+ /**
+ * Stops progressDialog.
+ *
+ */
+ public static void stopProgressDialog(ProgressDialog progressDialog) {
+ if (progressDialog != null && progressDialog.isShowing()) {
+ progressDialog.dismiss();
+ }
+ }
+
+ public static AlertDialog.Builder getAlertDialogWithTwoButtonAndEditView(Context context,
+ String message,
+ String positiveBtnLabel,
+ String negetiveBtnLabel,
+ DialogInterface.OnClickListener positiveClickListener,
+ DialogInterface.OnClickListener negativeClickListener,
+ EditText input) {
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setMessage(message).setPositiveButton(positiveBtnLabel, positiveClickListener)
+ .setNegativeButton(negetiveBtnLabel, negativeClickListener);
+
+ LinearLayout.LayoutParams lp =
+ new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.MATCH_PARENT);
+ input.setLayoutParams(lp);
+ builder.setView(input);
+
+ return builder;
+ }
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/utils/CommonUtilities.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/utils/CommonUtilities.java
new file mode 100644
index 0000000000..a16f878114
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/utils/CommonUtilities.java
@@ -0,0 +1,196 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.wso2.cdm.agent.utils;
+
+import org.wso2.cdm.agent.R;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+
+/**
+ * Helper class providing methods and constants common to other classes in the
+ * app.
+ */
+public class CommonUtilities {
+ public static boolean DEBUG_MODE_ENABLED = true;
+ public static boolean LOCAL_NOTIFICATIONS_ENABLED = true;
+ public static boolean GCM_ENABLED = false;
+
+ public static String SERVER_IP = "";
+
+ public static String SERVER_PORT = "9763";
+ public static String SERVER_PROTOCOL = "http://";
+ public static String API_VERSION = "1.0.0";
+ public static String API_SERVER_PORT = "8280";
+
+ public static String SERVER_APP_ENDPOINT = "/cdm-android-api/";
+ public static String SERVER_AUTHENTICATION_ENDPOINT="register/authenticate/device";
+ public static String LICENSE_ENDPOINT = "/license/"+API_VERSION;
+ public static String REGISTER_ENDPOINT = "/enroll/"+API_VERSION;
+
+ public static String OAUTH_ENDPOINT = "/oauth2/token";
+ public static String SENDER_ID_ENDPOINT = "devices/sender_id/";
+ public static String IS_REGISTERED_ENDPOINT = "devices/isregistered/";
+ public static String UNREGISTER_ENDPOINT = "devices/unregister/";
+ public static String NOTIFICATION_ENDPOINT = "/operations/"+API_VERSION;
+
+ public static String SERVER_URL = SERVER_PROTOCOL + SERVER_IP + ":"
+ + SERVER_PORT ;
+ public static String API_SERVER_URL;
+
+
+ public static final String TRUSTSTORE_PASSWORD = "wso2carbon";
+ public static final String EULA_TITLE = "POLICY AGREEMENT";
+ public static final String EULA_TEXT = "Test policy agreement.";
+
+ /* Added for OAuth implementation */
+ public static String CLIENT_ID = "";
+ public static String CLIENT_SECRET = "";
+
+
+ public static final String EMPTY_STRING = "";
+ public static final String NULL_STRING = "null";
+ public static final String STATUS_KEY = "status";
+
+ /* Request codes. */
+ public static final int REGISTER_REQUEST_CODE = 300;
+ public static final int IS_REGISTERED_REQUEST_CODE = 301;
+ public static final int SENDER_ID_REQUEST_CODE = 303;
+ public static final int LICENSE_REQUEST_CODE = 304;
+ public static final int UNREGISTER_REQUEST_CODE = 305;
+ public static final int NOTIFICATION_REQUEST_CODE = 306;
+
+ /* Method types. */
+ public static final String GET_METHOD = "GET";
+ public static final String POST_METHOD = "POST";
+
+
+ public static String getSERVER_URL() {
+ return SERVER_URL;
+ }
+
+ public static void setServerURL(String serverIP) {
+ SERVER_IP = serverIP;
+ SERVER_URL = SERVER_PROTOCOL + serverIP + ":" + SERVER_PORT
+ +SERVER_APP_ENDPOINT;
+ API_SERVER_URL=SERVER_PROTOCOL + SERVER_IP + ":" + API_SERVER_PORT ;
+ }
+
+ /**
+ * Google API project id registered to use GCM.
+ */
+
+ public static String SENDER_ID = "";
+
+ public static String getSENDER_ID() {
+ return SENDER_ID;
+ }
+
+ public static void setSENDER_ID(String sENDER_ID) {
+ SENDER_ID = sENDER_ID;
+ }
+
+ /**
+ * Tag used on log messages.
+ */
+ public static final String TAG = "WSO2EMM";
+
+ /**
+ * Intent used to display a message in the screen.
+ */
+ public static final String DISPLAY_MESSAGE_ACTION = "com.google.android.gcm.demo.app.DISPLAY_MESSAGE";
+
+ /**
+ * Intent's extra that contains the message to be displayed.
+ */
+ public static final String EXTRA_MESSAGE = "message";
+ public static final int MESSAGE_MODE_GCM = 1;
+ public static final int MESSAGE_MODE_SMS = 2;
+ public static final int MESSAGE_MODE_LOCAL = 3;
+
+
+ /**
+ * Status codes
+ */
+ public static final String REQUEST_SUCCESSFUL = "200";
+ public static final String REGISTERATION_SUCCESSFUL = "201";
+ public static final String REQUEST_FAILED = "500";
+ public static final String AUTHENTICATION_FAILED = "400";
+ public static final String UNAUTHORIZED_ACCESS = "401";
+ public static final String NOT_FOUND = "404";
+ public static final String INTERNAL_SERVER_ERROR = "500";
+
+
+ /**
+ * Operation IDs
+ */
+ public static final String OPERATION_DEVICE_INFO = "500A";
+ public static final String OPERATION_DEVICE_LOCATION = "501A";
+ public static final String OPERATION_GET_APPLICATION_LIST = "502A";
+ public static final String OPERATION_LOCK_DEVICE = "503A";
+ public static final String OPERATION_WIPE_DATA = "504A";//reset device
+ public static final String OPERATION_CLEAR_PASSWORD = "505A";
+ public static final String OPERATION_NOTIFICATION = "506A";
+ public static final String OPERATION_WIFI = "507A";
+ public static final String OPERATION_DISABLE_CAMERA = "508A";
+ public static final String OPERATION_INSTALL_APPLICATION = "509A";
+ public static final String OPERATION_INSTALL_APPLICATION_BUNDLE = "509B";
+ public static final String OPERATION_UNINSTALL_APPLICATION = "510A";
+ public static final String OPERATION_ENCRYPT_STORAGE = "511A";
+ public static final String OPERATION_APN = "512A";
+ public static final String OPERATION_MUTE = "513A";
+ public static final String OPERATION_TRACK_CALLS = "514A";
+ public static final String OPERATION_TRACK_SMS = "515A";
+ public static final String OPERATION_DATA_USAGE = "516A";
+ public static final String OPERATION_STATUS = "517A";
+ public static final String OPERATION_WEBCLIP = "518A";
+ public static final String OPERATION_PASSWORD_POLICY = "519A";
+ public static final String OPERATION_EMAIL_CONFIGURATION = "520A";
+ public static final String OPERATION_INSTALL_GOOGLE_APP = "522A";
+ public static final String OPERATION_CHANGE_LOCK_CODE = "526A";
+ public static final String OPERATION_ENTERPRISE_WIPE_DATA = "527A";//unnregister device
+ public static final String OPERATION_POLICY_BUNDLE = "500P";
+ public static final String OPERATION_POLICY_MONITOR = "501P";
+ public static final String OPERATION_BLACKLIST_APPS = "528B";
+ public static final String OPERATION_POLICY_REVOKE = "502P";
+
+ /**
+ * Notifies UI to display a message.
+ *
+ * This method is defined in the common helper because it's used both by the
+ * UI and the background service.
+ *
+ * @param context
+ * application's context.
+ * @param message
+ * message to be displayed.
+ */
+ public static void displayMessage(Context context, String message) {
+ Intent intent = new Intent(DISPLAY_MESSAGE_ACTION);
+ intent.putExtra(EXTRA_MESSAGE, message);
+ context.sendBroadcast(intent);
+ }
+
+ public static String getPref(Context context, String key) {
+ SharedPreferences mainPref = context.getSharedPreferences(context
+ .getResources().getString(R.string.shared_pref_package),
+ Context.MODE_PRIVATE);
+ return mainPref.getString(key, "");
+ }
+
+}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/utils/Constant.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/utils/Constant.java
index 8e551e336a..fd7dfcafb8 100644
--- a/product/modules/agents/android/client/src/org/wso2/cdm/agent/utils/Constant.java
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/utils/Constant.java
@@ -23,5 +23,8 @@ public class Constant {
public static final String PASSWORD = "password";
public static final String STATUS = "status";
public static final String RESPONSE = "response";
+ public static final String PROPERTIES = "properties";
+ public static final String CODE = "code";
+ public static final String LOCAL = "LOCAL";
}
diff --git a/product/modules/agents/android/client/src/org/wso2/cdm/agent/utils/HTTPConnectorUtils.java b/product/modules/agents/android/client/src/org/wso2/cdm/agent/utils/HTTPConnectorUtils.java
new file mode 100644
index 0000000000..0465291449
--- /dev/null
+++ b/product/modules/agents/android/client/src/org/wso2/cdm/agent/utils/HTTPConnectorUtils.java
@@ -0,0 +1,248 @@
+/**
+ * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wso2.cdm.agent.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.security.KeyStore;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.apache.http.HeaderElement;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HTTP;
+import org.wso2.cdm.agent.R;
+import android.content.Context;
+import android.net.ParseException;
+import android.util.Log;
+
+public class HTTPConnectorUtils {
+
+ public static String TAG = ServerUtils.class.getSimpleName();
+
+ private static final int MAX_ATTEMPTS = 2;
+
+ public static Map postData(Context context, String url,
+ Map params) {
+ Map response = null;
+ Map responseFinal = null;
+ for (int i = 1; i <= MAX_ATTEMPTS; i++) {
+ Log.d(TAG, "Attempt #" + i + " to register " + url);
+ try {
+
+ response = postToServer(context, url, params);
+ if (response != null && !response.equals(null)) {
+ responseFinal = response;
+ }
+ Log.d("Success", "Check Reg Success");
+
+ return responseFinal;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to register on attempt " + i, e);
+ if (i == MAX_ATTEMPTS) {
+ break;
+ }
+
+ return responseFinal;
+ }
+ }
+
+ return responseFinal;
+ }
+
+ public static Map postToServer(Context context, String url,
+ Map params) {
+ // Create a new HttpClient and Post Header
+ Map response_params = new HashMap();
+ HttpClient httpclient = getCertifiedHttpClient(context);
+
+ StringBuilder bodyBuilder = new StringBuilder();
+ if (params != null) {
+ Iterator> iterator = params.entrySet().iterator();
+ // constructs the POST body using the parameters
+ while (iterator.hasNext()) {
+ Entry