From 6d105f48440e826ff7de642cc3b6157433561868 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Mon, 5 Nov 2012 20:17:28 +0530 Subject: [PATCH] Added ActionBarSherlock library compatiblity support --- libs/ActionBarSherlock/.classpath | 8 + libs/ActionBarSherlock/.project | 33 + libs/ActionBarSherlock/AndroidManifest.xml | 6 + libs/ActionBarSherlock/README.md | 15 + libs/ActionBarSherlock/build.xml | 92 + .../libs/android-support-v4.jar | Bin 0 -> 271754 bytes libs/ActionBarSherlock/pom.xml | 151 ++ libs/ActionBarSherlock/proguard-project.txt | 20 + libs/ActionBarSherlock/project.properties | 12 + ...s__primary_text_disable_only_holo_dark.xml | 20 + ...__primary_text_disable_only_holo_light.xml | 21 + .../res/color/abs__primary_text_holo_dark.xml | 24 + .../color/abs__primary_text_holo_light.xml | 26 + .../abs__ab_bottom_solid_dark_holo.9.png | Bin 0 -> 144 bytes .../abs__ab_bottom_solid_inverse_holo.9.png | Bin 0 -> 138 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 0 -> 144 bytes ...abs__ab_bottom_transparent_dark_holo.9.png | Bin 0 -> 135 bytes ...bs__ab_bottom_transparent_light_holo.9.png | Bin 0 -> 134 bytes .../abs__ab_share_pack_holo_dark.9.png | Bin 0 -> 156 bytes .../abs__ab_share_pack_holo_light.9.png | Bin 0 -> 156 bytes .../abs__ab_solid_dark_holo.9.png | Bin 0 -> 146 bytes .../abs__ab_solid_light_holo.9.png | Bin 0 -> 145 bytes .../abs__ab_solid_shadow_holo.9.png | Bin 0 -> 192 bytes .../abs__ab_stacked_solid_dark_holo.9.png | Bin 0 -> 146 bytes .../abs__ab_stacked_solid_light_holo.9.png | Bin 0 -> 146 bytes ...bs__ab_stacked_transparent_dark_holo.9.png | Bin 0 -> 139 bytes ...s__ab_stacked_transparent_light_holo.9.png | Bin 0 -> 133 bytes .../abs__ab_transparent_dark_holo.9.png | Bin 0 -> 155 bytes .../abs__ab_transparent_light_holo.9.png | Bin 0 -> 145 bytes .../abs__btn_cab_done_default_holo_dark.9.png | Bin 0 -> 104 bytes ...abs__btn_cab_done_default_holo_light.9.png | Bin 0 -> 102 bytes .../abs__btn_cab_done_focused_holo_dark.9.png | Bin 0 -> 112 bytes ...abs__btn_cab_done_focused_holo_light.9.png | Bin 0 -> 108 bytes .../abs__btn_cab_done_pressed_holo_dark.9.png | Bin 0 -> 110 bytes ...abs__btn_cab_done_pressed_holo_light.9.png | Bin 0 -> 108 bytes ...abs__cab_background_bottom_holo_dark.9.png | Bin 0 -> 148 bytes ...bs__cab_background_bottom_holo_light.9.png | Bin 0 -> 145 bytes .../abs__cab_background_top_holo_dark.9.png | Bin 0 -> 147 bytes .../abs__cab_background_top_holo_light.9.png | Bin 0 -> 147 bytes .../abs__dialog_full_holo_dark.9.png | Bin 0 -> 1414 bytes .../abs__dialog_full_holo_light.9.png | Bin 0 -> 1537 bytes .../abs__ic_ab_back_holo_dark.png | Bin 0 -> 545 bytes .../abs__ic_ab_back_holo_light.png | Bin 0 -> 432 bytes .../abs__ic_cab_done_holo_dark.png | Bin 0 -> 713 bytes .../abs__ic_cab_done_holo_light.png | Bin 0 -> 592 bytes ..._ic_menu_moreoverflow_normal_holo_dark.png | Bin 0 -> 121 bytes ...ic_menu_moreoverflow_normal_holo_light.png | Bin 0 -> 131 bytes .../abs__ic_menu_share_holo_dark.png | Bin 0 -> 467 bytes .../abs__ic_menu_share_holo_light.png | Bin 0 -> 505 bytes .../abs__list_activated_holo.9.png | Bin 0 -> 135 bytes .../abs__list_divider_holo_dark.9.png | Bin 0 -> 78 bytes .../abs__list_divider_holo_light.9.png | Bin 0 -> 76 bytes .../abs__list_focused_holo.9.png | Bin 0 -> 135 bytes .../abs__list_longpressed_holo.9.png | Bin 0 -> 135 bytes .../abs__list_pressed_holo_dark.9.png | Bin 0 -> 135 bytes .../abs__list_pressed_holo_light.9.png | Bin 0 -> 135 bytes ...bs__list_selector_disabled_holo_dark.9.png | Bin 0 -> 152 bytes ...s__list_selector_disabled_holo_light.9.png | Bin 0 -> 152 bytes .../abs__menu_dropdown_panel_holo_dark.9.png | Bin 0 -> 922 bytes .../abs__menu_dropdown_panel_holo_light.9.png | Bin 0 -> 1061 bytes .../abs__progress_bg_holo_dark.9.png | Bin 0 -> 154 bytes .../abs__progress_bg_holo_light.9.png | Bin 0 -> 154 bytes .../abs__progress_primary_holo_dark.9.png | Bin 0 -> 917 bytes .../abs__progress_primary_holo_light.9.png | Bin 0 -> 917 bytes .../abs__progress_secondary_holo_dark.9.png | Bin 0 -> 154 bytes .../abs__progress_secondary_holo_light.9.png | Bin 0 -> 154 bytes .../abs__spinner_48_inner_holo.png | Bin 0 -> 1793 bytes .../abs__spinner_48_outer_holo.png | Bin 0 -> 1811 bytes .../abs__spinner_ab_default_holo_dark.9.png | Bin 0 -> 277 bytes .../abs__spinner_ab_default_holo_light.9.png | Bin 0 -> 277 bytes .../abs__spinner_ab_disabled_holo_dark.9.png | Bin 0 -> 262 bytes .../abs__spinner_ab_disabled_holo_light.9.png | Bin 0 -> 262 bytes .../abs__spinner_ab_focused_holo_dark.9.png | Bin 0 -> 387 bytes .../abs__spinner_ab_focused_holo_light.9.png | Bin 0 -> 387 bytes .../abs__spinner_ab_pressed_holo_dark.9.png | Bin 0 -> 350 bytes .../abs__spinner_ab_pressed_holo_light.9.png | Bin 0 -> 350 bytes .../abs__tab_selected_focused_holo.9.png | Bin 0 -> 137 bytes .../abs__tab_selected_holo.9.png | Bin 0 -> 126 bytes .../abs__tab_selected_pressed_holo.9.png | Bin 0 -> 137 bytes .../abs__tab_unselected_pressed_holo.9.png | Bin 0 -> 138 bytes .../abs__ab_bottom_solid_dark_holo.9.png | Bin 0 -> 134 bytes .../abs__ab_bottom_solid_inverse_holo.9.png | Bin 0 -> 129 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 0 -> 134 bytes ...abs__ab_bottom_transparent_dark_holo.9.png | Bin 0 -> 123 bytes ...bs__ab_bottom_transparent_light_holo.9.png | Bin 0 -> 123 bytes .../abs__ab_share_pack_holo_dark.9.png | Bin 0 -> 153 bytes .../abs__ab_share_pack_holo_light.9.png | Bin 0 -> 122 bytes .../abs__ab_solid_dark_holo.9.png | Bin 0 -> 133 bytes .../abs__ab_solid_light_holo.9.png | Bin 0 -> 133 bytes .../abs__ab_solid_shadow_holo.9.png | Bin 0 -> 168 bytes .../abs__ab_stacked_solid_dark_holo.9.png | Bin 0 -> 134 bytes .../abs__ab_stacked_solid_light_holo.9.png | Bin 0 -> 133 bytes ...bs__ab_stacked_transparent_dark_holo.9.png | Bin 0 -> 127 bytes ...s__ab_stacked_transparent_light_holo.9.png | Bin 0 -> 123 bytes .../abs__ab_transparent_dark_holo.9.png | Bin 0 -> 139 bytes .../abs__ab_transparent_light_holo.9.png | Bin 0 -> 133 bytes .../abs__btn_cab_done_default_holo_dark.9.png | Bin 0 -> 101 bytes ...abs__btn_cab_done_default_holo_light.9.png | Bin 0 -> 99 bytes .../abs__btn_cab_done_focused_holo_dark.9.png | Bin 0 -> 109 bytes ...abs__btn_cab_done_focused_holo_light.9.png | Bin 0 -> 105 bytes .../abs__btn_cab_done_pressed_holo_dark.9.png | Bin 0 -> 107 bytes ...abs__btn_cab_done_pressed_holo_light.9.png | Bin 0 -> 105 bytes ...abs__cab_background_bottom_holo_dark.9.png | Bin 0 -> 127 bytes ...bs__cab_background_bottom_holo_light.9.png | Bin 0 -> 124 bytes .../abs__cab_background_top_holo_dark.9.png | Bin 0 -> 130 bytes .../abs__cab_background_top_holo_light.9.png | Bin 0 -> 128 bytes .../abs__dialog_full_holo_dark.9.png | Bin 0 -> 882 bytes .../abs__dialog_full_holo_light.9.png | Bin 0 -> 1003 bytes .../abs__ic_ab_back_holo_dark.png | Bin 0 -> 375 bytes .../abs__ic_ab_back_holo_light.png | Bin 0 -> 308 bytes .../abs__ic_cab_done_holo_dark.png | Bin 0 -> 530 bytes .../abs__ic_cab_done_holo_light.png | Bin 0 -> 446 bytes ..._ic_menu_moreoverflow_normal_holo_dark.png | Bin 0 -> 119 bytes ...ic_menu_moreoverflow_normal_holo_light.png | Bin 0 -> 130 bytes .../abs__ic_menu_share_holo_dark.png | Bin 0 -> 332 bytes .../abs__ic_menu_share_holo_light.png | Bin 0 -> 355 bytes .../abs__list_activated_holo.9.png | Bin 0 -> 131 bytes .../abs__list_divider_holo_dark.9.png | Bin 0 -> 78 bytes .../abs__list_divider_holo_light.9.png | Bin 0 -> 76 bytes .../abs__list_focused_holo.9.png | Bin 0 -> 131 bytes .../abs__list_longpressed_holo.9.png | Bin 0 -> 131 bytes .../abs__list_pressed_holo_dark.9.png | Bin 0 -> 131 bytes .../abs__list_pressed_holo_light.9.png | Bin 0 -> 131 bytes ...bs__list_selector_disabled_holo_dark.9.png | Bin 0 -> 147 bytes ...s__list_selector_disabled_holo_light.9.png | Bin 0 -> 147 bytes .../abs__menu_dropdown_panel_holo_dark.9.png | Bin 0 -> 651 bytes .../abs__menu_dropdown_panel_holo_light.9.png | Bin 0 -> 720 bytes .../abs__progress_bg_holo_dark.9.png | Bin 0 -> 138 bytes .../abs__progress_bg_holo_light.9.png | Bin 0 -> 138 bytes .../abs__progress_primary_holo_dark.9.png | Bin 0 -> 572 bytes .../abs__progress_primary_holo_light.9.png | Bin 0 -> 572 bytes .../abs__progress_secondary_holo_dark.9.png | Bin 0 -> 138 bytes .../abs__progress_secondary_holo_light.9.png | Bin 0 -> 138 bytes .../abs__spinner_48_inner_holo.png | Bin 0 -> 1105 bytes .../abs__spinner_48_outer_holo.png | Bin 0 -> 1165 bytes .../abs__spinner_ab_default_holo_dark.9.png | Bin 0 -> 244 bytes .../abs__spinner_ab_default_holo_light.9.png | Bin 0 -> 244 bytes .../abs__spinner_ab_disabled_holo_dark.9.png | Bin 0 -> 244 bytes .../abs__spinner_ab_disabled_holo_light.9.png | Bin 0 -> 244 bytes .../abs__spinner_ab_focused_holo_dark.9.png | Bin 0 -> 324 bytes .../abs__spinner_ab_focused_holo_light.9.png | Bin 0 -> 324 bytes .../abs__spinner_ab_pressed_holo_dark.9.png | Bin 0 -> 279 bytes .../abs__spinner_ab_pressed_holo_light.9.png | Bin 0 -> 279 bytes .../abs__tab_selected_focused_holo.9.png | Bin 0 -> 133 bytes .../abs__tab_selected_holo.9.png | Bin 0 -> 122 bytes .../abs__tab_selected_pressed_holo.9.png | Bin 0 -> 133 bytes .../abs__tab_unselected_pressed_holo.9.png | Bin 0 -> 140 bytes .../abs__progress_medium_holo.xml | 34 + .../abs__ab_bottom_solid_dark_holo.9.png | Bin 0 -> 165 bytes .../abs__ab_bottom_solid_inverse_holo.9.png | Bin 0 -> 157 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 0 -> 166 bytes ...abs__ab_bottom_transparent_dark_holo.9.png | Bin 0 -> 153 bytes ...bs__ab_bottom_transparent_light_holo.9.png | Bin 0 -> 152 bytes .../abs__ab_share_pack_holo_dark.9.png | Bin 0 -> 159 bytes .../abs__ab_share_pack_holo_light.9.png | Bin 0 -> 159 bytes .../abs__ab_solid_dark_holo.9.png | Bin 0 -> 163 bytes .../abs__ab_solid_light_holo.9.png | Bin 0 -> 163 bytes .../abs__ab_solid_shadow_holo.9.png | Bin 0 -> 290 bytes .../abs__ab_stacked_solid_dark_holo.9.png | Bin 0 -> 163 bytes .../abs__ab_stacked_solid_light_holo.9.png | Bin 0 -> 163 bytes ...bs__ab_stacked_transparent_dark_holo.9.png | Bin 0 -> 158 bytes ...s__ab_stacked_transparent_light_holo.9.png | Bin 0 -> 152 bytes .../abs__ab_transparent_dark_holo.9.png | Bin 0 -> 166 bytes .../abs__ab_transparent_light_holo.9.png | Bin 0 -> 153 bytes .../abs__btn_cab_done_default_holo_dark.9.png | Bin 0 -> 109 bytes ...abs__btn_cab_done_default_holo_light.9.png | Bin 0 -> 108 bytes .../abs__btn_cab_done_focused_holo_dark.9.png | Bin 0 -> 112 bytes ...abs__btn_cab_done_focused_holo_light.9.png | Bin 0 -> 113 bytes .../abs__btn_cab_done_pressed_holo_dark.9.png | Bin 0 -> 115 bytes ...abs__btn_cab_done_pressed_holo_light.9.png | Bin 0 -> 113 bytes ...abs__cab_background_bottom_holo_dark.9.png | Bin 0 -> 151 bytes ...bs__cab_background_bottom_holo_light.9.png | Bin 0 -> 151 bytes .../abs__cab_background_top_holo_dark.9.png | Bin 0 -> 162 bytes .../abs__cab_background_top_holo_light.9.png | Bin 0 -> 149 bytes .../abs__dialog_full_holo_dark.9.png | Bin 0 -> 2159 bytes .../abs__dialog_full_holo_light.9.png | Bin 0 -> 2302 bytes .../abs__ic_ab_back_holo_dark.png | Bin 0 -> 619 bytes .../abs__ic_ab_back_holo_light.png | Bin 0 -> 514 bytes .../abs__ic_cab_done_holo_dark.png | Bin 0 -> 970 bytes .../abs__ic_cab_done_holo_light.png | Bin 0 -> 756 bytes ..._ic_menu_moreoverflow_normal_holo_dark.png | Bin 0 -> 122 bytes ...ic_menu_moreoverflow_normal_holo_light.png | Bin 0 -> 140 bytes .../abs__ic_menu_share_holo_dark.png | Bin 0 -> 699 bytes .../abs__ic_menu_share_holo_light.png | Bin 0 -> 838 bytes .../abs__list_activated_holo.9.png | Bin 0 -> 139 bytes .../abs__list_divider_holo_dark.9.png | Bin 0 -> 83 bytes .../abs__list_divider_holo_light.9.png | Bin 0 -> 83 bytes .../abs__list_focused_holo.9.png | Bin 0 -> 139 bytes .../abs__list_longpressed_holo.9.png | Bin 0 -> 139 bytes .../abs__list_pressed_holo_dark.9.png | Bin 0 -> 139 bytes .../abs__list_pressed_holo_light.9.png | Bin 0 -> 139 bytes ...bs__list_selector_disabled_holo_dark.9.png | Bin 0 -> 161 bytes ...s__list_selector_disabled_holo_light.9.png | Bin 0 -> 155 bytes .../abs__menu_dropdown_panel_holo_dark.9.png | Bin 0 -> 1362 bytes .../abs__menu_dropdown_panel_holo_light.9.png | Bin 0 -> 1551 bytes .../abs__progress_bg_holo_dark.9.png | Bin 0 -> 146 bytes .../abs__progress_bg_holo_light.9.png | Bin 0 -> 146 bytes .../abs__progress_primary_holo_dark.9.png | Bin 0 -> 1192 bytes .../abs__progress_primary_holo_light.9.png | Bin 0 -> 1192 bytes .../abs__progress_secondary_holo_dark.9.png | Bin 0 -> 146 bytes .../abs__progress_secondary_holo_light.9.png | Bin 0 -> 146 bytes .../abs__spinner_48_inner_holo.png | Bin 0 -> 2413 bytes .../abs__spinner_48_outer_holo.png | Bin 0 -> 2432 bytes .../abs__spinner_ab_default_holo_dark.9.png | Bin 0 -> 313 bytes .../abs__spinner_ab_default_holo_light.9.png | Bin 0 -> 313 bytes .../abs__spinner_ab_disabled_holo_dark.9.png | Bin 0 -> 302 bytes .../abs__spinner_ab_disabled_holo_light.9.png | Bin 0 -> 302 bytes .../abs__spinner_ab_focused_holo_dark.9.png | Bin 0 -> 474 bytes .../abs__spinner_ab_focused_holo_light.9.png | Bin 0 -> 474 bytes .../abs__spinner_ab_pressed_holo_dark.9.png | Bin 0 -> 430 bytes .../abs__spinner_ab_pressed_holo_light.9.png | Bin 0 -> 430 bytes .../abs__tab_selected_focused_holo.9.png | Bin 0 -> 138 bytes .../abs__tab_selected_holo.9.png | Bin 0 -> 130 bytes .../abs__tab_selected_pressed_holo.9.png | Bin 0 -> 138 bytes .../abs__tab_unselected_pressed_holo.9.png | Bin 0 -> 148 bytes .../abs__activated_background_holo_dark.xml | 20 + .../abs__activated_background_holo_light.xml | 20 + .../drawable/abs__btn_cab_done_holo_dark.xml | 24 + .../drawable/abs__btn_cab_done_holo_light.xml | 24 + .../abs__ic_menu_moreoverflow_holo_dark.xml | 18 + .../abs__ic_menu_moreoverflow_holo_light.xml | 18 + .../abs__item_background_holo_dark.xml | 26 + .../abs__item_background_holo_light.xml | 26 + ...lector_background_transition_holo_dark.xml | 20 + ...ector_background_transition_holo_light.xml | 20 + .../drawable/abs__list_selector_holo_dark.xml | 27 + .../abs__list_selector_holo_light.xml | 28 + .../abs__progress_horizontal_holo_dark.xml | 32 + .../abs__progress_horizontal_holo_light.xml | 32 + .../drawable/abs__progress_medium_holo.xml | 34 + .../drawable/abs__spinner_ab_holo_dark.xml | 25 + .../drawable/abs__spinner_ab_holo_light.xml | 25 + .../drawable/abs__tab_indicator_ab_holo.xml | 34 + .../abs__action_mode_close_item.xml | 40 + .../sherlock_spinner_dropdown_item.xml | 26 + .../res/layout-v14/sherlock_spinner_item.xml | 26 + .../layout-xlarge/abs__screen_action_bar.xml | 50 + .../abs__screen_action_bar_overlay.xml | 49 + .../res/layout/abs__action_bar_home.xml | 38 + .../res/layout/abs__action_bar_tab.xml | 7 + .../layout/abs__action_bar_tab_bar_view.xml | 6 + .../res/layout/abs__action_bar_title_item.xml | 50 + .../layout/abs__action_menu_item_layout.xml | 56 + .../res/layout/abs__action_menu_layout.xml | 23 + .../res/layout/abs__action_mode_bar.xml | 24 + .../layout/abs__action_mode_close_item.xml | 31 + .../res/layout/abs__activity_chooser_view.xml | 70 + .../abs__activity_chooser_view_list_item.xml | 53 + .../res/layout/abs__dialog_title_holo.xml | 46 + .../layout/abs__list_menu_item_checkbox.xml | 26 + .../res/layout/abs__list_menu_item_icon.xml | 28 + .../res/layout/abs__list_menu_item_layout.xml | 59 + .../res/layout/abs__list_menu_item_radio.xml | 24 + .../layout/abs__popup_menu_item_layout.xml | 60 + .../res/layout/abs__screen_action_bar.xml | 57 + .../layout/abs__screen_action_bar_overlay.xml | 59 + .../res/layout/abs__screen_simple.xml | 38 + ...abs__screen_simple_overlay_action_mode.xml | 38 + .../layout/sherlock_spinner_dropdown_item.xml | 26 + .../res/layout/sherlock_spinner_item.xml | 26 + .../res/values-land/abs__dimens.xml | 33 + .../abs__dimens.xml | 33 + .../abs__dimens.xml | 33 + .../abs__dimens.xml | 33 + .../abs__dimens.xml | 36 + .../res/values-large/abs__dimens.xml | 29 + .../res/values-sw600dp/abs__bools.xml | 19 + .../res/values-sw600dp/abs__dimens.xml | 38 + .../res/values-v11/abs__themes.xml | 12 + .../res/values-v14/abs__styles.xml | 118 ++ .../res/values-v14/abs__themes.xml | 32 + .../res/values-w360dp/abs__dimens.xml | 22 + .../res/values-w480dp/abs__bools.xml | 22 + .../res/values-w480dp/abs__config.xml | 29 + .../res/values-w500dp/abs__dimens.xml | 22 + .../res/values-w600dp/abs__dimens.xml | 22 + .../res/values-xlarge/abs__dimens.xml | 45 + .../res/values/abs__attrs.xml | 380 ++++ .../res/values/abs__bools.xml | 22 + .../res/values/abs__colors.xml | 27 + .../res/values/abs__config.xml | 43 + .../res/values/abs__dimens.xml | 50 + .../ActionBarSherlock/res/values/abs__ids.xml | 26 + .../res/values/abs__strings.xml | 42 + .../res/values/abs__styles.xml | 384 ++++ .../res/values/abs__themes.xml | 226 +++ .../v4/app/_ActionBarSherlockTrojanHorse.java | 144 ++ .../actionbarsherlock/ActionBarSherlock.java | 791 +++++++++ .../com/actionbarsherlock/app/ActionBar.java | 947 ++++++++++ .../app/SherlockActivity.java | 259 +++ .../app/SherlockDialogFragment.java | 68 + .../app/SherlockExpandableListActivity.java | 259 +++ .../app/SherlockFragment.java | 68 + .../app/SherlockFragmentActivity.java | 292 ++++ .../app/SherlockListActivity.java | 259 +++ .../app/SherlockListFragment.java | 68 + .../app/SherlockPreferenceActivity.java | 259 +++ .../internal/ActionBarSherlockCompat.java | 1207 +++++++++++++ .../internal/ActionBarSherlockNative.java | 328 ++++ .../internal/ResourcesCompat.java | 95 + .../internal/app/ActionBarImpl.java | 1026 +++++++++++ .../internal/app/ActionBarWrapper.java | 468 +++++ .../nineoldandroids/animation/Animator.java | 278 +++ .../animation/AnimatorListenerAdapter.java | 54 + .../animation/AnimatorSet.java | 1111 ++++++++++++ .../animation/FloatEvaluator.java | 42 + .../animation/FloatKeyframeSet.java | 136 ++ .../animation/IntEvaluator.java | 42 + .../animation/IntKeyframeSet.java | 135 ++ .../nineoldandroids/animation/Keyframe.java | 361 ++++ .../animation/KeyframeSet.java | 227 +++ .../animation/ObjectAnimator.java | 491 ++++++ .../animation/PropertyValuesHolder.java | 1012 +++++++++++ .../animation/TypeEvaluator.java | 44 + .../animation/ValueAnimator.java | 1265 ++++++++++++++ .../nineoldandroids/view/NineViewGroup.java | 79 + .../view/animation/AnimatorProxy.java | 212 +++ .../widget/NineFrameLayout.java | 65 + .../widget/NineHorizontalScrollView.java | 41 + .../widget/NineLinearLayout.java | 65 + .../internal/view/ActionProviderWrapper.java | 40 + .../internal/view/StandaloneActionMode.java | 148 ++ .../view/View_HasStateListenerSupport.java | 6 + .../View_OnAttachStateChangeListener.java | 8 + .../internal/view/menu/ActionMenu.java | 264 +++ .../internal/view/menu/ActionMenuItem.java | 278 +++ .../view/menu/ActionMenuItemView.java | 295 ++++ .../view/menu/ActionMenuPresenter.java | 721 ++++++++ .../internal/view/menu/ActionMenuView.java | 572 ++++++ .../internal/view/menu/BaseMenuPresenter.java | 231 +++ .../internal/view/menu/ListMenuItemView.java | 278 +++ .../internal/view/menu/MenuBuilder.java | 1335 ++++++++++++++ .../internal/view/menu/MenuItemImpl.java | 647 +++++++ .../internal/view/menu/MenuItemWrapper.java | 292 ++++ .../internal/view/menu/MenuPopupHelper.java | 376 ++++ .../internal/view/menu/MenuPresenter.java | 148 ++ .../internal/view/menu/MenuView.java | 120 ++ .../internal/view/menu/MenuWrapper.java | 180 ++ .../internal/view/menu/SubMenuBuilder.java | 134 ++ .../internal/view/menu/SubMenuWrapper.java | 72 + .../internal/widget/AbsActionBarView.java | 291 ++++ .../internal/widget/ActionBarContainer.java | 245 +++ .../internal/widget/ActionBarContextView.java | 518 ++++++ .../internal/widget/ActionBarView.java | 1548 +++++++++++++++++ .../internal/widget/CapitalizingButton.java | 40 + .../internal/widget/CapitalizingTextView.java | 44 + .../widget/FakeDialogPhoneWindow.java | 64 + .../internal/widget/IcsAbsSpinner.java | 479 +++++ .../internal/widget/IcsAdapterView.java | 1160 ++++++++++++ .../internal/widget/IcsLinearLayout.java | 272 +++ .../internal/widget/IcsListPopupWindow.java | 644 +++++++ .../internal/widget/IcsProgressBar.java | 1193 +++++++++++++ .../internal/widget/IcsSpinner.java | 703 ++++++++ .../internal/widget/IcsView.java | 21 + .../widget/ScrollingTabContainerView.java | 545 ++++++ .../actionbarsherlock/view/ActionMode.java | 224 +++ .../view/ActionProvider.java | 170 ++ .../view/CollapsibleActionView.java | 39 + .../src/com/actionbarsherlock/view/Menu.java | 447 +++++ .../actionbarsherlock/view/MenuInflater.java | 472 +++++ .../com/actionbarsherlock/view/MenuItem.java | 598 +++++++ .../com/actionbarsherlock/view/SubMenu.java | 110 ++ .../com/actionbarsherlock/view/Window.java | 65 + .../widget/ActivityChooserModel.java | 1131 ++++++++++++ .../widget/ActivityChooserView.java | 818 +++++++++ .../widget/ShareActionProvider.java | 316 ++++ .../internal/ManifestParsingTest.java | 39 + libs/android-support-v4.jar | Bin 337562 -> 0 bytes project.properties | 2 + res/values/theme.xml | 4 +- .../wikimedia/commons/CommonsApplication.java | 13 + src/org/wikimedia/commons/ShareActivity.java | 21 +- .../commons/auth/AuthenticatedActivity.java | 14 +- .../wikimedia/commons/auth/LoginActivity.java | 2 +- 374 files changed, 34299 insertions(+), 25 deletions(-) create mode 100644 libs/ActionBarSherlock/.classpath create mode 100644 libs/ActionBarSherlock/.project create mode 100755 libs/ActionBarSherlock/AndroidManifest.xml create mode 100755 libs/ActionBarSherlock/README.md create mode 100644 libs/ActionBarSherlock/build.xml create mode 100755 libs/ActionBarSherlock/libs/android-support-v4.jar create mode 100755 libs/ActionBarSherlock/pom.xml create mode 100644 libs/ActionBarSherlock/proguard-project.txt create mode 100755 libs/ActionBarSherlock/project.properties create mode 100755 libs/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_dark.xml create mode 100755 libs/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_light.xml create mode 100755 libs/ActionBarSherlock/res/color/abs__primary_text_holo_dark.xml create mode 100755 libs/ActionBarSherlock/res/color/abs__primary_text_holo_light.xml create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_inverse_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_transparent_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_transparent_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_share_pack_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_share_pack_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_shadow_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_solid_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_solid_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_transparent_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_transparent_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_transparent_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_transparent_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_default_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_default_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_focused_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_focused_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_bottom_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_bottom_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_top_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_top_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__dialog_full_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__dialog_full_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_ab_back_holo_dark.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_ab_back_holo_light.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_cab_done_holo_dark.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_cab_done_holo_light.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_light.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_share_holo_dark.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_share_holo_light.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__list_activated_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__list_divider_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__list_divider_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__list_focused_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__list_longpressed_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__list_pressed_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__list_pressed_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__list_selector_disabled_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__list_selector_disabled_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__menu_dropdown_panel_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__menu_dropdown_panel_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_bg_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_bg_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_primary_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_primary_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_secondary_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_secondary_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_48_inner_holo.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_48_outer_holo.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_default_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_default_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_disabled_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_disabled_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_focused_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_focused_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_pressed_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_pressed_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_focused_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_pressed_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-hdpi/abs__tab_unselected_pressed_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_inverse_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_transparent_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_transparent_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_share_pack_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_share_pack_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_shadow_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_solid_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_solid_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_transparent_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_transparent_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_transparent_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_transparent_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_default_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_default_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_focused_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_focused_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_bottom_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_bottom_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_top_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_top_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__dialog_full_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__dialog_full_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_ab_back_holo_dark.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_ab_back_holo_light.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_cab_done_holo_dark.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_cab_done_holo_light.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_light.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_share_holo_dark.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_share_holo_light.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__list_activated_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__list_divider_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__list_divider_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__list_focused_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__list_longpressed_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__list_pressed_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__list_pressed_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__list_selector_disabled_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__list_selector_disabled_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__menu_dropdown_panel_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__menu_dropdown_panel_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_bg_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_bg_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_primary_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_primary_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_secondary_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_secondary_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_48_inner_holo.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_48_outer_holo.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_default_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_default_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_disabled_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_disabled_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_focused_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_focused_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_pressed_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_pressed_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_focused_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_pressed_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-mdpi/abs__tab_unselected_pressed_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-v11/abs__progress_medium_holo.xml create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_inverse_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_transparent_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_transparent_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_share_pack_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_share_pack_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_shadow_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_solid_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_solid_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_transparent_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_transparent_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_transparent_dark_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_transparent_light_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_default_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_default_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_bottom_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_bottom_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_top_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_top_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__dialog_full_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__dialog_full_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_ab_back_holo_dark.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_ab_back_holo_light.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_cab_done_holo_dark.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_cab_done_holo_light.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_light.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_share_holo_dark.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_share_holo_light.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_activated_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_divider_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_divider_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_focused_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_longpressed_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_pressed_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_pressed_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_selector_disabled_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_selector_disabled_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_bg_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_bg_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_primary_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_primary_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_secondary_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_secondary_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_48_inner_holo.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_48_outer_holo.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_default_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_default_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_focused_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_focused_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_dark.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_light.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_focused_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_pressed_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_unselected_pressed_holo.9.png create mode 100755 libs/ActionBarSherlock/res/drawable/abs__activated_background_holo_dark.xml create mode 100755 libs/ActionBarSherlock/res/drawable/abs__activated_background_holo_light.xml create mode 100755 libs/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_dark.xml create mode 100755 libs/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_light.xml create mode 100755 libs/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_dark.xml create mode 100755 libs/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_light.xml create mode 100755 libs/ActionBarSherlock/res/drawable/abs__item_background_holo_dark.xml create mode 100755 libs/ActionBarSherlock/res/drawable/abs__item_background_holo_light.xml create mode 100755 libs/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_dark.xml create mode 100755 libs/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_light.xml create mode 100755 libs/ActionBarSherlock/res/drawable/abs__list_selector_holo_dark.xml create mode 100755 libs/ActionBarSherlock/res/drawable/abs__list_selector_holo_light.xml create mode 100755 libs/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_dark.xml create mode 100755 libs/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_light.xml create mode 100755 libs/ActionBarSherlock/res/drawable/abs__progress_medium_holo.xml create mode 100755 libs/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_dark.xml create mode 100755 libs/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_light.xml create mode 100755 libs/ActionBarSherlock/res/drawable/abs__tab_indicator_ab_holo.xml create mode 100755 libs/ActionBarSherlock/res/layout-large/abs__action_mode_close_item.xml create mode 100755 libs/ActionBarSherlock/res/layout-v14/sherlock_spinner_dropdown_item.xml create mode 100755 libs/ActionBarSherlock/res/layout-v14/sherlock_spinner_item.xml create mode 100755 libs/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar.xml create mode 100755 libs/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar_overlay.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__action_bar_home.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__action_bar_tab.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__action_bar_tab_bar_view.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__action_bar_title_item.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__action_menu_item_layout.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__action_menu_layout.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__action_mode_bar.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__action_mode_close_item.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__activity_chooser_view.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__activity_chooser_view_list_item.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__dialog_title_holo.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__list_menu_item_checkbox.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__list_menu_item_icon.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__list_menu_item_layout.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__list_menu_item_radio.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__popup_menu_item_layout.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__screen_action_bar.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__screen_action_bar_overlay.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__screen_simple.xml create mode 100755 libs/ActionBarSherlock/res/layout/abs__screen_simple_overlay_action_mode.xml create mode 100755 libs/ActionBarSherlock/res/layout/sherlock_spinner_dropdown_item.xml create mode 100755 libs/ActionBarSherlock/res/layout/sherlock_spinner_item.xml create mode 100755 libs/ActionBarSherlock/res/values-land/abs__dimens.xml create mode 100755 libs/ActionBarSherlock/res/values-large-hdpi-1024x600/abs__dimens.xml create mode 100755 libs/ActionBarSherlock/res/values-large-land-hdpi-1024x600/abs__dimens.xml create mode 100755 libs/ActionBarSherlock/res/values-large-land-mdpi-1024x600/abs__dimens.xml create mode 100755 libs/ActionBarSherlock/res/values-large-mdpi-1024x600/abs__dimens.xml create mode 100755 libs/ActionBarSherlock/res/values-large/abs__dimens.xml create mode 100755 libs/ActionBarSherlock/res/values-sw600dp/abs__bools.xml create mode 100755 libs/ActionBarSherlock/res/values-sw600dp/abs__dimens.xml create mode 100755 libs/ActionBarSherlock/res/values-v11/abs__themes.xml create mode 100755 libs/ActionBarSherlock/res/values-v14/abs__styles.xml create mode 100755 libs/ActionBarSherlock/res/values-v14/abs__themes.xml create mode 100755 libs/ActionBarSherlock/res/values-w360dp/abs__dimens.xml create mode 100755 libs/ActionBarSherlock/res/values-w480dp/abs__bools.xml create mode 100755 libs/ActionBarSherlock/res/values-w480dp/abs__config.xml create mode 100755 libs/ActionBarSherlock/res/values-w500dp/abs__dimens.xml create mode 100755 libs/ActionBarSherlock/res/values-w600dp/abs__dimens.xml create mode 100755 libs/ActionBarSherlock/res/values-xlarge/abs__dimens.xml create mode 100755 libs/ActionBarSherlock/res/values/abs__attrs.xml create mode 100755 libs/ActionBarSherlock/res/values/abs__bools.xml create mode 100755 libs/ActionBarSherlock/res/values/abs__colors.xml create mode 100755 libs/ActionBarSherlock/res/values/abs__config.xml create mode 100755 libs/ActionBarSherlock/res/values/abs__dimens.xml create mode 100755 libs/ActionBarSherlock/res/values/abs__ids.xml create mode 100755 libs/ActionBarSherlock/res/values/abs__strings.xml create mode 100755 libs/ActionBarSherlock/res/values/abs__styles.xml create mode 100755 libs/ActionBarSherlock/res/values/abs__themes.xml create mode 100755 libs/ActionBarSherlock/src/android/support/v4/app/_ActionBarSherlockTrojanHorse.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/ActionBarSherlock.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/app/ActionBar.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockActivity.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockDialogFragment.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockExpandableListActivity.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockFragment.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockFragmentActivity.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockListActivity.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockListFragment.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockPreferenceActivity.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockCompat.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockNative.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/ResourcesCompat.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/app/ActionBarImpl.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/app/ActionBarWrapper.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/Animator.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorListenerAdapter.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorSet.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatEvaluator.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatKeyframeSet.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntEvaluator.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntKeyframeSet.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/Keyframe.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/KeyframeSet.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/ObjectAnimator.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/PropertyValuesHolder.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/TypeEvaluator.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/ValueAnimator.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/view/NineViewGroup.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/view/animation/AnimatorProxy.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineFrameLayout.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineHorizontalScrollView.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineLinearLayout.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/ActionProviderWrapper.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/StandaloneActionMode.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/View_HasStateListenerSupport.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/View_OnAttachStateChangeListener.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenu.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuItem.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuItemView.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuPresenter.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuView.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/BaseMenuPresenter.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ListMenuItemView.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuItemImpl.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuPopupHelper.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuPresenter.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuView.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/SubMenuBuilder.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/SubMenuWrapper.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/AbsActionBarView.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarContainer.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarContextView.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarView.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CapitalizingButton.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CapitalizingTextView.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/FakeDialogPhoneWindow.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsAbsSpinner.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsAdapterView.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsLinearLayout.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsListPopupWindow.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsProgressBar.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsSpinner.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsView.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/view/ActionMode.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/view/ActionProvider.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/view/CollapsibleActionView.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/view/Menu.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/view/MenuInflater.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/view/MenuItem.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/view/SubMenu.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/view/Window.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserModel.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserView.java create mode 100755 libs/ActionBarSherlock/src/com/actionbarsherlock/widget/ShareActionProvider.java create mode 100755 libs/ActionBarSherlock/test/com/actionbarsherlock/internal/ManifestParsingTest.java delete mode 100644 libs/android-support-v4.jar diff --git a/libs/ActionBarSherlock/.classpath b/libs/ActionBarSherlock/.classpath new file mode 100644 index 000000000..3f9691c5d --- /dev/null +++ b/libs/ActionBarSherlock/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/libs/ActionBarSherlock/.project b/libs/ActionBarSherlock/.project new file mode 100644 index 000000000..31c647017 --- /dev/null +++ b/libs/ActionBarSherlock/.project @@ -0,0 +1,33 @@ + + + ActionBarSherlock + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/libs/ActionBarSherlock/AndroidManifest.xml b/libs/ActionBarSherlock/AndroidManifest.xml new file mode 100755 index 000000000..c4a75f32c --- /dev/null +++ b/libs/ActionBarSherlock/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/libs/ActionBarSherlock/README.md b/libs/ActionBarSherlock/README.md new file mode 100755 index 000000000..e8a2c080e --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/build.xml b/libs/ActionBarSherlock/build.xml new file mode 100644 index 000000000..56bc65293 --- /dev/null +++ b/libs/ActionBarSherlock/build.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/libs/android-support-v4.jar b/libs/ActionBarSherlock/libs/android-support-v4.jar new file mode 100755 index 0000000000000000000000000000000000000000..99e063b33a53c8ba7980a5b5244ce0e0da46ae54 GIT binary patch literal 271754 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

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 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 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 impl = impls.values().iterator().next(); + if (DEBUG) Log.i(TAG, "Using implementation: " + impl.getSimpleName()); + + try { + Constructor 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. + * + *
+     * @Override
+     * public void onConfigurationChanged(Configuration newConfig) {
+     *     super.onConfigurationChanged(newConfig);
+     *     mSherlock.dispatchConfigurationChanged(newConfig);
+     * }
+     * 
+ * + * @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. + * + *
+     * @Override
+     * protected void onPostResume() {
+     *     super.onPostResume();
+     *     mSherlock.dispatchPostResume();
+     * }
+     * 
+ */ + public void dispatchPostResume() {} + + /** + * Notify the action bar that the activity is pausing. This should be + * dispatched before the call to the superclass implementation. + * + *
+     * @Override
+     * protected void onPause() {
+     *     mSherlock.dispatchPause();
+     *     super.onPause();
+     * }
+     * 
+ */ + public void dispatchPause() {} + + /** + * Notify the action bar that the activity is stopping. This should be + * called before the superclass implementation. + * + *

+ * @Override + * protected void onStop() { + * mSherlock.dispatchStop(); + * super.onStop(); + * } + *

+ */ + 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}. + * + *

+ * @Override + * public void openOptionsMenu() { + * if (!mSherlock.dispatchOpenOptionsMenu()) { + * super.openOptionsMenu(); + * } + * } + *

+ * + * @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}. + * + *
+     * @Override
+     * public void closeOptionsMenu() {
+     *     if (!mSherlock.dispatchCloseOptionsMenu()) {
+     *         super.closeOptionsMenu();
+     *     }
+     * }
+     * 
+ * + * @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. + * + *
+     * @Override
+     * protected void onPostCreate(Bundle savedInstanceState) {
+     *     mSherlock.dispatchPostCreate(savedInstanceState);
+     *     super.onPostCreate(savedInstanceState);
+     * }
+     * 
+ * + * @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. + * + *
+     *  @Override
+     *  protected void onTitleChanged(CharSequence title, int color) {
+     *      mSherlock.dispatchTitleChanged(title, color);
+     *      super.onTitleChanged(title, color);
+     *  }
+     * 
+ * + * @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. + * + *
+     * @Override
+     * public boolean dispatchKeyEvent(KeyEvent event) {
+     *     if (mSherlock.dispatchKeyEvent(event)) {
+     *         return true;
+     *     }
+     *     return super.dispatchKeyEvent(event);
+     * }
+     * 
+ * + * @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. + * + *

+ * @Override + * public final boolean onMenuOpened(int featureId, android.view.Menu menu) { + * if (mSherlock.dispatchMenuOpened(featureId, menu)) { + * return true; + * } + * return super.onMenuOpened(featureId, menu); + * } + *

+ * + * @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. + * + *

+ * @Override + * public void onPanelClosed(int featureId, android.view.Menu menu) { + * mSherlock.dispatchPanelClosed(featureId, menu); + * super.onPanelClosed(featureId, menu); + * } + *

+ * + * @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. + * + *

+ * @Override + * public void onDestroy() { + * mSherlock.dispatchDestroy(); + * super.onDestroy(); + * } + *

+ */ + public void dispatchDestroy() {} + + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + + /** + * 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()); + } 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/libs/ActionBarSherlock/src/com/actionbarsherlock/app/ActionBar.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/app/ActionBar.java new file mode 100755 index 000000000..2497d24ff --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/app/ActionBar.java @@ -0,0 +1,947 @@ +/* + * 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.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}. + *

+ */ +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 { + /** + * 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); + } + + 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/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockActivity.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockActivity.java new file mode 100755 index 000000000..9cb57e95a --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockActivity.java @@ -0,0 +1,259 @@ +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); + } + + + /////////////////////////////////////////////////////////////////////////// + // 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/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockDialogFragment.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockDialogFragment.java new file mode 100755 index 000000000..a7c856bf0 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockExpandableListActivity.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockExpandableListActivity.java new file mode 100755 index 000000000..078f9b0ca --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockFragment.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockFragment.java new file mode 100755 index 000000000..0f24e9c85 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockFragmentActivity.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockFragmentActivity.java new file mode 100755 index 000000000..5cd13ba7c --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockFragmentActivity.java @@ -0,0 +1,292 @@ +package com.actionbarsherlock.app; + +import android.content.res.Configuration; +import android.os.Bundle; +import android.support.v4.app._ActionBarSherlockTrojanHorse; +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 _ActionBarSherlockTrojanHorse} */ +public class SherlockFragmentActivity extends _ActionBarSherlockTrojanHorse 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); + } + + + /////////////////////////////////////////////////////////////////////////// + // 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/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockListActivity.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockListActivity.java new file mode 100755 index 000000000..00c00fee5 --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockListActivity.java @@ -0,0 +1,259 @@ +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); + } + + + /////////////////////////////////////////////////////////////////////////// + // 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/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockListFragment.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockListFragment.java new file mode 100755 index 000000000..13ca3c49f --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockPreferenceActivity.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockPreferenceActivity.java new file mode 100755 index 000000000..4f80be515 --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockPreferenceActivity.java @@ -0,0 +1,259 @@ +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); + } + + + /////////////////////////////////////////////////////////////////////////// + // 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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockCompat.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockCompat.java new file mode 100755 index 000000000..05353d28c --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockCompat.java @@ -0,0 +1,1207 @@ +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; + + + 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; + /** Indication of a long-press on the hardware menu key. */ + private boolean mMenuKeyIsLongPress = false; + + /** 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; + } + + return wActionBar.hideOverflowMenu(); + } + + @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; + } + } + + boolean result = false; + if (keyCode == KeyEvent.KEYCODE_MENU && isReservingOverflow()) { + if (event.getAction() == KeyEvent.ACTION_DOWN && event.isLongPress()) { + mMenuKeyIsLongPress = true; + } else if (event.getAction() == KeyEvent.ACTION_UP) { + if (!mMenuKeyIsLongPress) { + if (mActionMode == null && wActionBar != null) { + if (wActionBar.isOverflowMenuShowing()) { + wActionBar.hideOverflowMenu(); + } else { + wActionBar.showOverflowMenu(); + } + } + result = true; + } + mMenuKeyIsLongPress = false; + } + } + + if (DEBUG) Log.d(TAG, "[dispatchKeyEvent] returning " + result); + return result; + } + + @Override + public void dispatchDestroy() { + mIsDestroyed = true; + } + + + /////////////////////////////////////////////////////////////////////////// + // 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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockNative.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockNative.java new file mode 100755 index 000000000..9afca185a --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockNative.java @@ -0,0 +1,328 @@ +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. + mActivity.startActionMode(wrapped); + + 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); + } + } + + 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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/ResourcesCompat.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/ResourcesCompat.java new file mode 100755 index 000000000..8e1efe8c5 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/app/ActionBarImpl.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/app/ActionBarImpl.java new file mode 100755 index 000000000..6ae0402c0 --- /dev/null +++ b/libs/ActionBarSherlock/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.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.app.SherlockFragmentActivity; +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 SherlockFragmentActivity) { + trans = ((SherlockFragmentActivity)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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/app/ActionBarWrapper.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/app/ActionBarWrapper.java new file mode 100755 index 000000000..e390ea428 --- /dev/null +++ b/libs/ActionBarSherlock/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.FragmentTransaction; +import android.view.View; +import android.widget.SpinnerAdapter; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockFragmentActivity; + +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 SherlockFragmentActivity) { + trans = ((SherlockFragmentActivity)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 SherlockFragmentActivity) { + mFragmentTransaction = ((SherlockFragmentActivity)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 SherlockFragmentActivity) { + trans = ((SherlockFragmentActivity)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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/Animator.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/Animator.java new file mode 100755 index 000000000..2caf5b4a9 --- /dev/null +++ b/libs/ActionBarSherlock/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.

+ */ + public static interface AnimatorListener { + /** + *

Notifies the start 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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorListenerAdapter.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorListenerAdapter.java new file mode 100755 index 000000000..02ddff48d --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorSet.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorSet.java new file mode 100755 index 000000000..3231080c4 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatEvaluator.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatEvaluator.java new file mode 100755 index 000000000..e41019364 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatKeyframeSet.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatKeyframeSet.java new file mode 100755 index 000000000..6d9dafa7a --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntEvaluator.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntEvaluator.java new file mode 100755 index 000000000..ed5e79ec6 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntKeyframeSet.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntKeyframeSet.java new file mode 100755 index 000000000..e9215e7f8 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/Keyframe.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/Keyframe.java new file mode 100755 index 000000000..ab76fa7f6 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/KeyframeSet.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/KeyframeSet.java new file mode 100755 index 000000000..a71e1ad3c --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/ObjectAnimator.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/ObjectAnimator.java new file mode 100755 index 000000000..21d15c02a --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/PropertyValuesHolder.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/PropertyValuesHolder.java new file mode 100755 index 000000000..84f7504ab --- /dev/null +++ b/libs/ActionBarSherlock/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 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 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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/TypeEvaluator.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/TypeEvaluator.java new file mode 100755 index 000000000..0ea319244 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/ValueAnimator.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/ValueAnimator.java new file mode 100755 index 000000000..d8a12c688 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/view/NineViewGroup.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/view/NineViewGroup.java new file mode 100755 index 000000000..7b830b9c0 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/view/animation/AnimatorProxy.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/view/animation/AnimatorProxy.java new file mode 100755 index 000000000..067d0494e --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineFrameLayout.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineFrameLayout.java new file mode 100755 index 000000000..2c428e907 --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineFrameLayout.java @@ -0,0 +1,65 @@ +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) { + super(context); + mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null; + } + public NineFrameLayout(Context context, AttributeSet attrs) { + super(context, attrs); + mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null; + } + public NineFrameLayout(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 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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineHorizontalScrollView.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineHorizontalScrollView.java new file mode 100755 index 000000000..129b5aaaa --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineLinearLayout.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineLinearLayout.java new file mode 100755 index 000000000..a670b1f64 --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineLinearLayout.java @@ -0,0 +1,65 @@ +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) { + super(context); + mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null; + } + public NineLinearLayout(Context context, AttributeSet attrs) { + super(context, attrs); + mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null; + } + public NineLinearLayout(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); + } + } +} diff --git a/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/ActionProviderWrapper.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/ActionProviderWrapper.java new file mode 100755 index 000000000..b136d50f0 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/StandaloneActionMode.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/StandaloneActionMode.java new file mode 100755 index 000000000..0a87bd3f7 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/View_HasStateListenerSupport.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/View_HasStateListenerSupport.java new file mode 100755 index 000000000..7d45e81be --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/View_OnAttachStateChangeListener.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/View_OnAttachStateChangeListener.java new file mode 100755 index 000000000..3869d3290 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenu.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenu.java new file mode 100755 index 000000000..0354ad1ad --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuItem.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuItem.java new file mode 100755 index 000000000..510b97488 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuItemView.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuItemView.java new file mode 100755 index 000000000..dcb50f362 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuPresenter.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuPresenter.java new file mode 100755 index 000000000..6f568c698 --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuPresenter.java @@ -0,0 +1,721 @@ +/* + * 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.content.res.TypedArray; +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) { + //Check for theme-forced overflow action item + TypedArray a = context.getTheme().obtainStyledAttributes(R.styleable.SherlockTheme); + boolean result = a.getBoolean(R.styleable.SherlockTheme_absForceOverflow, false); + a.recycle(); + if (result) { + return true; + } + + 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); + } + } + + @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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuView.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuView.java new file mode 100755 index 000000000..e090677a1 --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuView.java @@ -0,0 +1,572 @@ +/* + * 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) { + 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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/BaseMenuPresenter.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/BaseMenuPresenter.java new file mode 100755 index 000000000..6da26f2ae --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ListMenuItemView.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ListMenuItemView.java new file mode 100755 index 000000000..ac25c3736 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java new file mode 100755 index 000000000..179b8f037 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuItemImpl.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuItemImpl.java new file mode 100755 index 000000000..f5359fb40 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java new file mode 100755 index 000000000..907a7aa04 --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java @@ -0,0 +1,292 @@ +package com.actionbarsherlock.internal.view.menu; + +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.view.ContextMenu.ContextMenuInfo; +import com.actionbarsherlock.internal.view.ActionProviderWrapper; +import com.actionbarsherlock.view.ActionProvider; +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) { + mNativeItem.setActionView(view); + return this; + } + + @Override + public MenuItem setActionView(int resId) { + mNativeItem.setActionView(resId); + return this; + } + + @Override + public View getActionView() { + return mNativeItem.getActionView(); + } + + @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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuPopupHelper.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuPopupHelper.java new file mode 100755 index 000000000..f030de310 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuPresenter.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuPresenter.java new file mode 100755 index 000000000..c3f35472c --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuView.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuView.java new file mode 100755 index 000000000..323ba2d88 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java new file mode 100755 index 000000000..64fc4aeaa --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java @@ -0,0 +1,180 @@ +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) { + android.view.MenuItem[] nativeOutItems = new android.view.MenuItem[outSpecificItems.length]; + int 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]); + } + 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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/SubMenuBuilder.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/SubMenuBuilder.java new file mode 100755 index 000000000..6679cf386 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/SubMenuWrapper.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/SubMenuWrapper.java new file mode 100755 index 000000000..7d307acb1 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/AbsActionBarView.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/AbsActionBarView.java new file mode 100755 index 000000000..3a4a44675 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarContainer.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarContainer.java new file mode 100755 index 000000000..5e5aa2867 --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarContainer.java @@ -0,0 +1,245 @@ +/* + * 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.Canvas; +import android.graphics.drawable.Drawable; +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); + + 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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarContextView.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarContextView.java new file mode 100755 index 000000000..9ec250f38 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarView.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarView.java new file mode 100755 index 000000000..4636de17f --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CapitalizingButton.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CapitalizingButton.java new file mode 100755 index 000000000..fa3698f3b --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CapitalizingTextView.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CapitalizingTextView.java new file mode 100755 index 000000000..673ec554f --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CapitalizingTextView.java @@ -0,0 +1,44 @@ +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.TextView; + +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) { + setText(text.toString().toUpperCase(Locale.ROOT)); + } else { + setText(text.toString().toUpperCase()); + } + } else { + setText(text); + } + } +} diff --git a/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/FakeDialogPhoneWindow.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/FakeDialogPhoneWindow.java new file mode 100755 index 000000000..ad1b4f0a8 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsAbsSpinner.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsAbsSpinner.java new file mode 100755 index 000000000..ce0cb3bca --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsAdapterView.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsAdapterView.java new file mode 100755 index 000000000..c786dc5c1 --- /dev/null +++ b/libs/ActionBarSherlock/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. + * + *

+ *

Developer Guides

+ *

For more information about using AdapterView, read the + * Binding to Data with AdapterView + * developer guide.

+ */ +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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsLinearLayout.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsLinearLayout.java new file mode 100755 index 000000000..1b4463a59 --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsLinearLayout.java @@ -0,0 +1,272 @@ +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 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[] LinearLayout = new int[] { + /* 0 */ android.R.attr.divider, + /* 1 */ android.R.attr.showDividers, + /* 2 */ android.R.attr.dividerPadding, + }; + private static final int LinearLayout_divider = 0; + private static final int LinearLayout_showDividers = 1; + private static final int LinearLayout_dividerPadding = 2; + + /** + * 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; + + + public IcsLinearLayout(Context context, AttributeSet attrs) { + super(context, attrs); + + TypedArray a = context.obtainStyledAttributes(attrs, /*com.android.internal.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); + + 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; + } +} diff --git a/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsListPopupWindow.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsListPopupWindow.java new file mode 100755 index 000000000..d13c6cea9 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsProgressBar.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsProgressBar.java new file mode 100755 index 000000000..1c02d4aca --- /dev/null +++ b/libs/ActionBarSherlock/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: + *

+ * + *
+ * public class MyActivity extends Activity {
+ *     private static final int PROGRESS = 0x1;
+ *
+ *     private ProgressBar mProgress;
+ *     private int mProgressStatus = 0;
+ *
+ *     private Handler mHandler = new Handler();
+ *
+ *     protected void onCreate(Bundle icicle) {
+ *         super.onCreate(icicle);
+ *
+ *         setContentView(R.layout.progressbar_activity);
+ *
+ *         mProgress = (ProgressBar) findViewById(R.id.progress_bar);
+ *
+ *         // Start lengthy operation in a background thread
+ *         new Thread(new Runnable() {
+ *             public void run() {
+ *                 while (mProgressStatus < 100) {
+ *                     mProgressStatus = doWork();
+ *
+ *                     // Update the progress bar
+ *                     mHandler.post(new Runnable() {
+ *                         public void run() {
+ *                             mProgress.setProgress(mProgressStatus);
+ *                         }
+ *                     });
+ *                 }
+ *             }
+ *         }).start();
+ *     }
+ * }
+ * + *

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:

+ * + *
+ * <ProgressBar
+ *     style="@android:style/Widget.ProgressBar.Horizontal"
+ *     ... />
+ * + *

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:

+ * + *
+ * <LinearLayout
+ *     android:orientation="horizontal"
+ *     ... >
+ *     <ProgressBar
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="wrap_content"
+ *         style="@android:style/Widget.ProgressBar.Small"
+ *         android:layout_marginRight="5dp" />
+ *     <TextView
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="wrap_content"
+ *         android:text="@string/loading" />
+ * </LinearLayout>
+ * + *

Other progress bar styles provided by the system include:

+ *
    + *
  • {@link android.R.style#Widget_ProgressBar_Horizontal Widget.ProgressBar.Horizontal}
  • + *
  • {@link android.R.style#Widget_ProgressBar_Small Widget.ProgressBar.Small}
  • + *
  • {@link android.R.style#Widget_ProgressBar_Large Widget.ProgressBar.Large}
  • + *
  • {@link android.R.style#Widget_ProgressBar_Inverse Widget.ProgressBar.Inverse}
  • + *
  • {@link android.R.style#Widget_ProgressBar_Small_Inverse + * Widget.ProgressBar.Small.Inverse}
  • + *
  • {@link android.R.style#Widget_ProgressBar_Large_Inverse + * Widget.ProgressBar.Large.Inverse}
  • + *
+ *

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).

+ * + *

XML attributes + *

+ * See {@link android.R.styleable#ProgressBar ProgressBar Attributes}, + * {@link android.R.styleable#View View Attributes} + *

+ * + * @attr ref android.R.styleable#ProgressBar_animationResolution + * @attr ref android.R.styleable#ProgressBar_indeterminate + * @attr ref android.R.styleable#ProgressBar_indeterminateBehavior + * @attr ref android.R.styleable#ProgressBar_indeterminateDrawable + * @attr ref android.R.styleable#ProgressBar_indeterminateDuration + * @attr ref android.R.styleable#ProgressBar_indeterminateOnly + * @attr ref android.R.styleable#ProgressBar_interpolator + * @attr ref android.R.styleable#ProgressBar_max + * @attr ref android.R.styleable#ProgressBar_maxHeight + * @attr ref android.R.styleable#ProgressBar_maxWidth + * @attr ref android.R.styleable#ProgressBar_minHeight + * @attr ref android.R.styleable#ProgressBar_minWidth + * @attr ref android.R.styleable#ProgressBar_progress + * @attr ref android.R.styleable#ProgressBar_progressDrawable + * @attr ref android.R.styleable#ProgressBar_secondaryProgress + */ +@RemoteView +public class IcsProgressBar extends View { + private static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; + private static final int MAX_LEVEL = 10000; + private static final int ANIMATION_RESOLUTION = 200; + private static final int TIMEOUT_SEND_ACCESSIBILITY_EVENT = 200; + + private static final int[] ProgressBar = new int[] { + android.R.attr.maxWidth, + android.R.attr.maxHeight, + android.R.attr.max, + android.R.attr.progress, + android.R.attr.secondaryProgress, + android.R.attr.indeterminate, + android.R.attr.indeterminateOnly, + android.R.attr.indeterminateDrawable, + android.R.attr.progressDrawable, + android.R.attr.indeterminateDuration, + android.R.attr.indeterminateBehavior, + android.R.attr.minWidth, + android.R.attr.minHeight, + android.R.attr.interpolator, + android.R.attr.animationResolution, + }; + private static final int ProgressBar_maxWidth = 0; + private static final int ProgressBar_maxHeight = 1; + private static final int ProgressBar_max = 2; + private static final int ProgressBar_progress = 3; + private static final int ProgressBar_secondaryProgress = 4; + private static final int ProgressBar_indeterminate = 5; + private static final int ProgressBar_indeterminateOnly = 6; + private static final int ProgressBar_indeterminateDrawable = 7; + private static final int ProgressBar_progressDrawable = 8; + private static final int ProgressBar_indeterminateDuration = 9; + private static final int ProgressBar_indeterminateBehavior = 10; + private static final int ProgressBar_minWidth = 11; + private static final int ProgressBar_minHeight = 12; + private static final int ProgressBar_interpolator = 13; + private static final int ProgressBar_animationResolution = 14; + + int mMinWidth; + int mMaxWidth; + int mMinHeight; + int mMaxHeight; + + private int mProgress; + private int mSecondaryProgress; + private int mMax; + + private int mBehavior; + private int mDuration; + private boolean mIndeterminate; + private boolean mOnlyIndeterminate; + private Transformation mTransformation; + private AlphaAnimation mAnimation; + private Drawable mIndeterminateDrawable; + private int mIndeterminateRealLeft; + private int mIndeterminateRealTop; + private Drawable mProgressDrawable; + private Drawable mCurrentDrawable; + Bitmap mSampleTile; + private boolean mNoInvalidate; + private Interpolator mInterpolator; + private RefreshProgressRunnable mRefreshProgressRunnable; + private long mUiThreadId; + private boolean mShouldStartAnimationDrawable; + private long mLastDrawTime; + + private boolean mInDrawing; + + private int mAnimationResolution; + + private AccessibilityManager mAccessibilityManager; + private AccessibilityEventSender mAccessibilityEventSender; + + /** + * Create a new progress bar with range 0...100 and initial progress of 0. + * @param context the application environment + */ + public IcsProgressBar(Context context) { + this(context, null); + } + + public IcsProgressBar(Context context, AttributeSet attrs) { + this(context, attrs, android.R.attr.progressBarStyle); + } + + public IcsProgressBar(Context context, AttributeSet attrs, int defStyle) { + this(context, attrs, defStyle, 0); + } + + /** + * @hide + */ + public IcsProgressBar(Context context, AttributeSet attrs, int defStyle, int styleRes) { + super(context, attrs, defStyle); + mUiThreadId = Thread.currentThread().getId(); + initProgressBar(); + + TypedArray a = + context.obtainStyledAttributes(attrs, /*R.styleable.*/ProgressBar, defStyle, styleRes); + + mNoInvalidate = true; + + Drawable drawable = a.getDrawable(/*R.styleable.*/ProgressBar_progressDrawable); + if (drawable != null) { + drawable = tileify(drawable, false); + // Calling this method can set mMaxHeight, make sure the corresponding + // XML attribute for mMaxHeight is read after calling this method + setProgressDrawable(drawable); + } + + + mDuration = a.getInt(/*R.styleable.*/ProgressBar_indeterminateDuration, mDuration); + + mMinWidth = a.getDimensionPixelSize(/*R.styleable.*/ProgressBar_minWidth, mMinWidth); + mMaxWidth = a.getDimensionPixelSize(/*R.styleable.*/ProgressBar_maxWidth, mMaxWidth); + mMinHeight = a.getDimensionPixelSize(/*R.styleable.*/ProgressBar_minHeight, mMinHeight); + mMaxHeight = a.getDimensionPixelSize(/*R.styleable.*/ProgressBar_maxHeight, mMaxHeight); + + mBehavior = a.getInt(/*R.styleable.*/ProgressBar_indeterminateBehavior, mBehavior); + + final int resID = a.getResourceId( + /*com.android.internal.R.styleable.*/ProgressBar_interpolator, + android.R.anim.linear_interpolator); // default to linear interpolator + if (resID > 0) { + setInterpolator(context, resID); + } + + setMax(a.getInt(/*R.styleable.*/ProgressBar_max, mMax)); + + setProgress(a.getInt(/*R.styleable.*/ProgressBar_progress, mProgress)); + + setSecondaryProgress( + a.getInt(/*R.styleable.*/ProgressBar_secondaryProgress, mSecondaryProgress)); + + drawable = a.getDrawable(/*R.styleable.*/ProgressBar_indeterminateDrawable); + if (drawable != null) { + drawable = tileifyIndeterminate(drawable); + setIndeterminateDrawable(drawable); + } + + mOnlyIndeterminate = a.getBoolean( + /*R.styleable.*/ProgressBar_indeterminateOnly, mOnlyIndeterminate); + + mNoInvalidate = false; + + setIndeterminate(mOnlyIndeterminate || a.getBoolean( + /*R.styleable.*/ProgressBar_indeterminate, mIndeterminate)); + + mAnimationResolution = a.getInteger(/*R.styleable.*/ProgressBar_animationResolution, + ANIMATION_RESOLUTION); + + a.recycle(); + + mAccessibilityManager = (AccessibilityManager)context.getSystemService(Context.ACCESSIBILITY_SERVICE); + } + + /** + * Converts a drawable to a tiled version of itself. It will recursively + * traverse layer and state list drawables. + */ + private Drawable tileify(Drawable drawable, boolean clip) { + + if (drawable instanceof LayerDrawable) { + LayerDrawable background = (LayerDrawable) drawable; + final int N = background.getNumberOfLayers(); + Drawable[] outDrawables = new Drawable[N]; + + for (int i = 0; i < N; i++) { + int id = background.getId(i); + outDrawables[i] = tileify(background.getDrawable(i), + (id == android.R.id.progress || id == android.R.id.secondaryProgress)); + } + + LayerDrawable newBg = new LayerDrawable(outDrawables); + + for (int i = 0; i < N; i++) { + newBg.setId(i, background.getId(i)); + } + + return newBg; + + }/* else if (drawable instanceof StateListDrawable) { + StateListDrawable in = (StateListDrawable) drawable; + StateListDrawable out = new StateListDrawable(); + int numStates = in.getStateCount(); + for (int i = 0; i < numStates; i++) { + out.addState(in.getStateSet(i), tileify(in.getStateDrawable(i), clip)); + } + return out; + + }*/ else if (drawable instanceof BitmapDrawable) { + final Bitmap tileBitmap = ((BitmapDrawable) drawable).getBitmap(); + if (mSampleTile == null) { + mSampleTile = tileBitmap; + } + + final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape()); + + final BitmapShader bitmapShader = new BitmapShader(tileBitmap, + Shader.TileMode.REPEAT, Shader.TileMode.CLAMP); + shapeDrawable.getPaint().setShader(bitmapShader); + + return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT, + ClipDrawable.HORIZONTAL) : shapeDrawable; + } + + return drawable; + } + + Shape getDrawableShape() { + final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 }; + return new RoundRectShape(roundedCorners, null, null); + } + + /** + * Convert a AnimationDrawable for use as a barberpole animation. + * Each frame of the animation is wrapped in a ClipDrawable and + * given a tiling BitmapShader. + */ + private Drawable tileifyIndeterminate(Drawable drawable) { + if (drawable instanceof AnimationDrawable) { + AnimationDrawable background = (AnimationDrawable) drawable; + final int N = background.getNumberOfFrames(); + AnimationDrawable newBg = new AnimationDrawable(); + newBg.setOneShot(background.isOneShot()); + + for (int i = 0; i < N; i++) { + Drawable frame = tileify(background.getFrame(i), true); + frame.setLevel(10000); + newBg.addFrame(frame, background.getDuration(i)); + } + newBg.setLevel(10000); + drawable = newBg; + } + return drawable; + } + + /** + *

+ * Initialize the progress bar's default values: + *

+ *
    + *
  • progress = 0
  • + *
  • max = 100
  • + *
  • animation duration = 4000 ms
  • + *
  • indeterminate = false
  • + *
  • behavior = repeat
  • + *
+ */ + private void initProgressBar() { + mMax = 100; + mProgress = 0; + mSecondaryProgress = 0; + mIndeterminate = false; + mOnlyIndeterminate = false; + mDuration = 4000; + mBehavior = AlphaAnimation.RESTART; + mMinWidth = 24; + mMaxWidth = 48; + mMinHeight = 24; + mMaxHeight = 48; + } + + /** + *

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.

+ * + * @return a {@link android.graphics.drawable.Drawable} instance + * + * @see #setIndeterminateDrawable(android.graphics.drawable.Drawable) + * @see #setIndeterminate(boolean) + */ + public Drawable getIndeterminateDrawable() { + return mIndeterminateDrawable; + } + + /** + *

Define the drawable used to draw the progress bar in + * indeterminate mode.

+ * + * @param d the new drawable + * + * @see #getIndeterminateDrawable() + * @see #setIndeterminate(boolean) + */ + public void setIndeterminateDrawable(Drawable d) { + if (d != null) { + d.setCallback(this); + } + mIndeterminateDrawable = d; + if (mIndeterminate) { + mCurrentDrawable = d; + postInvalidate(); + } + } + + /** + *

Get the drawable used to draw the progress bar in + * progress mode.

+ * + * @return a {@link android.graphics.drawable.Drawable} instance + * + * @see #setProgressDrawable(android.graphics.drawable.Drawable) + * @see #setIndeterminate(boolean) + */ + public Drawable getProgressDrawable() { + return mProgressDrawable; + } + + /** + *

Define the drawable used to draw the progress bar in + * progress mode.

+ * + * @param d the new drawable + * + * @see #getProgressDrawable() + * @see #setIndeterminate(boolean) + */ + public void setProgressDrawable(Drawable d) { + boolean needUpdate; + if (mProgressDrawable != null && d != mProgressDrawable) { + mProgressDrawable.setCallback(null); + needUpdate = true; + } else { + needUpdate = false; + } + + if (d != null) { + d.setCallback(this); + + // Make sure the ProgressBar is always tall enough + int drawableHeight = d.getMinimumHeight(); + if (mMaxHeight < drawableHeight) { + mMaxHeight = drawableHeight; + requestLayout(); + } + } + mProgressDrawable = d; + if (!mIndeterminate) { + mCurrentDrawable = d; + postInvalidate(); + } + + if (needUpdate) { + updateDrawableBounds(getWidth(), getHeight()); + updateDrawableState(); + doRefreshProgress(android.R.id.progress, mProgress, false, false); + doRefreshProgress(android.R.id.secondaryProgress, mSecondaryProgress, false, false); + } + } + + /** + * @return The drawable currently used to draw the progress bar + */ + Drawable getCurrentDrawable() { + return mCurrentDrawable; + } + + @Override + protected boolean verifyDrawable(Drawable who) { + return who == mProgressDrawable || who == mIndeterminateDrawable + || super.verifyDrawable(who); + } + + @Override + public void jumpDrawablesToCurrentState() { + super.jumpDrawablesToCurrentState(); + if (mProgressDrawable != null) mProgressDrawable.jumpToCurrentState(); + if (mIndeterminateDrawable != null) mIndeterminateDrawable.jumpToCurrentState(); + } + + @Override + public void postInvalidate() { + if (!mNoInvalidate) { + super.postInvalidate(); + } + } + + private class RefreshProgressRunnable implements Runnable { + + private int mId; + private int mProgress; + private boolean mFromUser; + + RefreshProgressRunnable(int id, int progress, boolean fromUser) { + mId = id; + mProgress = progress; + mFromUser = fromUser; + } + + public void run() { + doRefreshProgress(mId, mProgress, mFromUser, true); + // Put ourselves back in the cache when we are done + mRefreshProgressRunnable = this; + } + + public void setup(int id, int progress, boolean fromUser) { + mId = id; + mProgress = progress; + mFromUser = fromUser; + } + + } + + private synchronized void doRefreshProgress(int id, int progress, boolean fromUser, + boolean callBackToApp) { + float scale = mMax > 0 ? (float) progress / (float) mMax : 0; + final Drawable d = mCurrentDrawable; + if (d != null) { + Drawable progressDrawable = null; + + if (d instanceof LayerDrawable) { + progressDrawable = ((LayerDrawable) d).findDrawableByLayerId(id); + } + + final int level = (int) (scale * MAX_LEVEL); + (progressDrawable != null ? progressDrawable : d).setLevel(level); + } else { + invalidate(); + } + + if (callBackToApp && id == android.R.id.progress) { + onProgressRefresh(scale, fromUser); + } + } + + void onProgressRefresh(float scale, boolean fromUser) { + if (mAccessibilityManager.isEnabled()) { + scheduleAccessibilityEventSender(); + } + } + + private synchronized void refreshProgress(int id, int progress, boolean fromUser) { + if (mUiThreadId == Thread.currentThread().getId()) { + doRefreshProgress(id, progress, fromUser, true); + } else { + RefreshProgressRunnable r; + if (mRefreshProgressRunnable != null) { + // Use cached RefreshProgressRunnable if available + r = mRefreshProgressRunnable; + // Uncache it + mRefreshProgressRunnable = null; + r.setup(id, progress, fromUser); + } else { + // Make a new one + r = new RefreshProgressRunnable(id, progress, fromUser); + } + post(r); + } + } + + /** + *

Set the current progress to the specified value. Does not do anything + * if the progress bar is in indeterminate mode.

+ * + * @param progress the new progress, between 0 and {@link #getMax()} + * + * @see #setIndeterminate(boolean) + * @see #isIndeterminate() + * @see #getProgress() + * @see #incrementProgressBy(int) + */ + public synchronized void setProgress(int progress) { + setProgress(progress, false); + } + + synchronized void setProgress(int progress, boolean fromUser) { + if (mIndeterminate) { + return; + } + + if (progress < 0) { + progress = 0; + } + + if (progress > mMax) { + progress = mMax; + } + + if (progress != mProgress) { + mProgress = progress; + refreshProgress(android.R.id.progress, mProgress, fromUser); + } + } + + /** + *

+ * Set the current secondary progress to the specified value. Does not do + * anything if the progress bar is in indeterminate mode. + *

+ * + * @param secondaryProgress the new secondary progress, between 0 and {@link #getMax()} + * @see #setIndeterminate(boolean) + * @see #isIndeterminate() + * @see #getSecondaryProgress() + * @see #incrementSecondaryProgressBy(int) + */ + public synchronized void setSecondaryProgress(int secondaryProgress) { + if (mIndeterminate) { + return; + } + + if (secondaryProgress < 0) { + secondaryProgress = 0; + } + + if (secondaryProgress > mMax) { + secondaryProgress = mMax; + } + + if (secondaryProgress != mSecondaryProgress) { + mSecondaryProgress = secondaryProgress; + refreshProgress(android.R.id.secondaryProgress, mSecondaryProgress, false); + } + } + + /** + *

Get the progress bar's current level of progress. Return 0 when the + * progress bar is in indeterminate mode.

+ * + * @return the current progress, between 0 and {@link #getMax()} + * + * @see #setIndeterminate(boolean) + * @see #isIndeterminate() + * @see #setProgress(int) + * @see #setMax(int) + * @see #getMax() + */ + @ViewDebug.ExportedProperty(category = "progress") + public synchronized int getProgress() { + return mIndeterminate ? 0 : mProgress; + } + + /** + *

Get the progress bar's current level of secondary progress. Return 0 when the + * progress bar is in indeterminate mode.

+ * + * @return the current secondary progress, between 0 and {@link #getMax()} + * + * @see #setIndeterminate(boolean) + * @see #isIndeterminate() + * @see #setSecondaryProgress(int) + * @see #setMax(int) + * @see #getMax() + */ + @ViewDebug.ExportedProperty(category = "progress") + public synchronized int getSecondaryProgress() { + return mIndeterminate ? 0 : mSecondaryProgress; + } + + /** + *

Return the upper limit of this progress bar's range.

+ * + * @return a positive integer + * + * @see #setMax(int) + * @see #getProgress() + * @see #getSecondaryProgress() + */ + @ViewDebug.ExportedProperty(category = "progress") + public synchronized int getMax() { + return mMax; + } + + /** + *

Set the range of the progress bar to 0...max.

+ * + * @param max the upper range of this progress bar + * + * @see #getMax() + * @see #setProgress(int) + * @see #setSecondaryProgress(int) + */ + public synchronized void setMax(int max) { + if (max < 0) { + max = 0; + } + if (max != mMax) { + mMax = max; + postInvalidate(); + + if (mProgress > max) { + mProgress = max; + } + refreshProgress(android.R.id.progress, mProgress, false); + } + } + + /** + *

Increase the progress bar's progress by the specified amount.

+ * + * @param diff the amount by which the progress must be increased + * + * @see #setProgress(int) + */ + public synchronized final void incrementProgressBy(int diff) { + setProgress(mProgress + diff); + } + + /** + *

Increase the progress bar's secondary progress by the specified amount.

+ * + * @param diff the amount by which the secondary progress must be increased + * + * @see #setSecondaryProgress(int) + */ + public synchronized final void incrementSecondaryProgressBy(int diff) { + setSecondaryProgress(mSecondaryProgress + diff); + } + + /** + *

Start the indeterminate progress animation.

+ */ + void startAnimation() { + if (getVisibility() != VISIBLE) { + return; + } + + if (mIndeterminateDrawable instanceof Animatable) { + mShouldStartAnimationDrawable = true; + mAnimation = null; + } else { + if (mInterpolator == null) { + mInterpolator = new LinearInterpolator(); + } + + mTransformation = new Transformation(); + mAnimation = new AlphaAnimation(0.0f, 1.0f); + mAnimation.setRepeatMode(mBehavior); + mAnimation.setRepeatCount(Animation.INFINITE); + mAnimation.setDuration(mDuration); + mAnimation.setInterpolator(mInterpolator); + mAnimation.setStartTime(Animation.START_ON_FIRST_FRAME); + } + postInvalidate(); + } + + /** + *

Stop the indeterminate progress animation.

+ */ + void stopAnimation() { + mAnimation = null; + mTransformation = null; + if (mIndeterminateDrawable instanceof Animatable) { + ((Animatable) mIndeterminateDrawable).stop(); + mShouldStartAnimationDrawable = false; + } + postInvalidate(); + } + + /** + * Sets the acceleration curve for the indeterminate animation. + * The interpolator is loaded as a resource from the specified context. + * + * @param context The application environment + * @param resID The resource identifier of the interpolator to load + */ + public void setInterpolator(Context context, int resID) { + setInterpolator(AnimationUtils.loadInterpolator(context, resID)); + } + + /** + * Sets the acceleration curve for the indeterminate animation. + * Defaults to a linear interpolation. + * + * @param interpolator The interpolator which defines the acceleration curve + */ + public void setInterpolator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + /** + * Gets the acceleration curve type for the indeterminate animation. + * + * @return the {@link Interpolator} associated to this animation + */ + public Interpolator getInterpolator() { + return mInterpolator; + } + + @Override + public void setVisibility(int v) { + if (getVisibility() != v) { + super.setVisibility(v); + + if (mIndeterminate) { + // let's be nice with the UI thread + if (v == GONE || v == INVISIBLE) { + stopAnimation(); + } else { + startAnimation(); + } + } + } + } + + @Override + protected void onVisibilityChanged(View changedView, int visibility) { + super.onVisibilityChanged(changedView, visibility); + + if (mIndeterminate) { + // let's be nice with the UI thread + if (visibility == GONE || visibility == INVISIBLE) { + stopAnimation(); + } else { + startAnimation(); + } + } + } + + @Override + public void invalidateDrawable(Drawable dr) { + if (!mInDrawing) { + if (verifyDrawable(dr)) { + final Rect dirty = dr.getBounds(); + final int scrollX = getScrollX() + getPaddingLeft(); + final int scrollY = getScrollY() + getPaddingTop(); + + invalidate(dirty.left + scrollX, dirty.top + scrollY, + dirty.right + scrollX, dirty.bottom + scrollY); + } else { + super.invalidateDrawable(dr); + } + } + } + + /** + * @hide + * + @Override + public int getResolvedLayoutDirection(Drawable who) { + return (who == mProgressDrawable || who == mIndeterminateDrawable) ? + getResolvedLayoutDirection() : super.getResolvedLayoutDirection(who); + } + */ + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + updateDrawableBounds(w, h); + } + + private void updateDrawableBounds(int w, int h) { + // onDraw will translate the canvas so we draw starting at 0,0 + int right = w - getPaddingRight() - getPaddingLeft(); + int bottom = h - getPaddingBottom() - getPaddingTop(); + int top = 0; + int left = 0; + + if (mIndeterminateDrawable != null) { + // Aspect ratio logic does not apply to AnimationDrawables + if (mOnlyIndeterminate && !(mIndeterminateDrawable instanceof AnimationDrawable)) { + // Maintain aspect ratio. Certain kinds of animated drawables + // get very confused otherwise. + final int intrinsicWidth = mIndeterminateDrawable.getIntrinsicWidth(); + final int intrinsicHeight = mIndeterminateDrawable.getIntrinsicHeight(); + final float intrinsicAspect = (float) intrinsicWidth / intrinsicHeight; + final float boundAspect = (float) w / h; + if (intrinsicAspect != boundAspect) { + if (boundAspect > intrinsicAspect) { + // New width is larger. Make it smaller to match height. + final int width = (int) (h * intrinsicAspect); + left = (w - width) / 2; + right = left + width; + } else { + // New height is larger. Make it smaller to match width. + final int height = (int) (w * (1 / intrinsicAspect)); + top = (h - height) / 2; + bottom = top + height; + } + } + } + mIndeterminateDrawable.setBounds(0, 0, right - left, bottom - top); + mIndeterminateRealLeft = left; + mIndeterminateRealTop = top; + } + + if (mProgressDrawable != null) { + mProgressDrawable.setBounds(0, 0, right, bottom); + } + } + + @Override + protected synchronized void onDraw(Canvas canvas) { + super.onDraw(canvas); + + Drawable d = mCurrentDrawable; + if (d != null) { + // Translate canvas so a indeterminate circular progress bar with padding + // rotates properly in its animation + canvas.save(); + canvas.translate(getPaddingLeft() + mIndeterminateRealLeft, getPaddingTop() + mIndeterminateRealTop); + long time = getDrawingTime(); + if (mAnimation != null) { + mAnimation.getTransformation(time, mTransformation); + float scale = mTransformation.getAlpha(); + try { + mInDrawing = true; + d.setLevel((int) (scale * MAX_LEVEL)); + } finally { + mInDrawing = false; + } + if (SystemClock.uptimeMillis() - mLastDrawTime >= mAnimationResolution) { + mLastDrawTime = SystemClock.uptimeMillis(); + postInvalidateDelayed(mAnimationResolution); + } + } + d.draw(canvas); + canvas.restore(); + if (mShouldStartAnimationDrawable && d instanceof Animatable) { + ((Animatable) d).start(); + mShouldStartAnimationDrawable = false; + } + } + } + + @Override + protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + Drawable d = mCurrentDrawable; + + int dw = 0; + int dh = 0; + if (d != null) { + dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth())); + dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight())); + } + updateDrawableState(); + dw += getPaddingLeft() + getPaddingRight(); + dh += getPaddingTop() + getPaddingBottom(); + + if (IS_HONEYCOMB) { + setMeasuredDimension(View.resolveSizeAndState(dw, widthMeasureSpec, 0), + View.resolveSizeAndState(dh, heightMeasureSpec, 0)); + } else { + setMeasuredDimension(View.resolveSize(dw, widthMeasureSpec), + View.resolveSize(dh, heightMeasureSpec)); + } + } + + @Override + protected void drawableStateChanged() { + super.drawableStateChanged(); + updateDrawableState(); + } + + private void updateDrawableState() { + int[] state = getDrawableState(); + + if (mProgressDrawable != null && mProgressDrawable.isStateful()) { + mProgressDrawable.setState(state); + } + + if (mIndeterminateDrawable != null && mIndeterminateDrawable.isStateful()) { + mIndeterminateDrawable.setState(state); + } + } + + static class SavedState extends BaseSavedState { + int progress; + int secondaryProgress; + + /** + * Constructor called from {@link IcsProgressBar#onSaveInstanceState()} + */ + SavedState(Parcelable superState) { + super(superState); + } + + /** + * Constructor called from {@link #CREATOR} + */ + private SavedState(Parcel in) { + super(in); + progress = in.readInt(); + secondaryProgress = in.readInt(); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeInt(progress); + out.writeInt(secondaryProgress); + } + + 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() { + // Force our ancestor class to save its state + Parcelable superState = super.onSaveInstanceState(); + SavedState ss = new SavedState(superState); + + ss.progress = mProgress; + ss.secondaryProgress = mSecondaryProgress; + + return ss; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + SavedState ss = (SavedState) state; + super.onRestoreInstanceState(ss.getSuperState()); + + setProgress(ss.progress); + setSecondaryProgress(ss.secondaryProgress); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (mIndeterminate) { + startAnimation(); + } + } + + @Override + protected void onDetachedFromWindow() { + if (mIndeterminate) { + stopAnimation(); + } + if(mRefreshProgressRunnable != null) { + removeCallbacks(mRefreshProgressRunnable); + } + if (mAccessibilityEventSender != null) { + removeCallbacks(mAccessibilityEventSender); + } + // This should come after stopAnimation(), otherwise an invalidate message remains in the + // queue, which can prevent the entire view hierarchy from being GC'ed during a rotation + super.onDetachedFromWindow(); + } + + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + event.setItemCount(mMax); + event.setCurrentItemIndex(mProgress); + } + + /** + * Schedule a command for sending an accessibility event. + *
+ * Note: A command is used to ensure that accessibility events + * are sent at most one in a given time frame to save + * system resources while the progress changes quickly. + */ + private void scheduleAccessibilityEventSender() { + if (mAccessibilityEventSender == null) { + mAccessibilityEventSender = new AccessibilityEventSender(); + } else { + removeCallbacks(mAccessibilityEventSender); + } + postDelayed(mAccessibilityEventSender, TIMEOUT_SEND_ACCESSIBILITY_EVENT); + } + + /** + * Command for sending an accessibility event. + */ + private class AccessibilityEventSender implements Runnable { + public void run() { + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); + } + } +} diff --git a/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsSpinner.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsSpinner.java new file mode 100755 index 000000000..038d1e031 --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsSpinner.java @@ -0,0 +1,703 @@ +/* + * 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.internal.widget; + +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import com.actionbarsherlock.R; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.res.TypedArray; +import android.database.DataSetObserver; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ListAdapter; +import android.widget.ListView; +import android.widget.PopupWindow; +import android.widget.SpinnerAdapter; + + +/** + * A view that displays one child at a time and lets the user pick among them. + * The items in the Spinner come from the {@link Adapter} associated with + * this view. + * + *

See the Spinner + * tutorial.

+ * + * @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.

+ */ + private static class DropDownAdapter implements ListAdapter, SpinnerAdapter { + private SpinnerAdapter mAdapter; + private ListAdapter mListAdapter; + + /** + *

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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsView.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsView.java new file mode 100755 index 000000000..a7185d082 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java new file mode 100755 index 000000000..1a532e06c --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java @@ -0,0 +1,545 @@ +/* + * 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.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/libs/ActionBarSherlock/src/com/actionbarsherlock/view/ActionMode.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/view/ActionMode.java new file mode 100755 index 000000000..81b4cd4d2 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/view/ActionProvider.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/view/ActionProvider.java new file mode 100755 index 000000000..ae7cb1fe0 --- /dev/null +++ b/libs/ActionBarSherlock/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: + *
    + * 
    + *   <item android:id="@+id/my_menu_item"
    + *     android:title="Title"
    + *     android:icon="@drawable/my_menu_item_icon"
    + *     android:showAsAction="ifRoom"
    + *     android:actionProviderClass="foo.bar.SomeActionProvider" />
    + * 
    + * 
    + *
  • + *
+ *

+ * + * @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/libs/ActionBarSherlock/src/com/actionbarsherlock/view/CollapsibleActionView.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/view/CollapsibleActionView.java new file mode 100755 index 000000000..43281b013 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/view/Menu.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/view/Menu.java new file mode 100755 index 000000000..951f4ccef --- /dev/null +++ b/libs/ActionBarSherlock/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: + *

    + *
  1. Context menus: Do not support item shortcuts and item icons. + *
  2. 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. + *
  3. 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/libs/ActionBarSherlock/src/com/actionbarsherlock/view/MenuInflater.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/view/MenuInflater.java new file mode 100755 index 000000000..969459749 --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/view/MenuInflater.java @@ -0,0 +1,472 @@ +/* + * 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; + + /** + * Constructs a menu inflater. + * + * @see Activity#getMenuInflater() + */ + public MenuInflater(Context context) { + mContext = context; + 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 Context mContext; + private Method mMethod; + + public InflatedOnMenuItemClickListener(Context context, String methodName) { + mContext = context; + Class c = context.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(mContext, item); + } else { + mMethod.invoke(mContext, 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); + itemActionProviderClassName = a.getString(R.styleable.SherlockMenuItem_android_actionProviderClass); + + 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(mContext, 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/libs/ActionBarSherlock/src/com/actionbarsherlock/view/MenuItem.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/view/MenuItem.java new file mode 100755 index 000000000..7fc3aa430 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/view/SubMenu.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/view/SubMenu.java new file mode 100755 index 000000000..397fd1c2d --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/view/Window.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/view/Window.java new file mode 100755 index 000000000..a340a4291 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserModel.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserModel.java new file mode 100755 index 000000000..379207471 --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserModel.java @@ -0,0 +1,1131 @@ +/* + * 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.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Executor; + +/** + *

+ * 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 SerialExecutor SERIAL_EXECUTOR = new SerialExecutor(); + + private static class SerialExecutor implements Executor { + final LinkedList mTasks = new LinkedList(); + Runnable mActive; + + public synchronized void execute(final Runnable r) { + mTasks.offer(new Runnable() { + public void run() { + try { + r.run(); + } finally { + scheduleNext(); + } + } + }); + if (mActive == null) { + scheduleNext(); + } + } + + protected synchronized void scheduleNext() { + if ((mActive = mTasks.poll()) != null) { + mActive.run(); + } + } + } + + /** + * 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/libs/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserView.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserView.java new file mode 100755 index 000000000..da13bc99f --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserView.java @@ -0,0 +1,818 @@ +/* + * 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) { + dataModel.unregisterObserver(mModelDataSetOberver); + } + 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); + } + } + + /** + * 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()) { + oldDataModel.unregisterObserver(mModelDataSetOberver); + } + 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/libs/ActionBarSherlock/src/com/actionbarsherlock/widget/ShareActionProvider.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/widget/ShareActionProvider.java new file mode 100755 index 000000000..83e9f0ca9 --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/test/com/actionbarsherlock/internal/ManifestParsingTest.java b/libs/ActionBarSherlock/test/com/actionbarsherlock/internal/ManifestParsingTest.java new file mode 100755 index 000000000..1314248a4 --- /dev/null +++ b/libs/ActionBarSherlock/test/com/actionbarsherlock/internal/ManifestParsingTest.java @@ -0,0 +1,39 @@ +package com.actionbarsherlock.internal; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; +import static com.actionbarsherlock.internal.ActionBarSherlockCompat.cleanActivityName; +import com.xtremelabs.robolectric.RobolectricTestRunner; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(RobolectricTestRunner.class) +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/libs/android-support-v4.jar b/libs/android-support-v4.jar deleted file mode 100644 index 018c1272b251e0698d28599a4d00833c82735855..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 337562 zcmcG$W0<7Nwl&&i+qUhhF59+k+cvvg)n(hZZKKP!*~MFHt$n_4pLNgL_qosgZswam z^390I$cPa$<{WcG%1HtNBYy+`dM3aw^L_ib2gEnvZ_>g_{50Y+B6J^P-@XBTlamAk z|Dy!pzlzEIV`b#86922RG{20vh_Ip(t+dFU^u)N7Bn|B>j3f=^)WmeX0{sHh_JISf zgfz9d^sI9kFhuGJ#sJn&3Nzr8LyBh;QnUj!Rn(M%LsTlaLy|M|TZbD5fImAz7Ma;E z`t>z`y#DA2LqlUnM{@&nD|07zM`H&Ub3@}lTls53{@#k!|BF`m|GjC&znT`e zF|if2wYJlDBL1h3#SIv&4qOBJTK;Zb{ zD@z@Ryi+O$$ZH0?!|9<;kH8&O&#SW!eBy9dnYF@Cf`2loBO># zZyvDxz*Z>p{4|9t@+>B-4VR+S*-ZNTw4t8RZ)FSPUpV>&Dt&rltwFhaYZj6v&SAl{ z5jyv^vsEN6diFj5eiXRl(wRKeMq#L6Ws&WwxfIepyvBqfoaz-co2NM7hk`^ zIdK075{)^j>HB?Pr)`tr9(DxkDXT<_l_7>P7S)hezcUs}B8?Rz_ItLx!SSbIZVaxA zV&UmI@{?6s?j%FQR7fx-^~*sjl`LB(4*Hbheyr)fV|sQkk9@2@w*+J4w4HxNCaE4m z$_SI=_TKLBKGWgG5}FZ@6f&F0;JA5T%ZiYyopXUW5fe!LwLRc`n!ozbh*22>7ZW`* zt71Kegw#%q)UgGd+k{lrVgtq&hOzGO$bCs<^_D=A@7;jEF(G*Y{;&a@5@6FjL!wI?h1$)sN2ouG2u1jVT zk=~ocha7(xv7KKEzsdT{$OhoO0eq0#$7g83{aO$tA!g#_oW7fwg8lt+afkM8#m~~( ztVbA)w$*Mmm#0hL8G0-np&w&b&LQ zTXe3}gn(Z0vI~kdvB(KX5)7{N_Ke=tqjkv^YBM}+oE^ZQgbFd3SqAfHLtF1oIlbaMV^Zp=atuw~- zFS2)idcRfivO^7{%tVGh)x5%f&0MIyfIS{GS6p-}B0BZv;sHuCzT;@zD|DM5#2>{O z3-2$H>EVsq>(xq82C(g*dvhuSHtYbaV=}3w1zaUKLrU$vL)WPiPAj)78tu^ZmnztS z$V@xbz%gMrTe44K`y1wm>``AV+OL=IsBsJ6z`-5ZZF{x0@pXcpMhi}3Vv~6#oG&iz zy8eO4ps-)iPXOP(t%Cd~MB4rTk4P0`2S;;ToBs{UO8H(1AO`qMv$e{`r7|!$+=m>r z!0pkjF$w)zawq;-%`_azF`GK#lZfuaUbyT0fs?91kCL2JL0tEr?pM;cj~};?-%M-J z_{jWlKp!Ap$o1`IXI-yo z7Lsv;;wa$cGMUSxGPHcQzvandUdVf9nRz{% z#7H{Lm910dW1J%)T&W4oQ!Qup7E z9G2@C*1)oS>-)|u0iC11J4CjXMzm;~?1|MDM5CZr1F{x<2=>R+n&ER%aeqzi+Lyuk z-wwy$H%F=eV^jOD%}?diR#6%GbG@pf`hY=XRgVgZ6dx5*37H=~M|-W-w4JU=I}TuK zdAa>|Xw9?n%2~jGeg_OGs{5yzL0))J5Z*ml5AcHU8)|;988hiAo?eu1u-oNX6KjQZ zqF=T9Q;P}Lc6nHvDE6DTA zHftXIY)(g7$*PA=(Nu>Gg=S5RnylzXgUs$EHYH7y)*&A%V0AK$_%hEqj8^6(Nk=G} z%@ddnsu&ouOz7r|)GE}S=#AwEb;|a6j-L4zM1I;pVNC+jn)Swut1onku=Eue5~N6h zwQd%a$uhFh19cRjMf4RBnnf2SMG30Wl(y}HaDuM{8w;64(AGN_?se=oC>;H#4K}y4 zr&j)|4i>%mGW6EwWpS!hZEAG%om1wS0<2!yZ$xXVi7ola7z#Ti6Q97$HQe0!Q>F5P zERwSoinEi5xgO5H3|4%9q(b8usf4U0GaNaf$85EGM;>4TeG|2_1-_23E^0MiW-k-_ru2S6IV@2|6`ZO>tfb zh=(#8637Cnmut*E$I>F!7-K9US~SlP8FIRetOQgt;`ZZWc-|C5c?bOZQEY_WgB#%v z6l6=Z8qR`^HBE6NlAAv|Ya~*0DzJJ} ztK`rSm!MjQ#Bhpno(PS@gxnU*zBXF_1;90-a7xXiCjym|vVZ1%R%o_r zYfnu-#Bw0*_h%Yiqhh1hAt_6Z_RfKFU5DMNdv{S+p&WOL(5Z8}4H$0vl(N#CMKY=T zV4lxeA!dQkrj1?weuj%C8x4tRsYP3#w8aA0sU~-1Z_Rm0xtAr+&rwRl#K5laNzw&H ziL=3Yjrahc4Z zW}rF&Xsj*>B+EfHSn_G_d2(pq9^(sEptM3whsn(Dwoefy+S?tw00{hs#S8gzC~af5 zwPUV56ob4gt<-9`*V4wgYSUiu+$DZqxgtd2p(T~{9es2VR{FrTiX1I z?me{6_&$gH67F5*II$tBP))1xu_0WRN}}Vo(=ZoNAV4jVPt?v7Ty?^M+ku70+Rw6# zXN31mgS?^2Z#b5Ap(Hx!W`9(ZFLo7K{RS2Gie!3@N!znly5XMM1?z~wvj^baA@mu- zdIquGRqY7gx~BDU&0mm>1K+`hl|O~#rQM;I?+bMuDJ~ur<;_x$!d8G1U&ks7HrNnA z9%yH6xuh(|THgtIhGTq{tr*sYQJsri35e(|JQZsh8_hUV+giQh`F=Djs5<|Oz@=Ja zflHual{3hq`m+bFZ6}L35$Sk{!m1Bhe?a>kLIWUOJTJ7&iD)#)?6O#0>Eh7E{t((^ z=kres3=^pB2L83NCPV$Z1%CTmq$T@*%mRN!A%7X+%7i~1y5Y~Y&A_Z$?z7eUB@0dK zbR=f*2t8qPvc4VpTrw2z=4SP2n%0fjbKy6ct{FDSu4|y@+=0~$I2O=|P=6`b{{C6k0ZVFsd1?rxR^VSW{L@_LVVb(2ZacRIjo6n^)M3N^PWx(2?GS z2OmaUiK9$BQ6u#WYyqO7T=JKnq4fs3<%v7u*=O_&l+bhH*y#hHtC@aK1oKKT$_3|Z zN~Av#8CN(jv86J(-^ZLL*$Zq4{Wn0AA==Y|ArP~^_LSC-g zW9mrNJ|rV6!hznkx1(kWZxXk{uPj8Icwo}$y?bucisQ~6 zErO$oo3`>e(-#wJZ2Ie(yFxEC_i0N8O7PAVEgSYnK@O6GYmv+WnqQ3AI9=vMk*@(+ zZOnIVj;Dp=LRP;W=t{sB#upV^FThv6NDepmSVx^c^}0>wZ%}Fzv!qk+Aie|Z?SLJR zU>!ks9P53-h;AR=0Gs5&DUv%92%D6_Yb|Pks*!fZ$T}3tRVX{@B=q^LZ6%9Wf9&z( zno~vxUwixq@Lve~E0+Iz+>Q1R2(z>Mx2XB=m8kx?62GC7xyx7R{Z~-@7v!Q9v}KX_ zk-1ZnX|+*H4g}$aUOF!yTA?|kny>8P`!8>I~U0L{@p-+rkqv{ZD`H?$ORHn%b|b|C)yjXxZC31cfOcLQU6n}1m& zMQK|k26%599@YXV7;^VM8(ct{jg25IEtqpF0bVb9s5f-aO+dHedP} zSu-Mc3ihE)q*Gp2)*zqUULHEvF>{i!#NRqTyxXz~k&#};*$S$yPbcSvpL|hc6T3(u zM_#=RDS%EYy%~|fgz0Q$WMS|3BGw@zT&r^cC2U|P5DagIeKRZbz+mAIOX2Qzu;YmGn|5Y#Y8<6$laps$;JpQO1?qFO!YXe_tT$^Ub zYuG~lq(y#4l@SloyU#=t8v!Ts9Ge%XU^c?mH<~#E(4<{lxjzffMMp ze+sxPLKeik#5Bihb!O;bj58@kE_ap{_)XjQRH0;dilE*(yGNp5-t~u46P~@IF*8IH zYQ;rkipFrx+8D(#ii6ITA2=PAHUV7@XF`z#&{yG|vKQ|1b&UoP)zWp8o#K^E!Nf82 z9u)#&_zg4AARm&zK9;$6p#%og44AS(HY=Tge~g;(!D_74Z8sML z?w=$OQm5Np-IixP6VTP&**j1VHYY8$6-O$^3aOjE(>pwO@N8eVH}OmW{iCwHrOt8c zQFM(|7a8E33E5e_Ee{+&^*VF>K{{=z?V?$d9qrb3M$N}M|wAlLTYeMSC z)C$Z<919;1h#xWlYy8le!Q#C!KO%D5oF(AA(^kvhCjya9Y?WLSOh&t1 zXw2GzYRa^Au-;&@2WeHmBheAttK4DC@`JWczMd58`;CFk(ywdHmZ=CEh&A;VSO{)J zN>;UqGxtu#KH97&#+u#;ENdY%*7t+0F9S-!VbUVk`ZU%eR+jZnqw!jnaKs#2vE{ zY&Snm9UYnIHv1+k*Vl(8U2JSpu3?8-6a3p-S~dGTTRv&ow(56bsV*CKJ5@U1_?biD z_h0zXEi0?k`;gRs2{~JO^+o92(8;gk2G#Wn=^kIMv~WpGG8Ph{KT27_PU=^vG%t(p zzNELkSMa2F)50tFEdo?$r#_!Gx#jm(WO1UQwI6X%5-&4`Vxp0fiSK<{!`3 z^e))g`LOs=%wi_S-=+~AdsY4TJ0N{Nh5CcESy$iOBEYAc4O5wm+R(cv--;joL>IlN zekrK^{i1TSL{@!>Ei;>Et;{N%5SdilTRMfaL#wqw6T*qy>A8Oi}o47fHgN)l7J zzIaVGO+)_sOi2TVZH+MHmp)S&WO&4Yd zKrn5gHbisVntI@4h+gR0@*%7z#uC}AWZP5SXme1~gU(zGc)VQ^ADear$FGCJ{=}?2 z@mPsE0ei|b?vP8+tWZ`P6*zyn*c+>XHJnT0O(Ld@_?jaRpv_~hpa_2B@qP0O+;ZN_ zf@B#I4OxXW39{t`Y+z%?NCt{K^JOc%Fd_891hD3xvw^Wh_UpKCo->Jx%50wa*&{+> z;Fkm}3}k_w(82j_x$S}Pu|X+IXY=5Dq5C2M!W)_3G=&d>m)z1EA+Eqc@@%WFEtBO| zhn!ZtAf%sHdz#$678fxl@7XJhch>3wxv;$ARxnfq+3kU}JX96Wf$3&5T=ythMFWSl zbi;s-1yn6R(cO{jvS=||@_*?W3572?iRSV1it4i&#wp9vR95p!i=dL^c+{)shQ-iF zlV`BPP!y1XE69kA!z8kb_3cl{$$PXJSsmEif0F&cObYH8L<}LGh^HmfC-T(rcO7PIMvKJwS`8MwOTnD)z~F( z$*51U3nw6k$De{%D?I>0JFed%1y{hb2qIBf=U4E0`^fg}fz#XOTaD zSG1np{MnmYu_>0xi@YHb(#*Ajb1BFEfH2yy%^iHAdEruWisA9LOJ|dft1x!2vL9!(5&Mc~q&+*Rn7q=Yq!HaT zkw~#sc~F6yx*rbx6MpXpM#Yc;;b08^IQ86&&5EGvI<`WehF&=_mSp#0@P~2tyRZ)vHAkUaY4aaV2Ec!d957C}s#oVwvji|Va zOpKs7Iq1_(6`7l=4Y_n=Ym8h3qCGEe>&>aN5GaeUUJ}J?#cg66$*?2^KfT4HIqh5)W@m*xk--k!MD03rVZkhKK<2ax4eL1t zZZ)v|w@tvP+?u0!cN1hKdf60O^SfNW<)RtCG2~0}97KqP==NbTVu!lIPhs07p4yqR zA<2H9d=?q=jkydHZ~z^nQ|{vz)o>>07rllEo>HLeiPHR|(w!6Ck0D&vdq#f_T|GqAZ)bJi4jIuh^>)VQs+L)TX0nM z9zcYC__8gDWiW8HL4V${%)8h_a(ljxuCO!v`R1?fXdz=ar0kTNl;@BrTZcO;8=uKZ z+iRGJWGXb{aVe>856`|C@hyN0V>ZVA_J{^lgD@KgMeY7+BIga!n7EXEZsg(xGDR8Y7NONV zYg5bzj5{O9i^E442~nlOOkq=bz=D+9DB(r?+MiVYX7p3@1*p@j61#!{1?YGP`*LGs zAsKSFXrSdPCZ17bre&jc(nvQtV@ZgG4*;R_KYAB4G@hZI8^xz(K42B3Q#wY+m3w2I zRlKNfzP}S|J*!Ee}0un%V^lOJy`w~cCVRZN#xW_$2ihiCDT6j z{3c$-G`u|}+g$szg1lC}s7w@YxMNL0v)OLYAfBlT^8pnrfS7EqcMA7DUm~JX{wDPZ zPc0*6AAVDR;w(FIOy>71*gQ@Vu-Mv$gmY-JE@T2pgy#`<+)W#g z<#9@m1VJQkM6LLd#ZYDry}23Wg9Qog9TI~-*_|Q-z@dfgPyxinNowlI1ane_5)#6T6`5%c;3pqcryxDG9dxy@raXXl&EPS${;lkAC3b5p#NI=?u_v zfZy4+2Rc-+9nm^4INKeViW(SEOGKVM!8+(#^zSu@E+UAogzfhD?RJPRGKde_-Yv-h zSNIMlh)v<^PVsAR;H_+6UOpO>x;Ob$DE7wUTj>kDx!2GKpX$x0(|00|&yr2Wo!rOQ z`>ah&i*DUexZ_vYQ*M-Ni%%dNo`SwYVom6m5^v0&dsTXVR*1IBj;NxsivZ0d0{YTe z^-yZ>SY?~$)7S;D)rD0TKgHp(SyiiPzX}=Xc`}E^@EvB%S|qE51CrSDmn;PPg18r< zEdRu60u3vnNZRg-Pol?GqU)& z*t)DWu>OgsSXO?a)8UU}HBeAGM4AaJwkuRvSZc_K|b^u1cWUFJq9 z6J)r^rc6x$FlM#BYyJr8B*Q%@EgP%YwB7Vjct|rXdFPxcd~Bvi-1NV>|5fU@)hp!& zS9W4Q^Q}A7_?kxx+Sf1o8o(+5ZpVW1t~!)A-t{w_>k{cUoHrrq6C|d`w~yhOs`HkI zBx>61(DwfG>e}`hOk>Bfp`W)F_#+r;8;UxR&K}b%SoxZTHecaJfZZQW-b5obEG4IB zQb}M-O8B0(VS6t%*xPInO};OMK3!q~`wias4LNitC6x+3G*Um64BHG%(Y_!x{I>xC zYS^89L(4ZPVK7t(I@HL?h7W6OvZ~>mYr*d~m?Nl2dMsR$I#fH1833W9(==`bDl_Vj ze5m640r?FU#@)e3+wLwln4LL8ytwb+I>a=$$HUi1w|2Pi$S=K8XT#ehlTW!nA7^MC z(u(!7=CK2RPt~DQi)s01!uuf`hAiV_kqOXd6-Ka22u9Wy%}$dq#Aj_EUDwHU2m}MH zz)-(J$E$ON7gA!VujM50yO9t%=zp)HBDyWdh?AU)D>f1Gf$_;DVw;#Yw{nKIFre>3gB&-D4{2PVeOI|5S-##6Lzmj&p#w!(KN{A< zd;(!ez0`6BKb<%;kV~NtrHB<|q#^!kfsc2lM@<_L-hxhQ$ej@=sn41Px481{VFi4= zmSR*%1LmiuGS(rK2a->r+G^Wo*Ok}ZgNxc)h155T%?+0jr3 z(ZJ3)u+!xFbLoXdOZ5|TGoMXm+(42+&K&+{(04w=q@e zDFMzOSRu7xDsCwQA+umqNGZj^;p6dA0*veuprWJn;d>$C&rS*C)SD3fu>Ha1=LLz2 z)Ajyp3NS*E<*ES~)p@BNFdS~O@wv#DZ;&uP!9x&G4@iWCJx|V4nGJpMs3&N%$76Y# zc1brRGc-*kwYb6pfHwJ(EdU`q(R#z_dd!>gPV0n$-f@?ji2J3K`+Fb5;;9<4Q}-?g z9$*Ze38|aHQdbM7-oz1C8AAVYxdfhtlI?vD3V1?TCJQ_B+4b;i;c*|+;>~Ll-)mF*k-9py zTTAKztd)3)QA?b+3OZ}`tk%3>McLhSCd4}%ZbOm^95g$%i%wEp%P``YJb=G7I-950 z!;w{JaYVmz4Guk=FXuO1N z>guvx>_OeR!PH6-fQ3ge;!SyMXtmSFs6K@rga+!>a@{G|Yx>`ouON)v^f* z8mIVL6pnezJUFp>ym)7?L?^9P_9`jo&Y}&vZC_A17KX(-DJ5QlGGEd%UyPvBlzSJG zGF{{3#vo%HHowzip$DeCmu3Q^cgp$-66!O)vTUS9U&KYa#K!AXNJGha}E`BlPtAf(r1~fjnJ4Ggg~y>(Q@kNw2Wj5FQp1~jAG_#8H?pE z88VN#?Zd>BSst_0l1n&H`@p|{q!2X4WB5 zd^qJD7ng6OnmiHmi8nMF!~qfpwQ>YK?}J|H@x7R>eLCAQrt$3vZ?8zvR#CoP35sVs zc7gBqiQtJpJaQpJ%WY_408Q3OLt$X?p|*=}eOAD8H&t)|U_iYUf-v{OHh+UorNXxB zVn@;#5K<$F--@$@fvFOP;$9miI5xLl2EOX&e7K95m^kaNkL|l>T#(%fT-vW4f4`@nZYb(h_41~v95>9ay zr|Zh2HG^xhY=w(eiVmTCAzu&j>&e~5_;cn$9i#KG&b(q-$Yn% zNe4~UuNcDqoWg|j@@w4fUr(C`o~?a1#8Wk1}r;&6G?q8&28e)u*{QWRu7so<5{aoui%0@SJ zyIw(8?;O~2i95-TDV@S8(5#@33yu`>nqFOtzt@F`8j%fXa|=kvu1bs6I;?60XRhcv z$lAb0jrKYaTtwz|>Iw)i@>3s!JzfXxWvDB-kM6lY+(6(p*$uoGDQ<{wWao8~Tad#= zeku)af2{#t7Qy45ko`C9UHmCD*&P9UaI{Et)<_;JwCNnetU;Jv`jA0{4i&D@wE?IW z)i>a$NWh1JhWiDEsez<|NvMVi#lT~Tq?`v%kF|$z7-ot>hJx+_R6Ei@T z_lvwIJSKU!{KGxrLuR!Mzw~B^s_ExGLdna;oftcFca2BrCnn`y(&si^b|<9@d10rY zjTe)3f;y5A+=WE;*PZrVnFxyeP0jWP+$Fo`Xh${jZbn3@s*%eoX8wz4Pf|Ry z_{vFtQH~cwmC}|%){2#w)^(+;DtmN=IfDko%Jd4oz6q15G8+@L_33^;NQfant^vFN zW9gI|*v}6sCt9!fU2vJFqf(-l zp=yP!$ATHJ8tX}eu3>M2cA(7*I}q>Pvi7S)mRG?VFGqsOBcBdnv2x)GZ+0DacFBzb zsCJM!)#HJ{kzeL2F|Hb~lujV6SgF8aFna2%(o;7UAWnn5$IMi_k>QU^P~E%RjWjYJ zl`|!Q~@D*P8@jF&AP&N@S zs8*>HC=NSV8;S)?vt@6aD;YZ@a8GZx22cfOVMl$%#f;q*XI(ImKC_rNB%t&ZMC+jg zEz@v^40vSX-XUiJ0{?P#9??V|o%`^k-9;dZK$8Q*f5Ug5%z-@N33~B!=Xos&f1N9z zs$bxf;y0)OQim^OP%T@dLe;^1C4%JfmPL~9?qj3rzAvVBszk@BAoaEe)d3Zy6M!{Wf=X=p47}dnLhMupgzbs z9>b$^+0GnMn9tz${mf9J2B#0|8FAbJ0lcLJF^@3K~W*E9sZC@CI zRM3(`FRuX0M#$4br`~;H42@ZI5-my*2^xomhb7tIxWZn>OqjOUU`6ZBU;k|X_etro z6*cCM^6-TEQL}TW`+ee_Av$v?je(g7E#iZfwx3wV0S3wHPR#S-mLlOyP8&b5qovEu zojpQw#kmurn4$(*m@G9tSQV&IAd@eIP=^RLa^cnGMSP{jh337K*qo2h1r5#crS%x7 zB#qMd+D4HI->{pD8#w|aF)O1?3RpDJET>oZC(eXJKAh!{ls#iO87Aq?e8p)T)yEM8 z*c&pa5c4oZb!ReuC!J|EGjh~#ht1tMTkw&L;pgHq9N$grMO$G zt&&`VRU>V9d=LsAVfX&Uc)62$8s`<2E>!)kSXgw4kV&@SzO^$VHN!XfsxjB*)rb*EYPN|*W z?kmz#i&!gRMDgB@8zMhGECyVH7{C#kG+6YIDnnH&ykmo3X0-_M#POr^8@}@;$88{Y zg@>Hk90On)*YzMUvvhkn1&oi40>ZIi=<(SI@kJ;l+_g&b+*e80S70OS$!ZmedXnS zrptCl`QReYz@PW{<)eV}Ig<9ywyn)~9kb8aDWgZcIYgcdbg{Eoz8Ta0M8COj$PcdA z?pJyFax_4iS^n;E>iELB#}7FfuT~Cb*OZ=KtQAynuZSMRw$j@R-9YbPY^FaZ6|QGNOX)D8^|C9ax4P(+jsAZ~`i6T~4y%sri4mc(6H)u+5@Bc==l;%$qD zGnLEJ#oN>|IZpkaa;?hNy~6wEQj;ajTO@5npD>7QB0ZtM*%RO|3?v*PegGD?v03k4ZmepIo(a-1i%j{8EEK5J$Z155_G!OX-cT(2xk?YcRDy(d z&qN!zx#Kd9RQCbra?uUHab3R1Spr(GW-1A>H+;BXF4m+o#jg-Yf)QmPVyX_-X7ky8 zJB+EcRrFJ!8P_dcQKNY5=1s0_C^+oWd$}h*7{G`o6}`D83I&J+s$4Cf_8}2KP)2bL zR~6>9>;pd|%;KV~%COMKLi@!3M{akiamdLQD3?StK_0M5!fu>I*4yxaT+3Z1`@&6w z*ZSeyD}>ImOpP=ur~a>!TrsMI+(#_E){IMFYX!V_QqTvv<3>@3yi%(!KjqDtr$oJw zKqSUZFI>;lS!W z#2e-sHt-6b^8K-1TYvbC4_nUh#b2@v(Eql7|NV5se~@PU*WCY0G4wBKhVq%inh^3w zlM3N(i*$NZE<~aZ{h+v{kokBaEQoHr%EACfM(WS&{Q6{6=g7`Ev2ti{P#w?^jMN=4 zxE?_UZ0kgJnYmwzZ-%_t8;)I$8;iW39}jGP?Pe$p!POBp*ALJZC2M9YX1~--)hx=t zKlz-RX<$ybLI!QchoediK$EB zTud|z{CH~TO8%K)tMSq*g-%IR^~Rpql~5p$JoIa|{py7hD@T~q81}z@SMlauUJY$}aY_4lPXtodEiL|m-zFzEpFyHYk zfMt-9nj#ElQ6yQ6rPHHbK)`;#9ByOLLY2do?*`0Ds?*e1wMnT7h0c#b{B@-2I~J_M z7r&%l91mEe;w;K;x-zme_9pM2cj}buvA;)+CK9BE4Eml1{LjmX<<=9t=20NhCC9<;?R=n?o?IE0mh$pu#B(WI z>y(_2nn&nhHuU*z!nX;k7_)KKKTRL=)NN2t(?@g1{WfTEvSL(Wxk0ztukBqt6Fx&Wzxbj zG}_3iUR8SJ?j%f>wU7D+;(24cT3ov zyncUsBOPmf&Qk!D|v{y@*>VQ)BwBoA!XP$w+}pE)4Z}4=t}rj<`8&$kj99`^Q2q;n5bb{aVC< zUnh?LS;Y4LRgx-ZYh&zgXlrfo*E+BKlBCY_!F#8(+uOmwm@EEEl4`-XuArQU03s<5 z(1;9N9NigYo#}in3e4t%sP)Z$8|*qC-jFN;pIMoNeTC`O<*;*gbrp}d+Z)UdEZLun z)->|@8<1*^)i!0-iQ25a;IMSaTagmuFN2Y#Q!r?aaaWLjD^1E;tf%jUD@Qm#Vine0 zTU>j9epZO)pmxR)2s&qnih3fr#xxn8v=Y{MdSAs=4|on=IipKyQXn*N;f4hC;Wf27 z0O_`=pg+C(z^ey(qFy~!il-$Zxu-)9R)uAOA4n`VDibRWY%dL+$7CC*P}N@L9hP^y zgosP8;a)xGHsVk%BTc@bOG>;71}8e3ijB$rEEAPy6h59vD&2)<@^{0Eq{nQ^T7_<1 z#=TNVzI-A&p}IYcDUI!2x!dbO;ACu#x;1KMh5hZ9PQD!#lNSDc$*CgUY&$syCIsyB z0QDFV5o3I>%#!;O8#2_R?l;{6^xbej)Ua!8ZjZLB=}K*3ns)Z)$y#y6S@S&2T-b&y zAxCT+^3FgFcP5Y3kxnod%>!z!kg|~-#X*$;t1wfL_Atlyco`QK-Di+Lut^C` zjY;$sBu@V~S?b>fmJI*TQGqEvn(eLJWB5};Bs8AUo8JkN->> zV1~TqAmZcv&l4lO|1uz?19Rs<=kCKMi2{2525)XwqQNq89~b@(1@OuZWK|qX=D&zV=Cu zDXK&=5;g-)y`DSfoM2JHlNM+81Jyy@8OZ8tr+U5C6zy(7aATo=1l#G)7~4a@HD~5? z4VjjhbjYPhz#qdzS=x-9fD`DM->^q#zgHcSjBP}##7ct=)>Rksk8PIh;dC)qC7Lf9S2}pScUdab4@m^BszoI zjk;=6Eaa74jWtqH=E;7%OlY;vO?0TR9yaFea|t$ndcHtw2~HyDYOyfD=r&s!jWJ+j zQk2};Qp?jZsX9}X60Nb**T>ywleL%PbiL|e`Z;DVAF-B)`GD4sf~(S!nBizB%{9y_ zf*tY~;zdp>Tl(fscO5LM)}qS0tVLjOS60mIySwcptgFPMdp5y`aKGjdN<~PzHJ#9p zhw4Eu1%CC&7(uUx<3MnkZo7BrZL^CYxy|-Sx~=x055aT%-5>^hXe;elMBYEVpk3gz8M=3Z@(hs) z@EG%#S5HHG#n7=UeF*l~_hQ+V->Q!^<<>e;)3|Td))5Q|$*6;@sna{T7N=y=K@DFH z4SA2ACiIO`Nu58kva0#W2!0;O*XQW#z8prVk2;N4VBp_H+iiXY|oVV%JaPBGA^mKHGffy)ddO&cwQ>;&kI&7p*J3GIh0P z=Xvo8+c0h??-_&D=&)`Vo{|mjhVqj%XzB-_U!kvrmamB-S(D{4caZ3%iiL|x z<}G+|u@-Ldow<&4iUj^Dr!y7vIy{c^b9)Kh%i<=QpwxZt2N6YX?v^uq4c> z+uS`fipA^Xrcd8Se_~7Sx*K@;qdXv%KN{zG29M|%x^2p}JcTS-w!B*Wc@eJLZ*rA! zEv9C)rZMNk9w+*3@P$$Z?{uFJ*~v!EiBra!bq7``oQgk zkF3+X?|()#X;ZSDwRiIibc1p~Zsa9D3ze>;GA=7M>BEOEdZ*u(g};(9AX97=M#=wp z>?gF=rL-1t!!n<@0~RG36@5mtT==QG*jsWnq5oJ>fzCNn1QPg>l8cxJj#>d+ z&i4X}AVQ{0vLGAn^^Sy@bjtQ$N6F|ncDek0P(A--4ft35`TwCc_;39AZB=nJ`tMUy zGE?}@kMYV1AhC(Wu_qNqXo zXgL_N#e-zVKc&0P!CZC=WmPcdZW4m({aXIt^D9jU1;kf-HN8We^@$Qnti$??*0RaC zMDi2`rfBlS!b93NK$1aV`kWyz-Cdw8tzc+MabVq)#SkaKjc9E7JZ*Tgc4oF_w$1{5 z6=^hb^#%NWq>(t87IIQ}C475@t20RxKDHq0pt;;5*k>e?1|w9mEgSZf-sRQS{@0kT zvT8m)PR?GSgwsZ&l#4&S%zX_!p&8e--f2TI$uX-$#hjY*b6|0$Rp?&0trQDfB*IhE zCT79IEN9k^CY)j9>#n6tb|Fa&$RuAXq7Spk86Xqqv`J}b3llq*s`!u87X-YWkta0GpvAZ_*MOp16K3qTndXSlPD{KZnVOvANU+TSEX><*6)V0{VD=Ws~mlgZ;DO#Y5DbiGT4DthGfMNX$0pQDLYA z^Q3R>#tTJi=fy!v-8KE%-WOD)aMoQZC~ZVeAq`aj)H=t#=b$6lSwUOi+3dTCDfPLk zTQleyqD|?lRhjt<3Y(WT9G#B-#kQdEhV)+_;b-}`u;Uv$2LH!Ao&R8flCshJHZlIw z2&hV7-TE6E@?^A%;~<*$cm2+TmoE`k4dsm^N3;SGhD3{N7CSUUs?kI9ZQ4R}C=-k_K3pCmjLu0+*i$ULRE z1n(O3^63q!5eT+dkpTAm#&#P5;|bae#v>Y0T3`l}9E5pp^4wvfAbBk>y<~-J3WAqc z_7^0d=*;I^N_?eU5{xSqBYRxA+mH!QW`Ju56~VAjH7=dJt%io0 zM;orap+oA^ofJ5W2F}r|zSDI$j;r(M{#Y7igG(+gX%gDfs*gP1So>8=w02LG$IYf zPWa=>8_^bY+z_LBZ;idL%FbW&^CD17EG}j^wM~k1HOa9nT33-r z6GJo->>{|K0_J~tr@h2OPmHIHR?sZcwr*BiB~>Z^Fw)RUZl6O%22LtpbgFZ%vTA9+ zs1Elkud-^2SzNQ4%=YN9=}wa(g@0+g-tTzoc-nB7`pR^e=GeZzc*1pq?me_jf%4`) z?Cojmy-}p~WGBP@6EWN}u#fSp`@$dTD?6mil{Lk98)1C9?c_%5<~dvSt(*N5&;Rll zEcev_6m9EGj{Z%E|Es9XdkVcjLNZ&Q2AV+q;g@jxMKxc`N;5)R=LlZ zGfgxyNk;k5uwfw2{DyaZ^a$Ls20Yp!t8%E%9A{#DM;&SQ-Mx}%14mDm3!uldgu1Y2 z$1|1topfSoTeDj9U=hVnvoYUFfdCcgk&bb@PPpqT07cep+Q*yKx7F>rYD{R%1@1hu zBaVH#WUA*wjd;yA^p-y>Mm@X2*HcG_0M2S1ZeG{Ag&AQcXcZ@`C<#BmA?=r4n|u<1 z&|e*vV>v2PE?f{5jA_wSyCeuMIIHUfX?Z7)$j-5Vu;dx`yT{rr6U!V0%5MN(@WM4x zWuP%T7FUK?JOlJZ8iFQy=EHDqMuaC16ktdZOi_jwp(|fXQ)~gD&N#q$&;S}08Q6iv8S}a*`06!#p`NV>9MO&sO@}eLxrctCS zimUcl_qQy8T7ZsF3P|_0;v`ON` z(+PAFay38u8`rs5h2IZ)w#$m3o;TL!kOh{Z_ac}Jl5RkhffE)IQsS}^78Amji>qi0 zkP^;TY8$~=W{+XoWrTa5E8noS3J+#I`;XD~mnb@ObO;!!K%$rwTh1dupYo{35|YP3 zN(MvP1vvGX5=X;5!YsBPsh%@xhQ zVxLq6Z=5N}`7o%T2o{eBV)rML)?V?6%|qXvx%4HpU`%vK05R1lfv_Srxat z?$1?%akOM?LD`5E?zFm9OOb(maas8eqmg1bbL}d3oVD4J5@nN!N`)nhTU1q_9!ks} z!!M1BdMC@S04v#5(Vvt`#z+LhxH-JukAz!Y?oukNBF$lX%@~ZvkcBg|{kSw@GIwxp z6UvG!bqQ9=tEkON5`-FR@!V$6gmU|y?D1lJ9n$2fHccl(ZIox3b#WKOhyaiE!JB*( z#=1E_IZs9IP$J58f|F_l#~@=`l(#AodNI2}v$N4F1fuvF60k%NScTY_c33=z+A~FG zp7p=FY^72LS^x@1K%Xb&T?A&|)45%(b<#ua%YsJ6+yt`la;^&XI`@6ZZPGB4WKlkU z^f^L)>+dIenfN;zt9UXDbJBDv^l~zRG5Rn0HK1U>Fc;NHJY6_pr(%3WYsoiHJ=M&n zt~Ax*9dMF1@;umbIf=%Ya3KUc?0F9G)yG!3G^mCJ+0_o&WkP2z^Wp3gN>LyR(2?nh zx?wumHqlwK#WtXi17b(Dk|m+i&pPkmwteMKxp2o&n-gZ0<6vd85BHAbnGqlY14ono zV^lo-vwlGSC>Mt|RrYS^`4TO7(;0vGSL!8OA?N$3bLnk9cbNJaA!XbWqySdrLdFIq z)4S4DZT2}m(Wh2!W;UQ-=z0AcU9DwFinmWUeY3PWVoB@ ze7Y`|Aq=~zIbyIK2sQqhoO_A?k-rxO_!G@vpXSxVyO8eti}(4*LHSbEaW$4w(WU5cI+XLHptv9o^<9=F}(Z z=f`r^-{7$ZTkEH(QXBTD4N{H%$k&?wqTEq!bE>8s0qwF2b_5Ff$P8WIu5iys%<$E- zQr(`!y=2TtYP+Oo4NnRG(t+*WdHFPncOGjJ-WS8=>79mwvebo7Z0i7=77a?M@(&@S z^AC^<4DC1WkB)x%j|#^%{(@x-F3`mpo}SSonkJZn+=V5>r+BUf`L~;5Y=h*x)&p&U zBUI9w_yWJOP($A?@nR1s&~um6A@3N(N6|0~-U}3Xb3vVW6M}nn@Gew=nlRa1xk(-n zX+)3#mB_ziR*keyx7mSJm&BxtFb0g1IKOss|0X6nz*O`=C4FEhta^|a8&Hs?sB$KW z@ld$!(r#F(hI8s=^oEdyHn;=5nCxy{X1im~wGsrtQ1my~2uJi@9@af?}m#2X(`g|)OE z`#qT)we6?BU^~oqZ)iBK=$ z2_+pQ9Md|pg(}D^h#>nUldVM>xg4>1-YSak4N&{`Oifc zB5h3s+Ps}fCg^=V>jfhYb*wU!Ne4jCFPX?(+g0=uEz@HCq%Q~ z_JH$s*TnFXnwt&Qert$;uwmS9##U2fFto{uo%}QmO-mLycYEU{M;t_`8*W zA_CQ%p`JI+h!G*PiDSy+rCa{nBA=~U?>BDLM=rV;Jqez-pq2+NdcS(GmhS>ic0+12 zwgs0Tv^NIe4CuZHAMfaP;d{2*tFcBo0rHSa+Xs>7x>~y)QdD&PuK8N&%T;8wdc6v{*)~1+c)r3;YvjPEGJL!kC((J$oNo{wPDH+~YMrPyKl5ucb z*_WE@RN=INEZ)#7_xm-idy%7jiIC130w1^nA2b(~1T zK__p|SdJ(@(&XL|oS&pdsV%kfLrw2+$~uks($h}K*~8R4AI3ylJQA+r>9&lwm=qr> z7+L5(SfKAX62LYFs6NZ_Cjw<8b!+j@D3bZpiR@>}S40_VO&ICPSq(J>g8K8wQwvt; zb?OW-qAC(K@5yQJDcn{h_e+vIH>!~f2MrKkqs&BBY%u-8ayF%jHuN#|>SWy^t$Cwj zD$5*fh$mZ?+@^KsF&xL*JQ~o2cnY|hi>A7isuAlIQDXB-TH{Kjf;7^CA&cM}k_ef} z2Mb-r)g`-E6p3SivU>&VNqo@BChb3gQCQ2#&>3jF(F0(J%`^f$-sqiEhl+wQE-g;@ zDVbN3f;fUWNMF28;2Qwg{jJilch^mOX&Y_U=*w)aF5yM;!}QkpLq*Z@9$o>aMU7q@ z=O-f&^EwnaT4+c%Ed%SrC^k8z&x**E{fdm199~Y!xw2^xHG6&fMTk^Ml;?`Z1eJ;g zp-a~CNAkoAJFrElaOB#Va;LU3Wy5YoKYHYjcKf%FBX zC$gCmCUUJ>-k7SbkxxxS*8z7oV9{pAnQ@+f2B#a3ZUG0-z-X*!fToIV%=T9DF)%JO=S5A%3(cU&ENxZsk|jT31)1%+uk)=RZh?&;oU% zj_;uOV6y+4bokf2dG>!l7Wv-{!@sCGvXozJkVL0UBM{Pob4ovt~zom(G*J%oum&uk)fSf z!dX{nP8qB1+$loitZqu0%33sTN-SC}QZyEExI6WfUTtr;^Xl)2cz?8NBK|t*9cOvn&a+hO+LgKZc0kqSY-i zy=tYJvbIZ7Jry4@&q&4-kMZ=18|XLqp>q1Fv zkn2`iTRW$refCll#R5*IKp1gwe(B82SHq;>v}9|kglnnzgubeN^e*rbN%jD4Sjlzc z0MiPM9m2GPCcpu4`YsmjI)6aYRc#NYlkx`hOxwgYU{|`6n99r2!@9_JYxX zK4J^+0eM=4Eaiq!MIx5zQPYLO@-rqyxe7IGMLs7SO;al=XVjhEMgNNJ?S2}n0-}J3 z3}`T=&~VOb!#>e(Ya)2vdGKq4RKpALR~ru5LUsHvs{*_H z82zjQ8L@pYR`}~P5%Ri=-Or&t%!(2o5y|B9&F%{# zstIlD1` z@j;Vp)9%0AO=A10T0`80dM^hrb?;F&L@T*;pO7Mcp8q({TVRkhNSzzAqXt62}?o3@(FH^iu1fGl^iqun$W_SzBIAZO$j?NHd9 zHUR2=mFdClU69muw6(5sF*93kGrQNHUyvK9g#l5}@m9tiQG-&uoWP9Sl{L*%mC=uYuhp zH!%DM)EOo#^hRH?AI%0yyh-1%Y-asKrN;qhJa}4rHeBGdc&OWJk=) zvia9Z7%sPCA=~|dM)2!b=m&~KW_1>0qA`oBR9!a~(smCWIFiL)k!I1ps<;?svm6)2 zV<6%AD^=xO!mODrql9W<>7Aj~yic^_s%2!~#1T4%gHdGV z%a1k}GsiSTT=P%fec;vL`q7R+Nm)TRj3n`DvQ){ZH5F#nSz?q7Lt znt%VA|4$^|KN#=-QQjx~gLMo3lE&tO025dX79<}|JOXl}vIBuhk`O8qqrms4{Gozn zlGNVn+~_>Et3*lb3esvu0?!Qu7nJH$St>zjsJ4X@^Yt>s#PxaiG;9Ner8?fb}qFMu`j-)Cpdi6^wujSyHg>xeDGVK~9R2 z4)&)3UFDkN^BgUr(F`_rLcIF5w9=6lD?X68&piL zMrmvnERXEk8`FAdZ=>6Ff&gC!|->NCxx!+XayKnvmW%?pX+07DtkNN(?q z^m|!KekiE9D1bkgBeia zuqwyji;M^~L7B%Mcb~iJNYxjr^(V`Nq8|49PqR6sOEP&I%n~u53}DYNprSf*Fmek7 zNjdFP)kt@t4tb-op4jvuPo98LDS8aeKBZa!YgZep0d9gw*}ePA&o$=bXK2wJTgOBL z8H8mM_9!I7cn3u_%w(bu>5cHm^;W?S)G{bXw9&bOP--owjTwipf7GAJ88+gSelm9IVR@}CklSTf#JW)UCB{=ooIbszCEv729p*03+o`u%?Ay7|d zPodP2*laeAxc#o;dAm3nLPC23cOAo+tt_ZX<4;3kL!(f?De)1>6~kBw!T$3zT0zYw>P?!AjXTE}y(;UsAuPfD zZaobnj~!bb9CqWy=KH5~o)d;bGSS4=5=mD;E&WR-L6gop&x!J4fcaBt8Ed-vTdH3f zbXdKW8*j@yEL(_sp!rKLerZjNIdl=X)QNa!TXOCLpq&7}!B9Q$$H_VjL>pFh!RwpKWIOSq%CR6`9ABoUkU$nghrwNr+5 zqGa1>m1h{C+sgFNfZM zn`e>>VMy^wh=1%A`XSS+Mj-i*>VxFLi>c0`t_k?YH$%vhgJJ?7Z#)@`jFT&WC~cQ? zWO`h39A|%s({(-^qySMD2oYd1?1-{~8L<%e*Fu{ZxylEZ7(VM0WDlh6V5#M!5kFMl1}LmCQ`)s zi#fe`)-h@<6+Ovd1^)he)tMBC7nr)(;wrr9E<6;E-4S zpkVOlpUSp?-;;swE1Vrv;;GI3kuED?VxnGzYCYX-TL?`{wF7l9#>{Kh!8S6lrNQ)0 zqJZ<-bLiMyw&8u~(Dc#5!Sy5z#?h_p?7*yYzWUCs^S@;-w9Tn@4deA_(FFaOkn4n6 zc1(*F8i7_RLWUR7{YMB5yyin4r!LKS&44l%X{i-7hih;s`#2b~JqoI9dvT`E@(2Uz z-ZJ{;MQ(j7W(CrUNBlhY=GkzOXw~M$U7$OIDQt9~oZP`R`Qog4PNB1oszi)1(LJre zxpUG0SpYpk)gouI{O`7gS&8EF0XvQy)O7^$`rhQO_0#wynwh;7TB@x+$2rIwu81R7 zGOpuraf&XEB_yrXt|5E0!giw3AW4Wmaimxcix=TCQo!u#sE)16lh#I`dyh9gkntg3c3 zqfw;8RAbDz1mRbVhVrmAmD$b5o{;9idaQ20Dn+-Qk&Iw8M9mGIETX7Z$h~vv^YsI} z_n2?!?Icb@Zk7U9+Y9J`EmzWeMp8I2XQLv(ja<*&NxJ&@m}DISWnX6LNhDw9^xFA; zNVUYHVtCsbKThc5NNizG3}!tpTM;k?aA8n4@4?ws)p9U>_bMvJT|bS)HsI$}+e$-< zGaM)ghs~oVbDM(WV6TO{!Zw<7ge2Ht){a7J83t@Dkd9=1HVf6@G#^k<0*MCpA-3i2 zd%5;Rci74g>uppLg{MZdTjI3rwnncTvHR`wLY^u@JswjOgu)w3>9R#5^~>`2)q)9@ za6*pO3Jp_mpBA`hCC@S9bg}2pA36rfjGG0;D4#ZlS1*{cusoYa$yk)d$tE4nVn?ZK z87D?q*hD7P|5aT%6&FUt4q0ZOW@VT1xM*F}b6g8cjSID@rAc@wEM(R18cjs_QyA$y zPVa^QXBSyQs7FM2xD=`uUlUoIsFjT2B9{=&b@-z4*O8@a-i$}49u}HNfg2OLxmK?b z$NRu9Iwo8@CJgo}c2~JmVVKBuW>Gr7WO#8BY^`*jlt9Z8-MY_Cyi<~Pi!gmTh>8s2 zY07wQub_&P4Qt)AQL6-}HHXAuE(Do;XyFF4>12UYz7y+eah8l)LZk%Ut=A}cb_3;4 z=nJ$srpO8pvj9N>krt5Cns|q&W0XusSyb)G1#MXLq;2>(vmP(sqbV`UCF+DCz;-@% zw~)+j9Cxi^o_DQgzNs<7c90}dDhqaoKMOYOPP2qL4Lu>O7;DHB9xH1*n+#a<58H_= z5?uIb+aUSaT-Tx~+o>Z`8n5{>g_IQev?_k~1l=7vOmGaR(`mkSa1Q%!L8wef7*uVF zMxTfp6Gu*7JO9>}lw5K4#V2MFzg;Q|q#o<@{&Qj@&fjd&*-*P4ee{AiT;V7KC2rXdy~Xc{K_MsDbw(`N2qEEPh@1hKK{0=1o#5e@h*|if<-x%5LdE`JuHLHQ5JWbK3+DVP#p{=i)zFdY1pGXP^WX}*u zyty>+?`7K`eUs%hS;>5&IgC2XU*E}mW7i^rF&E4Kw89MeekV zNoZaW6^qU|EE=X6A}5Dmr(WST)tvks%286eY`odx`^4#GE&FoBSq8zvdX`y+(_-OB zJ~ANTXg1B0v}OKgip_I-;`tKc;{7WUPG|-UuS`VjZkE;2bxQ-d>yczdxWs#-QcRtu zj8#?U16Cy*G9?nIt*pyRC}LJT#aWF@tIckj_m*R#x|4-*t9l)QN*G-F)zjjC{?O$l zHwYeD`jrUnom0-5u3FIU7X|9AnJT;N8-TD>j!(#-};&>1x2i zz6wtr)_MlIdxqNT3OV-7HUYKx^}CPY0Q4BT7YQ1s7pw9b7XOPm$Nf5mJDF+%uK>O# zADyp{`5hVM8RIiNkKZ*2l9#-5;ndpODqDTC|LUQ1QRgnA>lsV?j5-KJoMFK2u8Z3n zoa`l#q`*BGYU*VN1NlBLkW2>;GJrb4Ih{=%ud`(F@{atxKhyV%0vQM0A2*dTbZ-a0 z7&hM!z32^t?HXm`nBoiA7|iHM2ky&#T0i|42sro{s3zS!elc8rCEPe;#;xAOiRht1 zsvG`fo&bBPgC1j`+OaEJ2i`nrX(*h2^a$&EU%H1nk8ENqMiF=S5Ts`o$}?E;zDjD8 zjsdK9gv2|n%Knq-ARhdY$Rqvd2@j{S_Np%RThp8au^jbk-4xb~(5eyj@&d#3vbDMoUphZ`ySCn<6YeAgYq z<#_0|OEtLOuy>RJNgAIBVvHETKQs$Z{&ewGt(YG@y6*T^aW%v~yZn`b<)qxb4~ZAlbOKlxbGXp*EVbE~b1|rVk-Mgel*Ln5g|QynAI=lZ5(hC7D`3 ziD>ZWIgV9O>IWin!>>ODMXAE$s);B>sP zUAz;qNQc&uT_biKgKDGYxpjsr^*AaGI9RF-7PaY^vqI7JX=NPsXn$W%%I5bJGuckDsPU7G6viUQp(yRLQbT%`>cybIy);Pup4G%Looxwue3)b=otNF24)Pg< z>Zj|P+@8d!bAT!eHw*CY7&G9oD(^d6DEZx-6c3X>8^z}Z+?+WS?8rOF7u_lfUS0O7 zRNlpiPo{YAeiT z9%4hXWtX=d2W}3>HFPUtdgkpK)Sa*7w!=1>*Z1z8pi_YChSCeYX4cC!fESQ_QG(c> zb2K-csFcE`EHO-6gDaoqUx|B^jSm-cjmPWjUGAMZuNM0@m7~hq@kC_Q)?bC#c3f93Biiy(7xe1pAXTTS zPS0%&4^}|lj|=jc08PxJ0ee&e_xQ~lipnB7eGs0iEv3tmo$jF+Qtsd$C5jz8&oSxK znueRfw&sx3=_y&=l*s7c%Ynchn>eD<84>9$N5ZL#$*@<Ucp&6YR*H) zE5D~5s@Y?d+kMHsP+De|ule17Jk2k2(0PJP&#>JPc7@{2wOmWSvTo(4h?S(6VS0re z&AOCDcZ;r{Bq4C4wtNYiC1OsY*GEG;tiM%m%Ka0^^D%!*l#~Oz5!*)>Elvd z-sAVjD#!+Y>G_<;Rwa2Kyk(R@L;l40FGP(yFid>*ZB?4X|4;L7wttVP|M#qi_`mlS z{WqRYUiZNgMH%*?8rN3S*4ficA8?MBS1=?SpcT(bOk|A?2+-8oTd~6mX|61)YZ^6I zQL8#{O^ih~ZEHMS^Y?4qP^gy1Cj{li)7}Xqa}DwN;To9Y&2Hk-sI8&yQ~En6W@_Sm z!h71rcq((1uj_7o8^jf;M`ll3Q{_ezkwMdD=m+=ok%G7XS4_3 z^bwS$w*DQXODCX-&i+DD^UXefLZ$UZ$=W+=vSPzCYI1qiGhh-k!>0ZPHN%F?4y2}K z2gXx$&_&7f4?iKFzD9NHDdh@NwT9X-Ydr{tw_zJd{aK*DvQO&a6lOIA@;Po#A>A2B zu{8}$q17tfen|mJpw+%}} z(Sv}h$(nj2O6kj}yf;5PWFqsl%batRFi}AVSiqw-JkBMN{Ct0~QEN_8Vt0d@m!C?5!^YIfQRXH3Ml#&K^%Qx0wE{{1L_IByl^Otp;Xz+cb~vtA zrB0E2C7P?&vr%F*G>h)Uy2db8nMGmMx`}*a*ukHaEfMpq0t!{nA39U6PTSdQoG(KrY zm+x{fScP~whbmRZ3T5A^TaMA;;AekZqA63R&=^g_DsD- zLRF#bm}M3Bco!{a^Nj4Z(@dG|&$K9W%H27de+JIO*P#yo8JEh0ioJ=F2~WF}R;c*B zZ89$?S_}J*NH=p2P}E?XSufS)7XE1C2f3b(FI&*nI$~6x+Mq(|xWs2fvx+>N34aEy zzgUgW$(`NCX$cuZiJ_jP?4ghm44tCsAyh(2(pQLU-smc7Cr6i7NgD@K?uitc&e>)v zRdn2yL0N-F=P)%hiMXwb{DgJYf2p{w%%&d?m>vz43%J|oS-j(#Fp=&@@XX;Q$!Ev( z`t9|*-@`U19MANbz`Jl)E_1;aDc~E3b&eLJT`k)3W&E(l{eDaBWc%M@=dO`hy)3^y zs{O*37sp$Iu03ElP?+A~nZY?X9egHtP^>m}YJIuk8kpAmDwLO|uBb+!aGhsbg;g5U zGzw5DLcxeKg5hYK3k(hFj>6e8s>KKFTUAMDNWo^eqChakGgixzYW>*|IB~|^G{YZI zHNxKZ&pP7j&VqAFDOA72S%jURH_{Zmt;W(*^Hg^*h3S>B&?w+X47PoEB%|x4M35u8 z0s;acot0w5j-d*#98Wi~ebiMs>KCfVn{kKaWyif!Z%@&c!3X)Seay6a&&%I+ znb6hoo#d+VfQR2eRmY^apuR)u-N`zGZI5KHI})~YuMY0n2J~EJ^k^-l47K2ud`gTi zMq-h&#!WKmKxH;`Ss;E9sA7+(v7eel3#(YE&l>*x0emGjkO#H$m@|@eTOG7Ryq1kf zci*0Y%$&-i!oN$nRs*3{yv!2P#NvYaX05{2Cl}|%U_j>2Jip!+$HW5SvK;6eQ`nni zV5=IZ7c>`MFKfiQ^57d)*qcURE2K>{Z1OZ0U5q$a#M$|F@Y$wIT^A+Tmki;TpYl5s za=^upRxSUw$Dg#W4D5K8Q^uoQ<-}hR#9!YFCWPDg?w(EYIY>!VTtmiI+SKY11qIB0 zS0Kj3o(HrIX$-~Z3@9`;TP_=SNkCzu(CCkgkn`7nXR$CqWF*$gls#oj04m};vxu*L zhtx9-z~(r%f0A%1P(>)NY&NaDg?ag|Jc$@05CTd++sG*}R!*%DR!5 zeqd}DtnZ`}a*H*DnJtXbYq7@`O$?zg%DgtjeE|K*SYoGEJZqM^1#C<6rwOcm_OUO& zHd>)4%0F{J+^vJ;6{~|47sE$9OQgOifY#Y|KE}QV$Kw-|r>$$$0K(1) zgDbJ3^^Y4V@^P2nur@bQOB)(qO4_Mfdz;s`XcsHZ?E1>`7uI=kC;XFbKuKeR>#f#Tkizkq;rQT|iM@E_Qr|6wQp&%!R^ zKeqp;Q;3pZml&Xj=TTERt6qHY?jhO(t@MZUCoClg9|TqLoNl&@YBm#F7v>u$8~nxB z8-rNVL>`*&H<5OhHW9nra*PI~p<0;_!i0K6J;9Wi)GCQC;WVk6LrF3r*SoJe0DVTQ z(j8WKHBhz`C!YD(+~4F3+vc(^CYA|B$M`9)b%hwmnm;+S8^jC3WpvWb*oguud=Aya zkcCX4s6zmM^m{Z`VlYdJS5ZUs^_gleAuWf0Je1xX%A9>~>zdVNP6%wukKuhQ$!=;b zmS!+v0X#S>$NurtbB%Y_@j{#=!3)#I=po*iC)|^;vQ0j$f!Pw$p)ccy8`$Q^ejf=N zV*`{4M_Y2^lsS<^6q$_S5!PpOf7H1ds|dZho}b>sc81BxbO~guImg3M)V1mM_!reM zsl+{+FXSd8wP8s|FJw8#WA9X5OYIwECCZA<4iH!$y^uOYe<1-@ux%uB2>qR7HIF~f z0mVLXhy5`Usn{AGMC!zP3VqVJ+1L-3T)1$x@aUd@1iz7O;Clvto34kxsh0mYFX&(8 zWZ!AB|87;_KW{Jn7b#g&{6E&125-+NS62&n$5G+~lcEGIj%Y=N^`X<1r{df0!pv{x z!CJep&aV>kjbll5UjKS7h%`xtQ8J_Jv`xt3aCq#Tepz|Bxm$Mu`eVC2jGpXCywFxzs?Hzt~>5drpwNqdIxwfOmlC(-3Il*AG*O~a+m?$ zt6oGC%})?|zfo-ZpufPj1KKJ{JvZ00#$EQ&x7&$I&CB* z)_Me2Gom;xZNPh^*mgH){{<$98Q0gBzUyZ0KL*DAhrq!9xf=d&tG=q6C-MWz*Jpi3 z)}*m7ysDV*Z(sb303k{t0(db2LciEWVqeXm(Mw|J%c<&y1Rz3S`$cm(Twfqa{}}7CKi1zPCzPa} z8{9%ytkUhOdEWz3arDmdC&Sh|h877@Ul^IuNrtF#PRuz_^9;}3QS(gCAtXn)UfX&4 zkCLTfPxh8v|COTdHzEG+OH(3Py=4GUwDo?OuiV``0}+VY_HiX#na_lGbPOCv19nGb zsh#HlaUnMkDF6lQMN>6!(iMv|RQ z7tXyO!LJeJ0XVG>RdALly)4M_2S1aV$|Q3J7D)MT_gXm!#A$2Q5=Tm-AHp_t7OXJR zHZ}0JKhK5t3F(wBpw8&uS+$BJ4-%8JXH%Lmtqch9(Uy^KfTW6zwlSj>SXhyj>GEadIm;AWro zvW4fkgV$w0idb?{i67^0FecSIs#9^mIJm!tI!s7Xh;Vl4(-f9Cs-z%7#>-K*KPHVc zQ_Y1*Ld=qjEsTqL<)!YzelUs33M%@X5y9~ zI(yiFX}gG43c&=k%5!9(gTh?Hkl|zn{n#c-f&~v&!Ka(l8uyZl5e`C%R1528?nBE? zibZIlsN0GXQD8SDXG!TljoaPueyq+G51vtv*NAuqFO@F^IwmZQ3TKqWQo78z3o#w8RQfmKx5j150r6TxK3Z5zmOHn{F;*pXP2lVS*UB@^whI*&9_3v0`lh z78?T9OuUh{jJFF5vB{Mi5?~&2McU2}Kcl&S{r%fY1sFBfobCVN>>Z;k(bBHbs)|t+ zCp)%n+qSvGifyN2+qPA)ZKGn_tQa@vboblc=ic{>@4Ly!7<=rWD{IY1^O^Hu&Zg}{ zClEH}5udv=A#y0{lE3_Qg`uN2wIl2|L#o;nPTU3Kun_8 zmd_RC)&hf9F9OyKoKgJJn`cvtAk2enzs{Og8to3W*XXWueEUnVnZkEMGcQd6={F&r z=k{>k>Ll;@E7CT~oyjJ#c|G!Tx_UnK1RxnEW?k@bVyEcmYGia3={xz~XfMhG8<)=L z?_KT;Zz0~p53G{!TUb2)?+W(la@OR@6ra%VfdyUppSk(_r!b+cPdMg9i=B#2m%g~N zp6+>!ju*8CoPWzJx@f{w@c%I~jWdly6yUVh;)2C**07o$2wL{?mMAwPRJyX4-)DbT zG0bdy19k*yB1?3%jbDnP+#+%|Ut%>i)8ep*6nafl09Yk$a$ z0|R?k(`nXjrGXQJmMP$c5dENEVZy0J)&G_F!3A1T`-#wvDGk|=p~DAFF}gUR$$Vdx zxx%Wl-PzXCK>F;sFGI>T6j#&4-;X;{N@giZ@loE%{58o>lj-s`{=v0=KJ21UXy%)8 zQNEOTLalirBD3nWwRg`k<{9oRtL|%3hH)|Ji#oTL8o&Z_I{vUuz&XN~Tx!1#yv(EU z*YNk>Y84xW53jF$O9u45%o!@^BLyJsrH{Er^}U*$ep|a5gxa);uUC{MXckAAD2XT& znXX^zi0|06_M6wkfXA({jkBYc+@Ppk0aP7MhpqTw&+Yx~P%_#Z+tMMn_I|V2dRN3D zR|@9oqujv`l?3cxaRYzs&Z`M3<%Fm#Zh@|?2^Zm6BoLFvu*_zf^G3aBJ&P|+7}#VI z_vz^>4l6uKxi3IiqfLv5Cf2c7kC#=F5n-^y0p(a35{+7A@Nepya5P;x%372h5Mwz# zMO*a1Y8xkQ;IV;Mae!4Yca4pCe9s_t&mh6)Z$I0i(oWo@Xq<^yo3M2>n|BjeBlD6M z59rf0LxGFzu%s|;oJ7;VyZ2~dWvpsk`RUvG$wg!}WPMLg~kDasi_r zTF8%MgQE1lk9}m&k(%t^iJy?6gJlX2-N^_892Aj(WS-B+*`NitY0_%Rq8pMC$s(>9 zGR;&)RO=znwrDv0uvlGE4%4cNus<(3&b&YpZlCBNF7#;Dg%hIbRAB`ohI+W@F&y;~ z_ru7G(PnG-#s&oB!@z4G!}|1@LX|I}6Xf;?!%`u?st(ccW>Ccp*cB781Mt5Ka-Qj# zQZx0yNuCVlGO_hxfPT;VTB{a`C$PJqKo1H`m2AAc_;6hz?oa;(c{3vT*3ydnWd=HGJ#EytUz?nAzHxcO|QJC zO_*9okZ(#k5TcKLEGDx{i3-to2;qh%g&Pow8RX&(wH+fBqR({YM@EtqTUw1Z7;d{5 zB+4x#7d40c487Nm;u^oE|K-jzULAMOiTafTYA6k=PZMgO6|(6$dxxz19>9N3<$L#K z?@fB-&1mEm#UU7|M$5H`ckV-OtQVL3BS?suB@E0(jR^?j>s|J}pJh5yR0wjV-|ZA$ zvs&?VfaZ$LN%O%hZ+~69e?H{55*tpfHHf%On=o5=l6GctG#LSIM%y$+E=*l#5%N8e zq^)c&GGpI_5V>*anaa}A_d9+IiFoah0Nv2Yvwp*HtY2dWiPCl+%sHx4VdwjOakMcc zm{$S)>9c^+F=gCp(E`A(v9`#Nqeb(Gq^MbWoU;kh7*)3&47FIDyW0)pSnsEH^3p*n33QZai9 z_36_f(tj2oFJBkQM|iMbOrF06IC8}Mb+0csi;2JC>*VLK4;o5WNvtO zctX%c1j9*RF5vsvq9gIR(2*OA1eraUXG1;8sYyLwLPqu5iY#&=jF$6pCtOJl*auF? zv9)ZrM3j5cYPWzHW2TpzMGwDV>26CLahA|Bdh_!TKEcT zlzEB;hviaaV%qergGLz6aRWk~g6SF*MBZ*YyKxGxD}g!YNnpxXPLWXj{KPGGD=hPZ zw8vTvIqH6_%$Ouv1t|ME1c=lu zgP&we^d!*G>1xpr{KFiM;K}4vI%?9>-oa!)h?3l22n5#?q0LD zuq>uCJtAdJe&+}>v3^0GNxucFkK^4;iEAmaSGvgPSs^%(XUr6}#Nk=qlTQkop_bm2 z$30A(n34;)M~mUYi{j%2U-6U%A*_^o^xl( zyW{t1x%cba9a0x6OKr8yh5&gOD2^deUt0DAb>zbX#te49fpX0z2w%POlpgT4lNLZz zuT8EK8Kj-Fepx9R%VPU91e@jml~GSJuN>BHPT1;EckWhcRcl@oHBDh{Be=SEx1Wt>e$`X{V$GOSfF3 zu1zz&B9UV#p|@+}P2!I_)VV?5C8UE|q>huhgcGNpg?##Oba~2E^tHWaBodcxx)3{E zkcoL=0DD0G42>iYJGG8dF7!O(+h0s-SWlt+;@iixf5R?y3>(+OtOYT8HZu|dxQS=d zFj5I4r7L6~JYd^L9~7eB-?g<5ROIA0vyG$~?J~>njNpuE4|6w+5X;DmA)O3on7LK5 z9UXi5Q@W`%ybGP+qoGvwx7^*o!S(-g6JYoku3P@WDEFdqaBv{QRxGr)pQRtvo5>-< zP)`JGBceLs8b5gDu%1rXt;F}-nxs6$K6F1U;Tmx@o=ao5hwN2|S=a`deHX3Y>Ae#nhax0Hn&`SpTbFja%qiJd+55UD z+F1gig0;oZF~1X>bW>7}z1z8~Y&}$y24Ds;uzQdr>Y$8QTH+j7kQ_9VXMf13MEt5L zQhI|`c?#_t1la$`1EWjOH z5u{es$p4O^mwAr$Xnc9VAV099^z^5^q!k|T1KY=i%>J(n`ByL${YW{0R)%)griQfk zjy5*db`G>oOtiocd!m2Y8~m%=`JvPKq1b6Dd>Efy_42o7b3CDcswV9T3D2+2vCLtrc)}? zXxj;tLtL9(k&Zs4l&f9=raTMLyy*IpQF+#rBE{9boI$#>L1A;&Xkat_X;Ho4XU6EO zQ~N|rL3#AD@|Rb84ngxHB9(-!;~nKkekqQ6kpiG{Qb7$(x+i1_Rd0K<9wF{-pn|FT z%eeB3h@Dn!g&`qUN-ojH^~(6t!Z$?LihFTIlCxs68{^ywKGQuXFw_QJZ0I_|xN8)e zL5a~HEg!lq2_((87Y2wUaKjY1lW~@;${va1AbEs&QA#SBb|Z%|l~_kYb``8M8p~uw zrPsjQDszkp*6sfKR4xZ+3$wH#?!BNx-Iw1L-kCTmHK>~@U6-Al(B)cMsJ=<8@%@~2 zE(gBCZ_8i5WHf)R2G!xX(>P>U7rCJ7=ygFGHMxRzzbw*kozNNjWrC{v?(?uNcg{S+ zV>)eent^D8nW+^FXXbx3IRCmC^EZugMMoPW zyAR5QAkf0X$WYkDz{utgo!5Wdj|4ex*PVcsk&rN*10ub%+ocK=Yev}0X zn2e_xeNp=4F8B@;NG1srqD740-ueu~Q(*~-vn}b8?zH=0v41Z z?=SQ}N&%w(`22_NpM=0a4n0Fb+Io%--g6>QS{?Q2vxVd%MRAZepOBa(aUCQS0lpt9 z4L`SyJ*$dA{9?Cl;)YMtr65~ZN${WN4=9qar%*ys3pL@oS-%-$i4EW zMG&PLoDFrCW8gVqNxtXU6ndl+`ioeRAJCQ{!9^L(y{B#8on2Uk#BJn8+bW;4k$=Ou zR$onq!AdAUEOw=d>;_F1{i#NwM~JgH;vVA?Sh`&rZ!v z$<$YypHVPsu+`nwzj=Fsi*L>vSj4x9-Y`~~H2ddmo*b~4hg07BDnUGEsIZ@1@zwVU zoWFH-#O?rn@2rqse16v5!+k#7;!@uynTy4Z{jGI=&g!BY8No&mGcSWX9Vz+Ylz`GEMqP0 z_N-{NXvp8x1@bn3MDaY(6yrg@;clN2>lh$R;;f#oo{q6@jGe9zW?igL zl179L70^YsN>Js~|4;{U1qlOB1dP!*+XeAhHwl2l{xqUF=`dNSa4MR=e(5@{)NGrt zb%LNtgB{K`ZM3I^9krcs*SZe8l(ZU^w~ewig^&eg9!w$uM!(neL@(YQ`vJ=>B(Lgr zR06!a%D$=B8dVKsK;K*b=B6W20)x%ImQ;ZDUfDWpz>~bf>a)XCnk^=6%s;#Cy4#H` zXB^A=^{c+Wrr-NQBSu$@4aJgftUrh@9L~rMU&C*|7a-M+1X8q=NNyPG4(y%aJqh0bGcd`kAW&--;q> z&t|DQQ#neAn(opENL@Ljm7j3tg!i3I=}Ffj;N7H`Zre@Uc2hp=%drH{-GgPKL_w07 zgqzOr5x|)P{M;wk!UFq76&A@mvQEIynC&PKx#1hgmgr~L@;*AG1wcz9xV8}o`xFB@ zGm(!R>jj}G_Elu)uhbV3r@)fBzKTV=&3QCH9 zEts;2!^bo>OT@gx=F1sq2wbu}#tA+@rJ|enfnVi`bu?H1B!$3}N&auzp<~^eY;PEQ z>EBo2&GAlO7sxStBgjrff(4`|W?p$fy9pps`B%e(96SCYSJO7Kc(eJh(T z{{2cC@Fg*>ME8%|EU=)DWA%}bnEqFB_SeS8-ze?=2g3ae-6Ep-B|k#S;QP|?v6-V4 zM`IpDjC3EKOPWhH9)U1i!rD(73v_MGpAHW-xA`uYAP+fx3#Et1-F(-v@9v##-q+Vp zx3HV=mJp~I-&m?m^GprGa_q|Wjbs2L8CF@{*ReT<#g7z;NlOlRzv}@)`AjcF$nv`1 z6S8=PM#JaBfeUC?a-?QMChDmq%a@M`Xr?1SYNlo+3rpzGinzf=Cj??f>=`2fcbsoR zV_;E2T-!Lx;MXK^wq)*W)*)TrQKEwD2f*gvU~*S}5Y6UrDF$o=tV6Kx>O-MmQlrtR zpgX-8qyk^dHsYUfpPBU@#!Kkv9AZ_&XF)Dc%qpz(=}%M%3W-Ic`#_cJzl}N)|9jN=|DZ(v zzrXq;1_=P||EWh_p=552_|cyNi7#mKRHH7~1gWQ}8$vCK0yU7DCLvl(tvkB^TC#v;=Y6L8{zS&o>-FzhlTVIZ6Z)tN#x>z57!nv( z-wpF$k+-A1CRRKKAsP&d*7P&sB0TDPhxUqIIDhkwr5y07x= zcFf&`Fcw4_b;&I{CUC`aSLUD8t>z3WyB{vCaN$KRIbm%zTHt{CHF08}-+HRvRp8xn zEvWwbKDwQ0k}{$b__Q%fVbLn~A= z*a0b^DWB(6l(pt8*h%V?e||cm-n|TkYm0=Yms*_reH+J&gvuh-;TZIOny%Vt`!f!P zOW);-H&ZH5p4NP66hlIvO3C{Rl)+v^;haqO$CC^|!L;tAPRpoDgYQ(KXC zWGzWfp^SNiCxpUL<^d;f!!pPITZaoabWss)8he3A>CHyb869Ku;nS5}JI4@!wDO5!NCYwLzzhGn82;TG}3PC(==Yiepk>GX9>=+g03= zAu%C3CoNH8fD$3r!X%R=cGAez53sc(o1)^MOc2|mNE|{9&g#b2BGMvB%hNU*t}J5Q z_;XVmZHPmO@dGl1{tGhxTEhGdWc;gy`Hy*fgzB?B<~+*VFH{}z^~KPgw(s5GurX)w z{20rA(p|PV#gL>C*op$C>gKE?B+J^O8Y%gEuM`DQIdIG1-zBr^q?ZwgD9BTeq8@V? zGbVQ7j&C`gt4@WS3|1KLKQ>;Q54;YV?>gU}mv}#~Mp3Gt`V)qw_0uac#A8o(^+IjQ zKsqZl#q}r$ltCZB9KfJKe*8kCL9^1Xw%HW5v6l+6katW;?y~B)DWkL^18o}z{K|bni=t>!Ov29uN#;x8#I|?WD zLKlW@vs>aU^)l2KCH>OJ_baP>`S(-&Su6OH1nDVQt6cpL2_Q$QE5FegUy?2?)_vXQ zyzkj78@e#Bv7RIrLh)GAL{9zAKZ*|l)i1+z!4|YO=C#s~j86T0Ro;szK>A`)C%Jt| zbZ=VXy`4%zcoMUOK(%7bMY&p#B2+gS{BpEArY}Xuh`r$6%Q`k0za(d4u*nG}!7tR> zRISG*gX)W`tIPBzC($&VzxStewOQ-$Zvv5c6QsxM^QuM0`h9Ga0a^rrCY)~kRu!kR zJFR)_nWwtAlsgJvY-Tf$`>yf&LDe4xTIeg(wmA1+XqPSZ$T~}Ddw+(X86Lv(S%-9I znmyQCS&HZ+QUw~da+oi0Etvx;IL@b=-G2oJJq?=0HcLP5y_GC*wq;YRKSiT#m6Qu* zN+_5rTa%2$G;Ru;3p~TN1S+9DH?$EB;Mmtci@l+b0a?-`XR+P$-H7S$_2S2MXQ z5A{xOrY{K9>&HgwLq@>H4jiiSl_(7EmMrQU)&0AlJ?g|xdPt|n1165)bloO55W=b7 z2+6|g;)VNb)$!19;kE)tMz_J!c6LDLj)6Lj+5@PGw1?Rifj*41R;`UX&N~E;LH~#A zYv4KSCm8oDz^FGhTJkh;7sP=~lTo4ZPH4sV&4#oFGDUS;!uhxI!T?siK8 z88{trW8?R)tf)o%T@*S1wN6+R^Wf<8KJblbX?djtHuS(-2Hv*q@YRL^E4r!K>qg{d zihj)`_YACvHOQY}IYsNno4;P(n`t9}wDn5tM;(60J+sq`*FzPX%c%oj5RN95D5{p3 zd#zun<&fkOd&4!<5IPz?b`o^W2+pG>lnlL9q*FsA!Zb#KBZ> zG+&)eOB_(0@8IHd0ykk!AJe4--X0U;#EZ#-a&qC0UXu%VG%kRi9h!=fqC=DZg;*h4 zjU!B3n8MfC56~?`ypf0tq-5-}>JUdcF_Ls5B58-;a~Va<3~vdnLZ*r5!edZVLt;>t zv3n6)Xhfo>)&cA}CkD4rYw&sRer}ebC*K_GJweHHkBJ+``9L4Le$1`M>uq1CCXfc%B(uT>cxVv5H;SA$Mq4XYr!D5|#bpMhJVBPofawnZOx4in zERv>OXt;=bHEzi}N+fP{Vlcu>M;0_ikmD^71ntbwb4Ivh!%u>|J4yEaL0EikP`8`f zd6+tRLpV~e&Xv++EHz#FT^qAGxI#4~bJu|8RW&5pU(d>N+nz9QHDAaEJu9jvVKuP~ z%G6X#s)62W)-K&42<<6&OhU>ihQ`dDj5vkQW*AV}M9MQYH~jy3HKfEDux4BhBOoc`Gsi-t08 zd528hzL^4|qJ*!M?DH(J`xoe*H@xvqv2GNn(&3RLSF1EkYZMsmlV)b7%Xz=;E!{jV zpWjH%QCb4(+JiDhF=(qNb^GvoqX$RRajR@3+D0Z^BPu&0O|HOue;3*r(tU0Pz(#%M z$xXkxly=gFsNf!*BAHk#5P)#d?f$l3bxD5Sn*E|1I;p9Yq8L45p^_c7$`e`ns*V07 zKI`@$;w1JRN8i&vyeI_z)?bJ5@3J#Rpp(&`0q-OFSsFo=0mZENF>Z3F`g!w0P)iUA9u3YsFs2H=eD zg!mP~z7eLW0u*YvSS1?@lIjGtu}nD~%KNGeFdLLB%H`AsD4+t@+pI#zkGnIg8y_?I z#k|)k>X>1%K>X8Din!lWt8_}v{z86m298LrV@trjUl zNCqt!Se_qJS(bf~wpy{=SAD9T#~MK8RX+XTzSWetw(MT4r%X;zHp(Enaxri!>NR9q z^=_JjTwbassL{_YRqPUpM)B(^7T>K`I@%Wz;LU5 zlPeCdU05TLSM#8oSdyewg(za6<9+$-?A%eqz`(Kt%~D{`09?O0Jsz4i;aE9n#mi!d zc-7^;oVw3M9)2LmxzKiuZjsI+=}{i(TVvJd`=gs*AdfeBNR3mHPnI(s(dVS>&fpDc zY`z!B*sif}jf}k0De3I|o3P#5xLYH}1daj#_10Kj@_`^84$=^~zN+odM{$52WD&Xu z$lPGUKy{AiVM6)IAD>}km46E8~pjNxdYJ?%Nd)YW33oJRS=%i6`hxG_tL#Gb1$-F9-fY&wD4-UeU=_II`nHPP2n^%2G^Hz%93(3vCAB2`>QB3wBZB%+DwHQC zVgPH~^=^jN!*}X)zo^XPiC?qSJ(5e-6qr6dwBVetV0#Y@YSS9bZI{!9N-qe_Ug{HS=~2j5`^u{TS^G(EbWf)gQDC_}94>cxp; znR3khO!Yl>zF@kY)dabVdOj#U6-qNbeSVP}SlSW5z!R}sT4`>6e<5Mye14w1*Zp+7 zIMmbQ2~$9Y=n@?e67*DWoMVxrpEICm)jIRl84$ZS^7+T~rMusj-Dtx0-uEelV5jrPFf)MH44HPFKTwXw#cLL05orFN;Sv+dU2~aXI80= z7oK7DAof1MEQkxa;0aZ*vJ7-ddJj&=ejWv<_vYGv8)9OyRaxC%YSo_-J#pU$Fr(nH zsvPQVT%2@Iae+#N>H8eu)^!JfmVZFt-HIGE+Hjp`M_iLLd9NF;X6cM%zm{4#lf;Qc zwyg-X3a$dIPfD2hKJiJ|4&4bPoXTxmtl%OP933laXU19{-ZCN48ZLZ{4e#!ST@LRp z45N*TsiP%>xN7CQlrdeY=*wxDNSKIcGpeg6s-GsU4tG+VKQWzA+Jn@HJNM=0$jlN; zOy_bOoqSRMb|&RsW}>$-B-T~fSdwuHKpSot{G!!^a+t>NIM6*AO~k$Bg?H896u7%U z-i7xCc_0^%U13x%pj4g+;>Lop1AXq1&$Y_CO@uRRW`ep{!pK zFq^((k7eIOFCWhCx|+T{7}i8OuV}y*qRheFpPqYOEDhe{>UMrk4$Kfit86fU zUtrQ&pex)83{1nYG6gI|Pt)Z986wD6S@h7G4D?d*n~4&_ap{Q$7*}8`n!8Y753UtL ze%I>4PovVh%&Mc*VabVAe#wlBWgv2|FO6?p7HL zU>TT{t3a(!E*Zpf&i4ysSp0kWyxsBMsQw!u#5XY{S zYZ=*kRDj)2YrMt0AmC|6h4ZsuP$zK)E5UN&whypL=Z3Gv!M;MZ2TngHf>^@nVctZv zam@ITV=O-gc-R=J3%w_yT#iBBG^k7MzFvp~mE$Fx|H>5=+T(N^2scbF!nK+pGprF) zGbsq2$GK5y&rwE5)KqXkGNK34h*)@`e7$Q*x zktLBh;twL>8JSp;qlYgyy)_sx2^gO9T=f`kPUxyb>wYnz?_^J=SVDd*vBVe1<39lc zoR;KaDTc?Se{>4FAZo7@Kb}O%e|d8K^+~Y&&65B+DLa^2*b~Y8^TU64qcT*q6<0)% zxx*riu=uNe`Le0S(@4Ny_!XWI(n;vQG08*-c1M$#ilb=)e5uG|PffkWO`hf&J8tna zj@UAc3!}!Ryp)QZiI+$gNu+U!&MMEj56+wKD{qgpc;CO+pvZtKepIat42V`j@bGb; zc%n8*K63sux>|&CJT%DIA8O5n>lL9Z%kUjtYjoELtG!7*rFcMYzvNHk+Z_PQ;1yLw z7Geu=GJ1cj;+Er8L&&}ZCx9P&UM^iab#aSA`NAD?S>HXAMhHqc!-7*&L1=R)z~P%x z8nA8%b0xBL=`ppW!X|Bf$W>dlJbl1Y6Fu_*tyn7)UtR2&UGyZy;r|2f|O6AiqZaTuXZc7kQ?p`jQvLNmX@_iAi@nZ zOU;qs`le~b$^31iL5W*J`M?=SUZrxwxO9O9`6@pBDPdrlr~Wb8%qpS6@D{3NZGA<_ z+g6a!L!b-|hjp>C{u+zqz`I@nR?vxYm~F1)HbIsAQ|J|qPr8fOg#%$)y#p!lGdS^w z35CLvy$Ra}TFS9F@u$Er>s`HI3v|b_^74}0Z$X;#afovah4h3#(ZWDCzN2&Mfbgm- zxgJ9FsH=!@>zYxzv$R5`1){x>OpVF?FvQHEhi{-ALABw~$mq|0g%mrVD0(eM7pgAj zM4asF)!bso6RtTQ5#_N**!)QHz(;m=#bZ>COI75lpaNl$S{X8XVu4}Rs2dHg#l)PK z_xIlt7@)FTFp(+h7K(maUm7Q0ti*F4^EaRja$~o-X>u2=x3tL~U0>-S(7|Gx*B(8P zzxB|aI4`@jne66~E)=sT6gkomB?qI)9G~C0CaY4IV^qZsOL2KazgJ6>_SArUFVsvj zh`?Gd`nkfxmYB}-Du6JD9JI0Z%>-aBh~eZ`P^rpfyC~9%%FV3d-iCFN5RCKWKLW8j z5csPFj3zG%$^=xTL(y05-fgba5JHc@L8CbKGvQUrIK0Uu5uRu!I9HjsVnSxY%J zEb4ho04awgU;o-VoWx*@B+$zb`30=ItIDzklH2zLa+dZ-cIv&>iV`DF0)QHi{}+%z z8qw58MX{2?@0PwDfnNlvMhwXWCs=zJxywehBpJpN2GJ@`K|Op1PQ=7{n#HJ28ofVP zQap=Xp^xxsdPAG!ic>@jyC2V~LEIZb+&L4L2?1OBHT;Gao-kmLEc}XuB-Vl1wleuN zm|OY?zZDvva8X-6I;M`E1;HaEJ;_4K`10V~21VC=vuWaxstEDMxE4Ov& zb7{WOvR1NOQVllrJIo*0CB+Hyr1lZY3qB~G|4u6Zx?A=)*rf!tH~*;K49x#2Gw_$F zouD8syYgW>FUXVb589)O?~7+kD6iZYtO>7xSz?x74CdQpv%I+C&|UtcJ#tgG8>%md zH=7rl-kgxA*sd!mC4=>5{gmf%W2aa5lWw&-KTa7+dCpe$kzR(}s-i}Ur+`(fr@+v? zfG*-bgv4mpaKD_$pgw@EmJN~{Aw5`Z=?Q%-jhBMt6xRPPPwvqwJ$w*oxH?=(Ibd+F z)^{K(;&QNoi1NVjgka`+7J$}ElfxdnUuH5VgigEzQsP-4>S>wJ;@^9QCQd6w-)M4^ zaC9g8Zf;a=)%iM8GpZ|oEigEW3>i_f?Y|_ZL&Ct40yk%pDPi)E5m8bG6|mt8xRTl< zlOmAyQvH=RUIOmHfrKP4{8<aXylN}u_H?Jvjjy=<8cTEN z>RqoBJ-Qu6G>9^Z(U!*HoUhTewXY`GPdc7m<>zN5y3yt_$FmfVu2_}2IraC&mcg-c zOfSkk=UVhJAWu6Vlp|ICDG z71#eBtNvrE_178xzvm|z%G!#UA6D-&wU2n}8j$(aV$=BHhbeS#AMYOTTpp9&B#@Hr{`2R|d#;P; z%$xLs)3>(++Ro1aUj#wg$^p7ybKADS1wyjE>@72_g)0$QZh&GhrrCorGV8n^SExR+ zweVE~hP2R?402<yXQ%i_u zr>|Sq=#LCOV%uzWI=qQi_{jzgYx%}mhC=sgCu~R{H#ywWe4#&52@h=Bn>YW`dvsk} zqrYn&s639A!c53(E>T7Rlj4cQm}#If??<4^1KdzNi*?C&iu36J8Fqi&iIT?2Vczkq zjI?K3LtU;EFUc=PMBx$UA<2s68~P0!RPkd*W0%C_v`{eysqlEZJ}MHXv^Wxptz^g3 z1HLyNH=w#oJZPj&nCcnd?zVhDAEugZ{?_nztYz!W zQjDQD$4^QL9ek9r#{D6oYdLioho+eoa=KWnq%2c+N*Sz5WCh(gm3htUPNq4rxu2RQ zlv8OuN}F5IH`aFO8ZGfTEj>itXmgK0IJkF1PfI*}9w#$sc&wjJJ8>a8N4Q_O;*`9~ zn0exvM2imhOu;P4T0hr_koxd+^m<*gJU#x>{M6=yW(qd3dFphHJk1|4^tFnxIzaE! zjXSe`T^HAO8)WH(9!#sYsOtgU?!vDt1_dFQHose^GYGBXp8HqmvvY8PxZY+xD;S`! zw9q2W)2vmV0na5)ZX0uZQ)iX>hSg7=k5cf6tR<$Gj?v&awjniyQz$5884EBUyL?on zHcDJi?Ep%LAraV8S(VF}7FWROt(T!g zj2qU=z60Cvw%bkj<6Qz`_`b=P1>I}rx}zJ&4RhPFHdS`a%l6S*zuT$@bIg9zWiJu8 zrh(6yy?7rQE+(-~6j-l+j&bX}1H9lq0*wUZ-vQ76B{_utpX3mQ(T`PiWNup#bMyJD z*Ix))hh!@JHaU<4&^-@)!rj`1Uk&o(66;XBMW^84J$zvPSXZ|`_SqgGXKH32W@o=& z_weXw|Dc1EM-Yf$*BOj;Lk>_&sU_FZ^-sm<3>#Vn082)cvkB2%#8C%hWZKi1qXugl zi(K3BmE5{es$kkTf_uMiTS^emQiEG}pltWB3)+%Gg9=(JQ+%ixnh9iGX^4q=Ghe&9 zOw==-kf`k#OMh72AHCua%e9CZ0LV5C|97s!4l`4258N<=PlP?DoQ*-a16&k$+TR1 zt1G}a_VA)o;1#WSQ4l8Vn(E|ASy~s3IQk?=3~;muvOHO``8&ziiJ1RjOXxSL9-&;u zxnZP<=*z9$$Vt;*=<7S?$CaXeqMuSyD_S~TN+7L9L#FWp#UdO(pB~2X3R#qn`2v0u zCkQBP7P~+s+oLk$xW4!^rKpNiQV;zIG2MR~VmSYQg_!@@Aj$ZnHO!CvR!FU_rZOT- zvmFqc-Hrz{Bpi^;hhz|UEcunBd-hJ>>B`z5H>>`N)h+w8+r2LMuO|p+V0Ze&dMSYa5s|m-QCE<@oN7FhrW7;wH zrOf9CW%Uv~s)lse4b-bz0q&W2X%Sx#t^ve!K8Z(pVx4B47#8+~qNB|z+PM5Be)h;|( z5RydL+}_YYPH-Ci`7RVK=ITPG6L1!rg)Z*Z1wPaA*M(ua^853~UTz7uQ>`TB$U#n}?N8_pX)7#*X5hLyvHLAis%l|#mOux zc3}7Y=+NO=FtQ0QXA+5u*tVQADzx@hfKzg5mxBut?nmw*T=xuNYM3`*j05j$QN#H6`p>0ZA4h0t;x(gynrl|l;%_ji38|@z%#l8G%}F{_J|(n>+OW`kmPU5A1jCdScz&z^ssRG zxZLUgHRk_o4)r%s_OCMkzv}&ejyz(K=`gW=zVGai688-SOw~r+;6aM4zmF8Vzd_DKxd_D^)czXHWrVf zN&gIu*FS+qk>j@%*$2>Y{TI;uwINRYzXQ#`8sh&r`U(zL52g9}gH>1B8wOW=B79(w zFCI~x{}2Hlz8It?9Vii%AI!Jm=x{^h?XeFR!g-Ak@?k5aM#Zv9>tbnwk49r@w;9@f zh00lOeON=ilkNLN^TkA3QW7iC>uU$E-9_tt=Gn(`1N(jE@J`mMJ@2P6L^SYCjy8PU z(95*d)uD0e)zz`_#xHXoX`LNNJ|He(xYl1cov++Kzwo^Im{`H^eeymW#HCMJapg02 z-pmNUHsD>~8~ovQ3d6^~uD6MQ4u|Q|=zD*8(EgdMH|ybwhj)Dv63*sE?-OoM7RCde zAXg7#l-=wkY^2Q%fcAyTCu#=?{4IUK3%E%O@<8bk3n7dCz$D1EGidVFgXYuEt4EI} z1l`Skv}V_kw4~<8dfzI#Q)^fcW}m99leV=5kO$2dY}U?$eK^<$a8_%(8&RwE?S0wR zmlXQ9o^f5*2Nsz3Z>-)sTN}-f5UdvrkKW*y+%ITw@8jd%O^>v!7dOWcs~$t+qFnd3 zpLn}vOAz=3eODo^JXXK=DI9`QT?eOKLipofe?_1Z^2L|JLBY@my%!Dymg%D;)5>tv=U%j9@%jCMQ6oIn)h%+7Eqf6ItQG%%}$@UC~qmDN&i9;(yU{|k+~;< zYVo4$BQP)J#JVUqmn6-*ns6HoC+Z-pYx1(?4x3qS%W1ADa5c9-kpQ(2A+wNBv$KYF zz8CRu*#FeMQRn5l6Sz0(5)4U*na09oZMI%*lxCmjJIy zO49(wF{CSf{T4#afD4nz$08NW`L~Nb?Vug(VWGM_D^;f2Rx2cCq7yQh4qH)<*g6DA zHj;!!0`&86;cZi4MoEI0Qq5(|i^E9x5CWJ)D2gUqmh?G2Oc7Q0j(eN?$pKMJ&TBHG|rpNnkQD`udnX!xLf{z))${ z`IXr?vBDFl@d}vx+c~3#K^DcSP{|OC%Bb{tO!0y=L~Mk_oSHns8Gh6?iE~6v+z?_X zaZB{u41$h2C3wvksXmP!ccgYxaI;90PAIYrF$N`D3)3b~QqwX|6G~|b0zw0H+h6`W zTN7f4ImTq&D2DdsMQPk@zZNqi&qE7`mz4!`jQLKeQ!0k{zx@JzG~>W3nC%mcV45CY z)~}b4YiQCuE|oZq7D|aXP5633Z0QD-<(`JP$RQQSa!Lj5*UdP( z{3}!5HzycupoPpx{t@<-P55HaJK7zNygnP^(b?LT=kuXW(+)+&*0(1@_QIA04WuI! zsBNH$-;*!j`ccnUp~^+{b1Mt$sC)$(ya>NfDY+imDR*~;Z~AtED@ z)EY*NoB=rXfdSJ>_N(6@W{k+%cI7vroRKbSB_N{eS0=;jicZi!Y4gibBNWj{s3rCy zkdJVSZ~)N*cPq-TL(>TAGuFD=YwK8-rFF&&w}!-MvP9cqAxJC-5zI#^+bv|$m^dN2 z5|mY33!~)~IOQ1>8yQU(l)vq<%+_wkW@XmW1%;=}2-!6wUxa~p8DnoH3BKB`k&(TCCS6gZ3w=1n@*kRrk-bD0rNj&dx@#C2-H_wFou-UA6U91Uo5quV z(3`_vHgZ-hlyEg@p%d=`(k1CQ!G*Q15#URwIGZ45Cbl~A&6yo%{>DOzDWK}{!J1k5 zMJ}WuW(k^0iq|d3psi_~w)@0;{(CIv!Xnv9Eh+=V3G%PnDXSx8PEHX{k{4vb?6a0o z{IDZI!VM3Yh;84G?BN&1QXL8!M^-6tK8ZuqNZGcGxt%?9hvcg}T|ev#?=><$wq}$4 zg3T6dPCA>GW<#=zO+0WgB*^3+(UU5-u;NK&^UVjXlgavpAQMD=qc$;xwJ|Nd_2ZU) zh4JNUTPJKE@IcxYbYzk3MoPR71+r&^O8Hm0XEMZk7UrzOhv{)4p=C^+$^ITZO2cp6 zbLlu4+hoXTaZGRIYoU<%7?7ARLfj{G@BMTnx zhyw$btne@&hq@n%Mc)}`ZVbLOJ@t*L_9xjguF?T}ZPFl?3^xjN`ISwiplVAm_d6WT z(D&B(e!LqH-3Wk z*v0lhKO4KMxh)2XAH#h*l3yS1lJn@au}(evD)4An%}n+ArqcHmcqV_rW!6fQN5IDw z7vLmM>@WaQ5ZNuM&~wnv`N&7w^Yv8nPCdW+&`S2rH?d=f)C;$ezt)HkxK05fUHRv7 zZv*H!AcE(je@H4i6ZA7#wwF9}S3UMaOQ1zUEfZ+-HnuEse&UkR^px2&r5RB`*|D1~ z9KE3N(pvWO50p)f&kA=CQ8dJrz>b{nR*4b#NoL@-(`+-cYr1vCH~JmBo&I_|BB8ld zWLjt~^t-cv`C4Z9-IM`7XJ=&BOj?}0Q;s4Rj&nMxdu$B0yc<_A_6z*DaO@{%Ne-f7 zQrCt4kq-A1?ieE7PNf&J>L3rMu=($v{+XBV70rmutegK2U+=&qS`=kzrfr)qZQHhO z+qP|IzO-%Iwr$(iysD|{nXZWLhYP5S!;hQ87pd|bD4{Mjw1Gp1hqI@=Zu? z`W+|ELzI%VLj`-xmuR_-fvFm_hgxao9RRoaz`uTkGofi=KCCO`+>c>ar7Xg;8InL$ zQf}DRL_Y9BT-ec@`>5*@^U-rsZfJN#_V|zdBzli{ZAJ!mnRo_8`YjoQ8Y4UTofx97 zR|v0t9@4GpX}Ns^WIPi!Z={?8>c#aD z`{rPqy+hM8E|OqhABws|Qo2Gb+V3t|BYSJ&tO$Zdbp{BU0V13|0&N#v(~q2m(AY@g zEZygQOAh3ptJq={Z=2SE{2+>MFrF4f?FcqB1(vj^q~ToRV}8&@Y*j3>wHQT-qQI zV47tn9a#&d)YPoZIK_<-1<5`Zk7}S)VYR=u-}{UznbL2mkr+(O>+D+V6VX}yRJ1&@ z)<{*Y=?^rvT7qr(;w_t@CBaZvwA<@eM+IDgLB>VJ(Jrv1-$&&o0qiW1WaBDfozsu=UU16@uQvU> z_;#snJ9B6d**g=~DsC3!YnDIVsrLb)1dfhx#1p~p zHyQQ*R#+1ns>lBj1$`#Ny+s8PDEC9juw7Yqs&b5xUHBkrD;;c{*bv=k*FfnevY<)C zXp#esvfgnnG&A-$O^m_Ao?~TousI$Sj|<}@2kI;)p2iM_TFR6vBjZ|O2&1e1!&-E_ z=dU;!MVjo0%87hLmw18%vBLyTv(VL+4kGMv*ZO#dHKGP@7`O*%hR|}jnh5c@eM7fZ zt>=LB)Q%wdxBEyFsk_QbgQLu|y_STThOw93sptohO{%roTMMmb8dL(wc7|B%3BRbL z+KiVVClQqp==o5^SdKKRy`ahByfMw=!aHdijbkcAqq8YYBaGw(kLC0tr+jANeeBQ; zY(%zpITgNoovrSKh?mA{qTGXZw@u)|HqI{UO{{C%ZnlsJ@~E8*O8SzCC$0ynjq*=b zN^*TD-Qi+rjZ`DjC1qx-Ev{~c?DqhRtG+>uqg9LTHa+uVA=)l!=OtQ@Bx%ZEO7aZ^ zE@W}kS8T8jc)Bq7ZyBOb%joBgIi_Gm{nFujaNg@56PqQv&l~sHTnmWaI^ufXflmdb z)B39~Lq>N26>lc`tY*}$bnSDH5`IK&aG)N*)j?^DQ8{A%H`d=;0B`V-J{o(fm|m*U z-NO4<+(Z1iH`dX&Fy@~GBX6#lTl?tSqWd`9!_KneFs$ECpj|X$KlPaN6>6`Jx6Oww zvFmTkY}@w+Kk@!~P_37^vS%?BxmKffzLJmJ@*js~zWk5e${%#7xSExQ(#wvkT-C?f zvLB7%UyhL1t_wZ+@2CntiD$aXpVbOKk@@`3J}uu01*sBTbb3TE*V?CtqJNhe~k#dBD4{ihKGuyVmg&2B@3lvtu-fFW4X2 z;g>-^aR+k9ZY#urNpXN|89y?NWuN$80uW8BIDU;Xcqnu0mIrSed=%Rf)1e^wCi%+q zu!g_&n)uOIK?l6#n3c;o!3*^p*VV6r>#1J(-jjqFI*cXoBS-=DB!_L7ax(bRWjr9r z_;0_Uz&~~w#d9Eq#(t#bQJ+)>jMa6bXp4lv+~`nu+~fn$;{E6YC|wYO?hI2yv;Bfy zAgO!BbHW|!p=pB|?x0sgbp0vrsM;ZFQZhB*z)<>tU&uSe6@(bjzeGvqbucA^z59I3 ztxCS%X+1TGdbd*TaGM=`RnD_|_;dA$clnHUBCo`O_>lOUH%k>;7Sd<55HCU$E=q5W z@Fk&l1R#yE%7TsncmRC5&?N4-(Ic0*Gfbi!v*K?cyeds{Dl^D1F=p`7XKjH^lGNTl z^fAd^2(jZ282z37*aD-O`iwhQcQttha(Y9tgOlRID3Oca7aEa*1zg{rcUGwvuLF7E=SLXv;yRuE|M78vgXB^Q#x>j z&LUv#i3RcaN}QMaHmHN|VR;oK%03u^RrOZ+O&!78PEO5=d!`pwFG3zJ5;mqCC-hTC zb{|!jX2C{GR4xxjM;t!%R!j;R@jTOy;)MQgNw+&fp51BIpUCOKul+FI{@$+w>~Q#j zqTU#)`^0r<_#sL65Y~gEIF#q|UKIW`>i#9l68T}0;=HYrIS=v@46$`82f{T@VtMF1 z&lElwm)BwcKir#>;yS}=IguXTZY9ZjfOX= zMv&kkrM8iJ4k-*MVim^}NDNZKg1_apeb8}M;Q?>P%Fo4ToF=~j$G-^29~qPnO5rjp zturd?U?w$0abk>6Hj^Mig~R|a4Sd6A_JLM8P(t#e5D^eW63yp;S|iZpfjrc2DgZ|3 zBT)VfxM%IqfTGa+ zR18s`BNA4<)J-a~_0#0Ime&J+8HR92qVB*GmsxGx;+_s;!f6&-Yc{jeloCvCjVZ_p zn0gY?xe=4`BwTVQsI?``-V5`tAbS*JzZ=ROX|O(^g=mYB>NMvX%HT8O=b`waSKWl& zZ$0-%px9K^rUG*)dxezp&Auc%#9+VsoBc~0bV7Y_K*~9}u_yH1+;2Dc!ai_t<~hk@ zoh7YblihbdG@o98ysJ7Zbay2e!^)+zKs^MS?QZILb&NaJjco-zR}NH0c-XjS>k~#Z zuP**FlzIoGa;=(_)RnK$wOYW zm`Wbj>&(4z8{Gc}y7x_R?-lme3#Gir2EUD;<_eBNxdqE~Q@qcSNPdLtJG=ip)=_-3 zD}WIK&w0xkI*2!6oN+-|>3r{nEk3~Ac-Y#oTO#~UHA5?>C^e)8N z$hNU^W2GHDR_vs*FFx>N-T(M8`$6V@izE9%4*&ggz_*?t0`_=Hcc~lD29=reXZoqs z0|aunK2d#4(-|bffs%xum^ONydkRWlx2_;gRJu*$u2-hB~)g+Pt^lm zSUc(xrT7`7a2!E6wtg5j(3^|@)rIqqOK4t_eRnc5{HA>nYTh58RA*vaER=(;v(X4& zBC+tsGh*A3B-HBVrA8E>?aynMF>-4xpjL7ls4Yn``LS9h^H>w^ zcj4S+TaHieLb+4=W|!cm=QV_li-z3Cv~uwl;e`WpXMmIYSJ96hdjL`xIJF+@wu`ig z{A&0X)8cg5G?6HUt z$YD)foWkiqEF0FT`4dj#^vrswmkq|Q5?91}%KRr$-caa5nJwy5QxdjP$Hu_S#kPzY zd#QGgoG_NHteHX1AI-sOM%$KqZEQcABU2&OGZ}eNLALn0D8jsK0FezOjAE7nbsGxl zg}-@%T3{&~j%CZLebzQ4Q>&`|MAevB&C7LyT!^wun)=x-p|mj7bt+oWn@uir`PoNn z)f7bK=#qJ_J=mn?omkbq+<0$jD z<}p=RS$+~+u}&H|#&_bLPiTm&N9BT7MmkcMU=a4w4o;eMd;s~*<91d=OuDjt`!aqe z;M7k}Xoah}&k4e7${|*lhUGmM8?5b36I$Q>tHBHQz0wUKc7I-1RbK_XY$U*2T}hHi zi7XHPVQk=76js5rp~|IX=8M@hwH!;Z-jB>nv2ckAJD1Hp@rO_xwPjeI1qxb<#9~q> z=QS*w6k`)DohxvPm&`@*i%`pTS(-(Gt`rMR9GkRaoH;fMMXeggyq1+KLDot|H;>Mk zER)6dz6vhF979}MG&MHBPt~6jrY71gkKL_`nL8bGG%1=@PfXNi)Tkb-QuJMqftrz; zR8L+9nVTK+H8Xa>bE|3E#beOo1Is(zdu8^WERPMXjuWY`XDd^rs5dGbsnRM^mRBZ{ zuO>`@)o5?SD4QQZn;ki-%gX60%BiSVl0z+*Cep?t+(miJk7oJwT6P#&n!i35Q`U(e zD5uC)f5=eGZw1<4=+g7Pu}#f?0}h*h2Pm(7){1=i8NP(XE%Ez@H%9H7Um&;2e4zM< zetYD^+!fL9DsF;$7jzDRbw&kw;72cgs}^9nRcA*%dHiKMA}XH+x60!B7M~K<^3e}% zyHRGB#rBImF?Gx1My#F~+#7k(dTZlGxt<~4%6JiT=g9V%JQ4I(O7}KBL3>K&kbQkR zF!E;+l%6OR%EN@ab%|3inhgtim9p|zBWQh6#m^dtd3|ErMEPX#*4tMMrf@Xs@oO7= z6$@%B@2MJ+8}NH0^^%HbzV85ibyo6g>xn%ZJK)k3t2!+CXL>iD@s`m2OYq@AKLp9= z(K!x$%Ck>jH;qr%TCaHDBB`v;$q@2M@YrmA#wmsNurC@KQfvQw8Z@UYhq;(#3{koD ztHF=$rkj)Lb&@oe)|!bxCeY`x1?cI4mGt09KBLl$>Or!2GEL2^2?hBQRV}UZSKdfJ zTG=SnJ}d9D04ulcRu8~7J;oFb$P^7E@hF?`xv;K7Va8GnZ$X2Pt=E1tV7QHen2p-} zhyi|uMlY=nvX$pbN>AiL*ye@x#VqZDyt~({$L3B>hF#th&L^k+`u&GnkH6C^Ef)&_ z07dx!Z=(30ir4=|TK{jw>%Uq+{`@b(w}`E?qsRZNgT=&8OAhj*gv^>}uBls9Blb6o zcQQb9IIijw?!jZ|?Xk8O{kyZ4B?Z-+4uRhjib#?tuZEa8@Xg|Ky2;3>y^WItP_wte z*}#BkLS7M|uH(W11!_@ zqqPxYR*GPGnW&(QT+`I@P(s;F+Vhs2u`HErNmTEEdTz$p5|B{w6*g4mF6imKTX3F= zLu-H0O&}qx33OMbzS}hc46;^*zDV9yuLUgH_mlXSfh0 zMEEB)&i{M=Kik~@gh7z~pQXnCmVE!41d*+z@SoY+v_>d(6UNq4GTUoeXmiUX9|zH* zGSbW-G1yG^*NT*#$QvbFiEp{yb3;=@;C}vm6YjPYbqeuAq}{JiTaP}cS+`q1KaY>- z{yym%=4%rL!8XlTWyh*uoo&_ov0y=j&+6=gyawv@6QeNW=(dxlW6Qxtt3kGlFLCaT zLOI^k&F-h^pMpMOGez8pJfYcGZkjz*nx=Gj+^cD(zX9VsvsI72LXon!&yT>NhMTE& z?RH!b86+ZlW3#n55_&MNF^B3{|q=OemgYwsRINU2ZMM zo7#%Y-EuIWObyeLsy}^sQa4KPgMq82=p|fTbQ{T4`?&4D3DKtUa9sukDe0yzJ0$z~ zSPVe5Rw^^s8yJ$zcB{%AQq^BBO+>QP#qZ!DGg~3Ju)B6qsTpc zw}n}s(IT3E;$*NAEtU-du5+>);`)pp7>jkHb9TtG1YTttES>x(c!W8L90UEIq}}?z zb=dsRN&bIH^!V>d{=YzI|7DYkQU1@Q2Oc4-RvmP5l|FcR;p7HTv?gCcX&A(6iVgpj zBCNx?>?W%hs?JSHzG1dq_q%Zrse>TCK75m|Mjapp6{AM+M<+yP$cpb6Y&^2wXq=s+glzySNg~zJ^E`1c5Mg+w|Lb$(fedt;NfXaJy**@5%Rd!tPMwU^=a9-naDWta*H-X(`eutIwo;mI^SWgi(Pe(W^<2h zb&qvp7>VGG1u1-*IRr8g-#6T{f?9l%S?mSCh+B^r_UY^WPlzPT>4}T%zo9DezYW#@ z>4c&GKQD{_Jyic|Kl+cpm;W+j_<-vxtE}9$AE!uzK?HyZB0=&LA$USgBLoTyl0)jt z_Yr3BGbceN1sl@otD#^6iU{jam*xG*D^m~ZzPjjeU9Gv)baiq0-g@Rc?P0UiB}ES3 z{cXp;eQ}cOJk9aC$@ZT8ecc}g&`;*RG7#t?iCw<8I(NeLsbPDA#hZImrDVqT&IFh4 zSq=)h2J0OMzE|Bh{kY1TrCD+?!|6@AEjX|N>)i(Ke!~U(4eEFEBFL~~c+XDW5y|a5 zcZvf~e8UUcy$o>n#RqKL1rB=G{kUR!16F>_>wi`9fcDWmTf_o4dIJo?XMPs~IW|bb zf9SLC866D06FYk(hxML13X}P1>(9;mkwa3H!~C*VzrQnkcfxxMeW$`>jqJ62Se5<}oA?<&s#W?39siX+&%_IUL;rh3lhi8{;#GDRJHE^E z?uGZmt6-|h!%P1jmi)7Pgpc@w@AXFZrBv}gb(D+!1)bz)@lY%M!&dS6x9@lRXjgt; zi_%-H{Z{4&Uh>!C0iXPP{^*zSO%6|e+K=_E_t-hP5M1`KSN`x<{zeV9>UXDj!LRJp z5B4V$91BFBte<|=uXxrko+pqPQP4dY1&dMg%<*Fx0?{cnt0&(hGt1ZFk(KfCMxzP8=U!q}@Z*C%$A_bEUiddoc zh#%!K`eA@Trg*3zX@>L#!V(Ngo=^qCBoN68(oiLaDBg8p{#Zgs8HNGi)_g{NC%oq9 zqEv+v44HF`kLcnHhbPQiMC2ji37Lm*Z-xxb(sV=0WocrFMOX)JR48eNL6nf>Y>6>c zg;xzcb40Sa^z{<*k|$xMsr;rP6ek%4$?a1Gj~)uNbVEXuNofV!NReb6>qwFk_J{`5 z5_QSPZ0SC^{J$n8>bBvicLTZsN=tKfi)T(96#v@z z{l%6d2$q+Sw^)*)6>hvvGF%e=v!d6GJ^O(GjDrZ_;Zv_G=M zav#fYfRwb&BV&|zgnj53hli7O4I`yw95N_qOG~KEoQlI@kku4y8!2j7hgnM24iHx( zFVCF1DQxM7V@lR6BTbd8nTB1JwvG_lqHveIqbOVxTec*{Zwh8@?&S$>*@kfMsWrX= zW<>UUr^%+Bf$3BQBoNJ3FWnz-#whSK~EBG27bR61e|H^~ovyCK! zC?}J-Po~IW9cBtOf2=z{2&c$l8FIp74Vt=wx##xL&6|xXagPw~m5%lcIq^%07RCSLzbs-(?-9>VfQEexiKh>sExohjv8tWeVakp zWtuZZn^t!Q0&YnTAgAloHXOI;Y3}LL)Z4D;Y8a81*{&Fe=|Ba$#KQ7%iHY)QmhlC1 zXgp0pQ$d-@N%Lyeu1U?SdN!J4l5g74iy+Wx z!vLJ98O9_V$(dJELxoyVK4xKFUIiD5@vs^_dB3=PG!5~t1p4U^W$DHvFROS^Ba8zE zwen$U9u~JN#?Lpyjfy(gK`2E5Xh_^t; z!oH06-*>@;7KEbF{#DQpR{;%~pW9M#rEAP5<9{Lsh+1l1kBD=zazKQpc2HT!$JCy~3AD%FKIp!I5nl}{xW=VjQVdB$)BUu}57HO3+0XhhqUw^GiC#Q6aXppg`Kz6(sbAniQ5bqF-1LxN zZ~{!@;IDu^&ENU>xLP|)m_cLtEX+Y>7nDr~H_-x7J&S+dm=1gq168g@81@tQA!tyX zek<42JI<0SMEXimt!@7jPHnX+?Lx`k?o9XOi*4fjDw;bx)9=#sx1lq9+u%P3`Lb>t%h4VIRH9*vfk-)%lBp_Uipl3dS^ z^;(CC^01nB<7A_If1u)*_j(nFj7VyBWHAx1{q``Fsk(WOYHRw32P+*EUaw(rui9ky z_uNAj$_NvB5H#X+8RCU$$_dkqdU(_v``kMGbh~?N>R7`+4cv1LOw7;688Si)ML#Xkt6Ks$K9+eacV;5N z{0e-^Fnz93)wo+Yq@7@|k<_y;%!t7yoYX2st_{`@#Y=xIlzx6c?++r}^jQLJt98z_2Fyrg>~nM`)!h;B{}eQ6 z>t?F47A~X&7rr5mbOmr*ELGld>r$euzF!$~-!vcw@g1#2*U>xH*D|-kQB~2<0a=)< zsY~W@BeoMeS>gCR9`&R0p_KdusLN1?)1xGQlqNpX<;%w67 zo2>oI4u#-_ z-^GU&GNs%nOXR}At-4!c0nG|*tthFW#<(jSmHnHG^RVbWAB##9>3OxZae3*6(6!c( z#9UFjxqkxziJQO9>liG^z)d(Ak?M|Sr;H;E#=Q1$H*K60JiCAR2(h}>!Cnlw``VKo0 zs|G@-{?L(p!9MDmmR$(_;N3rVS~l!m)q5Np6G6C?k6{7d<_fH`L9A^7PMtyAiC)p# z28yD0ltER9i_?XCugf(Xb>HEPI+%p14B9B3Z$CpS@bcs!M|$)=`Q-pGVU}uZ*W) zydHtXT5+)O>Sj3Rhd-ltMXwxWhi-)a!T>QaEZg79r0Mn`(B^aN+MrZ;S{is6v#f&2 z8J$Ybg66~V=b;W*T8r?yRXvWoxts8znuxac0XE74H&0A^ZY{ivrBr!k1Cd@%qYg5S zBKzK6DWZXZ*nHh{9r#LsZgZ;UrJ=fNeX^zgx(%We?;DFd5|9Ap9-gi<g4un$BPg#zC&`J#g;fF_XvqZ4g`N+z(cGgG2E|a$kCX32DrtUr6J+hDQOT+6rILP;IFMMG=^9ed# zXL$BG>hEBz`*7_6-EXKRHREG{fA+QD(KB16L`psXAC$Hkzk4f|gMlcN>_#cAC;sAVJ&{+@# z@mve>MXJN1QL4jY^fV$wvoTxUb)A}omFd5OBRG;qam{5e9&ogz~Hd*xbhZexo`Y5}M zKrFB+m=pEGggenNr<&%JH+h&fR$1k`uDDu3+zCGAMn{t1*fpi0ISsXGI+H7T!@~5q zb|H1Uu+cT6Wh^VG2(D}5O;1B`H4Z$( zj%`z{#x-iOd4L{cEUtJywnOl@{m9~-@kzwzOp&~W=S-omFBA2~_m>f!JO#qx1Es)T zM)ZaD1_}Zm^Hbi+Uknw+r3cFmcOOendNg zy^_9%smL1n3Q0v>Al_Gl^Q8%`l96o12t!q?sx8U{bhH9SxwX%gj+tw-WQ}oV;XF+M zeN5%+up);w&x*i2thbbgOU+rD3gi_s(Ia#+U&}&#iv`-U^ zyu(zJ$3z|NO>cVg>bhNRZC!>T#1OUG(uul6asw)eTti1GsDGAui~C_L`J*C7$xYM{ zHxtenoz+4tN3$l2xe=SAFLgZ{u5MFG$4qqFv&eCLtWTO=3nG?KulmEmQc}>Vn-KX@ zE|}sV6+Ct`28jvK&{AmrS_Rx8-l`#G89nYiY(UBr0an0)0^7=`=nRUse_RK&iB}L2 z40iA<*70JEbiF^)VE6_mTr>?Kd9ZYFdIM7IQeJQ$r2o4{PF4;)`R2hJV`mhqgw;4q zDIND$TS;I9vDo;BM_sxoC#kH2NbsyW|NCb8ajh~MCp0jhTD%tBnSTZ&6x zF-$cZZ{~Td!92V!43qtxfXF155uv6_ zN%uH0d5k)yw8arI023J)Pv*Z%FGmuAan0w+mHjlww6e@xGcv{B85hTD`@mxOEDJeF zE~-VF!}M1W_9*#%9Ji#LtJje2=wHft%GOni+T?WEf>S0IR)TDCt|XWIEimu#UO{;! zQO#0V;F&bD`#Q&!wqt(H{RB->=xU-O%9r0Rn%A*Q(d;Pl*yTKGB3m8&-5e*Mx*onr z{jp8HjE4nJtaYUt^hg|^Nb~bBGIOO7#o%DI(4hU9y2G}Y0BojQ7YShViMaV zuC7Pa*13sH(=N=7x$AKpVHwFZ(&>%{#T zyP0ut9Md%&2_{T9LuWz7pSFn$Ay*sp8_M}n_J?1G7}C)Z=xnx-_~Wv&;y}(l^cuk)pllnu2S5__}q_eJxbMfNNz`# zHw>C$_6b}zCabI}s)w&D{LHqKDe`8%(MPhD_pxb{!iP5S=_N952u-k}GbLi4!5T}x z!Sb|8OoYLP1xfd~bO;OKtb@Xvc*<6?_q!VUYsV(XU^Yn@vRI=exN}{@cy&GKMrE#e zHIH8>yLH>+qB)0+kAiTnFMNIT)mr9CQ}hhQon7%up0hW{6AVjw7xXUS3Bhhk^^5Sr zIPsO`F}>z!xg+Z~|D`74SNscyIR(sv8;$O&;aybtcSI9pt%}MwFm~5>ePc8kx&2#` zRI0@JqSkuU&QSZw>4%gk$`+N)Mm7x8+NN{Y3@xtpTHKI-0^_;e7^zG5r#aW{Uvd4F za?hreu@%wVn&NI;vG7yi%xy5DbsrKK>2;p?5 zPg3&-Xj0Xv(^Iz6)>mvOy;W(Z%xlPVqY(pxvby>bs+)@&U6W(G9ZU%w9Ir4c2rTm*VH7bgZw4+jTIyl>-1BQw}Wab7MAu(tAk69 z^@y1E(<6GPOzKj(P)Hn@Ct3ks{OYp4txu2<)zV@Z+%(_M8Za;Qpu@h$t1nc(wL%~F z+}7?>h9z4fi!`eyn<`QkJEEac%=y}ygog)~yp?KV@hYmT(R*V|rMTN)6z6$dH2QR# zrwUU$ZA&yyn*+m{w#580(Amh!Fsr}R_y#Em8q1qdRQ216wFwqC z;A*v_VBTPk1xOVF+Kv7G9Ewgj*VrQ5{7oP;;TFxq&u~xyahim~4ooePwtRRKNt@wm zfvvOj;+F2^MqK{q2fef*MvzH{*!AwZQn`_wb#LYZ&CpBy$c?wDb zF`%Ei(s4(8t*(t5ZfQ7!%^OP^V!BRHj(+xK)6rA^a}=q3L~Dtv2g!Dz%hKK!2Yeih z7MWK*n>>cEo+w>v*OPtthZcN#B2!^?!ZeO%=>&^PZzg#(x5A|@k8kvvEU-*ti0cz< z@o@0)#F1+on_Qia-s-{K#y-fVaylRth8cy;8|QXiq1Dp?$5wXDl-CByD~qSM%pmpB zD%fSQT*3Ar>WLx!@2iK0jt6AA1NCL>2@L2J${?AW5O1WhbD1gq{#xhu9^4=bCEg@m zqxf}A>?keuK^8>jK^-a=@u60ME;#21aV1m3=5wRpV*L7Yk2n=x!ZmuhD%C~}vzPi& zPsw(6cR4rovB6)Knb|%2Pu`BsgE!6*O)Q4mjg_xhz5_#h?HDGPj1Z}#fcC%av9{wj zDKXIM+DejUSrqXVVB}^3*B*ml9GG+M@{csMnO*qf}o{l)$Q%v>^iqFI;crT== zvFeG`RWU26x+h_x(if;qFqTWqD4-YP{IA1{Qe?71y3~G0 zWl0_gYYbTco=bAQe3Q;Q8=JFNk@h{K=z>O7|CC;X*xRYe3NrXv_C$G!8HvK#i(#uqP??`VuLsyyvtNqXJ5ZO zSp{y^FNY4=2i>?%y)$wsXWTzO+cPdG5Ql*O!u+6t`snG;N3fiHG{RW` zjAEXsgw~>uNjrV3D|;{}FSU=c>$-MSdeA1ZwU4g>p56c_&C2!Ya$=61*4m%Uh|V;1 zicdQ?==vT)6pmwR9!RP6jwtn_kEIGe^sttFHJ08cb&f0Rx-xXC@7h9b6waGjbywHV zpItqH7evluTs^3)RL@ykwgcRdQ|(N-Pn|8=~8c0X0F837wgY#K=MV? zPBDQ6n}>0MlJrNeT>vhY{DnmThByG=>cy-9Jk|tlG*zkGUxZV|*Falqp$7+9w?f1rzeiXB8Lm_kQnFnEJVyuaEzbILlJ!Wfj=7A z3_!eALqv0eMDe`-Dr+4R{oykztRE-!2o!p#zk`%wY~~T8j`>BHf2VRhB3??2BTVwS z5VQykS^xnrhJ=%}xeAY2P}Yj@+L!*`%)=(Bj<3fq3RgX^)rMM}R7f{le`GB?L}V_- z0|Ab}xMPQj)>Yc2#28oQ0~ew5638E0iy}9L-k=Z%dzKV34~i{wVZ_j2E&>Hpka4Ag z(Ltig#qZPS6D0bCR9v5}$ymtBSXi69LSGDF_Vh!O9z&`=9v)HSJ_~9#? z>}}_Nb^ed1cjV^`LL14Dp3;racn2g1uHRm4iTpQtOLa_ksy{l>?PmW9=VA?txPbf+ z2!_@GgH8Vr+wzkJn!eeG!j+u)Q^rJhtW-ed7WUPw@yQuWYRm#Ud+ZO+!p0TO4wT2@ z!n-W9yw_sFMFRPomO(o9xI#Ggei}O#-?_o?6pwWGVoLbv(IkpTkyW9hu`qt9AdYFb zxEkBt!_8ba89&r5q>_gndjeTMcMc3Jx5zx_%D`H_OSRCYW~M4|pkx@x-$9%gG{u;c zlXBU8%-qxWQjR^cu&=rSgI!gr5Y`)3RA5mWZzqhT%x2jf6R;{6w=Mhi?!V1CIo0 zal#;C?aJ+Fv-j;0pieG!BaxWZ^5i4f5~7Jc(54INe+E|w7x9>qdX%-2GufjaC`_rc;tdNx z6sJ&V$k;?$2CibjHorFsLFBihsc^{dw82 zJjZ{5I5o9;ZER@Hq^|OdZO@p>qF}eVIiEb(X$OklEepgNDIa3U5Jv)834Ap6rc|wi zG|NnxlBECIP`gMlUauc)Oq+I?MhWFxGb8>$v*%U8hAIZ;x$*zyDba@1Tc+iNh=OD} zEZ;`Rz3pcn76LPl2w6ykBhNlE1^6dyn(_}bJP9X!1#X*A2-Y`}ZI0P5|Dtp@RiUPy zlGfBom9_-JC#6isTgO#2X{l<`sMyFx?b?`!nHLBCUL zr!~IeNLDvbl=O2?#fJUig`<=S7V@UDQX*g?oIzuggN`kDv8t@*vC_ zH5)iq_k-m_Y%0Z)kZ zeEFj{sJln_LTRUednfurVQ2VzWYvCj=?-LfH2y`+8=X5X_UQC>|HZ94LAMw67UIpZ zJ4W?Rsyj+|aQQy`)*ZY%Mt9t5a6{{hx;xAENc?5_1J)brH`RJq`_}v==8O9a-zy;Q zTX@#r8p-ROId-%;jNzL^zQ>hJ^9lYWlE$dE?u#60!1@XD3Je^|Ps1syR+!-n7YC?% z>6>odx&u|LN`;MPYZ4)QX(xM0I%b+%KYK0?)mWw40z-MV;wRW)8Dn%iD7?*`F4sHQo@~3Nqzm!BL<78TlPk#dOkR>Q~zR50F`B=YTJT)PBWv zRk}~T+vvBZHOh7to0(3_15!yf1Xwn{3fFIe8UzN9MU5;s`pX5-{O%d~0|26#Pb+c@ z+pnsgR8V|Yjlc;4iz@Un20WK;SA+r2uS8WP!=RkzvIe@UK`s(?MWexve%IMIcpK{6 z1OEK1yQOwB=yUA5wAxLdc`M}O1CV+Lo??xrnO^T#5C%s1O8l9&(VNzgOFyb|V>Shu z^2v7cXNDp7d*BEBTso}$O3k_3`HA7P{qHT-2gQM7Tyo z$QHzHGMCf84L>;Ni|}j`-}PPT8wcmp!S--14mzX|9I0)n9Z}4a&t^Ji)F8%H)18cq z2Ddx(xw?)}PilUHUo2KZgek|~yl~`7fIUc<-{xbd$>zwdxEMc}Y5Z$jxcJYvt^AY! zUkx>1BwUs8PExs)*iir#5c+#+yRKJ)tnM_Zogt^L`+cBe9hy^7&9V>fO3oZd3CB#w zI%LiLFIMp;r)v9t-!}WMBGu~b)0aCs|s5@%TQn~`9 zE|A0b%CoUfL>7ad$&E!AH1t0{J!n4+PYjnAh~c+s8HYkft=ojrq#*fi2*|3{p%55RCMLM2(1AJI8rTLtC<5?k2!L8VPE25xtd{WMZ=eKG6= zTF*as5 zgEW+d4)oPn%S=aWQ=e>NU-fz_>&*aRt1WTdcS|hZ=e0rHckG~LO!EMK-RF%BeLs+Z zQ|mys7_fsH2uRGjjf1m;YYWett!eRVQy<>cwMCG_QbQ@|jmo(|+pJ}KFJ_l=f`87K z?y@oLRx3711W-GA$W)04(|Z7%bdNQ5*UW><6weyWaLc)S9qJ1o9E~wgY@d3S`8HVl zOWGnZacj<89xTq;X0_|m#W{ax{k_L}z+QXiKDrK!aN3VK^K?3qe(R95#U`CQ1Ls~f zPI5jd?qSW>+Qf`C_*HzsB-#Di5X8^Jd4x};rWfkbBSLPoy+9(b9^k~Wt^?lvsJxNk4>7ZXj6*nd1vGPs>H)f%oi#~)V6QhBmULWR#T?;zvi{G31k6!fi z+~BY;W~&rqZEg4)6;WCMS-^f?`O0v&gQJaPZ~0y1fo?mhQ; za7hupbh3|r!+CsJG|W0lm|&_Q{WdX}0PrDsc523bk_6D&DC;-zX#d+G@i&R+K)qq} zks%DyArLe=(gtIOh~h|ZR&i-l4MQj{1!*&A5x`1*J@^`mM=yJtX(46Q?@A09x0HwZlW=Kgy*k#i2G6zVm}?``?z7wpAhz>U!AOB zKNPb^ysnWd6bGyv3R|?LkzB?BZ$Vi!u9M3L zwwMU76Y*_wy2LkWY~kIGnQLTrsPJQC|6H=9a1(OFLJv~+;a{TM6)wne7Tv?h8wmGB zsU$h`55k$OqB!#p!;Kq?_kk^%U1iy{x(lq~Y0WVQEZ2(nV%JdjWY?Vc9V^s3b2lit zmY+kX8^i}v*O2!qE2KXq-2^@Jx~RLBc9FMDufuX1WCv!~QulV(*!Pt$g~l6niB`Q5 z<@jVQKH*Ex)D~yNMdfix4j)A8I(n2|XOkv@H)%Z2G7YW18PW^23FDrPSCzN%6dyK@ z{=TI+W!e><;iJf+_{VTqUHDy7UJt@^%~bl~j^MD@&g%LcQ5$4u>#qmh<2FmTP+SJ- zjoI|yv^mD@G3iVyL-CHOo8s-p&Lk|yRUz5KM4K8{Xq#lptptt`?#J?JzpK;tza8yg zy}Ybr94{jL+Nu=@7S(qG%x(Mn6Vz(#d^3RPrFUv>id}RE6R3MApUel1I8tpv(PxNf z85{o(Y404INwn|p#VgWs@Q7uC@yT`1mUb8RS0l0u32kl;s_IOHS3CHTl*&S250==nklfnKac<42}i zjHNH$jJ;KvJTue#66I=9Yc!6z_S`1t{$GXI(LkFcmAmBi=E#2vc&7tgn(F+d{ zAlckFYWak@G`9wo>c|9g8*>s6E@bO=M%5uV#gN()2-hN1Ugk%Ic7ie0uSW6H;dp%s;mhix+8ZEdb+BC9x{tX-?-G z1kxQ7U${?Pk2MBM%>BeHR})aj3Xx<6KA9+AD^(Msdc;$fc%fJHN}%!ypoxAt!3EIV z5YrT?fWqmth*olCmfaMD_fNirp9nVhS$?YumI7W-9HPVnJ{z>()CAcJ8I8Zj8_g%$ z-dRNEJx@c)b0WxdAS3SaaVTcQ$FiYSkKUx_A(g&?W*3~gGn-tZrf5PZl*}Pk z@{ya=?NhDJHVkMRrU;BtVdt2eVAB|hZ<6RMrNAnHJZq7V%n&X4mSjAhSzys<_ys1w zkX>xzH4TYy?`y-5P}!;HXb)+6Tp{V~aC964SKz1gglf}_$;V!zxm4^B>g9_d>bztS zwsa5V`$P`2+IA%UYDnq}Af5C^=;8yKOL-gC?B3qAkAF1KCjs(>_fWB}#UhfPoh#bX z5od0fxuDM+;$Q&(zNcXfSv={FjpTh+elodLlHjT3VN{_ild;kt5S2f~Dm_HuBZa8* z7+xsl{xIc&5b#HZWp{D;!-7aQhe?d*Nd)JjNKYhT@A0C3qxk|0`M=%1!?M@YX1@P% z%_CfSz6)Lzq^}=#SHEhg=T&~Rh~EYjy}-vq@?xV2iIo)En8MU(*2i%B%Iyb?*X`&sZ&d+LR($QNCrWMOnupI2huKoVecIg z?;Q#6oxc|$8TWqabb>-}z;LLKU%P2Ug}#C=bDQer=6AMP$3N>rB$jnpznSf*j#rdh zZ7ku1?z2yDQcn#{aeD}UhU#aec6gBev3qeK(gLng`{>3ZeDi7)u92!X8|e|#edBbc z>y~KQzlo>o5q3CtU{?4I-t-Q=cuHhc_laGVEVzgD1fEU#M^c#&v6us-y>d!gNb&Kf zfU+y2`?)!{o7B&N44sc+*aL396?ZAOEO-m8@9F7V<}}r!i?h@Uy}#_@S0y!`5iSCSH-|{jizPSXOtt3!O^8+=5V2S8VtR zauEV@0bkP0lO;PRiwutE@j78t7PqM9GGTX}%!-C#Q%P1mQHJ!tIF(HLj9TCVO#OT&)TRWSBMCzmGFxY=XWN?9lYm0Smn0dzfA6wL3r5_R4#o zk_)g!eKL&oqC*W-#P}B$pmwe7h{>%*QI_(VmGQ2<)iHn(y{Ak)YcResr}N87X%Z)J z-kj>n??rCybqRB}rm2l==;zkl8}2)&vACQ~2p`;-loE6c8!6EgZHiY=irtfbwBn@8 z_)ZnU*4b^}6jaM)BO3AJbr{oi2tAt;W@YgpN32ZZ?@Ww}0ECoQ=!PW#R4nTxr3^|@ zp{#x9_;dWwmAg9Iu!7FmxCVlvJKw-AR~g%JqOpqfk0Q63?Wt~y9^CP>l5T=N_{$4F zxoOHD)!u_^CQW#duolY4q1=R&*4JFRw6d7T>2FBXynkM zVqhlw8DDCO1nDw#BFXfeQAt&x}dGX+y>sL&5;F|_$XFjOa zO#M({U;goDT-}<*ka$SGQO8sWq}|4?EJ}EEcDeUX2S%n5N~?;a{qM$NeCO2Q9maWs z*2eJNvE^~Iu4*6g4FxwUUbpzC+9?G(*;ca6#iYzZ3|)dWE%==1WdO4@@MLwUT>C@2 z;QmEx-^8keSb*+Ixy;hY)ukhtR#%&8V!OOeuX);Tb5weryrh`yhuXJffp?PBK_N5k zHHm=8XIw~2z!*)3f2Al0Hpf7OF>S1)F&=>-vB=hfTBwMKDrQ;QJoRbt zE@MHN(mCPb6pkw~fb1r64=%+%)JEM}dY?5J($ZpAz=`r2{iIju>ig&tqfnG1OY|J0 z@Ps!FhK*(~*p*CUjizvnI|sYfO0V`hR;yeGmi=r^*ws2>t!xMC^-^;`a8j_!E@)ug z-BY9kHh0;*7k-`LHnACfd)Yo@rb+KM$Q|#q}la?^&`-9PXDy}DyT2r-0Fh>GA0OAA)I;x78Uar z_xVuWy)zgv`1@*E-M^x)c}ioM(|Z`4>p&P|d?3DpK;+{#+(z1zOO`YUn>GplQBvoM z;tsW*1}Vcim7|dI`4B%~?2NN7n4D&24T`Lxs?@RGx-&Icq#sEwsi-_>v`F{c+PDf# z`Iapy(XTy6GC$t$3-+7tqsVb8OskLC0I5moIbChRBmT1vr<-*o{(05MF9d!I`VqUu z*&H6@8R=2>0gNg#Lla(*i#?DHC|DR87KAad2bIL*-G-#yHDC1>ML_0bjeG~edr29O z_rP;o{S8#?pX{0lHL45gTqnFqjZpjeiP?OU^u&TnO>a~e2TkH!cH2?{GU3>A3vcLf zheSeGc#5eHV!UE6oZ>gGK`TWOgDV=GB7Ve-b+$4|$yo*;tP-o4K7=bT>UsSiYwJP{^kldITrwIauHDaBl8i=gSgd?7a;msT7 z#?@?Y(Z7#cG_Wld{$kay%Jt4S4}D6@8B#oSS#)`7So{UoI?iv`hLv8Z`IwltYF`O? zW&DKX$;dSHB*GZFvOU6RNRgW6ADW(doIWFz9Ml8x_3>z){?iX)-!6q_Y+Aj8bNzg) zt?k?lGUuiFI!rE^-oa?ZZ58xmgK69@Wpz~fUeoNxt;oJj=MQAf7nD=Def_jy-ZY6O zHnU6`v-FBlyM3?RR&cR5es+N0^g;*s$D0t>n1JA*Xp#%Cwf7h=?BSK~{VQ<*xAHjf zmMQ+#_mkeyxVdE&5T0wg<1+}SIziTIL|}}I3BCFQO9=B>$&3z9%CG8@m=3oSTv0sP zF5qHGF{q%R-dmCRQH=R%dsb92(6tL0VOf<}@g~f)L?j>=s^RhA`|-pVGcUfO{^*3Q zsefHz{l(h%Y&Ghnu~6bnQC%Sv!q&x5ZRuk`lTTFJxrMAo@_2XuHu6eKev*VpGt|tK zbZ4ry?waF;nVi~zdxf2uHh026uJhs}Xz!CS^KMm_0w3b#?ND(q$-ax0|(U^BfHl*9;TBhdV$d! zN0R)XSrYu|uuU3d)-swzZg+%7}x!pP$)9(4~(Q;Cg!(B8Bo z8l{%B_C2f0N8jt6EIk{1>QHGb`)>|6jxHjyR3C%r9i%mw0Eq;~g|tX@bC?lX$h9oc zt6F!|cq`+>NxCToNtzU;1jgK$H0z)MPzKLLxw#3L^;Lpf=8WqI$z9OKLH3naEI>l0 zWARq;JasmuxZ`+>)4FwavoE+4YkP1~J`mbSev?cbnGQ88y_go=p|-bvEZN>B^tXmv z18sAE_|`0gcxgWtej&Y3^8}b2Y@t+Kf$E}C8hr;A9E24hw0Gq0cNAKREG&{nN03UFj~5PLb1DX z6!ilLZ+F!|GUS}&DZuU_d z<=0FYL>B_o=@GM(AgrbnWX*mtvyLDx=S($c9# z9orN98s!)1a~_b@N2({AzrvtTfANy7bms+67}0V3a6kY694N5P7qy2)vU%Ts6{_aH%^Og&Qq-mRAayYD;f~ZzGmo> z-7JG0X9Q3&C3#=I2fWl>bkATSJQ z0$0)ufzWLoOpM9=f-^N!#=C@J>lrl$I;C%kkJ|Gr#1;7_;-mIm!XQ{-D>0D$WZWEk zBgm29BqAtM+197k;Vs8dT{EBN?r>U>FpWaJuL88PMjz9|PyIGxw;;&`l~Jaqmf^`ZfmmSZ?I}&wv~@Eb!cKqi5w*9 zkB|0LI%net)wK+G)QQW}_Qm2~I_WJHM6P_qCeG`DL4233752xSUNQ)XdeYp3`r>E> zAK5JIfed?W*^~Yv@Ko?e{$14*S8@;jD)tAZd+E>k`x#MP-@669?4RtTbAwvvFWF|f zKauKZ{wBg-H<#95cxTvoK#1__7XkqLu>%!zL+5=6fp z3V-34Jff?{s41HkMZ8JWmofj+TiUt0XAiNg1;eR6qAxeHi((Q?|fh zl?A&wLWPh`s2>5LZNAUsBR2B;9JW!B{W-^CTEi-qsH!=EiW3F;TDaPIxN9{X&|7Uj zTw4LmQ@_zcX?>gMqoxi_6~(RS>_=pE_y4j5K^(=mvMqQu#94&VCiOY>B+FF zB4BLL`p4s7D{I)a%;~jMQJ2|+Iru57^1yAf5hubW#*E((W@ALKVFo~ziNFcmDrQac zRijTQgUXzN_;Y?qZTn;q5kED8B$HN>$fdyM3&6fq+ta6k-JVNq< z%A@U|FZWmA7GCVq7>1P>Gag5gg-7Z;i6hW66^#R);7#^>gB0IKg^}$LfSHD0JqAbt zuLz=)NC?|iUK%UG?`VQ(Eeq2r0bcYihPsY2-K;8PZI~zBqAJYU1e~628bYx7r+qdw z$$>A|PAdoi54#`9glsePs3Tf-4>;iM>juZ_z$I6Q=#R`L&Y}`DHM>z`6Ika?z&e}o zDFB2LRAuoc5{&v;EqDL*iT@Nti}-x&GS?^>sHZUndHyu-_35jtnmfn>;ntmQJ!abM zrm1wvN5Nq4p~)Ox1EWM-o?Ia0tsDs!k*2CjUeTtghtHRb^#+e|{tFT(E5SxnG}{T0 z{O?j%yb}Yy!*^)5=!*oK6`}IEOTJOu;7HS<PCRdiXxNj{5_e&bIg?2F<_PHZ)i1g*68 zed=}Sl(;P-0d??jbv3K2S31o>*=9bS4Yns-rjvVkZU+AhE;i?znn z)6gdx-UY91RTOge=layI3Z&K;oXcgu>A#osL2imDe(;a;V6a++?ZWsb`uV5AB9zR3(g|R+ zCz;+M4e=+)WR@Zrx#adPgNmAQ62OEW)u`>eB|L@zM?P3c=(gDR??6}PYf24=0HYvK zJ%!1$9V|00!lTRhZg_F!NA-*tYb95G7sVm5gSm|q8h;{JdG^T^5WCfsvyW8}UqZO$ zppm`_T<2d8gq)a+dB4>C^&A$4^h)Ao6&>*Du`JqFGap;ybe|DLjK(9do!aFuGT{a` zlu{Jev-?aJ#_@}W_Q~gpOT-qv4bHy*wC*V9}yte(|FO>euQ7C-^y6XhxV0k7p4 zIDZ0r!na0y2@_K%*L{Qz&+$Dtr&4{f6j$HS=-z<=M>VhxkvdM~U*_CQqw}`?zeuH`zgqMEgH`%>)Xo0~ zeh9g$k-M4cUk|0L*}wYFzgRa5srDW8J1P)@a4N+HwRt#@v~ZYc#+cy2gCkbF%M=s# z4O{y^??cMsE%(Wge^VdioXw+^ha+E2Zu`u%o#bY0{iWP|fzby1lg z;@&=2wW2q}8HXjW|0^qt@D~Bxh@#u5hQ<*eh8rsbuJ0qVC?azzh8wJ_Ols=qSp`KXFK*xe_+*YmY~F8ZywI;aLC=>k{YM!~2Tc zxiU`;Z4A8;D)}Pb>}gw;N54mUgUdDs)`C+?8`%s| z41qI$hlg){xB(_1kp!MHwjcMOo0KE~hBL*THc%IplmG~4D1=8VPjf@e8IDfo+E6!e z@#uuOl@^w!ULmIObk+6eFuaZ76iO@(L&T7SN^+QN8r>O++q& z3KDWk-E1ltbi3aO18Yn$Ir*B5Z|Gt?IcO@F6UwE02msj?QzU7?FCK;F)^!4^gd!r< z4w_9es8Fk-z-=!bGe(Txi{^3TFuH2C5_EXAESn=iAaArv1T)fpRHtX73rDlIuq@5K z!YZ8%G_e^%D)?5WWjvwu&Y*XV^$4e5v8NrXmOoK>HM)4$;nWf}ugqD*E+9B?@>KDo z$K#$R`j@(M_8jdOha z{1~t`C=a#}G5ME!IUrv;-9@C3qmXm`HJ{^hGi8^!tujp1_TrO$<1zi_cfxzp;dQ#y z5eKG4kI(2^^bL%tdidMri_!%YwtZD7`o|Vm?@ip@?#R}w7>7WF;@xQJ)+>UC{r>cn zdn>5@R)646w*!}G4>}Kln_=wNFL6%$gO{r%tpKLF%e1Z5!fzJ4#BrNv!e$!& zW1^qbw+FTGlAgylORab>x1%e#pZZX|<69rCKG896P=ePd*PLGapq$-9HwX57qc;wk z2fUu)r9Bf{H*L3m*Pp#1VcLB_;Agxf@Nk%nk&kv)I%MD4*g%8;+cJFYY|oaGWs@Q< z#tm~s6RuTduCby>J%Cb`Ja;a%{Sg&>Nu3U7H8OKkGMgU#LH5z7+ zH6VddyG;CMwS0=zMEIPJ~F$YWanGcsfmtq2fz&dTbYR>Ac>TDYbV#2l)K7Q zT9Z9TqkgZb%H-v(n%kBw%;l}FPEO-$y+teuhul2bk&=V55`H32+l-4%u4X2hVt!mm zped^?#e=PBTZ@vBigRKO;w7f9y(n98L^2{cf}r|bq3G`-6X=ebyB#IRxn-c7;Dwsp zV>Goj3*h*Dqa-%Ay|Dn{1S;jx(O;naFS*~iPTo+Wz_>OPg$m!2wyz3iyxo(sDJy;M znZda&GX)at_ZX@X?)S?U)j4c0s$ZzJ69WTa4f}7lsrb|pxoaEMgEokL>4}8Ol4DKO zZOv2(sYw*Fc;hX0zE!Pmm5TGW6P^~DpI9JR?)8@H{i}>Vl+uLWr`qi8kqE_p#6$EPvFX|0E*?s4f|$y%&}?=VnJH7BaFX(_m{^ z2rttJcc?}r7noR|Vkr7%HfkawK30?$37isgWD#c0KEw$P3eqfPCz9l5LrGnnX~_i^ zdBGhQOdA(reJq(Ol2cG6isA-50V~V6DUuE&T7Q)^m-m~=C0inm$eOmQ{zYC6%6IK2 zFCVA}y>0oVw2n7o>n;Ul_yTIGWF4y}Y3r(zzMS?(z@mN|aFW{&V*$(1rbWY%YTdjG z7cJym&%-KJOW0GSJ&+`zmSsE<;$%;Sweq^C&9!FR9HgnVXK~vsEln&fxGX!NLmmC7}T$f5N(EFRm+jQnz#SG^+Q{+`;R=$-?&Z|DvcGcpo#jhOK3r zCyE>0Sx7`v?8e*IJ` ztH#AoI+L|cU-W9KQjInjb-E3PIfBN2ID_{>1i`lb{#tH!09Mmgu5al;TSmTA*;M#= zQF;2)U@k7@@nbIt8oV`L0x&Zb`vtLg-1(B9&{e7T2ru|>le?y>-E7~QYZS1H zsj|*t{9ul&N_6f#tV``(Ez_JzFK;75niFrobEeHrZDl|9A(6RSPh9 zh&i?A{^8OzmkOKbMLC-dn+K-AB$+onNT?quVcd_LW+|EjlPBC*sP2mLGshA;%CT81 zT794XG2CpGa=I^ED>nLcxK%5}X3%<*e0spH02*2xLQ;(B8e5r<_Wd?Fd=Iitcq3}T zH8rMdH5AX0#eJ4Adbg{{Kdf1mw{9XljnOU*gbMe(452wc(a`1F4?s8Jd=j>xT%r17 zY;78bC_FSz?7)^&GN(yK;r5(DN5h4Fc3uD4wF}nmI|Dm^<-oPey;#=)%=9AGeYQaq zS4W92=$`>f#sbTYNbfqthavaZp`pQ9BzkP#I*rSD1l?*n&O*jOu22pWGFM3*MG5cO z82-aHf+wwDWpZk$6BCl>`E>NO%;`ElBKa>RN)9SjC{g4D@*I#yC^$>JQ$tQoF5lRL z+gJSd(rIst59O&DTEd@uE~|d(&)!sTD+&y`#paT;n(B`W8v7^A*&+et&xSQPeIhK8 z6r{pbHcVT!OiQ1aOHWzA=S<*p;ucx(aZz$|$8%aZl@zbLeq7z0G?mb8t3t6@o zZSVMuajQ)bBEAazVDN=&SwZe8K#6hRA)S>FqcJI=#F6)AyWQnpHK*z*Ys6w&$I-&B zm!PJ_*PvK(}5_(u;n>}l$qh@1S*R(`wS6hXazbU^W;e*L3~ZdKQc>w zJz}*VfaV7;=Ldfa7=U~yXL&S3@ar=kF8*Bi?!A?oY0Bn)qQ_aJoe18Cf|CxHWLD*q z89HQ{M2{uR~)Q^`hd6^r4aSB2&P%@!o@{1vEE@+HkLeE5E07+Dyu9C(iFvW|`Z&_{4dar)!#|i*`s<|Cay}G1A zgO+$8w4#!3Iy0=o>y}wVeYmbV#86jumya@gr14V+z2?)BT`jRdv^jqmD}CP*gXC8O zd0}@n0#byx4Qp!R(nNT)OGNb1LcCKBCN5&CX%)rlD7$H=0~~8+)mUr%=qeW??uu^W zoR$RCPWiX$u~lIW0}<0pQr1JO(bnzAXi8buM2M`+(CC+})U>2Jof*fJ_%w9f1J(FD z&?yl-fwJm16o6%=)>xPmDBnNDvYMZ|=Wg&IASC$zb2|L*1(p9GH2rVN@IS}##ab7t zcq^EH^iZlA^&z05iTa-QB}pM9q^|(-d0aRqlCYx?rYiH~*Wx;Z>y>(L9>wWJaYfQu zjneP-xpv!e07a@C(2hxV1D>P|->Jqw!W%t#Il%MB&872z_}(Ie>n@*{?47q9ml=M^ z%9-CErGntCutcGb;HufziYJo6o+5#YJ-Vjz9h}~LW#ybAq3Ih8XJFX?Mb zG@y*gQU}7+SRETeP7=0fzzBl9xp6~HbykK^XtUFV>v#Khm(5Wd;m_!NKW|%e(gZ$1 zKyLKRP|}0&XEq7S&&*s?oXkV>_2x&{$*~jX)T?2*5fi?&s1yXknr6Azt?9%R&iNB( zHl6Y1)=G*tmxR3%DO@SbxZ>|!c^wl2M_b8e?U`r8``hfB{>aKWE%V>zvKHkT5@r}J z-4mH`G94#noJ?Xj+C85+2VzrNRCQo8@=LqL;Zi5AqK~l{ST3yw1Bm7HXW0%1+cvE> z#;5PAZl<@Pt{-PuARgM#8dc^Lbqt7Mf}69_6q z&Xd;>^0oO*oH-qmJ~M1Qw21BYA0{cK@C-~{Ql%>)C??JG?8>dBL&0^0ST5J&k;JDg z2TaRU!EtCV(Y#?N`0b9ST)BM_T5R*;Cx%PB&Piu1RX2;~1iovgI8yzI!&%B-OP*lG zU(ZC2fO2(7VAL|;-y84JZj@?t6XSIANRBiW+x+#TqlkAY6Y(713QAHX^cNi_`^Me= z(0($oLBHhZvUp^33VUdn2VE+Z(-8HBazX(;QdE zN=&0UT`SQE&44UpIUF#+fc6y=$gsx=m)qB%hP~KW%w1vCc8AiT4#2Mte|0J>MaLWZG|m z15z0p49cSEYuv)=r`&!Um@;5a2w7X9=*-`h?=0T6`_y-Q4GQ+a?2Kc78oX*0o9lPj zj14?JQ-f_*$|nL#!RPL96qZ%yAl!^47(anq4PZdZs6hm^qV)1-ufMCY@9YNlN9u~6 z9j^SYk@C+u-_dUn*9=!PciS(+jMDWeviE~&*Km?wsv<1sj=OB) zkPIbUU~k~miQrmamE&4z!#UvDbx`(9u8-YT0!rRIS8UyMahM;BOwC0hJ`v$n z?(b=DYz_bdgsThcGx^<4-aJXAN7U$I1>W<*XR&43o{eBwl;-kEDc=LvqL?)Ki3(p9 zl`R>_$k*Yz-oEkY5KmENdiWw>5wu2@n0KDgY>JrB!GbF8Ykc#GG{zZ**{$x5RrV$# z4d3vsl{}jem&ys(7k)VGE#+zs?fv|wbF4YHPE$>cqbF<$$y&|h5iX1>iib!C^Q+)1 z>9iNq)mM#jR&+--4{S>$g(o=8@~>8T_7K89q$2FIqDWRX2r9m0T9>}T&f%D$o!Xc3lauasJHQ}~E zJwws{c0*HSjxa_MOAo1y8_O*}5Rb2q`|u;ut%ZKs%_<_0;JqI5+m5k){zOuH+ad=> zya9;{wP)=jY6^Z)GHK(0F6*brGQ487Vn>Cr_I3dQgr7LjVJ~j3osqE|_yyp+pOUpL zbIqY-kBF3Gis&>%Qq)d2EtTczXT0W^_KIEGd4tsP3n*h%_ONm?z+ z9LIH+3GeT2!l3WE7aZ@Yhto}h)^j$ftI#P=jRa@{*jiTDY_8YZ``K(P@4|sW!a_dX zX{l(Myi;<_SEJG{I9_nF|5~4&ov&}|ZYytMTwSc~6Ut14vP5WZjBR%n*Q~5l5|c%t z1l-f2B8P$9nfCHjn*rw#*K6z<%6~<-cu+ywhOHMuky=-g)#meJskVr-L(xS7M*arF zo^xXMifV=$gVFsdvgh#O&vje}A%$UwJ!|61#)^h9PD*iakivM_`IWF$I$Rm*XsWJq zGXjLF!|nP|XeCVG)7>WGxs}(@`7(Ng!3SCk5$71YG~MZ)ztx7lfWjA%ulECx%Om0u zT@j`E z@F8_!+D^hujQnUwHaIQN`;>{0o#^E(P3z^i=InF|)Ia-;*@rilMkzT^Q@h+wXL$!~ zU$m|6lD72p?t%$VwK0@B+7TA2orxcK5$Wsbd~0r*2DGX~)lZpAhuFJSM| zgN1nRVBmcZN)(~&`#40hNk4bU)`yae`0-Chr{EfXG=`#+l%F;8QOHe<8dArEWUCSc zx_MOOvlsLy>okTh+dDhH27-NGW^o+{T5v3vitqaFk?QcB3*fNYYt?@usj@Dh9d&b; zmf`hpr1l(j-=VG6HsNYS7PqBLAnjnaO-g5OuzuL7b~0kcR+MaVcQfe9vEf+YgM|?N{roA@k!qm##Kk<-{7wMIWaCV zmmG!}{*JzMxt~IV05}nr1lEIP@ras@HhSilZ|6&^wp$(Y5AIY=@g5#pH;)ziK*ZC# zvXzQheQE#Ybc_%<$I?KU0yxZh2r5h(H8~2n9AR`!+7zyevKA6A6Q`|>%Cl|vhF}`@ zi1AehvRqtmF6%T)JG?NPOt)l;J9p<8i}KLPldUaWMeA+H(aLjg(#~q#6Zv`G!|`*( zB|b46Pe8tzRJH<#RitSd7Z@jaf#!{x8woAkDp$+IfJFY-q4F(-Qy&H`)g)Jpp@l^> zI0wXD5uC`=pBH^&=y!$6C0W0Ocg>NZMyje8vs~s zS?EUH&DOf0M7D5L0~`IQL6uoAm1q?-{cQ*6@&>LKds2D!qndyB)7ZEM;2SVoHpRx{ zj7X;hud6^`fzw?W+cChv>y@+OyUO2a{OWG?>j}xHQ7nb%?ii0rk+@cY#vbJ|B)Nx;)(v zL51woLX8^6@|x)FG5hp8AB7QvAy=r^cmOdo-YjtpoozZ{zA;W6kcq=2@^&_gy?z$O z?3a&emR!V7@A;9Ka0Zi&w3xD_lC|?`n*R9WX@ro!zVSxLU5AG;%OQzt(=5+4NTwUk zjc4)Bp4H}GLJ@ojo_eHR+v#mFqQF*8T(%Y#ljCKx$<3~4udt>atEo-t=nP)MIPu9K zA~HTo1RW%OPyv6zQNbTo%|O$azP^cUfWX96P|^yc_Q_M9-)KDJ*a7+BZENHmc=``W z>JhZbVe8X)$w*=Jt<$}XT0G74&r{q((w{TikEQVj2RJ|GkDVS1^C&GqLWZ41G>s&=ltGe zYjy;eB@lm}>y>|*ndr|Pf0E)oFn*5^ZG}VL5AwwExrGo2JZOcYx`iG1)GLW#c$lb2 zV;R-b8{*O)wni2RwoN@y_f5Yepr3N`#UL_i(b%=}#n*h$1HU$EU+EuHx$JZEz1Y`F ze+y0+VWl6U(l+N@IHpR)RKaFhvdw+?Qg``a2?HMy#ODYZUOGvCjER2s68-)QHW!PN z=%>-rm{vr*c4xN$L7g6b-lqBo(Jr$e$TUX9=VDkJFm}TW*z>dbF+cMUxL-OGHoR6~ zLl{MwsadZ>tMBE(_g38ER!Fut3Qp)2V?z6eAqO`ephK?(!*$|Bn=?R;c?CqkkeHD* zF>t;DrLqF|Wf0B-CzVRWr!b#&5B^e7S_pWog|I zy_Z%_8zRm(vk`V5=|}y*%O!_h>Nt%eBA6Pz{0hX_1+5|VyZs?b_>~?N=179H;j5H} z)fo$EWsv}NOHDeGF$xbL^|B)M*j0!c<*-Ev(EBTCc0_;#FSDcL?jgd)PXKp6R>@5x z5Y1;!D8yjINMpilK69Ps+r|9L;SamE!EdHw#{A`S+T92F%HY1R0pMj)*zjWM9zHU7 zY=!sGC1q$3bAF#7AR`@`jjWHCT>}_Tv$IfjPuCF-K3kBc<^-f?MI8N1)JpYL*S;dL z6!-5jtLn8hnWF0jU8mtjbs^$SFvW7(vv|D?xtED$<$~79~n;EE5(W{mjDU%tBdpKmv zgRag_(8P`|&yI&THe&Dyv{W0~`SFma%28AD)*69z&v{I`DSxsH!XO+u-Ed^%zn=b; z%3}5;PiJt`*FsMOStT@`P{4{mC~;I^)(kRT2V*|AQh${6cNkDMTP0}gSHlsbE}E-N zkz4K!!S?L@szES>z&P~b6xJidb8%i+mJDYb?1%aj@-6z}9yl#8fL>U=w?KNp-dhQ0 zqOM2L<^I&(CYLI^`8{Z5y*Y6hSjB5xm1%nP)27};gO_v=D2Lo^G;>H^jVQHFdmE!FyOla3%WbL)+B-ju`ugZN$}HtjpZ7k>*bC7oHk2RrQZ3 zje;K<`RN*k*~^AJ)kAzbVc)Dp2sj!lnflGG17xaKrSTH-I=Z{up zY3)nxTI1H|BuVfo5aU#-atoMqO9?z9-1p5QcOk^@07JJ(SN$Ey#0B=Wd4vO5z#4Q| zpb#Om_w+<|l*%*(5j+DjHi}cr4fFktfM}Y-fipN;*bcRS-|{A3VcJ;SL?)F>@@IV1 zrVsuo-yh_E%#}8p!DMb%HX?eh1__zLid^P)cZS}iMY zVKt^Ex;uSTa{6fGh_4|NxuAE{r zXzQfmUyIUU6eomBTkd;Efex?Nk|;CHj2-C#j#{qVmoXIYcW^Htjtk5yx=Co}q7V($ zK7#IH(pR7wC3+8<#rj~qRy#B1_fU9^$BrMU3aAEao?~|G`9Y8lMaaz8W&5*Co+aqa z`IiIXqe8I!+O-~3ijh^jmzOszj`OC|vm}RJx%Shein>acf?t7^k2xK8@;SIDr{8SV zqVUIylHG3$<=RmCF0)Kz?+La?!h_Vg8~*P z(&ZJIWwZvMG)iIsWrmy0HCV7BOI_yfn-%L3Izy&6mN0Zv@D%2eO zZLq-qhY<1KlY89%dz1cmjpYB9)cyAZH6x3Ewq%Zqyeye8ieFBKsj*yi?9Pq>s2ega zmWN^_F{m)iaUMkAUm{fID(e|6+yBMbIYkK)E$O;!+qP}nwr$(hW!vtuZQEV8ZQIqi z&YYP$bI-%MlUOUCGj>Mqi1_1+oWAo~B=`ZMZvgoV{KRTJRxtVfJ{8yQa<8X~l03a{ zKA_hKB{(=Wnh^uchne&fM3cI6gROor;b)5dt5}6hLRY^(4DWdQV`#v{MGBCrRqgYx zc#pxNT_Q>tMU>l;0t|=XzmIK$iZPfzlq7uxfLh$oVcbYP>ElQ)IKMmVzD2%x;?a>kj2R8}lMP<8doe|0lhN;1 zTrFj)50vKE`zAnGKQ7>CmB)A%2Fg8*87|sv(O`i_j)v|N4Ic>KfpjwnBQS}Q$MF#7 zeLKRtWfjRJO_A)fnvEUnBQr#S-fPmpMNX0o@-SzF=?pq4H-}znHH)n`Tf0l_kfrSf zU4^!#CBjXTK#^qCfWoqbUH*sem+1qkA?3&J# z|A|>*h72AdG+-zK27y32i5LhXFcAV!G+6*rYG4xwNs@^{DhD&92{*Q(2o^z;(;2RrT_6%ewDQTCLAt=bbF}BuSvpp17U3k87vvwVyM`=c-qpUtGj_ zNs*m*Qak4pkDYQ`O=9NuNDi)zGOu;Qz?;wu*Y5i8cT{Kg=2Z#N_W3^ETj*w2kh>K9 zp>v>9#~Hi%!kP;{S;ls#?>)kk5Xo($&*#a#=&8C z2un^aB3X^=ovU3>u6=w4w&~7Ck!P>K4#_Nj{N7ZE5A@FYJ`?-SNbBS}E?rM!_|1MN z^ciQ*=oJ1TeEZv=Pc6%f)q(43!%SG{1*KbFwtiao!{6neCI1N z(J3*}X(7>SzYt4a^0D%~k5=Yr5hux%dC^-{pRXJP@Adv5o!`K4p8Fl5CRXxV@?$L5 zn}=h|(c#epch?u@>}1NxhE!>uVGC*2ez;9KNl z4^?-q0`hBl0uy1<5IbAO~O~4!WZ{lCI@MhzxiqKovf1KII@LoZFokl)zJd`5LWb{Xs?_%S z`Q$Mn%k6SykZ4navI$2ItL$)e;X@4I*gy*hBWsK{@cN4LJ_j(9Ms8?vUm)191dVI6CRoc8{wI7a4SgR+0m{_&J zu*#a&9~HJ2nQ6^UHrvZRepBnsQ`9CQS1Hf1)l(bGY^~L|t<{OB!-tVoa3b7;{TvHz zXpj5#3g*Hza|Tm^sO}`R_!>2hhDg5M-Sr*~`Tl;wzgt@gVHdE%!8(E*{fpSUB=#D_yrp+7D366as_i8dNEWm35?6VV&pDFW9Snr%=XmxU}C5?#bC0! zzyP8lt$+c0&19ZomOYH8!98Xn4)B&~!>+weZdX$8L?6xTZEH)gjR-S22Oz9TG>d;J z0}h|wyi0`-h?*4X#=x3$MW_~s_4`)^%hb!d01E~hJV+dC#=#Xls5|S}V*#gwy?6n6 z3-tLJlokD*aA|D#wld!UT<`?Tb4YK_M0Uf2ml7ENmbl5#leto?)Aa0<7=Czv#rX2fSIdY(% zOz-|WV}0xQi!mRdm?glCY8i5C1|zQLut***;JPFWN_aNlSj`tcV=%HrSbz{Zg)KA) zvhZJAUPOtt^v4;_-9?lOxReHI{+pg5j6JokL6m`)TB6>NsKO|)FQ`~g7L1T7Ep*|D zLha@@+F^8u&~$jZ;&z6LN#*^Ga+>*5W3gkT7Bq!hx6qkfGKI+v%Ue`)m-@&b?xBMuoS zL^Gi0Xr?b9Wgs=gkc(^dYGQI&v1|rMvpGrCX3n`Xf+#@|GG69)Vq1M`w`Ai2ReCv- z%^z6_hN;rEZ5aDf01w?8n6{69;M7wE`N`Pl+i-6L^QT4BiK}8h%D$t?eO^j?1?d&o zV_QM7b1bO{doHm^7`Q|&6E=yvKS)(~`yPQ6pTTMk#9K^VK9w;TUdd+Ov zmYrFJDyw2!m1LThtyPSaA7f0ETEVK8o@bQg26&b@J4=cgV^;KUg-1cs1NDsmDv{(b z)yFu;Su!YdKBEo#mhfj<)UlinnqTB$-6@}DUj%Z^&&OIK3i_7vXI?bQ9c;S%tLut+ zPUN-q8-MoV77kjwFr<1;4n?kcNCCaF7%Xqlz&S(9u8eH&$^W!BR4YFdwppp#iK;iB zt{7Qv$*4?i$;<(dTLIK%>}s)m8N;gDD=mMy+^tm0z6{o?P}!Dgu~T#@YY9zQSInMs zNlmz09L~$@SmkTap^h6t5AEg}*)7xE9mLgxZP8m5b|F*Do*Jl&eeS1o*cH~}qkwSn zZo7#kf10Z^&Z%q~`s#(?$OBlfk?#H)iqsomAJ+A}5Yc+etZY)XPhmqfET8MFNKPf= zz8l;()JRNF*fnC!r4mm#Y28udU&DUc_y^)EP+__v1$0RY0LReO*FfJom&^oZK^E26xmkHbGLwsblo@5$k>=}{=HRv1XDo+m!R|B5-QJi`F#0!juqmt zt_rqE`)U((_BG1hIp;~C@5%2?8u@%fV#2&+*vaVP>gj$k0i1Y;DNKgEVHU=+{NV-) z`?&8Ufv|Dop^4e%F;RzBwdP%^MgpPOuzEvLuzIwg_=(5N^FC^c!L^Wjs8OMNNfE!< zaW+C|ozXp7!>S0^i`<5jJxksfgUba&*J)=ggYbx#2g%{PK9zZPX+H*Fs1sSxV*&t%qihc zD_EV|ir z5VZ-f*_B_y&RS{ySZNZ>&(Qmblx}_7SRTZ5N&!O_^?(jO%G71v>=_Q_6~>|X=pJQJ z_8##{)Aw-8Wi4Pgmr`-X)Uh9+m{GhpYS+E~bu}K~P`7Xlprn&McX0bsKKbb!sv8&D zEh)>%3*wZl6H9Xyp-W_Z1|7j8bF}jMg~XD5XD})dreSp!&&-ndbYCz1do)UQ6p64t z=#f+_jUAOZgFU(qI(pKt#P|9-k+gY9e1s^8Qnf4v$?0(2v0_@4yV?vRpb3FT+(0^))c1&llKg``u;HQJUux}D3UDW>)cuGyESXhtYs8l9@i!x^n*`ibE3D~X^APKN$10jGTispC%Q`;p+A zBOuVnf{V6iyfIpOEfd>F0zJt&7qN31^aFT1xYrremKZh|LGMygd#>6y4Qed7&l&(! z$uvZy)3*rU4~;1WtkfeI7*ik$AtB?Ex-4Vgi%{Q8IE#5oyFN?b>V-ZyXE0sZ_0@>$ z;PT!Y>6{+=@&y^W63i`Nou=T^9ge2U_&qH~=f;daKEsY5xt4w;7@wMbv8AZVuPTp1N*|!}GG$@$geQ5Fha%0F^B`=Q6in|mk3QW_Okhc6 zAfZJ_374TmTubhuG5(#gvKSctM5SS{9vu5>?(IEWPg$!zaQ=$*cZ{&KtycXpiPGCV zTa-6Lb-pwTFH>x0woybyD(MvZQPk)?@iSH4F1k0HH89xn*F{_%QECgkho$gyXzvWR zh2EC*SA|(;09!)%rJZMfv~Nh4>p~~<^k?eH^iEn_@!PdeZ&Ekxr<7YCv|q9Jbti1^ zyvHIh{&h1(m_0!JY9caoLtlrF#bZf^P3ed5;&>tTf+X)Mspq3wCYsyQv@U4|X$Z8> zOL)bSfsJgUDebFx;2UB2zsOly?vCNK!;RjVzd&Dc?*CZreW5)^VA>!i8#pk1s~1~L zKvE^*eaoXp>baD33oK|~#2V*cLrh^5ov$CT6t~!zMVd8_Uwc)x*Ry;OaB_&Umh3t+ z@#KQPh368X;|AUF0+)K_mdr=7VArTYuVjFecLGG}0D)_Rm#)KBpO}J%49?19Q>bw4~a!8t09v>9^;Zy;EJpCfZA;IH-bK{_g3pPU8`L zA{B*K@>?PtB}CqjpSnhID&U1gp&$I2hTzrSMN^KdSoDqE=kzh>2sUBLi?yE&T|+j z{IqKXCnuc9HM==A6yWx$Um)GK_yaRt7fKm5lng$db}E@#i;mYd3ae$WUcF>|_r{4M zkK|O~8lwa??{#FLF9aMYIAw2vL%fnNv$AFNBnC~Xt?nUc#n@Ac0Nk^;4< zB5ap+xOuvaE))FbXo{~)o9ksLdAYN0=@RJbGgnS5DP%sOs}IXdhYm_DdSq-lg(t@K z^b*aM@zk#^OP#DdbjVQ)i@9gCE$KN^@+!$av&w5;yN#n#Lst-P>fquOA~jjDrp%C6 zm8dZmctlIaf;lJ=BK|;eXdOT5Y(Aj0UT`=kglk6(xcg`ga^@TpaP)?xsI}pPxvW2S zH77*5Gxv|2p=pLkNO+Uj?ZMn_(e6&5_<)NGmq+xsEZ=A|yXu~3f5XiO`+TYLtd}%u z)RSIuRHsg?@XuIjd9N@hM&GzvrD}iWAQjCzI}-9(G*FqbtKqUpbM}P={Ax+}A2r^U z25k!5*)yu*Di7HKQ745U)$SLjRD(Vv*y%H!4Z%j!wj14hIsGjFVJv>e%jYrcJ=N9( zn>UGAcbHTlKqe)dh%yR2d|4ikt_-O!g6sh)yH~8q=w-zvHaXZUY6T7wPp~Vh;>at4 znlcSuRzxSd@uEyfBo~Hi#KlY~R%)OpdbA0IReiYEoEf?@WDJf>AJ-Zfkw$(if-A$* z5-we(Vk|K9n^IR@&TQ9RICn~ANV~y=!3LAeu{h)cPC9frbLrYQ`G!N~E!`||dX8ht zKj}_iDj$$@?92-l%UA~ie8mJ@wL(3vs1N@|ib zq*SX*lxEvZ14i{n+Wbx3I(9SbbMvR%sV#5h&Mxzh`kYG-SJWd+)_+{odfcnll^$WH zqO1yzt6_;$KwfY|Tc>=u)Wz6?m@k>Fjt9HRy+u(fL@O*Xyg4;5UZ>d!W zWs>ST_sH?0?m?ohNG=CxIYVT5gC+pUiCIiVBhqsNFM$2x7BDx3OxyBFuL5WhQdvVT zSt6aH1HeeWgQA#nyM#9W=`i0QItPfEV{vAPx2qs#s~~6^$7f84&*r!7!`ZLShlit$ z4sAA#RmT};6cOS@Hg3ydus#VI$?p=azJ@ zjuKXTNUx<)%XS|nm+C@g5Ke)UaSf^C3%%bL#U^}0>;M7ALb7^Iz~DIIck~8^n>7ctx0aVI(;`)mgM0 zKKr!?Wjz6f{*Z}2;B)6Bs`rx)3AtLe(~@ueG!T`E7hAg?@6 z!QSfe-YbhP;!TPk*JYP@&>^S$Pc%bKgr*i^d3I{tJF7AF6Dpp~lowhhR4>Fxqh1}Y zYnuN;a(yswp3x(`)z&@tnv256`JUqO>7y4I3usNK5Oxq;0ZQP!nbpIicgo7R#j&(T zHF6HE#m?eVkPKDcFmQG(d~T+=3Hsb@%RhkZ+cDe6Fg`5vf?E=K;cvG|S6et90VZ;_bx59(}BTNrR(HxHGL<)=fX6d0w5 zAE@%FmIGzhWV493pXsQIKrdl*XrL}Yv8K|gk~RsZ%&V!2aW8U2mPahs@-&Id7HF%I zZk3WP0IG_<7=MP}udjE(+mVOWo{|-ji}K=>);T=!+~RTj+Sv24%_H4z^LnDr>;6*WM*^)!Fh%P(`#*SWRtP{V5L zuBB<4jWoqi$tVk%$6R)mrN@g?&9@e*XoBg|6E@BIK&;|hZ5B&bkCtL^?9nH3oLP;^V?zhW?lQx|QWYvGSS zmHKKTnknT2cO?&=J%cBc_9u@y=jslIc)_r}$PSdaHQ_Tus=XtKteiW4v$I@nOZuez zqXEV-*EZa*XUOQlhlyHdnt=7|x}QdJH8t3^o8Hce63PNuH0Y@FZ9uQ)!;dM-uSd2w zWy7gKc*#V^YE+hvy9>RQxt%?gmk^y_|LU;t@hy8@2{*J(o`4Ro|H_nsnp9Tpq70d(*$Q8lTWrPtxt}lBYVk?ASR|(u zUWss`90ixzy1a@@`3nT*3#I?fmf-=;=kP?O&`0ZW9kyDfxA8)T!LV}Jz8sWy z^GwdF_|35~3Cn`2LPfC%IKnzgPCso9`u*_XjMFPLH_Tsd^hGo?y=Sx>+CzYrhT>3r z(aIGP^_%`5m1ZF1@(SdihPY?0e^+hf`TtiNNhIx@T@3A9EDc>O?d?Q8j7=T>@$jF; zW<_0TAVCz~gOw7?)MDQ*m04lUCJ@AcxhN6{qxk)gvLLz3X}ZS7<(+fN(|r8dxXA0( zw0Hs*VCNfN=aWt6o38HOE+CFUH#o^EZVK))29uhi#N*6-uhotdCSy!Sl}suo9n5js z8F)hBp7P{b*EsQn-(8bv>cY5V`PjpT7#0U|)4F(}cES${^vRs*@+!s>?{j03uM3dE zUVR7QL&+y>!|@j&^?2c-O_9($#d5Yzc$79=t43|Fc}$@>I|c5HTOUTpPW`8y#<)DI zl5*m`odQ753BTc6qUPiXWz9ni$nw$LD@Ms@4CD{Vc4sq)p_j!%w~Qk8yJuKT67uFb zC=5a*s?>+5Ov&rme02xzA9;!WgKVCG|6#e05KdEtpE|MWzpWGhFZfXZgYHAZ_)mMK zBw5=d38IAV44S6ZXjd<)*;YRVqFGF)4$(_#3x|`Cct|qHM&tiMA#=g*kTyf_`lR}r zC&T&z{9*iHYET0fm?o`pdf!hwyKQGYJgltX14L42F(feF%?iO_qrsRwQJWLl8!Zg6 z^{=|}vTu?PHMRo_73{Z?>`84s9Y$@B=d08X8-_8Tb?l(z0O#v`;gTZnC14{6)LG|} zs}>Un3OJQ0pzBG$*-b)vZPnBYmHr(=+^SJlE$}7kW>`S*U9$=%O3pBfsGC(uJgB&r z*MnS2#tnQ@7osqqiSXNnAuZm7QEe0Z^8^v4%n_VSzLS=Mgb&HRrgeQ~vn#Uex9?ws zW$0P=uJIVMfT*|O>FGg}l~4C=7*@yVi>`d=4BL7YQCFI7t=7ceL)4X(EC=*7XHr1g z*=S?ZTO|A94O8Cn?s4+B461`>C(>N6@c_JL#gA@~xYyJeE;Ry((EpCfE5Z1H%WhTT z0L#!iwQ_*nbQ1{q@^YQumZ0iyDTJZ-`daiWZ`+2eep3(79#hiJ{h}6BsRl% z5bl1hG!7?E()96+fIdff|M3rtw<*js_9`DB%@MVuay8H5jt{8{yvjbjd##9hh(8W7 z7Y<@lSXE?p4(Y_jkd4QP_gJ$sv2q6ydsIb?Vcb~;iM<|3i-OsVb%4h(Mh)|ANttAX z)0-IQAlrCXZAjACHn7n%@^F~bOB-?%@APK(+Mp-wg9pA^Yqvg-!}=*{e-oJI-m|DK z#MStk{T~YyBQ?%M@Mm$7{y!j{PuLZKA8+!1kADNw`B%M~l&Ou4r;(}Qe>#;EC0Tit z9|g;>Uo=c-pbx#i6Nr%w>E?1gK*V@vOr}8FX|a2WF80W^V~IKOUnzME_-_C|l>Cw~ zo4^im?z5V6b=6w^r9V?wwL1WMM%*H}ISAGo@O6j=NwYr1F>YbO>(keq`F=*mqUl%G zh1+QQU{u^mLBR=cJuxOubj-TjH~k$SRn7x``k5S!TxgyNC2Y<{E(9i-k<1zTxm=jK zc<5Xas?TDi<4XdP3#3)BDcq6Sc$THA-+CDuS%xzlrZ|M#n95?y#`{H0J36f|d$kIx zOII<#Z0MsQ5y(qhxW>h~;I(z31QimHIT1IAW=v1b3hk9Y$?=*6zA)_@85Lm*AF0E5 zXP_)DTUc-CzX}o%^0o=0=goBD9juOKGxUcNEwcj5eQMD1b7WnwJN?MVQdkKR%qTZt z=u}i$c^t$-AN}S7Y*wGpDb|tf)_^agxdzgt4eqe-dxLSWHeL>>3qCCsAi?Q|+*2>l zzIWnc*fxXRPv%)U@A5Lfe}fDMnQ>G!J)$+~35RfUUp>M-X*?te(J(7L?I9;=QtS5D z)NX!6EZ`7eJ~9YpKh6sL|3Q)ctC;cMtcCy5FZ~D_T}%}W%}t#IO$;4eOr8Geu&Om6 zeQ;E9zsWkY*0x}5kuzI1$eILbvx+ukt;^GY=+qXFMYb%Y0c!n{Zi#T0*Hz4omQh6E zM+hQtf+EENoE1(fh0@Z1)Z!c{ASfazRp5HNzIm_Rt&Le*CdPsM{%==V?w>nE-`^1cU+74D}9Z1U?9HVC5;)^TO!+IrLc2Axwo7v`f(VD6ybdwvj~3@69um@>lOS zFjzUXYqSeJ)}VCH1UsVEz#TNUiN*WLJ=UO3oebYX2VO@})j%7QO(Q>Y2e#?K*eN&~ zV>CMIA!H4?ms;2%O-&gc&;}l>&0QjO_Ke`Lu`K~lrQ$G2NpaKRy>h!4 zugWKI*u0~bC#!Fm6aVwuK+3AQ8onsHsZ&k<0ywmqU_HMSxy6)hUyd!Q-iDp8jibh# z=?*d3grBpS%G$o}sj2X#y(C4A!M|&YbkRg1>3kkMMkKAWbFqfcUTB#$=?=A}+Wvw8 zH%%p<&Y7UPQ!|TYZyr|5o6B~!GO^L#`zjKVT^d@5u&1MGrlg_hCdfb*h#dmIMeY@ z-R2m+LX^dMkc&4Q$Pioe0*VNKG-5O0y^3=N{o*3s$Y4gBX=nDJN7O8u_y!)Wq@O35 zJIZcIMk(Mas`L8yZRq;AhFyY#ms(P^Ztfz~W(_Ps*RU~B0)ORe8v^_8&-&3n5X~9J z{WRk#w|+zyLD0+(vvu>5_?lyFKopGMA0D>jYfX5YvgO44ux7Psxe11Z%rUZ1Fv_Ne z!b!_}P$DrbKpIm6uVz;oSV`I;OKX!mC-`WWU*!ke_?G0y&%0n4a*He zRV~(uv0QN|4%Tbb>sRPX-^t=&U})m{Dl+jBoZe@~I8WX}=C(gR4| z>Zc-E`Pva`ze|WZ6P{wu>VrIE>48UY@tQh&w&9^TDE%56xUR@pv}fv*CKs=GY~caA zQ%EjG)W&2@b_NBzGg?{TDLV-7pfCu3M-0n9BG1wln`iZ!A#io_8WO2*sPx6|5_NyZ~4mnU9eaCimhxZ^OvDd?L2}rWP(ZD{eLu{m4#a_y8hT0p9fhba`h0$jh1#nf8(O@Iv=mj~c%eo;z{)JVmN7D=hF|PiUMBCWtyO=j_ z*;XE)3u&W+!KJDqU7|2+Xtqh?IOPHTLV2xy6EAlVGv~G3#7^O@Q_avnW?P!( zHpMjlT7DCTbYhYk+i^K~3tKz|WT=1EDd-H>^hTZ4eMv_X6>THlEmz`A+0*B2M=j(# zjqA(;Y6-gr?f49h>~hD3{iOm#fEElDT}`9Pa#0IP0?ob$-vsmrPAVqM?>ik}@~Ur+ z%b=n->;~ueJuuIQu0)Jdt4SP8&egT=#(X$ZpavawD!V`g(sJTR4l3uZ-x76J zD@?tzEbBi@kA*ixb72`EF!~A~Bmpv?Pm|0{B$Bhtd@+3N4+Ty0*Yt`0?f}sy#hHYh zcKK$TtK_>b>j%FTd=DH!C+t`Zl6aIW{6i(h5#lrq7t-L(dzg2~2dM2HP0;*Cy?a2I z#Llh2pj|*x2Ez7~d$a^A1qjT@TW!9Y^MFC#x3>6>gA?e?2)yA)GF^Iw&g4aJM5Fu1 zqfKPJm7`X1F7>*Ab}N^*U?mL1wwx@bZeqr@&|(R09s;l0vyy0l?gE zY%6?+o(IFQs&eohFMwcUa_p~@%m^w>KQ-o)y5dPC=H80JjkUCkE$#Wb)?#yWnYFdz z%5g!>*YN6!BykxQsEu$sAs?sA&3I|i<6txrL8)ZG3L~JqLj6UC7yEBOZt44?`Z5_~ zMtQKhHRRS@mi-;0<_AbwxP(;g{CD^Mn@(Sm^DQdP>2YLq&1ELFnfQz5`3kD%8I#T9 zB!>!g*%K;Uz0J$MuCMfQC8kP-2L!Te)U7v8mdB<=)_d>tn^iFSZnyncL``gaPasxM zybs2#9ZuJ<69*8>*gu9fFzW`=Lo>N2+{*In^1mhslpiFLd)0zT3$Ul>mCXpT2*WDHY_$H<3$q#OA@+H034`$L+x z@4I|!!c-aTfo3GgV_BB1xK)4 zBBm0s7HB<(uZVeb*KMJTgwi)mWCaJkA(SwSpv6nR2tFCLiJhR3Kd}T(Oqg)cIjhlH zwj1LUZcOg%hdrIB=u?OB z1Wvg}%ybUFUsz={&}zU#iTA}>R9kQovcf;j%;>)?SD-dZ`1l`q1IHUGm_R51fG+g^ znN0cDT`tE@>|~=TQ7Wr+|35IQEfXh6Mzxn0d23CL~AEbb*v5jJ?_WfjYDD&#Jc-EZFqcVw(Y|} zUn@TYAXoRu@W5cMQIyh=tZ<>xaWXsFGB4> zs4^{FDt($yn4(m7O^Re)Gyz;HTQO4PLI`bi>+KxdVFV8yy1-AfG^kj#(4NKbKN29T z6TpxVKQYDO&;8#*`G0-e|3)bPukqzSUvaUK_B^dFkmVQmL z8UF0|dM-QGxKLEYAbPjwM*GRO_qOkh-$QR&4S+^a>lefxB0b;PC=rhmJUm_sf8PE6 zf?rEs0)JkLb6ygE-pc{>Yr*GxBL3BD!=>MF)TqyECcOU30rqPN`p$hJ=1!{ePC{od zb(;T7)TpVqQY;T)Z_mvzyw7VszV2(n<=<84$)#}X_&)jfJLJz~-MSCvPn~P8i7@5F z^)s-|pwf#wXr6;%OnxuYFY~X{v@2&lo||E4pw^hmHDlvcEbbQCG?`&m=EUhDyji94 zGfCMc(B$wM>th3{DYIsqn4=9?-K<#5Gs%EwtA~xns^ekJfK;nSpqjgY5711pxC&(} zisJK#*>qE~ry;R`;h@mmW|gf>1!YdzKHRl-Zxw@wvWihzVoZ2b=Zt7Wt^9IVLe?b6 z6l@L6DI!PTvv51h4$(BN2RCOmCR8j2SywNXjOMl?KbnkLRciBjo3iyxhXj#7b^St~ ze`2!QKT;pl6PS(He`j+ULb|9_xs|l7BFmjiS~m))*Bw_}LwfOB42_$OTPUp*v_BS^ z#a1R!%8pEjmzP5>$xQSxpSHx3oaw)C$Dl?)q?j0$lxS`8v%qJ78h9CpAhf|}iD~um z7n#b?ieWpFX~c{cW>1C58Pv^}AuoljK3kJhzTk+oJF#A0;v@_7}JnbeRB8a?ZQqRVixU~jF2v@NZ8SkcjRtt826MpfM< znN`c7UdmZ)%(R4^^+YrjV{VB8+A|o(Y@B%v#If$l{RK-<*94@@)F~IYkP%3xJ%JwP zeQ3F+Bq$S0AG|>eIVJf@a3H3y+i%yQr;qca9vADdmd>XuA8kga@zCNqvq`vnAf=sB zp&cwz?!QiCLU0^ucXeHg@SGe(LSrSPz#lKFsygH*v_)~pT$^R1A}C%H@7-o%0u}EH zrG>P!s%gl47#Fg=;LzCE6ALwKEg99cfbN?NN=QT)m(Xwhm4zj9m7ks3`~v{Lc94>t z{iAhVIpVEH;m^pbnUQtvS@u3qpR7#SnhIgMa;QjaW=n%vZ!Dibh3)O%!xTqSOrSm4 z?9wWt%m9(Qd`zXP-%nIsv`XY4`ox?A<2b@MZXZ{b3Gm=6MG47apq<~iA zVw+XDo{kzr05y<%G6-kNJd%}FpUmOBj!qL@cNbMoTM0o#aG?Iax{Aw_3k~b>YX=riED_SfTp!gc#vMnuMU9^BGCB%@5E_;>08er0)p7Jf%w!$qoN!M#N z%XbNSukx+;r=TEQRaq9ANx>%oYDxTXd@YmEaGJLX?BUT%crSfi`mMWS_lT@1@9&@!& zv$pSGXHNk_Gbpj@X&n!BlM;_H5OrV*!3wUR(N+0ZS4aQ0Cqzw~;n8n6DC^6g6cCBV zSYS%r%><%neoI;n4!;rBXtBj5#-9hh?2|Rcy}+paWY0Jk^-!&C+qFFxu^vAULp+KF z@c{C1586SxHK6|8ek#sNrV;PZMC@^W%YLc>ss|}DQPQQW=C2yhQq@k{v#ACw0Uz?T zuSYC zzA1+6Z}CIR{bD)r&4;+CC&z2fPyQsjY|>mNmii^i_Uln)dg)0gxYp;vm^AAmd#clh z4yg0aFKm$DMWO_jm%e|CJLuh)*IB^N`<%p35|+m=wJ~n*I>&obum~6EJsW<1gH6Xp43!u)wtr z5N9`zhSgc+uj8O9ZJ+meeh!m&e{U0}Ds0nus0 z%Td7nkG5k1I7ENOgIB@#y@s#Xc;t=NwKVQ9D8IVuLi+0YwCMtRCzt7*-n@OEa^Wvg zf1gwGK~j;NhZEFTy?lCg$~fz`9Qka&9!j+b$VRYQrOsMLOM8?r%}Bbi6D&XJ8J$G^ zxkf^saQKbXfmm}ZMF1zehO#$|-u>C^=xSG$LuVddWwN4Hso+EBDo7l$frv#gaiQGY zY0RdS$ycVZqB6t%DEEwGt15TZ zg*a#Wsy>s>6XI0E87wYg8(gsv8_JA9+9#0$-`Ss8obU3GxuqD?J<_pLzRpqqZGv7DT3(a)GPy`g>6d@JiEsLXA~c`91?pdKfZE{{*Tj-xALA6aT> zW@Y^^e-O2XiJ|wx^jUt3(g?O%3~9|pPZfX;7N8auz#bHAk!=VI4WgKhq^8pNg#5Z5 z7G{v_!uGAQOTF@mZKk`KfOu)YoON?!#HpMRqE=fw4fxC?Dv4vgk4L^nZjm58ZVK|u z^ftZ2poE^$Z60Xs-`b-CImHQNLc+bCuK7-zyXwa&WQ(9$X2yGA!ZUmfm(?I|L5oX$ zEa`J78gm#jr06vv(TP?~!-Zr&!5b;;F*L zYvFrr&^1>lh2I{1#%sCY<%8O*Tm^rN+veY&Df`R+kF4&jM2<5#Bmh7o+P~X@vHtJ& zyZ=PxE14Ra{8v&pN8Qs0c@*_q&05>K9*m~$PEY{B$YfkY=LmwXoK{4E2zWSDU_O~{ zeqE2qa@cA(p@nn3#4@o&+)F4kG?}D`)|Su!Fke;z-Xdw+)~of*_rz}Ddr5!ivS!)Z zGDPyb&dlw&`^NVnHshr9`(^b20Bx`LCvXD})Al*-uYO|jPJ_Q0@m{@T!?zpoUb}Q- z^TyBG+8fMilkWTQZNtV#xD|Z2LQY~WxrC}bJb6-7 z>Z0gyYN)Xzdx^7Yl}K_)BQl+vgoPA2W!|!(K~ZX(02cXqaeg8_Vh#0Oku^!Vkwu%{ z)Fcp*X-GAd$rDcY7!ax>G4|0avxz?cFr)Y#(3FGv3^c|DWXr_MhFViI&Sp7TwNkR$ zOhTC%M z8ADZB#8FCUeBuH+1Gj;V;!0wYhRI4v%07W5#8*2i(8RmEMtq#=-3qSyhk!)r;wwTp z5TyrR4@PQ#nOZOb&Rt7d8T~7hwUyG!at)yXnBwhQNK4)6InrHY8T{qQf{l0eEY&RZ zS65oy#t5W{q`$znYJ8(4+NC5(i`-0+>>Sn>hE~PoiltL-x&l;uK?XTa2`D%9{#5wz zR;qQ1jnwvJK$LU3O_0b^;1Rgi^PCe6G3A+HiW9@CXU+zoZN9}kPw3rTnoOoPa0o8< z5s4t%ytt?C>*(?bm2vC=%92%KyAorr3F3tV1-!MAorvTyF{SMKpb~Y%NG1Z1_;D7- zx|gcHbeP=mGucr#=5q?_vvWqcXovp9d@b9IYKj9dC)7|eGH|>_?cF{lqWr&Bci>bN405z3S{OYFUE${V^7eHQ~D8Aon0M&{Z{fjUJw0#^{QOC{yk7->6n0A z0XE&yFuceo0zcB7@#j!fUgRsL-*o;Ks+)m>a#gGT5dEz*)!zmxBuCQah&DKF(NCz} z=Y4jf++q8(He**LbJdpo-}V^i#^s6bMQhRbR_D|dmIqXJj8TlgPo2o{2`EXY=AZ9> zh41XW5TRU}H1aEef<`mJSzKC;T{uOKe0ytBWQ;~Vy^J6eeTViU zC~={VRdC@Z>yX~0 z0z>74gsRZSvrLjGc(HbQD2f2mM|fb?Dv;_VUu(ofyrO^8?yG)=4GN)eu~hb3O{l~g z3vL-~`8bo;h#rh=?MrB!5S#1W7n4^^9eFir$0;m{ZyX)#@$EtDKfSw>8z5cDs(B2s zNY#$% zs;)d9+S57l-H$y?N|fZx%WHMm6YAi=$&WR63T~$HQ1n&Fz}utqyV4R7nPx*`}CtHr5eL6&|dCqg7OXNMM8ww7>^9WG_WiGk@AuVd+}XNiBDS$U9Q$ zYGkU^U{9MIUqcB4MNa`;CY6a~9tBRdhwjjtxFBOUS18-%FwMa8&nCE3aV+(p_7su5l@ga$B;F z`wk$RndARv zwy6PxXX@8G^KMIs+<@~U!iGh90ql%mSH^JxWw2~2#i@jY%c;#Nd9o-l<2PlTu3u-Q{jjOyIM^aIbKx@8+8Bjy%>k?6JJ z7Q7XMN>lerQ+5QdfDR%u(gDBv@K9ic!S8XFHF*0e!^K;1n@T>i8&0@>=K(`f15L!) zK--^zfI8ONnsQ8+hp?%rgChw{=sB)!W&ME%8V4G%CJoSI3pE6bEZPK7ux3LGAQ?({ zrRF&Afz|Rc4aL2SM0>{X-{qn#sl+@N0=H`r_KldE!t*>a)G#3ahzm$IZg~9)hzlQ` zxgraGGS^&DCCV0ZIAX>toGjS`(KQPecLa!djqGU{0}nVpxhIs*^uO00i0JORf`6Zw zx_IjKGtE?jr2(v2m*_)sM{88qX}rzG6ron4n6R^dXh&IaM?E(P-#6FT$r+9Td7`j} zmJ}P!vJke8%(z(rC;>D0z;w_Z>|9|_2BFnqz0+h zRHR_1L!vOJnwoCd%&5jX9nZKbCxXrrA6Qdhnq`DiAA*(*#mptN21;Zz8c|xn+O__f zzwinbZC5Cdlva#IiM40Mec+M3FrPmU&R?Fnk!{J_U$FNtU6%tkWykped}4BL8hAMK z!9AkUs4)v;61IxJ>|ZJMOSCy^SBSfDgZQVS@HJd$5e20@TA%u>n{9S?HH67;2bh38dk zjrD`DZFcvGo9G_;;yy_kW2%TbuN0%5|+E_bbZtb00!E~W$*?hm?*45JP-pjuk+-hg+z zOr(|k9d=%TI2T4OtAnKFt&u7Gz4P>bI)1k984-x1{lVdEH#?tbSj5rnMW`{JCUE`A zr(dMp1nnDL`BD$0zxD8yEs`co%v}aRC}GjVO;-a1O|fghSt|Cx_P?&M`Q$$uepw%d zYmyRwOb7=Wa)?I^huUJtWN0joE{8&nT=yV7IBPWNcykH!!5*JQSldC>-kI?1yIzr& zUB#BVhsBJBp$WQDDWl(LqOH%`nnw|&8(7!A%3bXS8r+DpOQXCBZ;j6d58pc-0S>15rjWX*^h z@RWR|wlyQ`XSGW+^{zTA>*ux4;~oxsf~16<(a{|5E57IKuj=RC?)R(3S(JGYz_M`F z=}#jqQN)Rxc(}Y2!(VWSm`+5t@qb8|dCv+!UFL-2=J%O-iLAqTvBXT>R)y&visR*> zw-*PTL_%lBM_39^G_iA&c0IXn1G50v1(>I>cCUq5u@etu9KPeS`WUHcqD$%r7wUWu zY_FYDy+nf?n-6q|zFEEdh3X5zf!O#aj{QkGm}j~#`^Q7t%)aY{ zf&R5B>hq}3pP9Hl&{CS>qA!t&w$!ien{DpEmyBjEUZPyBQJe|*$1>=puS33 z?N4u~Q_U}JeY*NO2(RDOuoG59%C33-zUHF>;(go8T~_%FCx~tcU>{ZK-MZ^b$SrJ@ zBaqr}3*!<#4qVC+{Wa2!m*$ax z=${DW=G6>6ES2M=?W$3RN+{Oq`$Tk=cB!UYP<2DEt>kXS+{9L_Oq3>BIBQ#JBS=sz z9Yz^3mGo=`a^%FjYOWUL%NpP>jjONWtwqApB}oxEIC35i#OP7QN)SW?`?fq?;6s)P z9;Ck9%7%;Ck20}OB3M-D;wl7523XW5vife8!g?a$MlA#o+NL7#n*_bV&t=2#yJ7 zTFzwgXU$v%Haig1I+q&z4dzUPByT<5juY~h$sYOFw32b1GYYcBh1|L_>*!z>mm_^D zTZCJJidgSy<00547)A2Wvja$mHaT+Zv7O|ETnhY=O&6-8KTbqXilx#bQ~A5YkOXOu zGGduF8&&@7Y)RDF9Gqf$D+L=C!-&>p9q>XU?%#b;t%^oaEck~=pqHNu@Tsk!inStmHj>GT zWM&S|v1yw(kzB4Y)<#ejO_+j7|)ItCvx-dC3 zgJG5e1+vWLLRQS{LZI}oZ9-oKU!5nqA)jH*!#$22LWX}np>(U~%wVWoh)f)TF{v-| zg9k(}R94A+)S2?}X4om7sCCNjGUx8>Ifc-yRymNVQ}HHL&BGLWHNuNb**H7K47Bh@ zR9ta#Ros}=Iy?Gel+qoe+3irl_^NC$jwz#eN=MrtSkeO|cTahPEUj9TzzY>^6=KdD zFM0Wk;jw*fP8$lWgE4XTBV6{)Y801Y^0~XoU+gy#Mx=kb0_c7?7*cu)dN$K4dddyJ$eUVJqiY*JQ@b1Jt~BrrN`tk zQhS1PneKz>nbLXXirjChWbQ{R+OT`f=-fq>R?|Y20B(}dL$~V$s*a9D)R@r>OVvBo z;gP)7O>!4PV8zpG2BZYd`qK9{?xWC~(Y>h0Blv}(^Oj4F42wP-CvQQAaAZaePy?r; z_CJiL5%hnM`_S3ArkUQAjA?w{E9B^75_W<~#{Nq^kLp0e3>xSk4tdm~ikC zAp2r?`#k|0PXrFlWGITVF~`SilpNjNq^~5m_1z0rnND`R#>SqlQGe17aS+4Q#g_Y% zc~u^c^;MO4?6G{ecQhip%s4BsyKvz}jMCiz4l93>O{x1f(6+lrF&Z_t@OTp58vt}% zDA0oM)x0Cq4RnuoTcsf)R*qBle7qy@sh4!jZ(y2^H6XgFRs{UFHd;6%08nOx`JD4W zn|=12bX>~&(ps!03owtE%%v<{-CpOBBd3JSuUopfJ~z;KqJ<}@Nh{15Cx@pif!}7j z3RKd@TDysk`n<+km9_a&Y3!xo^nA#UTxBkvd)YFg(=%D3e+lB3BVM#fJ&7~E^R`BZ z{qj95R+Sc$#izZyEpvniN2?qiJVb78t#tsPZDI#34riQzLd#~Dya#Z!d_*NO*+Q0z z?_{aXJ8U_=d~e$aoH_G9CoTDJHZJkxi`-iF^OFM4>nGzc?yWJkkUjlk906MvMP-42 zQ85mqA!EhQY{F~N4@`Nn5BH?MRhj1=C}+P6opo9K+fIHZ5u)15M3hA!0A;5Z)xq=1VFYLt22=*B^$l! z^Y0}ywp<3F{S6wC(a*8)3WSNgDnTC z{PaDr<6^I*+2t;TOqfDU$a*3XLQF~{5Om~)p&$_0;td@G$ibdKYY7%DNy-!V=LwDT z$20;0g}fjRAG;49>?-Gtb@J1+M`YkVnSq3{JDf5s9jfF|VPM_x>w&l)hcRups2ks7 zV4sii{HU&XX=v5+!0q}u6eDL1J=Cr2g*GW=r#eez;zntZZmCu0tvlze_VZ0w zMPAb_S4U4LcvtZ{R`q71E~#PHPs!QK3}_}~i`yKIY;oglh`CO!P{-Ijf1(|N{1w+D zd=X+ttYiD>;X55xfrAq1j(d7C6L@z<_NAZgul_|)wcYmaPjvnz%mz6xCb7Qv_9TeAR%zR9qzrG7taEBOOR4k|F;pzcxdqImPb>4F6E z29xba33{*DwaHTFmQPmC<47~oB8193+IXbdwTfew9Qscy+Br`fjy~E4Un2%|NA%tG z9CAna=^ZVrL&@f7ibbp1B0#Ruy~Ny|M7&+0{3$_4h8>gQuK*v^+Vqpnm`2DKOGuvu zAbIX$p0XXHg+Zy#!w_zW{V$UuE*XY8{1R;DFl?5$n5A0QCo@m;IHkpV)?*-xJfvx% zgLIZ09o`@*Pj=P)AYZ1p6S!P?U_A+I4`kcDl5fd&Di_maw)QFeVm1N`)S-B}Dd^gq z&U>DFx|Xw4&TOM3Z5wH-Hi~_jZs`!&t0W#*I*Cz^ESNTNx?7QZR5%YEGB8V3iF@i< zg_}&*Z|U6GP{bKgA;i_8R4pTEx(0Vm=;v6^q!CiH`R)@Airkucg@*ay8C7NJzsiUWtFn3~#VkE%b()lShb*~dvxUb* z;OpB6eUMcq*n=H-*c_70obfcdf~$GsxVa=ZvkP6bOW}E=<6{-l-4&7<>T7Rh%WLEZ zKSDf%E{44?>^jBkw!Le-^sK9n(KB$X=Gsf*eap<})r$0y^rO9cgQZ@SsE;QV7SoPv zPWe)!wpp)7ksWU#;lP(eFIa}g2|S?aAEh%c3YC`Hro<{_?Vx-M^vp&AjoWBhu|DlM z_EfD@yJKKDh5bJRu$&w#Pgc>DP_)lhoAnAGFZyMt?nnuUM47{3g4An^M#Vy6{792?k$g zHpDy`@VX#ldl0GWSB{pXCM&$@8jjNitpiq5LRTxN76-hn4|st+`O$dcz#)-4Mv*#3 zD+p{fMd3>eLApInHp05Z`o^D!c^=%$x>L6c{JEn3mAWhX3J`wyPTftx|DPj`|J~>Q zf7qF=;$&#&Y-s%dc%VvDw3Shr&;SJ`sx}2^9pRXL=YeBRk7_2gEw0?DsfOw!w;_`5 zEUa^zHCq4V}YnpB``twQI;^*oBxz?rRL8HZ ztSdRoMan#y1=I0Ya4Egljq&)F%%y%@x!@^S1P5iFug*@i2#)smrQ333yu$cNFjt)R zf<2F<1I2mk2og4&AJA~3GH7cFP~F|LeAHDtZFw9Mhx8$sOeN>Vtd%sdX%HZ#^R^)u z6)>rsiKrWKO%{;nOMlwx&wWAaVx;|h6bTOHRi`ye*mae!u2+R9vfmLTeH4g5{2eWJ3E~h{aV{A=$%pAy=b_ zaANwZ%^~2c?J-+lz-4fGE}`j!8G^82^5g%U$|JCTF% z+f(O$Q9ba*f5*8~fs`k?zTN>HW5M@9@@x%4B%){p)e)aFB1Re7%3u%xRK+XmpiXci z7`K-I4X6n7J!82TFr(5KZ|{b6R&0BPYp-3q$B@8JI+49K9S>23dT@_euE`S(gI z)_+eJ{(EyC>3{$DhoVhN=)bT}lD0gmAlm5HB9H60OTw#2b!p3>B9H+KwI7Rd4yj;y zCkT731kT2tqia#@R}$hY*loW$3V-sgOc$MmB{lo$)Ssr~_nE1$?2pH{QF$P6-Z*2Z zQ%>eFYMQF*Y^^)14E5d+N{k{(MiD(I>9I7pewWfIB(0_@h{2Q6`e>t~UirYYTmTwiku0+=|%;;}3;DjEBtt8{yH>}L2$<_)$a?_9xaOpXp!S6jv9p3wc( z+NCUMZg;$%B#vhegAl_?voYH%obOEcghHx=G>zvhwXWS6?OWn9NeqNeN+ZJ zB$0-78!^$jk7|kGv-c`$^zy#vxb~AeXAn!G=UUma*5?dZwCar<3WsVYk0^KgP#%eH zMqf=tA%sNf0L!?%joNfQV^a#c_$FVK>StJb(FNn7{4Vk`)Q)4+bFho@HRzIVjzr}s zcP{Z0?_v9~imC*(G=k#=Yq0#NBx9bTtdK@P zFj=s`J}cZp?8a)Efy2d=qmt4qc!?>EwjxDwjF6~Qm)XK9#HPfb5v;Y!kMvflw{^+I zr!uXQj}3C$%!%CRjV#>t2J*`BJHF%GH$UFv509&kUmq9d&k+_Zx=u-1jdlwbjc83& zy}qI5WxA}>`%OMvgCMgVC!QQVea1CedIt?Nq6`fqn!VY^<7u95xeRsfr>dQOO?9`2 z=RTg(qL|jlA5$2i=+VQ_rCCC~t1G5WW1qCN^UGU^nAIX)(whvar z6R_vR6Cr{mMjsCHb0u$jTFSq-gYT5|s>=_voQm)=GjRcOXG}l#TVVgD5D3yz~FXCp$GhC`Z9Y$053-e975ctEJAIUEoK=rVgS-=NE_mhqg7@yZ>g--!8J-zprDCH zRb54;yp@uvu5buOLrzZL2d$Y|E{0YH;~CmlanzZ*fX{5VDcV+RsB1UHXJ`U>TtXwf zISkw(w3({iws9aBn}H4I@vLDTL@uUZ9*%sKkFS}(1O?$Gx4>7vIl{-SfM-q{o zn-ROGc>zX@i*t5*m5Q*W2nLKIH0ncKWgf7c!HT4FC}Q*gwq2Yj2taidmcPoK$wW7K zq7MTjPfj)*Iu>nTauQJsjvN`YRcF#kYaUL-0KTv}x6!2C?)9js7jF0eW5>#u5rM4> zeRYHmZu%?1mA5%H^QCb{Sg`VT4lCzw&RB(JO#5V5xT<_cxXLmnj5s*n#$yNq^g7T9 zyv8>f@HFRUbvp+&%&Q^`jCfhd;AO#3<2=v7`ylJJDa_ZrElh!J5zPKmG0Fyj=;z`^ zY#m)N?IMOM9aoe;ASzWRA?X%0S04MqMm#1@O$dy(C4hjChKEecMa=0W;=@=Q%v%_F z?zM3_*x7di1)G&KUwIAXRTIG>ix83+CwgOGkd)nZC>UqI!5+rcxiV<{3Ua}-;F3n?} zn#6e5sjpiNTXQn$E+CN*%ckUO(n+rAcyuy1u6ncU+b`fBP#t(!=WW$Idw|<*dZ-W`yr)$w4Sm?6 zpSqfLW|(3^(N%VY3RFqlMC6P5Vu3#yQL2o6!D8WgA{gz2?HltMM0uDD%;j{lE}YKm zcku(kwC4s|oK+N870ZAC8A)u@PepVSnP%GCw$Hz@svGnI+AzodSmlqYO<$N8*n#vU z5C1N6(t)&D0bWy{69?|%ex7j-p$uZX`ZjU@i#G0Q%s``b-nPQbm~v3-pEKy6ef%BPTychEr@mZtDlVR@E<^Gq;2kfY0o& za~@VxQ$@K>-{FdR*NVsj*?k#iMpvIK+=Odda{zW!pMx6T^G9%xs5;Hy0rND!#B7pP z)clkmFzgtsuDJ)XeE9SmM(vTRCG2R}-gvKD$XYWsZ__`kvQLlZPN%*D@24-XdT4)3 zcC5E4U8X$vN^5&@qQ0|KN7sQdh1{>GYu&r8Iwqz!U75-HUFAJ>iz?{xZtB6`oh5WD zx>3LHTaf_7y9Xstq*n1KvMyh2T7vdOQ!9%4_C@RVgfZ8I#n;3!O%~JFiqXAsnIA+b zZzP=(ZS&99i|!`~xl{AH-?Je3Q{hhWABIoRABlh>edriJy3~cR!bm^kv_4TJy)>D3 z+?^!7mnDNT{T|aI zoz({^8_mWIDhor`KX*x}i}(+^&3dBrn)JFZUi%wM!wk>av~#Zo;YIuucJ_^5UPyf2 zsmk6aFRvK%mp%?#+kkI6_WeCUAvdXSp42_w(6&CGxOy@a@rKq6UmFiy*N7J*h3aDF zg)-kDH$GAAzCzyoj~P8fE;|RDrdj0bNRF444S#u2n<(L9WRt}6O9`uui7SfOEb1dU zb&DS$WaUH(hm*Pa@;W#`uxKGTXqwf=`Wadoc4-WHj^WmBx=F|(Z;&-h}R9R zmgDbr+GsDX6|Xr7ejo(t=+?W--Y=?&n}USe2$Y!PmUBu2`9- zn91Ml1Kwo(RB(qw>m>Gj5)&MhcgLo#|J+d07`}WPI^I96j9>B-)>DU0vpH6!A#=ls zjt=uYz=IB4BwQ`RON(OQX?Fk=hG~0*G|rMdYem!MYGgMsV?6f`O@ipir|pgOPWyMX|X1CENQKZG!wBsu^-w)`+DB zO}kvoresuv#TJ9dqRXN{ZC)y;Fy>W+2R>YvmDFvKsv&G9T#lQj^Ca-3RJt&%m+ooN zb>14)Bu_RTlwA;k4vBPn6`jfW$G#bDpv`cFBm?Kj^r|YevOv7ur%z+$!R*}b>N8~E z;Ix%M1P6%`fUdS%+S-G;nmkL_Fpoyb{Vz_3Z>^c_)2=>ac@rZ4h#?6luW(5__sk~T zzKf#!U)jJ<*}O>b)0m?xnTXb>c2BI@0dBr%7OoVV_ zH<7>>nMh;x?l-@SdbO+4yjbn`c|>x?t`dE*p@>!dd{pL8I}Hhg(p?sa?ZDr5@aG$9 z$5yZ&>YMpzR%8xq=Mlez?VO$Ny9M-plzM=A6XU3f#mI=|4hIPNk6MhD%upbEW7cQ| zTsc+lR&=TSDYWU^w71<%m$wp8-7QPnNIML4{P@F_xfpVo7lYe^eihK2o!09aS$V`( z!tetsosySD?P-^(16e;|)KZE5?6Zre_XjQJCAop=NAQ>4tv7ODPQBS8d{W2O^UA$r z{-Y2!5Z@y4_^y6U(f?EbNAVvDVHtaKbtlXJ+pZs@^xg6)B7Mrax51t}r8p&I1x&bg z*d!>SAr+93lg2P0RqlqAY^JhJ+BIzstN(b%AHhP+l?96?{{;F9_!7%$q8o-PM>LsE5$T4bB7TluUGfS^Y=%Y? z4&QUVB{wweE%le}6%U5^V`OQQ@hf{9Zk&;W!3q}nU8(jRUzf?!fYtIn=su-w@Xt#u z&pM@>OTqyRkgXmSmu{RA7@V-A2{{d({5P^VqxhE1jU)2sdg2^B{Hz)B;p;Hd&{cZA z;U1?BGw|+I%ubgo#T0VjiB!y2(esFDZ5#Vm#z{!E`53{_UTey|D@bbY`I16>d}?y0 z@;#zd6*dgjVy>j!jxE8z_VEsnVae*0G0b zfkEM{g@t4{XUK4TYlpm2C%pD&-QP)*2IO@bujtzWgnML@4KG5WY+9o&HHOguJ6zvO ztXnZ3QJe(vHn3NgcFD%DYICG<(1&w#gICO|oUJ*Cps;L`Abe6AsZp3rr@d z&QJj9yfj14qfg`8PYGN?-b~mw##MogIGX|TL>5J=7J?;8(TgwrpJ?dm zU5$N=VmRPi!UEZ&FxX5)2)~4#tSE`DN?4SH&Q!L9c0=OkOZ|h>7j^`b6bj-O$x zk;h0EErvmE(}{vbjf^5RBnI=pMsL-ji=udo+GTa2Sl6&dNVb$@{gl%Ca>{4Xt`ge- z{d@~x3lv!-*9p@_E?Wy!T3ZI$tQB@Get}9mm2)?_dt$q@x;4oB=gl#+kL|a({9Z{v z=q4mSLH_YbXzm0e^nUk-kpD-x$^Yhr4FBPg_#Yjfu)VE=q07JeyqfPmPel{kZ+5E3 zy>xLMu~aVYGzS=_%Z0mH8Bmqn3R2QH2STSD)d#cTcGU`cf9Rq`NuMdYNB%Zw5|h=^ z=$No<%W5Hz7I#7HA5H%1EE^pWfg>v;vpOXE6!t^WXQrdg+^&pG6Lo!x?>kv(<_&Ot z@0acD|M%Mc2kJa~Cr&ZX1)0Wysbco;c(IoUk|6u-B+a+$lCPQRg%}!uekT5Pa z0!%+&sI zB41CT&Md*U)~_&`K}2&B{SGtIqqppBC$&8v#a!fJZ!i1Nn5>alWVgd^GIbH4$x6X; z=&8YCnt~m|*nu4C`nP9w!>+(QYg?l```eU@Kd9}r=BrO zK4<~17*>$2fmBPoJhin^Eu)G`vts~{+pnt$k#JcCV5@7U()Mr$wqy{2BMq~kjJ-=+ z_Z?wZN-uEuL>uEc4+tJ1eTQOa=2JC3HrFT8aV5=YsX>T*MPmF2tKCKFY{nNVFnG=tqH3_xtLL5382&ov}d-QoVvq=~+E!QA9O8nbsJ+aM`;d)2-n`gBN#r4Yy8A$@&OgiazKV00_5xQXJj<$=Ixb1s` zUYVJfSlO(iQdtX{*^&mGQW*xbOQeEInVZx}av@U=K#(ZMO(83e{?sMo$aQU)?5f}q z&{?>Mc_NzgH1C%)g9K&uwaPVA-V3r*u6cBD$m~gRlQ?-(a%4WCh9GEr_mLDr?1QgA z+)*`zhiGIas=27Zs;U9yJ$LWGE&@w00=q5>dq2;Px?jB80bC^PF+2CV7xNQx=&JBM zM4=Ff9BPu4c0D}Tj-g2Qfp+aUwdYTCDy*$@aBpyr&vWSu=-p7y^O!X+X$glNFU$rB zo85bGv-ey$w7=*scQ|plLO^Gzk&3+IY7=-m4^X~xfJN&tH=zeZsOM2&ljMNqs9#)w zGa3%>fLPFGStgHC33=;g^^ut~XGf|FVg(k5gu?w4>SCL%vA!h^WI z0w_f9$HzKjnp20l*Y?QArMKc%0oU;Cj5P$fY$N$LL75 zFSZCG-plOWP5pMGGx!U9bwaI}w#Ur%z6rQ!hd0D{7w(oZCq-G2G&csrigd&~i9n2C zFM|QTXoYct%jE&R{TTEG?%Z?d$g+6#qr;YEE-P}-_CpZ_baopQJ>10d#7q3=cj@Gh z7Bsx2lBZq8580@25FE1(8CKOsm6th9NbU&Pd10rh^vj_twhHbkR>GB^q`ni1A2`L8 z7TVy43HOK)^ z$%8g24qEt}-EG>8da#mw%o;A{GodHD=(C|?3X`HZu}_5-bo;SSXE~r zXK1mhpavSob5W5`)$|9^n`9SBQF{ zJnEbuN9_L+J=Ym0IPfkUaWvo6ZQcS;7NI?0iTfoxo6pNmn*=9W`&YQTj!*`TfHV$- z&q_k@^rB%tc$oe1R@mx*y4x@&&esS}%`*o+iW(Ms{vj~uLh)8%W}P7?hxoKguQOm& z9vW@Z#{>AM!~&9dz|&{v?MxD&ot3O*xhzSK`Y=zOeyb|HIALkeV7Mok4B2I^1WeW8C+H5BXFq@*889&op((rf zcA%w?;`R)hx!QlGPFlHBJwAzE3?MfmZqc=qh}aeR&;vfA%0Ps4Vl?3YY6Mtm;)h_( zDE$_wIcaNPODy3oF7X`@iV0q!*D#__T_cme3?w_McfspH_GI$`Md=V{k7^14cj=P=9Yvco*nORlz6)VzGda0%{P1(3Zzth46}PnV^Y{9qf~n zIY}t#TBh0rM#Io6-Q)?TmXl;Z_-5{X36o00&y(b2h=#iI3!i| zP1s;OX8qzq`ru#EcE62QQ=K7!vNmF}GP^`Gbc}42Tbn>>PO>f9wBfll=Q)8|7bU6A zl55;#9rijwIWMWIj_aLzTA;D{(KGS1$gvtw9W8ERikFabYf{2oNY4;55ZEnB&soug zi@qzeOy?vRIx3`!0{#+~gi7#hkU;Jg%8q$6vY^sy=GWg3#2Ve zK;m2tGw%~Qrt#&WjR@S^P6<9Y*SgK|UQ)z95ktu?3@ti@enu4pTO3a7kD-;3|~^Z1ApgKMWK9c83rx3maF-{t(qEzJGiQ39g3v9W4- z*TjNmGP~dO!0Pl=O-o2X`G?xx@*f^8{;yD1l`99pl$DmxA|lnvq?$<7EC%~7x208a zKohes2lS9g%XNYq%(fwa=~@@DJ+(f-y2N^x2+qU$^PlsA6>Ie4wr`DeFzWx`Hc7?O z)>QR-aPVJ(=n%C_=VW#CuT5V2u336RXitkR_5dn&=s#fE->95!!A1!~SVEGWb&kF3 z0zibg*>)vO#YQbxlv?TPvbN^7rl5`2T*%TacN(sY&;7B-zXNIhegOHg7tP{_;s1Pf z1P%`}xaB;{_v}9Nchh-h@WNjRq1Or=7sZ{HK{Q4hznZF{aH~(w(`Q zR(AZtKKqfw!T=SnA+O=SBrn0|Eub{^;o5{jIR`{eDD4#LN58YH@%B zlnpi=2KBN_0e9BSGH=XhD{Qk&Tns^0wEV1m41OMeX1dHw^mr2T&#{rD!O_Ig3j0_n zo6Yhv+@A`j-5N7xRg1^4*u-+#viw;D6D6?bp|LrDbJI#lunW~BE6x1VrSbr-JZwqx&O?)M zidKaRt$JAQCcjG6YexY#|1}wvO7XP|x$92{TU@$A{9Ahp!FS#Y2jbzLSuVdk!i7?aT5aM}Ik_?@q z@C!@k_}dX*P5f!=I(|yWfz^;rLl@(+5E(2@55tYHEk+0?G%?IVLtdzr0U<2&`muqp z>u9n7`&}C>`EMB-iAVD3uu+@lZyH^LXI+}a+ZuhI%-V&bhIkqb$<{60>nPX_XUGZD zgj6ZLpNCD#YDR_EU%auZZG`6Q97IU~Rm1xTuP?}88DkE}Np=Jf)XUXawYm5%@(TgT zt3UPj`@Oz{y+Qsw92c@zl1!%By+A93voD12A9(JU))1%t|j6fDHC!VliLoP zK;Ac=;Dc?}I({L)xmP~$6Q;bO+g1l|8T7y>s^XVn#?>YPS>4^WtE9> zZ)i~_M{}7vA@l6tItZMG{ou7Oe=X8=il{Z`uFZWA1pJg%`!#0%7v)jk?0qo$1&9sN z5Czc~0`jlpUl=9MVA1HpU`}eRK`vt6xWUK-itvR&pXb}WVcaSuEO5Vdt-pa&neUe> zK6F$^!HKW80ejXF(UQHzq+ClWq+G0HMB0rr1#Xe$+GB^?`~r&RBvw0O=BW3>k<+FywSy~Rc66;;hzKk@&ZpQ*PG>GXWVk*wT2&v~|k zO#f43EQYSqBdtu_Ii17}BR#Py9f>AmZndb6^uxi7^-Pkq2H_emj=m(Il2=6&5#5OB z1JVH0=xB+aqqXXOve5;$OJfbYF=#H}P3UpvZV+9Tav3UH2j!ZMiru}P zG*Jem{+O^VN*z%lL`*Ifc6O^7xN?^gWaIlwUj17piomEg1w|Gk$?tV26f>-lgK+EY z<3ib{{k$`QnZ{K7;|so#rkO$@mRl1G6Z>|+fXViuZD-CXd!n$Yv3+scrM7Zk#MvF+ zh6W1Ht!o41Hj?+PEK@kF5r671V=o=Sfcr2}Hzm1) zV)l@<5%e}H%}ehKs2|~HVA`<$t?)8!opZZOP2RdrkgSg@p-^(6z?mUBAfQui9~w3XWJSUvee zOcDRQ;LpT@ugL8~{kjAhI5&Y!W!x@v4NrSzz#)aD(GSw_0maA zEi~dIIw*G|g2_U2n+>sNn1uy$Xc)m*9+}Odj^U#0yIP3tug*l1;`?=ainG9bEW>?q z;kZ=?&C;h&)jBjeCNeMCaUM&H@aZjUjnmg~7&=qzDZ0QnW!EJNjx5=FtJg7- zE^qwwE4MDSQ*F-K17_f=Kp?$lg|BL)4)4xDfdSp1W~rwQvcYW$9dCQgth*+TAcl}C z_5Ccz{~3E$JILr}OX-wX*ENlz&mmH}+a!}1SV$NRh2G~Rt303#K6|6n58Iu5BP9r9 z30;T&jXnEui_yD>D+A$v2k-JgpTle!3k#|BlmEsl{n8f*9UErF601v)f3D=f@hdr6 zo=nN;BD&myvDZZ$j9;@n2{J+I1!8(YCEIqy96UP&s&IbQ#;RcLP<5>Id!CPxIw(IR z7Rk-2Ry`8u$t$0-Qp0=xV>fTdA`aC_J=UJtLDeM(KiWEI4Tf!M#}(?_U9i5a_7^LZk?9yO+b$`L@G%g7X60E$+wrX+LBLz7VwQhxA|F$QMQ zD8wX{jhmoWF@fF`-cTnX83A#;Rv-s9s@b8xN|0BQ*(&tof=MTgD}+k&>#q2x{ZDrj z0kQNOcMko>9q;7-;q1#exe6N^TbTZfa>l5i`l}qG`(2r|%GjYWQ$_(}e}~sF3rB0D z+1P}bWg>w>&j)DHkCSyUXJDH}h|E_mm8>nORii(Hm(Z(L$JeXm&ly?275EGJf6P6t zTz~w&^|l@_MG9m&SCiS{v-}id#CC%n+OYg_uvG=L{Q&Xf}Sa0Ez753Y_uIYTi`URn6{l0T1uHw-rKl;C6c4{^E!)qOpBAgpgfS?wpb zv00ZE5NZuF1;?PxOEiA#My?@M+x1{>aNHjm- zjDx;fw58&2*OsiKs~}<-OVXvyp1`R2rRaP zchFV%Fqh?9sK3u(+DJq}3r`|_YJ)|=SQF8VuB5T2v}D@5u>S<;FslmsYu{d$RT&@#h45)igjELA(b&QyI1HOkspxLpIPLGR;v2C{Brbx~1S%|=)OjJ0kG2nA#O zgz_T0@IpyzJo>qa(lEIUBdEFjFe%8OWBn(*F5 zM0wWGXg?$ONSMo1d{En{+8v)ldwq!K^OzI4_>_x#LMt%NF5t*Is`hwJ6dQnTsoABP z^hQ?RfL^0FzF7;V`HJrooIx)P}t)MD$Ld<&O?kQ;971a zk1Q7+msp_M7JtB{if*ZB^~+-Cn(|w2HB#maU;jkl6<7( z%^^dD+sr??!JoIOH1rL;;$(G>&*muyscCwa9Qds3E{ebKYV~~c0>Mjw4+h!L^>>~D z?}Ec=FGYcKE9g2HX%3z~SzNfW_UpFf-3goA2{W=8o@7+B12(DT1B?`0Q=$-#zT+sW zHxha5{p;OY+%*)MyhbUtblQ!l$-ykrs{wkc(>i-@m)YQvxY3$)X%jbGnIr^lE1u*U zy%S;d6+b3YN_&#>TAV$~cwDN*2?&6P>HQk&JR7C>U_+&?k^;6A&|YPJG3Av0of1ii zYzvq7u+I>A%Z{f+W4!)UF%v41c2^Vdne~ye9hLHfYq_hMiOXMtqHND$!Mxgu!?ufE zzw8~n;<@adyy95>5Npzxz|X4BxWt$tI37)j`dAx-OCDKK@CL5kHCTq*NF$Ch--*x( zaBrtRah~n-hgi;2YKGZ1@&-i&p>x>@d;jOj8fJ)8F7c{(Qo5DBpn3{;rEn44;uDW7YbL**^%XvU7VhL=p1ABgKE@a1=#D*GD5;-vj33D528eR3FYvhVv=}K0}z8-)kUP ztlze-OWveor_nE7YgV+AEMH4V>R=@Pq0^6QT@imltxNe+dCeB|cNPJD!=+X&zX65n zXpC4yjD(#ChGxmFGEWwDm`S|Bvi`uG^Nxqo9J|!HdAq=v!d-N&B%8Y7qE6I4Zn9y@ zG-!d(oZ;%?k!SWy?(=2ZX8WGV9$i|SL@6_~EA>}oi|9D!!5)m$57^rMfTZ*s|4DwQ zDVR){&yng+9;+KfK>8PLC2dV?M2(!OWeZJI95ck3fdYs(*`_`053#N zgp0D^Z08Phy2gN4b;XBdP_iOjvUz2M3^G+8chDy5fzQU(5%h#xF&)~uCL%OO=ogNl zWByY3-=hI5tp(rKjE#qDIa1r4tkua}acZ3i>BusPoHCi5QNJE*U96G+NQ$JP$k!@ql((1!p{GTv~LWOY}?lDF1xyH+qP}nw(aUJ z+qP}nwrzLW)~mhGIrrVOUA@z+<^HRWBi zo}df^YOri3vx2)QnOqrf9hqWP#xApmX@CT7)LKMJ3Qxqiq&S+?crP}y%T+kyWm|UB z8yU!DJ}e7^1S?N^&L!4l?z7n=p=}yqXg~EP_xlr zcQCSd{x8-t1ucuOaR#nBvASdDhP6_&H)|{PI54e&muzUb5X1o4o<(1;G{Yii!wvAd zv|pbiD120;Pk?W7yBQl&24v{hMi)bw9v3Z1Z9LxI?=aig=yjw!tq~Y7lQ%hU@NKAc@HG?sYUkc{jWW$!*R#$6&*zy=Mi1eDu8g}_AWxM&3W){q_3PY zY3nW&F13_AhqKXXwRK1H?$Vw`BgQ8IMd#TO5XE z{~%CAKk*c@UQXl@ZPtVq)72e#aEMCpIoXHqs_R4LYc4sO;4e{d8N{@0bfk%N=vKO02~zT|HZ+4ko_a*2`0$AzrsI$#{e;~w?gELyHc1Y{!+Pcn-x8vy@>Dcp0YCZuzQIUWCvfZkW zR#j77ZudBxY&lAIF`=&XegnFKse&BVV}^xvI81J;G2J7=bD2fHM(GuoD7Hp?(PEKy z@1UK%I4(U65SJ*}kkL~SHYea#$keM!tD(vP&at0nM=t7x_Gfo=VIP{Edt>f8!XeLz z6NNwXYs|5CDc_;cVFAZ6&r9CexiZeuwW5!bwR{%$mX>CxHxHBU&akJr6Rf7e-{OQ# zP2|2YFy0!y7-a=#>{(G37f$mDHW=|A1RF^V>|czKYLzTYqb_=JiLKj|O058GW_&rd ztX+GZd056_Y}2qtZGO1aPjm5_=+#aZ%zXQu_B&3X)@I@tAZQh4c#rcHm>Y(Kf5WRz zj4XO2-|(X3&C6$*zR%|^=I0w)ydABl*E43y z%P3pzR&#<-%oIJOuXj)G*6amL*;@TKqxRQP zlmDQq^Dk@obuZu7n4+_pqnntuvCV()MM{uulm22GPnW`kBBlX==T~&(?YTbT6&?Oz zI6?><&D^>Ss%f~T_P_?8B@`g|B^4kST$huVyRw2cF=_sAd-@L8{Y8bvSaYbqt)DS{ zKa{6u%isl8*tAz>0RZeDS*4`cOplNmp9{{&tID1vo(jqD;9|pX>xl))PZKX5KI%w( z6Qq*!@5sC*$}QKXPhz#t&v_UNr#4DWK!7sa4s;^- z%0A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

#47#uw+i;;Ih*mi_|d!0jp5{_kyB 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 z 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 W72psiQ - - diff --git a/src/org/wikimedia/commons/CommonsApplication.java b/src/org/wikimedia/commons/CommonsApplication.java index 372708dcd..005e3d6f4 100644 --- a/src/org/wikimedia/commons/CommonsApplication.java +++ b/src/org/wikimedia/commons/CommonsApplication.java @@ -7,6 +7,9 @@ import javax.xml.transform.*; import android.accounts.*; import android.app.Application; +import android.os.AsyncTask; +import android.os.Build; + import org.mediawiki.api.*; import org.w3c.dom.Node; import org.wikimedia.commons.auth.WikiAccountAuthenticator; @@ -103,4 +106,14 @@ public class CommonsApplication extends Application { } return outputStream.toString(); } + + static public void executeAsyncTask(AsyncTask task, + T... params) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); + } + else { + task.execute(params); + } + } } diff --git a/src/org/wikimedia/commons/ShareActivity.java b/src/org/wikimedia/commons/ShareActivity.java index 023c4fb1c..61a70942b 100644 --- a/src/org/wikimedia/commons/ShareActivity.java +++ b/src/org/wikimedia/commons/ShareActivity.java @@ -2,25 +2,23 @@ package org.wikimedia.commons; import java.io.*; -import org.mediawiki.api.ApiResult; -import org.mediawiki.api.MWApi; import org.wikimedia.commons.auth.AuthenticatedActivity; -import org.wikimedia.commons.auth.LoginActivity; import org.wikimedia.commons.auth.WikiAccountAuthenticator; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; -import android.os.IBinder; -import android.app.*; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.ServiceConnection; import android.util.Log; -import android.view.*; -import android.widget.*; +import android.widget.ImageView; import android.support.v4.app.NavUtils; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; +import com.actionbarsherlock.view.Window; +import android.widget.*; +import android.view.*; + public class ShareActivity extends AuthenticatedActivity { @@ -85,8 +83,7 @@ public class ShareActivity extends AuthenticatedActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //Actionbar overlay on top of imageview (should be called before .setcontentview) - getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY); - getActionBar().setDisplayShowTitleEnabled(false); + requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY); setContentView(R.layout.activity_share); @@ -109,7 +106,7 @@ public class ShareActivity extends AuthenticatedActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.activity_share, menu); + getSupportMenuInflater().inflate(R.menu.activity_share, menu); return true; } diff --git a/src/org/wikimedia/commons/auth/AuthenticatedActivity.java b/src/org/wikimedia/commons/auth/AuthenticatedActivity.java index f5f3c47bb..2899a09b3 100644 --- a/src/org/wikimedia/commons/auth/AuthenticatedActivity.java +++ b/src/org/wikimedia/commons/auth/AuthenticatedActivity.java @@ -4,15 +4,13 @@ import java.io.IOException; import org.wikimedia.commons.CommonsApplication; +import com.actionbarsherlock.app.*; + import android.accounts.*; -import android.app.Activity; -import android.content.*; import android.os.AsyncTask; import android.os.Bundle; -import android.os.Handler; -import android.util.Log; -public class AuthenticatedActivity extends Activity { +public class AuthenticatedActivity extends SherlockActivity { String accountType; @@ -115,23 +113,19 @@ public class AuthenticatedActivity extends Activity { // returns, we have a deadlock! // Fixed by explicitly asking this to be executed in parallel // See: https://groups.google.com/forum/?fromgroups=#!topic/android-developers/8M0RTFfO7-M - addAccountTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + CommonsApplication.executeAsyncTask(addAccountTask); } else { GetAuthCookieTask task = new GetAuthCookieTask(curAccount, accountManager); task.execute(); } } - - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); app = (CommonsApplication)this.getApplicationContext(); } - - protected void onAuthCookieAcquired(String authCookie) { } diff --git a/src/org/wikimedia/commons/auth/LoginActivity.java b/src/org/wikimedia/commons/auth/LoginActivity.java index 8fca1c208..40aaed2e0 100644 --- a/src/org/wikimedia/commons/auth/LoginActivity.java +++ b/src/org/wikimedia/commons/auth/LoginActivity.java @@ -119,7 +119,7 @@ public class LoginActivity extends AccountAuthenticatorActivity { Log.d("Commons", "Login to start!"); LoginTask task = new LoginTask(that); - task.execute(canonicalUsername, password); + task.execute(canonicalUsername, password); } }); }

zAm29SSjP&yChl>pO<3YquMTv>*190g)QxQp=^}x*@Zzo-)dhUN+bBqeH#n z9QG`jlcbtoY0sC77R6X;wTZN7=-5S!Gz%W2b&<~Fts?g@9<+D~g=LNvF$LGH4QjFP zLyHDADIuoOk!vOiUjt^+AfL`tb2|#q8xSR6nU-V6+*M^R;m`L)5}hmjJ4p*hkUR5W zu{Nv8n+4LZh7{608z9L-w2V4a^GStigyonONm7|vmSr?fYBz0QTUW#LU|B76|5V$Z z!MR*Cz}GOB!|028YuRU^!-TszEu@}7A&T7w!;$%0r1Ik4lLQ9-E!USmqf}mz*Rd;T zy+8qHsJSt40S?G+vm7ueJK2)f@~tl2IlziwuRVkN4Ek7vs^Fsz+E{ep_4tbj*aHHd z+MICIs?KuGOcW(%N|-|(u0K0?A_fBTxJB11F60zsPBb+&+KUQCIRZ|CO2CMVjU6y& z5~^*d_|95F9vYpsa?uyks~f>sHOWqD>+|cKquMt-ELacUO0DnHCFwC>@CbMWDcYqc zc;(Ah4{XPwOMp)FCi>k?GmtUYDNfgN{-$=7b1oi2!h!kR;qsVbm7+&JJhzr7PT-uP2v8EhN9 zTq|MJ*}}uiQD6n32@^Ddc`2!U*A%450cN~LIKLIbxN3dzVt5w9)h)`=Os5*j0kSY+eB0O@8Z&+nm@c67TNY*U`AP!*j8~NtcQv( zHcMqYcCN~rkFTIndcB0IOU9g?*Ch4DK;d>Z5S1rFMTAKuVyfzs_nQ}V`H)$gAnt@w zhUAD=VMC}@Vx&dr{7Z3@F*zDV(fCNE@(FYhKZVvbEouz8lS39?u`#XU4Lp`xBWW_V z(G;z1pX{Mwj2MSuk+S+V%hDZlk~eq8Q`k<&ybJ;KKuOj~-DJwXX)-m5b{%3JLUnae zE&f-W6r%t{xMdGH@@Zm7i)UYw3^z#?<=zs_deeS+K&{d`h2ao)v4S1F2sS<$Gu-RI zIjpGDuY$Z(JSa1cqtcp{&&JYnGW-JIC$&H(9F(%})8kpk;-nLZD=TXI=)P*@MT?ij zB&!ORHmJC<&~N%E(pj9bR=$j;ThQ2Y#M%n)R`rIC?^6FvwbjqYwN93zOQ}%7lmJQj z@YZ~ff^mPdVAMIu)O_TyIU+vd!%^9&8+bz{sBT=U z*syB|8icBz*qZx=%1HEXX(+JvYYY9LxVBFW->Pl0R?iFnLYQJvt;`W3$B`~?;;V@N z_VW$DTGD9S>c45JoSBhxhW!+7k`?x&Q2F3)TB$-mp0Fc?igAWNEa})=BKtrhJR_1c z@kU8~XiDFn4)0&!cQCWE_}bu8h^&kV)mG}$!9X*yxgk@nX&;=ZldQm`RZ0)s8F)6A!S!S(W4nXR3~f6^{ZNQ*j+{DWRyhe|?K?%;)yDt;3r9J)bue@GBCb{# z!JAKiM*L)6ag^|WjLB7e1an1dT#!>FtiTFn#V%xRRij@NO25m$UoSy$x>hfO#Kfh@ z9G)U})wD!j5m7AkgXx`sTwv&gT4XhbyqCOu{qWx1Q*7pfmYpUyf=8 z7}oJivv0Pxke0lYYox@U6Y+Q(B>LWv(R{4=g&%_p$*4g9?Aknc09{w(2T|8yPR1sn zTwLs>-;amE&Fo&)G7}_sa)=yTpXPhNT55%fV;>vOm~6)*%<~dU z&PhQ;MsPy%aD$AQKlOa-Nr4c&??A>yDLWTHLjUV=NbaZ9;>eUozow`OJN69zTU7Ly zFjFO*-$4>VQRR5uD%Qs_Tk7|NQGgucSf-$_-};u4;TG#Irz{_KAT@Kd$3=MjUJZ7@ zK`8Qrfwznh!eDMNv?)E9oFRrc?EK1O+K_&axcQbb{ebH#ttBS^Y`H+RC(N{ zku`aZA2cnH%qyd2nlHGUY0H5~sTh-e;6E>{_bYY^KwW<1w_2pOEkon5>LI}$6jUMh zZ@9daN#(tKd&b*Qk}iLUJW3g|(kF8}nCrG>1x-0Ta!SLi<|cFZ3+IPhR>=p5^QK_Y zniWo<w&LcZb*FNiEM$gq0!`6OO5oy$XRaLo+~ z*$SKy(+pnNUcLojvLke5H(>H7n)1rd_&tGzJWY!$MT(cwV;|mopdQs_kSzegETYL1 z^4)Cj?|H5xv@!vT;Z&I7lMP3f^{&W0;k0X7wjkw*V}{oz!(`7;xxM;gaW2_VIakd2 z)|9n;{z84*7*7k8OJ!zw2u$Mgm;$vVl`;u4lfTwklP$Vv4aW%dewtLKvd9VUOmTkeQd^3tcj7_+c=W;Pbdab_?PlWi=5Y9Pb{p zSBRhC4g*V^PC{Oz7+KKk9DGO_ZXld)dYff`nTC<#PLy+JMj0eEIv`TEqrPAoEI>#( z@_$B!$DzQjEw*2m*{=^7ECAttQ@c9Z%73h~N9n_6f81$L(GQ}(w2dE>e*y*nE? zz?{2-{ClDpedjYhduRCx%^?`_EM&Sn{J=7ZQ-52am-%ppsId%bV=3Hm7C;5n@x|~kvL#7_C8kWJU6bz# z^Mmy;Mtc-DX9)F+t9G|FPTpq3{LABd&utI)uWap$>Gh7(S?hM->5Bn>M}P3=7diIM ztNOAtqneneMB!O>b{u{ct#_&U0s3kX!(!Cphtzwc8X00Hvgge^&C%zZGkJMRV6KWi zs7a4!-2rA1_Ww}!j!~Ai*_LodhMi$YWZ1TC+qP}nwr$(CZ5tW3QS z29|VM^2sYO>r3KcnFNUXyV;b-tsd`ivapC*UaH_=#Rg=OeREt&_l2zt9%m&B?@K4F zvsd{{7ZjI|MagWVX*b) znpo~I$#;sT+pC`8X$_YyGA#fdV&B&^F$nx&;$71(T0^L5mU z{eBJ89>ho=e1t!+V1MER9)bfNNc&##`(E|?UKs*U=)vYt_qhoxxX1k*m6a;A>ub97 z^ft3Y9T(mE+t|{zmql2Pc{j68HS#UE#C@{yd!`X~P{6Lkfu0F}zKh#kR-agX8&$p= z-p{&Vc@sjKc%z?uAfLQZ%mho$;F(si20TifD;wx210~$ZR!j=W625YMl4%YugAM*E z*|(Mnaz!XN)tLU$c@Tu0zu(X~b?q)J-nJ+(j)d1rBg%;AkP`D|%tzR_&h_~Ujq6cQ z8QrcWXp=D#%`g;wZzG&F7RzwOZuvvMi~jSA@8?$-1iJCH^E^<|@2LhLE*)@>b%#OT z-LQU>afiVmT3wbbH7)*12@s57nwn@mooNFd8H=%&00_?mQcQb9U`Rv^rI{8X+dTDA z!Roy}0Ceusk`~2L>F{BKdrwm#787*e(Cke~{0#$i-B6jfU(6YOiGR*E(naqs9Jl6I z?9j37F1N@)WT#J}EX(b!lgk4r6oCy+NCQXYH*Cn|WeOz*EegS)#b`;j;+Ep5X^}?p zeTc6mSL3)t;*D6pz>%2Bh*4=Ny_iAJMdqSFo~Crq=_17;4=>A4c*z)669riXS&3da zO;GCdF#B61VeYOM_fj`jIU~%`H{1v0QuR@B@P-Vd=-fU1O$r+@>{n`I5sJFy_SmTj zwyZ*%qsh0pWN%7IR(wbeRv=!_Dg~y3_2oekE#xqL;+~l8xinHc+RHz_#mU zHGlFlf6_8f-q6=9h0BQRrVm*)QU6gzK|oH)ms^+8 zcV$PB=UP8!@F1?}fDvS1TsnsZmx8!jh%UmCc7ysD7e$r#*>s+qajzOLftm=9ucM4D za0Bid8*q);`VI#7Rc>%=eG-={iuuS}>~!^`a2~_n?hEFx{gIjt#vYBoe*ADI{yRa0 z{=XMQ{>u>VUy{iG;hUKlZ)pwB`)l|Mau{o;R7BmQ=QSh=be%lZx}i~&FpeSD%+FNM zi=UD)$k;k8JT;T7OrW@vSv5B;~0-RgR4>(@IGJ^S0+^pp0Fvz3v)K2OL( z3V4^8z|i2=Mx$JdT)o^O9jo>^XlK;8!!ZE=*=u*dUAytby+e~3xHIT0mxIyfcd93t z7P|-MbA~>CW}P4Yoy3JQ(9z$fnbGrmb;Tad$3XeJGs?O4w=28-x$_Dg+`s5Hj^myJ z&4M`)3ty4>tIB~^B@bb>>=%)+`|oZ8_CTjr+7(p;BsK!b(UOjQQ0L@4HWb6WjY^WA z$gYqHFnr$v-FhBTA!MK7xObz54YyqvS>ZQjjK3O2YnZyDSZ^iPFT}B<5N#`itU{_$ zx2DC6eP4ORY)2mX63=D!EY@-03Qy1EHM8O@Pwp8Is126y0F@t~1YJ&^EDT}{ODJQ+ zgE{JCdlWHTDQGLG8HgAN=QC?+rfOcNtxon+oqsS~klO>+iaHPEtgagwy9-jWuijBq<2_~t-I46GUMNo{%y^DbN?8>2VIuSLgOv1l|JY(`8lLC& zmv83I-&(y1{r4!8vUV^vGS%00FtxVgx3;v=bs+fv-}+y>O%r9t&G=}4aqg2Z2_vDr z0#Z5X|JrDch4B0BxQjC2o-d#WZ)g9(g7V?xrC>@S7lyZ@3Po>!l+izNy!-;#gK8PX z#vebkHhyj8zg8*$WC-0)?%R1NRy>D*F0NmYZ60e#hhG6ot}AD)I99*G`Uj9(vc)Fq zYcXX#A0gKpDHX>1TS1WLv(&fG9toU_n`u`@!x|8!X#lG~&b$&H&z}#t#Lw}x-BnBB z)V7AD0}r_Ah9!i`K2rF>2auctp9_Xu{_=R0<} z{QX09e>feG zbN}L&- zKv5&C7bTp8aaN}eSpIFUc=U3{LeseK+-gC4Px@LF7%uknhj)St@c;yHFGM^2vBpuB zJL}Wu^UDQd4~$iEZNMQs#e!IX5|n16&14KCtH-$wnh6q!R7TXW z81YoW;jZS)0{l5VAQAMr+H3x^XYqsWIWF{13ulG-UK)S<6f zBQ;yDs0z#Sq-MO>;o4lZ0_NC$mxvHqO^xnu5j1GCMS@IIZD3m@NE%eRceRl|hGP&r zJ@ZB?#%00b)P~J;`>fs%|II|Xlg^Og+TZ46-eTvl(gE2t7GLc|{Zqi`&zwe?Wp1fbgKywdf9f&959NM&;uPZ(Z>ZCAK3!n`X)6l|s z2dI0C-soz3YJcn4_utqr{}+Tr{{g{&9^!v_Y{yMo&GY;k>Xw`%3P|__5Hlr}`88t~68OXR z2QKomWM!LFLW``snq`J!8(|~9vApd;A`7nV4uA{Ez>2`&q0G+S>h=V4eLel>=l&n& zAJf-0VR)AbwOT`(LBks`8%5L^{XM2{8weeXw;JiiQ&Bsbmu~N!$21hK#J3V^{Akb> zQKAkEE|WCI!?P@*b`3aeTD>#0e5*(6+V zLc(huh9p*2j%Z>|8_H03Sy2hxtQVyOF`c<$+c?2?D~X{}u4fdf`kY29w+0K+dYjEd z@v|gS5sr_25c?w~BPv!#=g$rV(2*5up~RvkP7Z0^DX>fP0LwO5%&1xK`n*Ab+vcW^ zQVJqf$@2$oQwfe}=vcxJ-Br(vUk8UwqJYo%@}3N#6~V&`iD%)lP~0QF@cf)bm)M$R znI~RF{U8wc*+1)BBH@kZB;k-`h^h{rA#53kl|#*0v+%ouUhK7gYfaq1XF{CLjBXK3 z+t)>$Ps~&6WYdq*d6paaGMX7;HJ>QF5%xhO0%j`HAFfINMOp(ge{{FLfn)Tyz~T9O z()wpZ77wO5_?iO^ z7m64t)4Sy7nQl<*WUvWdpYHQDio#1t@(lPcyO+5ssZWZ2V|Y27<$l?k+|KRg^#QYk zjb2Z(+ZKrdL$TqYqrchOEMz{;>Cdpl_2##TePh#iz%3z0D zwl4M*sC#Mp~=T*s#yROl4hEIK2G^ZC6N7n8)9eP3SRq6#~oV53-r<- zeZ(l)_Y}VAS@$do8yxzTfM8HWf3Z~3J`UtjEtbSqleJxVGS_e>)w@YDFP2@3!qnhy zTUFzh3WO>`1^cRGgUAiLJA@UK2?+%@(=N$=E-6)VG%91pEkljmGjMO#<7OL1&+1sW ze*SKfk1cHEe-czi8nX23H_m?kZJhBx;Osx&aeon(ot3VI$oC7?;@>z^khGrXLClDZ zX9Kswn2X^n&6)WjihkD6aF1X3Juv`B3IIfiB84$5rlHYZm-*8>pu5KxK@KgH%l4IYNh(nVg&QP^5 zakAC=Eee{A9GkT*uHovG^3JFUWrn9yO{1F1#eR|Ko;DWwg>9Sufa^C4{e+73rQ^2? zY_x1kNpA~tPOWbw%s-TLQ7@k7+b-KWBpc#ixUw$Fc-!y}#NVvaap}+LEf;4ut4?FL zxK=xVBZ%$rI12`i{Rj&5KurMFwmA?@)#cK_;lJ(LbY%D_!llBcm!a(*hHd=`oKH6f zv8pFK#2EDOwVmrnNI~8bg#=u#B3}~~<_En27VI`bc*noSYN8s1I&3KDyMlvPu~ z0={^lq~1BdG0*tzGWC3QeQ-65C3UHz7_{O=tq5VOj5OQS&pAbNW0R6Xu*@)*p-vSK z+0?gEY=~(X!=d1|A(KhyybUj_sfeZ zv{T&9at>8pol!`g(fXp1(K4ZHRVjc+p=M65J7Ll=Qh86aMdMZwsF%AyM_%R{^f`ZE zwY@_oPw-~P$k`}6)%8gAad^6V8-QY87ihl)Atp^`*~*ywy#c z&C#@S!D`}uSJm!*u2q@&ReMyi5M6v_#x=|`#zxs6FNGt;b)$TuT6+B9_D{H6yf>$H zS|C$F`N_|uxx9&`m;p~{gb>pZ{W#rMSVqDc5sf$dj`^A}MpLl?wu|XHQRio^e#rtP z4xN=x=|t2$ex~Vi;yxQRKeQ_~dc)R;8zXrYAdQg-Ge)v)nQVKP?)$*!LT@Z!Q#bwn z`)j`f@=@{j76sH>@g2I=A0MI`0R4y;Q6sQ(=;N8bhquLG2N1A)W z9j40ZrB!(X@X(WJ!biT-4^S|ZS3xy}Zx~npEmbrBJ&ga8s{c|a9PR%V2`HFL|CN*Q zH*Z?1VmBdC?qD=hj;XnpkmCL#St&r?|0$~fPD4RXMOAOmPI#j&Dmq}o;PEJob_Ep) zKp@nfzL8G#H#De(>Y}bx-`I>25ZlOY$m$!S z0jEmsN6Cx`ISJdRxm!i_CVP$*45Pi)Ou)NYq=_W5_(~mOJ3(d zY17i(%P(!`5W>*wtcmvKA8?fZ5P&jC3#FNptG1GJc9JQ4M{H@W{!3gWQ_mHR#t5rh zUV~Lp9j~-hRU>X)5D6Vy$A;?(@-HMuDuELiek1wuZ;{OO_ehqo z{=X3NKY`q+3hj~M==t?|MfkK4Pi(!cl};||-bj6r*wAQXM;S;!X-NRCDQ^Nu=V9lX zhE3qSx>-m>vS9kdmsM6aY|7G!uR*DBrf338uuS2`a>{ZC=x97+&wxg-xA&y0`^&zy zV6u>Wrv>(Fd3*iq>60P4^R+PeM}-o(Cief{o#l6=Si9PQ!8aM2Lq#_flNdJnJp%Jn6d zCdzbJ_sJY(K?{`2Vh`n%ay#2{eW3eUPTs+Qi5Lc|9ux$Y;3!lF5evh15z5NvUVh>m>p*ipzB%`74onv32h}2n(?~|9V^HG7iz2OO+n2H(5G%A`o{?ka+xc<56EKQHh=!_p9(?r}2JoiT zghVY{gs2V!v25!1%wa+HW@e7(_`DQ1Ww9o2dqCgNY6!5#XT%mIRM@wOln{A*8bAP$ zm?LSxTsCtX)R~mvZ0b~CQE<0M%?^1=ni*bJ^E^QIC<9(pd{@{O)XLJI5d&&$F-3GFR$KK5Va9l|f>zt=Ju@}r9G)zz zo$-2JQ7IEUEXlT7G(HDTD?rq>jz$oH)?6>;J@$}LhX!%tM4<8EDRpJTy^D~R$#sNyWJGL`j>S4K3WuE(D$72M+^j*ZSRTUhZkwVmZcE*e-b_bp{YgyioJAnset{`?w zmKDQX9EWhl>O{{U)2c4mUxv7WxM-IJA4W(LknA<{eTgfhdfLDJC^Q1OYi-b;lNhU2 zOUsc~KHt&1;XbKVLi{Z!0c*Rr8R^*a05=-R76L%F!Zvqb#R9xH^ z8eoV)UJr^a(&I&;&v#`N_Snrz?G$;ZL)vOF4J{*zoCG&T7MpR!C*?nV(yU8s)Os5B z9ywYdUKnayBn_U|Kw zF1oma+d2dl>4J)T^4P#xJ;n1c&-@he0g!4J@Ic5SG}?4Y$*`of(j_|S(K5%h5?vuY zL%U4q5*=>@XTrBkDCQ>4GJMMF6dKhmN(nJ-5UiRM6et@P(g~!721L07U_lq?#3_}@ zRSKoAv`sQu!`LrG2Wz;oMXm!EY(L*K($?r5#s~+_3eMQ&N^xs#T|gMp$(~Yd)U|{! zNq9u&M16(!6T+O549CSs9TU?R4;GQn-aHsD(9KO!RIDj|9FrV6?pbB!0c@1`$g|L; zjP5o$$NAaA%Tws2Nj~tCxY+!bK@iDEi`J9uGyl%ZmZ@%ApQWmv5wncZ7sA8QEffN5+Z4S@3D3h=DOMEsY#frLYGrz5m%qKYn!X zi(Y@GTW)kiNcUo6Tg3j!R|NuxYL~erRImk6T~*>Ug&o$cbvx+t-HV-X&fEqoZYbcG z^YtC#vO1&IoBhc`;`XgvHq^OiYX0<)Gab4LMHIG>wLWqW^MKmd9L$x{nbZY$dx-qf zzqoim_HBpLH%#=|?Tl!LJ1p;Q_fYlY#-067G&3gtE*=GM0+=ktd_5>2CQ8qAZFDot zTc)N#TY57%6yGZjAymx5^sNYUlD-#xEl0-kB9>r|6}-^2EKqb$h8D8DsjaFVP@&_4 zu*{IBtF-0^5LpzS1JE7Jp`c2{k70l_SN5?HGdRY2zn=@Swoq4dc@#FQyV8wB284W~ zrqcNP5QTLGI=5EZo4P5Sn$Z$^Uef#@vH|*Bthj%GVf({g@IMhs5KldXYiW*_o#DQK z$eab=uHC;C;N&kbA5iSbt&5vc9l3{;UK_huPyKWd zL_~P3Y2rD?XKhrLHP(zImRYcQk7-^-d1REzapN9B?_XHh3$7(NS|&rmmpN|7`(#b{ zBw=z(_utJiNdr<6h_I27Pw_^beRY>P!=~7zMdn~dF8IV*j>#4nMJyy+rozFvv&FQW zS)#>=5|&UK7WWjikrCc9e(?D9bhg?!cL-+x&X9XK#IfNYh`Jm@ZtL^Fz*MRSr3v)Nmv5o(y`dr47rh@@x2F^r|?C|K}a5=Tb;LBfZInI1RFCue~2Tuj^J zom7lIYrh@ulvl%MdfQnmX?jl`I7Re}OC8?PaSNT&z|Dq=kG^E9ZNDbi^U&@c6Nc_S zRRzSNR9@7sHjW-9qnH3g&{Vu)Y(*N&FY84hC2k)^_5qeYGUF2+g69&q?wHanqB|pA z7^(e|lv7J87?g3<2QR@4o7z7RwBj=P^CjlMi)<0i68hs!FH{oeK8fOPwDfvH6rzA|^0LLNxV=rL#N&zp@uj73qGPxbIBnI@ZqFDg-W#Bf@C@mNzx=m++klterCjpo$Yt zl(NSmSK6=sB+}ywqf(|Wf{6`;vlpY*8pD+#_7?I?mdYzE=F-C!EEgG#cWsbeAJ#og z!f<{@kHT>PEQkP*bDD6M!5cuhPMG0`iY`5dmkyk01#6Y=-ov=h8&=`y8Z~)kq91gA z9=giI-tKqv$aa0Recea6zsJ$x_8L)fi*u3Q#pQ^^xp{(oN1fgyb^+&BCUg-BycTan z?v^{=S6vJ{URb~GI2)>7lj#Y&K_mn6?_2bD7fsoTIA}O;J7bv^BLUb^+Mw= z;y44Jk|xh~(|TDPFB{pH4;q#k%z7D{n{9?QJlixeI@%1A6TAJnZxoQdN0@MlJfsZH zl|SZKfK(oAO4-@BNmivlPjEoarqvlza*P_UPS{*#oRa*NA=}3Hk-+Lfj^C`sIcV|0 zduH7Eifr%7jDG{-SUxqw8`@qvau}8!&^i(sS2b=F*@;C=)1WLN)(`@SILD&@(q>^1mN-_vt3jj9;}a|rpOVB8?F95`;2 zaQC{z^QZN^>+20I>~M8dc1I{5P^$B?OpF-AsVnqF6VX?P(x`SB1h!&ij#>mnBakkV zAR%{C`669~!uV{7(vC+XJmr~&g&gBbm09dT#^I`C8E?4(V5OojnOl%#mioyu^BG5b zsK!;_uxk?xiW&DW%V?V7ufnR7<#sU&=4;V+#VI_=HfezOVzoRGoGj-2`P=>KMqsG- z$%HLc8_qSN!T6Q{Uz`@4EdJMNy?KHy-WX}qy0_WV@?;dsghs&IWKo>9-`-UVUmyQa zFG4E3USgoX=S;BvuC0Rm?{mKYR4i)s;H1J-0m6! zCxQ|LHHK2RNg~V*45%#d_``BYHX~rJOfl8h3wR+*6L5T&GcZ13_cNtvNePE;X4!JzoHMurP4NN&>HCA#0FzID6=Wa=rr1x2DmOVAhA<6NE{Xg=dd1Yegg` zXu7GR5t###4{)3`=yvO&OAAct5}35HX`U-9gwet+O9zVcwvpQU+evX3oszEaeaua} zg>$UCM9*5T8Rv=lu8_o7JKlLobJarf5{bz)2rGP^?KB+zmt8pNR+ z*G8um=5}93hgeWZHL$Kcm|D)d+}+oP7Jst2<59~YFz^4EM(dH;&IoG4UaxfB)~2_R zRvwOF%m{Ige>zmy{AD7=2SXYmVcA?Tv&Lj`!{8YInHht-F%6L!@-$av{gPH>IW8|0 z!VaKC)6w~0heoz6NVo7=8n?U1Fs{Gy`sLuAxfL$W%+3#*BFN(u#Q}Tgz`;(7Yslv` zvX+rr3F&ftkYMAiqW2Y{t@6A39KI%mY_*Wq0IkI5ehZMJ z;2DT^k!>>i+QlS$2YZ#wm5@Vl5Vn@iAmt74FSTHKrGR+yyF@|yw`u{;{~L1tU67YE zw0E>{_$PD<fiqaBbV_OK*tpD3 zbl~b9YuoWkXgmWxQ<8o8*lah%sHm!}bhsZ)wH{|U8&g$zy#rmtR6~yFGQmRHAEh+c zn(PzeI?o~Bp!A7}mslabYBEc?b<)gTo|gRy6caDpl-5-iGQ;PR&(f_l zl9FPhGYgmM$+RQC7pS4e+vb2xOX9lKH`*S%9Ag1z=v`G36H50D(H{*M0vk;V8d!>y zY?CNYrz(DRj;r62OsfQKVR-#xQMdj!`?!L`(5`NU+VXg%m+tI2*{78vkoA6&eiF}L zXFcfy2wII9(d%>#=87R6(DuVJ-I6F6J52}a=hd6HJu;6IF;6J&7db~Op#v{6 zE8molsJKewPdcPn<+)T3P(B7n=T9?VF)KVx8^457VnbOIH3(yfSoM#h`&_3{ZT~l5 zCjPc|`rq`<{8!Qa|0*vL{JQ|~zYG4A@|yo(ocScGN^$jYj}W{PksNds`0d{$F)4^j zl0Cn+M;x5xK$BLDoSMP8$3gY_0J(i|3|r_6BU5f?cKW9{-JOoU&3}!mzBIG|REF)) z^i!yFeNc>4ZLa5f$%FC~N@Ym2;A zqTZ4dt*(`@sT71W3N=#(r-6%=7}fa|8LUF!&S07a;2^LSrZG$GGpp=h0NX`6ql|pv zkwvq8&pQC6HC)C{aDhLk5E#`uLrK#>t1$3ixb4+9&<7U>Yy!yR78&bCWf>!L4hLMs z^yAOjpSt^=A&&i{T}UANSYYfMJ@kJ|H{Z>Hf7dSb&&vD1SVj5N8ABQAi&MPrV00kL zaJZIyenj-lq;?E4$oX{OIa`yApfG?!xJTb@}T5p>_CS z4$v|6h68-P|HBKuNBZR)n%nDWc>2R7K6gKb%kVoS!jG3MPhu-Pq)=3r1aXxs_M)gf zurr&AHiybY2x={0D|Tcv#1cPHxZ~hHAC(|NTOr_=F9Nc)5u1%K$|VG~d$Cxesr0>j z15L_k2gDR+d|E)}7U7{+6H~6}D5b+}v&_j|z3GMHgEX+1LZJ+zLa7M(-E^)`pQNWo zrflTPI~GB7Qj>cZI5g33CPnCz6fxs%Avi1lxD-=lz68eaY`}kgg6Z!(Ari+%DW6s) zbxaSwn4t)7ss=8FCNAbCMa#&A60T(~I8J-MrQxb~sZDmaT#Nljt|^RYvft16B!d6KW9)b`_eD}WGiIfN|Tmm6C3MWyn?C|wBV(3;M zBp2J*{wPAIoPZ_$BH~k#Dnv!>$iYQ0GVy#4oX78rpHUw;K2eh_W;E5x_E=T@-&4wgHPS+bEhlRt<A*E4*qa@a-_6iMfbv&w#JD0(q0=XsLY`J4 zRTzp-v4+(LY4{cUk7f3|QeJSBVL4qdwV~H&*j~h@5qFGw5RKd5$#tGqm<;sk$XT(;bHw2$-v2cagw5$qa3vC> zM=yg(X9y+#?itmM(4k})GrBcA3x8}A?=QPFVD2S@j!5Sc5>DKG&5amczm9!q^HJTd zYjSQc%6-nNm}IgW8XT07E!}3+`8i3Bk@5Nwz{k{WnuZ|qvQ?VSVw=*-S%zOuWZ99t z%5F@P!uMk2`o6n87e%zMF)Qz9L)%2`Oe745?+V63^{Wj{1vb%be-bXoTz@^V*7o5qC@WE}~cu>Nd$pyf10ldK9g?@1@Ov)MpE!QB~@;_b;f}RRF}DvH;Fq zQY~l`5O*B>Ppy}mQLar}JxTGruF&e%L$6Asa=D5kgzS#LKx|Ua?-A8*7$&QK3!rM4 zt@PSnEJh+A%1>ZV$|^7>s8+2vVs5u5FJX>P&c6uT$l16nwNQ<65*vPctdm^=1mR)HT^2DsN#wJ1^SMfkAupWSsjea2ldUpQhr z0rZZczF;+W2M2J^bkBPiJl%MuHawDmGe~yGEecd6BvwS8(MnDE6h-_LbiLU&T#-X+ zI+#;nE`G^oGo@P2Id$~t>hvSJ>m5d)9e8(~ldu$vZ(J0$9}JuWnxxWO*-gTf^>w)^ z6@j-v>9Am~-SRhbLRnLxW?S8&tJPH0YuA8om}G2emx7*Zt9If|kWSUAv>rLaB;(W? zUH~0gc;eLq9j{BJnDI<_n37ecs*b25C%%yLTJW4oXxAd@My}a+6noin6oQCL(?Phd z&o{^+rJHfklupG3)Fika&l?G_%DFpedRv}(km+R_+0QlGQ6!l#@(2<^hl5K={hEOEB&U?@VckOHJ~TX?1ho zx*|Q-_tA^J#m3?E0O}mBn&ZQ{*Q2Rdn(MRmxuZpUw^|>P;(D&CFwp@#K*rUPB4x!m z(_gnTjXkVuw8t{O%TRBj_{g51F-|?_JOr#BD5x<&s6H#`ftg*y*$^VV2Y#z7IxCo4 zBJ8p&|LxrTCavG|=nVKeiu#70Zo~ZWl=I;IR{}*pR?_4N_T$F_(%+@t|3echzoVVK zwcY<7*HkJ)x+5y1c@eLkpId>z$MJ~d%283KZ^l$2Tl3VIl2I~gto?v1SsK0k1;RwH zYs3WH$o!{MvD~sk$9z$(REcuM$TyEtEl^GU>eb%EqdRr<(uf{oW|(6-YutItwfm_( z<4R)d^U1s#(1m(SQWuaby${fZW^|T+j)P(nf6hZHO<4Y!qiZINqKjmndNh=J{GOcd zwJZWVK5*FZwGr|4=6mprUr4&-SqVbZMQ$l8&PXlITKulT;yEfpYV1S-VtbGtoP+%1 z3EqV&@M<5F?nU?KH1Tylke0~KFn9*CA@x|F^ue{XVh6#;w8Fc_AT=TZu~TGg5WOJ_ zM3QxVW(TdspFrI8Td47JD${k!ahss9wm;}qew6)fiZ&MYG4|r|@lE@OMNCJxy}21b zP^a32bka*51~8i)st-^hgsNc_7nNo>JtdqYH)KX_EsgJ%K7ktsh#uLB{0I~0lXeeD z_T>HySPgUO9dOP!Av$1Va^*>%w&qzy9t&{{Hw?8BGt%lI8aF!#T&QSl(w;CmdbT^JXK}M#t`L^z=m@qs`vTVDw(~SMLZ>Iy)-%u=vAEyY z=HVhrSv+#Hn2INx|d*D$Db7;LKe*McB?ipgz7@tfX0q(CFn-(R98c z5}ZRUrIlyP)RgETO2S;xZph8b*VNGh_)4!0_Ep^Nt4oaEH%+n1*J`}(XL$=vg2u1e zEMCdmc*6 z+s1}mjD|57s`Ixc62rvHG*Ysy=dkbX2OVn{Or>>HXcd01KyWTRu6$K<>3FQ8lzbA> zVn(Mi-mY^PU;+fuu?^9`*OsF5-2yj?eKoo`cQ4R7Zq4GmgC+xb~U@1uUxq+XD)mW|EDEo zz)w>>=2fsdDA`tMP!+m+fE;}*8rJ0bH&?-K|4M;weMFN^e3&xhL{7yn=9Kx!t|$6s zPv)G)%r!cd*)!6kIckp*ba&FJX3E@kTZEHdau_qB#q0JqBS0%bNLkZeav#bIDBxQzUDqlyaMqqV?x$6>>t*Bl z*m{BIYnv4=xHwF{x3@vrpv3fJn3*__Str-ZxS~JZKjk(Nir5L{V3f5P_mMUdv`&hG zwPEtw`{$$4?d7AwU?;awLAb8EX?hFj^CV)g3MNc~w^39();~N6rzws=u!OmL(UY#v zdx9lqSz!k)cEj8d)`-qZN$;g8Jae3Jl)(Y)-dMPs)b@A1gFh+31qqghM zouAw_Qe zS>g+66ot$mBcykkn#O1LzCi238kXW@f)cE9Uva%Bm3`K4WFN~}wuSH@hAX^qBe6E- zC%P7gyM9!H8#Mc@Cn_unrmi(T#m`eZ#4~86OobMfeV)1h#5zUy*eou&>fN1giMz@q9{$IZO6b>`1ocv0d7n;y#5nS;VAN-(G$KYL8> zCXMaBrR4PLirx`z7j`&IK2UK^(o-*`;8dsJJRt}n{`p=)247BmD@PE^0U;}(_aLBk zlu8dBBGMJ62N;YO(y)NwN*Fi7;*Y{xP?$Bsn#H>96QY44Ya!%Dpe22vM-YoYiygX* z;OcKo#rV69`hH|qB)a55U@L+^F5)pnP5J<0x?|C2#N-6dYXC$=48wVRF?-73jp=@1 zO7}3x0&Us)#3i~RuGg>vLXbHiZ^gBON?(#h6GKeBEYC5Qgv)q8GFPE8%nCx#Qjzcv zzieB?*6Y&&7QqeGb~JZw*k`KrySB-SwkrSUP}r?=+C8jNrS~4``#7_=zK+V^yQ$KS zWS4xgYH;++RH_l7)5jSdbhK<(Bkb)d^<=mlTIwrFWhM?GHrIC*^O^^>(16GtyrJ^S zf`I2&fkB`l{L?-QBH!lXFSAu|V8J^->Rg29hHdYlY}`1?ci}kT2P29X*uO#=;7xU1 zm+z1U=Wj!r|HsiR`hOJ=6xF3Mgb_bM*eg^w)&Tg*2qZNiBB>Giac71SKr~EDP~d?D z6dp`fqbG>cRn4h=IUaJ)Bs)nykX)k{h!n{iuRdfIyH#mu;Y1~HI=*M*tfyY4m{Gp& zu5i2oQu-#j4rna#qS4})km9>k0qxN`toHyl7-i^bvU`Jx=u~KCgQKA|=xa=LMtL-I znAdFkXGMK&$CV;`oG}(@tQy~cmu+R2vUP&OoSflzGMc2ct2 zJiPa$F1-W)j?q*U196;&;TN4A05^%0s?X2jEVDU90s;<-u5+0&qh}TE1=p;(zg4?S zZ#F=6m$md=ap+gBh$S-%bn_?3K@z2NX);r?w%Cr95-9myH3O$Y=>I!JJ=TKainR`@ zwad-+;gIum0DUeL%NtJqP$bS1n@Z$D0@QypvuB$#hI;-|EXTUvl8ikJ5WWgiTbFQ2 zw{iuJaRQnf2D5d~Y%@4(S;=_sMO7*4IJnBJYSd)d8ughaoHxS279DU+ZAuR~+rSp9 z{3nWb70M{~5%aYva&|9#%SNH!&*QAZZlv;&PGWXH`S7xyFfW;FETY)RzDpQ(@{tdL zGF6l3a+W&seeAbkm3YOM2oMp7?JkIN zlB4{L3KUq*dy?z62B=X}61O1KpV3bx^qEyj*2W?%s$TKNZpoHN>^m5;J(Z$W1E)7ZJ2t{838ErCC`pEz(lW2gV%OOyw9^FrNRu=D?coRY_U8&BN+J+?1B zdzUhg81!;M2=)wb{DLKg{J0OD0Sf=;=T^bhUhGO?zlv`c!&cvj7jEpnME=2SKV*AD>ggGWZ^^zD^zYIX*1s2C|6SMl*X+|@AAikV{`+Xl zuPgofG{AsFOKkz#P!|&i42>ZTio1sg3rHbgq@@KKlbG)U6Xj1x!H=eD<+!h${=o2* zGzz1ipK9>Dj6QQvhVR-P<-snfKR#cz?yld)KHdKMe1`17>r%D>7=KqA&GkvAQ?eh? zp$t}6IX+ysP;{~MuVn9VO0IOLyT_F5ikD5SCEf1|s4#xh#gCy;a@XUaAA{z{O!@;V zZ9%V+lylA&+y}-tofNaRo`dw#x(gJ=lJJ`|E~Zw4wUt-hGV&Q%xlG>O-shhDH0zLM*T?}!hZTXdr70ejx$Q# zHUObQjKusHJY$nM2x%EF2*oU_1}8umw5iz`lfiFBop*BOWs|Fvl+K7)(?2oZ0EtIKcLQ9D@Lflp1>~{oD}TG!Jx$QWD%!@ zHDqZ6OqtP{Y$eP)k{{B5JGq(oqag9@oF66Z-XOgm~=5etCjiy(6 zAJ=D^OBOk1r)uZ42R~WIS393+5dGTyrW^hQWtna}wg?ZsnM~V~q~MQtC%E##+!xi46(Y%HW-E%EqU`{`vRWT!R9*ZyIO zSiP}aYYG(1Rvha4>6s6iGcKBKQzK#!9iP|-)G3-XLiFOx=gaPMM8}zY4Stb9V4jU9 z-=R#6gdJ?cbDlc+7P)s>XdK99Xaiz>B zInOeBdM^jrZy-WR(v91lm}2kU9c4$NKtDr|9Z8#x7j)to?_r*BjO@BpqV-2}f@pn$ zh!#iDvuN14@FLxWT#hIT@?JUW&nNxi-lja9aFq~bE6k*tUJ`to$GRSGxF^F^Y_%97 zlc^SA*i-vKDjVUn0-sc0@;AFD_dPc;m;5FvIn<$L45v~)(WKuo9lRkSuX|Mc# zNQQrQ%j?QZ{^acnoQ*nPB@y{URxw9a;b*=8wHZea{!G)=w+!x*D)b0#+o>h!cnKQi z|Nl|;jnS3AOV*uqYe#kz`%VAvotZlqJ?lL==R?j~=gae} zda7zy)!u<%mTY1erB~1$;REPR7u&0%Pu>mY>R8izfE6_El$rqo`$beL>NL?IX2j)yGw`UeC5oj$^4U3A@mafL zLh@0^iG>Z@6J5J;iBr&nIrPPXPZ%(M#6-9x0B^DEl8x7gsmdBAk3*}c)rgSe$LXjJ zQ{m%(n{ER-mVIFxYhuNBc4`^Lr;UrB+SeZHPNQ~4A5p=@cwfkztejWvnL|XM*vF$= ztah<5PIZBVQB!nO;I_6{C^}cUrLfbDrf9g|;Hj+FpHrH^JD#(svsn1t0rIu9ST2o! zv-+Lr-7vZYp?r4ZqCY?X`Q&N-{M7zwY~*jT;FF-#9m4?m!>ifQiOyn%0eaGp>BmnC zzSIU%;pMs*q7_H+3d7ihI@4s!f}w`gWSZ6$rVt+pGLmg?9(bT=exUdQ26#Liz#JSO zh;`>p5A99L=k?Lk(^JD4{m}b|5#}4ri?@xt_6Nh4U2<3spda}~QB!`P&PlslwVvOx zcDHcR4mNc>U(&+-o%@`uI&Rmg`Cz4Ptp*UbGxKm*M2^x&y3x z!j65-fcNXZyK-b72V0m@6hB=m0~((lgP$LtADeCOZ1j*pKl&Jjh6dnQ2j_8QE zC9={W!^y;ka=b7#fwibr0-_CmfJshatC9^TfU;A_nirBL%BcYOPDRlUxA~?dl3OXL7IB%o#IVrU<%eBqYz2q91uyiJ#*Dnm}+e%UYs^8 zMQy`bTwYk2!>=M>p4jB2j3Jl`w&oS4!LVHFOMrYhKL!tYS#|?PF)VX@u(!xbr$H~~ z>H6lRK{UB4WmN!=X&gi>ECfYARprI_l@K+*7ZnGzwOb~2&l-?Aw9&*(0U|n%XZKNu zT?S}=zn7i9CCFj$N}l<_v9y27H{S0Zdg{c)iJDVo=O!B_OdU2K2 zsIX~j&|$nzM&Rw+0GGuNHETv4C}DhP%6Yx7{-p)S0vbqjx!~SCUDFl}CWKHesb{j9 zClW9h^P^fX4GDwweMs8r-;;3Lk_=p0;brw!6zGT3FdPNHV`iVMk8YlrWT1w`JR9w2 za|LT954qYC^n2Q2xlga3GllcYhZ;S%+x7(!pSl=J7e-r`x6y1ihibMym`3~MTLqh7 zj!S5aa}kDoHRb44)dkfoLdxPX!MPi6+hu6#I%@u6rz6sv=f@`ESY&Z%Kx)r!;!I+~12Fivf2cgq$UB#8>^tbd!%*Jb;O~VsYqp#W{R6iUBF| z@BvQx7Pj5Kn4o&TYK+D18~@(pqWG)PT`+$b;%~#-_9>?l8Q{BhJ{TORQ$h%`C6);9 zL|YeiNSBLx4nIa1fJxcP?xr+Ti0}~VKKF-|&2a%@=fg7#n3~cEUiq^u4ZG^>V-jp7 z#4S4y99qSN%B-_~+2ix$oPx)GtPy5jfUV~j#;*9&6E-SWX0zh=eef zF&QB@kIQJHik?RZ?(mZi?B;RCb?@*a6_^t6k=K_hmBAgt?&<~QyUdmP_>xZ>hlkC_ zep{xvk?rn|H%XdS?uFD`K74vb(Nm?%8gR$*D{{kczk_=k81CJ+qbpv%~!?q}*D2q1cJ-CdL&FV9^v(4TqKTR{UAcWYD z9QY%{r{Sg&$a3O1Jy}V~ku*qySs^UD2UYFpez66xKbU+)GbCL3#Jns{Zyk?ugLo5@zl>h&kR|s%A;s=k8xc)Wa?e8 z^KO|vIn-*BdwdMxC6^d?9AfJe5Bs?@|k`3anf<&c)}BBB5$1qvDqq67K7 z?m#e2yCX8S-oTADI`1FMw5`!!WU(5^x@z|YN6)*J-O)&G>l>9@jj(mD(RcpbSJ*uA zfJ}69O|Tnscxvw-*+fk4Xd+=AZLxfP2Uld=K7zY(IVbQo6}0PbjRr&g63|V%J5yW;`_FPDLE> zlF!DR#RC*VHH%S$2PV=1g0+UB=g-nf35Tq2lVRX`;gt+XwIkhDNq;U5$(Y72q1 zuR@17C`Cl%<#YRrHQGJG>QO+L?9kWAc4 zcV3X7p_ck;2V9YcvJcU=;_m`$Vi#X3$zA?U#Swc@)P&@vrCmT+`@Rdg)w*B4j?8f@ zq_$4CDfyMhTqICRGyR(Jy`f-4dK?f_}KPABHcK$RcyPLZl- zqu@qVi_op|URx$pu>A9gbb6N=R>QjKQ@8+EIC(#d)PaxnzHFN8gisr{H{fhOGHbek zPZLT_6AXDDZW%mtj^t{{O~1wMoh@iXD0_BDD~D1S9uy7XQC2CW|a2{#Z-+LfhI*K>P=sa#L^Ao?I*KnYcru`KOCB0ou=zovSI`z z_4urHDgcEq(+@MlAKS7b+q4u2(#{+zdt^$Cgf9xs#|j1pnSk$#-SE4*2Lp<=IDs;1 z!o2cM#B1=b^RdFeAZ^uR#Kb7!q+!SZOm$T3zC1X{w%tkh<)OuE!fZ0l%6U zD&4)s@q{rpCcN7pX!+cfS|7-=f3)Eet0H7EdyQ|L)EDwj=K@F``D2dY`-_LdC@G0@ zq|O}bcOvU~_%zO7yXStOaZaCY+O)GeJG?WoG|8#vjP;eOG`1z8Vk@b4!-Y5$HO$@Bi6^?Wa&)Lfc?B5oYuX}o9Yw#DtaO~ z^HRlbvOqyUHjZ-n&E-&pzC5X^MfsVemA~XgSWU(TK<(Qe7D^N(d}6uo%7)%Hz%$3IlU^ zCX--+XcTHvxo8te551@oZ&aCDmo9QQnUG9VXV@OB^QIQdJ=(A|Ax|zb_aZzq;w3KF z&WU~*=ZzU{%mD^gQLi?eLHt2$eup*Yl|hs#WMDD`CZZJ3fhT6@{L7X1fZ~j>^9p!$ zqg`OXmXLBXg%|p)_^JrhNxjrS?Bcy9psC+p}t@S^fA zm+rcihVD6=9I{;0slgvg@o86Zo+x6KX@&X7-6Ujua}8}6BtC$q zQ6kfM#&~()*oh|v7+Mc0?VL~_Vu%)qqOSn&Bs-^=vRwn4gstokr)?ay^nbG|l&c;g zEJ8?FbEwTNnu@Jx{~G_0a0)S2g)vq=Dx+`)qXrjrerrk!6_vWnB;1vCD7|7d!{fl$ zpw{a9uM3d*-^dB`9GhWQIu0xege6U<5KO~I=RJ;25}|ze#C&mU(U^ewJI=+gm@F?K z>qj2G@`c<@?(q7OZ@gyupm}TxL;bKem4m29yCgyI8(nBNFPXGzkzGCV6KfR~4+vwD zXdv~AL7%F=b#%GSt`lJh$bW2d%L1~ZiyJWk0Z62L@Sw;CZ%|_WT9BeYfYfWuZ2{s& z92QU9;LdM<%Qxzx$7j+Jtpz94VQvBG0mnqiVPlP%3l%TknN6!=E5j45vx_GfaK$|t zp@ekga0=SzpKa|uvs>p|sNz`VVK^rP*774PC6)?e6yc8)nWZXe~r zz9<5%BoUT>j(S02MBk+lpCF&jsln2u=Agup%Bw`CgYY)Md_J#GKs{+bAFGU3B5K1c zY0o~1PhtLgBP37u=yNLW7h@GqZ$jZaX0{@OV4gQdmY^ihFX$L8Tz&+ZqWi8-lGnwN zTbf7YlWwy87Ssw) z-U7(hP;yNcPK~$Q6*s1Xp`A4(m`oAv4-zqNz(w4sg_21Byxq-cbgo)iJIpTj2JcUblOZD);dH60a z`}_)S4F+wiqP_Y-u?0v=YtjR-<6*&^mv_$8s?Ig;i@hm~X0(;p*M+q|wvy~(wSV%m zgV==`K>W`T3p>duoWN?`C)D#;S!In{+lDGY^XaSKcry5J!2=H3zC_b!NGODBK&|i z?Wt?|ZNp?~bzC@KLTsJ=s!K~<4`XG3eBhRm1fdz(e1`LR>gJo5ldf2ynM#ya7N53K zf?!?oFvlPkOuKBga-w3Lo;BpM*IK`_l|MEJDR4H4Qq`p;l|ZW<=7vU=K` z=#u9Qc6DMqF<>9@md@AW@Zww<0l(2zb$Oi)9Z@oS|cV4?g<2M*qdR|sih9s^8m|SScE5d5T|lo4+LJ1_$K?A zdC7*;;&sz`&0*ws;}tQpn-%g00qcF`Es`Lrw+ozd4ABDx z6`yyE63Cwtum1z4{iojD-+e*(#R=&S`NMr_?bMLoC5jl<8>h~k$?wO{c_KdqCSEXa z9G5Qu--%oK>8jXk$46siR@7c*P2)-GbCk-Jj(pX4YeegF$|*fEGiGLHc1)xvj-=m~ zI={GGjOsfwgUkMc-B@z#d~nOmbnD!BJsMD7__FH15ikWrei{At&>h&n7z9OaW{^!CaJ2B1*PS#B!996NTW8$&^`x}F&n;-LbD&ilb_e~DD0YKvpz+EYE|TVj zo#KOpI`ZmTQw!^04}6{T+W|ZHprDNTqT&Ft3B%!b!s7SUVBObYIUA@N(kxZ$wiZJR?R`2yO5>S@UpR8ra{rN zaVn3A#DawQd>JomvMk?YG*4Eo*}|8hP+V+6@IDb%wZ^+&YCV&40=3Dag$})@T($MA z6=n%Js$-b@s+8llr~bNSnGONwNDsxiDdq-Hf?0Pc_U0EC<}1~t(y}gOL}oUG$pjmi ziNo?=kNnI@-}C}3RYmD=T7XUEIBQI@3-8p20QYZ-bAze%rb5o13R0*^E2_ms45KH68YrFcqME-h*e-UdkQZ+s!gBl4zCa zWGs+FV_N4s-4&-fM@3Tcl-C9_g@n$KocP{;qC_t_oRAX2$ejHy!Yqs-K_dEe`p)Hz zpt(|xZsQ+`(16#59iC(1 z(rrk&_g5Yz;S?jh@^nIpnT1BM1Q{g_TrxK;Mnnnog;K0oq3!@Qqs5U>gjZBwS#N+? z$V$W*_k`@XeCWMAHr%RRf~TfaLo!k8)p`CXRT)lJGW6V86-7u?gvILiS_jVoM+&97 z9!s!?BeWjYR>7l8`ANw#RX$Kh(M4}Z@Rcbk#UyS;mHjNg3B5u;t#=1%7;y7uXqK7+ zWkPopCvj9=NsHGLM)O!}c9q@Um`)IWuWY}uI9QU9m zoqA3RCcZu2g7anKbbWq?Y#4t?vgowp_k$D&c+YlB zYhw250MHqkudBN{rd}pLrpcnA##Gew1TcLg^Sq~xcNhffzKa)Zc1l2DvzbtS?No`x z|A}vZ3Dgo);sEo5`Mi2AK^OKuro~dZxwdh_^YH#hrp0R&%H*!d z`3u=og|BQ(G!Dqf-(*HQ-Q|n2tb)9Zsu{?BVQvsg8_sRhbx@EsR<`|&A(59SX?#%9 zy4JkTIVz95*PfGft<>mkqZoA!^2|Y^j z$D6$NF^UpcZNqWo*`NoRRKc|^4SmWuu*sJ!C}om0E4h|Rxt7}5rkU9kdZZzCpk=eT zb*JdA7of@w7uBZJ?;lCfhy8LR?an=tXEXZodRmxkN&R#%ZC8v6f~4O~bq4rdaNCq+ zB7Sni{rV8Wd^S?pp$59>)J1+bbd$X>zA>%-u3ZHR@v&V(QT;X~dUlhi##xL{us3Uz zH2=b(UD?#pV152w+aJk-pfFLpkL}t9cqd7(Dd?EvK|HyU?%*RGZ0gxb?ZZ3&k=Wqz z>-E0&;!iL6^LPH<>rNM&I_JOMqoKL|f4%c*U^+J+QUhCA!UKV(q%<)54(I_Ob|RSf zd0uBQAu&tqE}>A;_VfBMSf&sHuHR1a8D7VU z$Ie!nJdu8sB}~XtLvleYZV22eT9aq2pa+=V-X&VFVysXf*DA%t<`lvl6!?1OA|fUC zmXh#ND0^YQ98fSoG0&v9K2$oReUd`9Fd;YVtLi?S?ptwKxHfhTIAtr%(4{PpOWuZS z#%z#f0uo&?3=(*as1yWmF`PpS8mnZ=9YjNz-|-iG(Zq}K!8y~ z))!QO5(~k>S%PAawR&1=UWu3omMfOUV59f-gdxT;hA7&{P}w-D0fctVjL#C7Yv(fzuzR6r}#} zDu4HXUs!-HAWgsrt8DFQ zB5PZsZYV{D?~9ZIbMU(A(}&Pr)>k9o2%;qf0-JQzpAQWDBuMydK%`>fb12V?2rW)Y zHauqR*T%+_7T@>FFMwy(V3(ukcTv^iA;K`I7J_EKaBuHu z?_dC+Bo$P&Bs3LN)SnFGK_DTu-lC2mmN;X;3PZoTy1V)Z0R94e;e%Z6@T(wj2e0#q_ z>|)tB0HWOw^ila@(L|nLQ@t8X>n+NCzT@pjipCWt>P*6qauw}o63`(yv&pVn8>PC< z4ykCVa?++SFUe1dbORM)irN-8YPLs5O>a+JM`wi>N7Fkw6}PFBm|2LrO*-}~UV`%* zxpIhLt$u$=Y<0=_MqdPXJOiZQ*mp}n+y>?f^cKdENrd1ELgsICks6bLmS?QZ%k78Pcff)%{phwmBL2x?mRR zYy}4#S({JWOKo;z*Cnc?22b>wtJ;G@FK8?CB`H2q_N0ehguot6C7+Tf5EXhVd>Y+) zqhiiEINqKCyTI%p7outHID2W}2B^)bKXEBKv?(glQmf-fnI@*^Z$DtEp+#oU3$?6l zOLf|eYZSpn14ag2Yca$XZrv@NJ+}HwT4D^Nj+c&Krvp;7@LLP_Q1h61qt>DP5TJ7; za?+j!&6pJtk#+*=P z$4*&p_J$9PvOe_-qIt$#-;dy8Vu9Eq!fVKt8IwiIg|Qm8L9%z2l=LvALAJN%S}mzJ zS?2;nT?|BmaTj_7>qrE>^zuD{^bw9K29v2isQYeQxDJ5)NkXt@H8f8}Yq@f;&P(2p zEBAOf3Rh0oPt(s7a;Am-CO*1F`s($U{Y>%DjNGG%h&iDXm-8HM>7NGBFhR8+KgDz++)R*ZN%4C07nfRBUe3)M-}@gOs7%NVE~=i6V6} zvdz{RnomO7s{9}Bu^OKWwys(`0DKeTV-lwuwpvyVR7TLI$yj@)*KDf~<2c&s@|A(S zas%b2$CaioYYN!w0%MW*XUd{_QuUnKY>{Aum{D^~26Nn(ZTYVEu9Y$p=fm10=?Cp1 zOcv3FM2l=J=#G(r@*x9KL7|O(uD@6LiSyT1Z6l2^QC=*^7F!?D>O0!rH=`5CwHAhL zG#>GanBJoGYglXaq=RMCGF+F?zhQSnaYl@O(suQtTtrZLwq(DzL?j#?*IfJeR^4iM zc-oc#}f!i zYIV?MM|2!ebq`_2|MWsP#caGN8|&yB*MA+sz9WhSC4(aZ9TY6!F3 zKubXf1uBK}>aD|~Xykc!xj`Fn93w?-l1YBzc!5Af!MU z?&oORrIG9i1Z4w!Y!tbEaPO#ylE*ls4H_{M>fuvI$}kDq7ps@q6_Yu2=`94!B3ct{ z&Le0WlDB_ilO|M{Q`}Pme~!7>m7LH@|t1?;kFz zd;aB(3IgB(c%PGxNS|rMf1aVE{`AX#BY^XlH%fkr30YbGOC-yWSCyZUK^pu>Oehfb zMUoL%MMkzRegUDH6vSK03wqx5yDyoFvX4Q+#C}EMdfpE0U|~BBYZ_(QDa97!VAeOK zKuW&a%c$>kyJ$Yh{`p)L%n2Ei5r zDNMYnfCt%D%x9nqU!BpRmvk{hM}0}q(#3AoMsxw?n8>$86~$?~;QZ4EQPXBMI~*(g zPN3e-SY*p(#_1*Y9ZL5Xg(&x!jvZF6!ap4zMj&>HH0)aeqrv9ru?rc@8dC8Qbzg`+2IX&MGWMUKkaOgo0t z+aExOenW@6Sxufhki5R#KP#~uKZ?LT?Lxc5LcK)_WY!WZQGM_@msIfY%xaBv4%a!b zMx1;ut%ff#A^t|DndkAnw}42g*7Oye|2jiwZn#0mWffebb$&?%k$vb6CsQgPp$dcU z#49@osr`*w*OKr8hTpx-6vSVN%(1ulIt`a>v9pA45bHR!CFCm$iH@2e7BI5+RZ?Cjr=BWG^1XO8s4kw-^3?EU-Lk@Rd z!m&D8YqgX3%Bdh?6GqTqI$@#}l-x`E>v zpv`l4X#;0L{DJ^DiSCjR#%&?k28N3u3FP4Vvx#p3=|Iw*$KL+so4wFvi1Qx)v+^{c z-=x%2eRO|cop--d?|Qv=I`|I;7mFWqKvwEMGCu&=H&9Ttpws>U5AzMbltjt&8m{I_x$^{tz5@(Q^4nq?kj!p~ zE{hN@iPuE#?yID|Pll54zeE4-GI&?r*=c>cF4Si+@IS8m&w-Tj|HpL&^~`ll{;vHJ zA2THOl@~7HBSKXIBmAX9U!XbB z9RUeU@HUU~g2Kn0gW}W$cBR!92o-T$?2bPLCa1uTHgt=@rAz1a+8oUhb)foK$#~e)OP&Wo!3r+BF!LC~Sge zO>;2HenlqGTQ7e$ERJvTmdY}+${3xtkpEcFXVw*hFO_UK);{= zq$iMg`e*g=^V0_Y=lTEh;5q3Zp1;y(H7KKF^x3=nuTHa^_@@pF-21!>3oR3Lew#L> zg^5;Acps%AA(D3$7*a9GrwEIiXoIaI{vh}(36%&K0Zb4*Hw5iT07O1cOz-LVxcde( zd+TSXdBN(7nlTHBZfO|C`O2cBOjks>COWM7DJ>FgUd$0Le}&o!?JheT0+g9%ao@;M z5p~Q$8@9ZDdnrHGgEQWk3)W~MKda6cUvv_}7^zWWMBeB%DrMOmgI(!@ooXpLulrw_ zqF3V=Q`aG|0yQ(H^o!Y8;Vyh~`8deAJneH(Rwd$9UcD=&KgNoSjK~4Vx4iH*xowBM9Z9Ac3OuACsTe=ypbLirK@|Ae<=S zAeu9?n>+Aha;{u|jL?WZrN0jCnZoW_Pp%3)X^P(CTqT&`!)m14w^$vzZ*3BG%Sh__ zzQooKfz~LXmTFJ+_Sa~PpgAz&&8H<{|FH}FANrMlB?13qN%A_52Ks;LTK=s^6R-3l z|6LjBohp=~3|#?1dy0Ul9^5B7PcoBhSwxo*cPga1KAnrox}WicN)sjABk$s!^Q=y{ zAo-{={Rn&RsCfjUC@O+weX#k!{lH^0)9d3EvP<$cM+fO*c+U|CKWQOq0Mn&~TpE&W zeI`qnHY{xQ!lWs(ECAFQbs#2>BJiqXRhd9Qs+SDQM5Py0?wP;2OuQ;m!M#ieF7!KB zcVWzRGDJmA7aY|JaxbHKsJ8*Cz1FUQG7D>BtB(%NNO_JPdVidzD5YH?jzk7?KLh`- zIu}`%7=zG@%ebL(IWmiDF~Sp0xX=`>W;Ztn6T$v~HWr^&Ch$YF5rl|AGBT*dQuE{gXBa%`gOn5j99HP z)P${Z+O$ZgOLyIH+@yxkybfYBm2DjdcIq_6rvbQ;Wnjw+o53ijOjLVj=xe)U`}wHS zz6FuKWX(bA{u0ebznt}fj-xOJsp?Xy-X*39C{1)C>S#)xh4JPHop{voequEQl|qDr zJI6;%I?8$wD6*_eiSFB-!ahR9ktM|#4H|un9BA%Bf`|wLB8RpRbvCNaoRF9ZNmMo} z9f?5{tk~E==+w&ZL?&@LwNSl~Fw_>1BjvtKz7O#*h%00im`uZD9g|x>SLX*{LOqmh zWs(+|+1C5i5vodbg$JoJiWX%Q=rV4JKxqi3LDKw+L&Ct6(6oqQH#1YqK6XaqK~m3pNmi^a(UlbgDKbX! zQzla*&UMR#`sf?N=~afCw#%mJp_l|6uO~0`ynIJ=*tpGxuGuF1+kP;hqs#)R-9V7A zMtS)eHVQXHC>u7=D4uWWCUjG-=_Gankds(q1h8G6H_ipda|DW_x#G>fNNb?S8cn<# zN0(BSQ7GSOY`dORTu5#~p$KDgLBR+Ry?BaoNggVuzVk`mCe23x zDJ5DNqxG0Mr%&!3L0?nx7=1VD=n!7V6!|2H_xx#1A?KUm9B^(ld4$A{Uzq_tr2?C7 zcKBAW!zU6#CZ3_z4l^T@+PBgXQrIDGgO<@T)H#)2gsarM1RAUleQ=*m5EILIdyz{{ z+Z^d#5cMU~ivv##z$2uqs>YjK416JO48>^&y4^>Yon=yBpyFck|F6N2;xXQyuWRY z^0A#YI)C12<$qu6`uD3@9Dj(Ak_MIzqV@(Be@8oi|NAcxXL#g<*yn=QK&OeBoSqqQ zWP8YBXef@BE15SR&y~z!_X-mx1K|>MYwGP9$Kw~SIQxZZ{%9Y&B%O|@vCfOk3F*D5X2sQjMbK;IQ+bWpS*n7@C5(K|w`~FDB$_~Za+(FC9!g;^*fO41M z5M>z@oZa|q3wO9+F=YSqw1WPKdGh_?JpUn1nEaj-lD7PvfAAaYSQ`D)#87^WxHLNL zCwzm*q$(nhWg$lDQE-fFeOQ99C)Y_zV#xMcspMmJVN3 z<)}!)Gc659|6LS{fgG02ki=elclf{`wOP*qJeAXdZN4TIGYEf)4P`0%l;lL8$c~S4 zY_YMfO3C)jQ&>NDwMbT%c&B4e1fCvT@0?nt9}8z;QHEI(R2Ds<2Khq zzD5*R4^R(H#JV3PHTJ;2)6!+}=%)Hl1Bv`&TKXRtrT#ma5Vo~)u>MD6JU{M5`Zu1? z=~QW%--Q9zaYbu@E~01{hBiLPBcI+YQzL+=*K!Ub*5Y_iyOX&i{k~1T2`eD_`t>z8 ztZ*$rjfVF%|G;MADa~%8y>Y|K{S9Q3Xc$jmm;Tr0G1!1LrLQUy5RxE4^fwsnazs~mWGY;rSyt@(w0*D79!s~6%;4ag8fhUASt4+IAa3>5Lm+~DTwd$6HTUPH0QJ%S zkJTL0mxys}^ll7dd^@$pY1b4#3?c%38@?WsGm`bnI4 z-ni)If=rBN{8|iUsmF=pX}wH_J>nS%9v37v{PDMPs68GjDA4W z!%KD(x_0BW8}|Uy6H!K}(AZu}#MFz#L`x(%b~lIyZxMJ1#_%A6b95n@$Z>kCUSZvb8|SN2CvcuVavkYf*tCq72HhInq;T!}eDX96>mv024;rY$}w zpzI`T6;UMI;Ikd=nyHuvfnOJAtKlwr;iLl`WY2c{}?y^gUI6F&i8-EVG%1! z0~b9j3*CPkkn{gr0a>FJH8BpzI2#xuj>!Zb1z{TjC&pUY!^T7K!gFjm<$2TdNQWRhSgy0%U_fDSlK3e04GeA+e!_q4c3D_nvf5u zqlI!qyJ-CNOULx6nw&^b^%)Af)X<=KY@5D6$?h`lT_z8rNACrvL?R4EtDe=!ucj8Z zHp691v<0eXharUle0N9*b1b`dRHABTc1%%CSnYT08Kz~oBd}ZbNTt?Bj|lNjkFEP+ z$5i{6?odMYKU6Xuw<9uTl6A^ebqiH2DjftQ#l+B*42@CEGwifV;`B%LRB{lBPPs(U zT52C>_9Rv<$2bO152^;VC1!g&r0})dVwP0UXL_BTdj?m%{RJT(Fe&s#x*5w<77d-> zGjH@&#t$vK=0G3LHU%ERdt+I%4^-;yke#>`me#qcW7iMn-8sy=vWTT8QrErWxxQna zQOWGf=_7q}?C|l(&I3$UqNm2%#w#Be;Hegh7V;bMU&42YSkdI>?|)QSwg~BhPhQ^( z3g4au#~V2|-c^BAwvL~rXzc}j$>~aIg&mi9dli99N`3bEtxM9GYhwL|mgAOP4UQ=TW6R~j!0AoM zj1&o9n=Q&AbJRnmt^_$)J5Ye&uES|hmDF4(GGuNIv-6Al&lj!Hz&5BMLQQ6&FOwRZ zg5owg9es30W!os;Wu*HDxqrvp6pLqsnokG2{rvn76!gzf;GYuRKljxBR?4?0oY+lE zAPr;!Lje0cv?`X~0UD!M)~jh-S=5s>EYGvCvFU-W)T9uJ07kq^dQMV12pO$k-Mk35 z?<^4-n1Ugb4@0#M?JsS(I6kCFyuW;mbbcAvq4F5UrO2nZp1iji$>>k*sNU5pbAf?Oi}5K+j++X0nXKzQiFk;}s{< z)OtnFImV)Kz9yH0LJkp?Zr=CS@znVojo)jDnSdTw|EW4;C&oJ+cMA2{c_!mM)ceQ} zvz6zF6iU49BbB^BjAfbj7Ajt(Tgw);!V#@qGEYpBWZtY4<7I$$Ks%w>Q?)N@jpbKB?u$kG*2(pIXW4 zFFD43mojt#_T>0|xoJ}<4;Gren%w&Zs&HAtPnn?i9A-x^mEzS7DvB%QJIECeKMFmR zQ7dOcz4>g^U|E39s4;3cKlH(sNN9f3~_px6Em5lUJc*m_+m#WNX0+`J#SX2eEtPgTLX>R=I=!#_BzPU7xVo>WMWU3OO zSZ#PWG29of20o@e{F5F+(nt6?*zF?NBqAP#@1tJ)ySK@TecV7`(nva0#XYo8Po-QD zh{rAv#5N&@1jP|{T=l4VL_X?QX}8oZL1t2Thu1q`3{#7kQpZ3 z^ep^(2wba6j$|TQcyG1~4Z>M3yDn@`sCP$1F3!kDn_Qhri|+FCJq6%yN}SR&pf}(K zc;$csq+KrYR%i2|O4-2W%Az0ph4a+gF*RJvF7kA|w&}OwRLq1vBc}JLjxt_9Z&!+9 z8U|w-9FZ4-kjx4`oc@CTg}8JAKJ#lrsDJeLe};bl1pWUm#6Bh84NZ(3Y;}G&@cuE^ zE|1f+Lf}OT*w`}DX|;gwtXDIZ1&FV&R+eKVtsfG~Bk}7ar>DSQGhD)b@xD56-V0|g z{}#@4^?R4ii1d?TA)znTWg?mL=2KSD@ct#Ta^p+cmNx|Q5|Z4nF&zMM(lWft&4dt^ z8VJi$8NT-1tgfgP$33^DhQ8Jk`*pSUuDuSz^ag8Agk0__G=^RqKlS?N1Mo=P)*((> z3x6NH-f4ToQRk^5+F7&Jqn7F8V=Wp~k%fi8ljkUPR|a!`+-e=xgbzPl zHIfEWNkvECUfnTU0nS4wzm;vwR6{yO6Wi7?P4e5pSX!@z9O#NMHKe`vX++Q1pq!OW z&XJ>f{U7H{7OX#1C?%Xf0u(|(kS!w$4xOb#4Mi-I?wwOjVb`#=)zT#3)e_NJcJ1N*;X4Y?FtJo?CT5@{n&8kC%sinWWf!p4yD7v=!L81#N#hD*} z3;*`ZO=MVPC!fE{gAT7rBv%Z=)DX%*15cbCrpKV?XVmOI?T>W4UaMmOynN~)p-U4! z(Xb9wFE|B_K?D;7W+FoKa9e<-uYJ>Y*il@3uXnfzneb0RPxvIe1Vc;M7Wlh{MO2&c zvShp519TY(JNqOTbOwM*BzRH|-OFhv#F4*mNbUZ8+nhf|pLe z;i(1{{y*B@GN{gM>lzJ`;2JczySux)ySux)y99TFLvVsSY~0=5CAj;gyU+V|_c`aw zd#mf-UHsXsDyVv%X=}_e<|N0rbSY{#=Za(2KN#)yl|3<|vLb<( zBUGb5fa0namRcc&5HK)s@!l9~^7p8e>>uGr@bR454wbz#;@q$pdf$XaaRMi`B?aaO z0m0GY5)HSEd2-|WMLP+fFb!KQ&aV}_oR|~mv_1*^8=9AA^ zR#lRElix)9lanWYTT0b+?AwWL#~@EMT$1CkJAR|Y(1c)?4t>tRID0=1T(XN%w5G5= zrFZj(Op|^JbUdkg+08n4iB5)A-Tdnq%ASx&v!<Ad7(qnew@ z4!`|Krs&J!A4;j{PK@Ha7{A4+)zBA)==TyW=$ANv;QwM0f58C=f3t$We1GTq{K+#c zh+7l~$i8MFzxwQ@>kV8DZ-4f68Y%eNqxxk#WrDUqFD-USu^zG>+$ur>nZITuC2m4P zzMd1>Lg_Fw^n33R?;u^E5<*sZd%8Y-qb#>xe(uhV-+rKVS4k5-=@;+nJ@r7z$@&77 zs=zaev1KxdRnP3!^<%z!agO4$(sd=r|gxFn^l$StDwfaGqb~92q4K6rP7k}py zGrFbWV&W>37Ym%%2ORNcjILYR1I)dUhYla(=pq2BFRCQ{iXqzPKH-i*nB+tzaXTr| zqTXCr^*7eC#>+{u!qw+jm)z^At7m~DDZhQBQ5*sgcMAOfdmr zOf4}duTBn2+X06oF&;zP0myX@J68*0O*}-v6x3N3m&aJcdRBJg+rwRD2hhwUlde9K z3;^{L@wt(8#LTC;bKax5D?%>DDnBVU%fYD1opaHC11uP1%HMr z;sE04jH=oZS?fh@n)6I;D$MIAZ(MT{in`As9mOY0A)E^ifx{g5wvWd<*Ao|*)q_>0 z1-Ilb==LxjBKz8AY}h``tZO779_vJ-io-lrs})$R_E;1SabLbBiBgdut!x8PrDKo6 z%)<&ux0ISeBnayrz{ar4MQC zC$wzB$F@B4igsocY*fTx=i{%l*0c$j(V+;3k>0Q6Wv!w*R$i6g5`B$2nV(h@!wx>X z-C_cqIC}5wUZ94ltlc5{tCg-%brpsQaV4i(I)36ux^2R znxH`9zbP%Q5q!v(4Gg%uFIk9sibyk$Op~AUb1uU~L!qV&*pW_nO;7$J*MV(6i3SRE zC#Z04pfq`n`z2o*a;=8i4MpUO(Li>NKGQVIp5P98DXj?HBPe}J5Aqj=8f-s)K4tQ@ z2X?b!+mgeIDRUvUiR5qcUO|$(Of^1J>D!mRu# zY-te%B`Y>QyibYMLlTC@;wESq`ch&T#(XBoXqE`mmSUUA`0HpY;@Joqh52kNiYwtM zBw9(eP^7&@uaXu`4ZJ4rSZOe){Hr!U55N_FcU%`*Ofv4XYNqH4fU~6%Tr)}+5g3XN zC|F}qFWoM-c4&mcRDe^!eDO=)LI46KRo9d)M_)4tMV~oBNEsxnT^V)OW_R?}SF8+CB1-&w@eVDN@K02W6j(7j8xUP4a#~(jN@doieK#&z_W4 zcCBe)BTP@G&zCHu#Buj?A$K6}y5TS|jw>ulJ$hU~n4L~VcC#9pfaqAbkuSk`=0*J> z08q+~VEOhQNt^%1`}z0cpYIoO;5XFlk9wc*e?!uwerv)6uZ!_BTPgtOd>j%<$A|S4 zEfe>E!p$iTO$6rhwcpsYq@-P{%X!a9P_BjZ|UwKP+-1T{E8{Ojd zx$O;R14>ydf%Z%v2nUmr?OkurPkKgPKIO%4=anZwS|=%kXl_-oD~94^xIFCVq`Ce9x_%j>nQNA@Cf;6Z zy;pRYF65*uEfY$DY>*=22oSg8%UAOlB`cjUzv5iz{;nc&b5quPNz?XRoNh3NZ%PAmHB_ z%QmQj{c!C+0$yAG$<}J5(Xb)p-P|21t=Kf9Zzdx`fthcif@o$vxMW;kiG*GXUsB~o zUAQ47TjnLn`2a>MZEL3l&OM#LG(7;P6Q{SzMLDu`!5W{_wa=xfW%X>iZFxEI_3`O2 z`@`6FbTmS1={6z?lja;q|d4vOvznUoH6rP6PKNUp7a zU))rr2^R_$i{V*7HcM9Bb2$mdV1{8!8$~`{T&_P{S}SZ)Z|~2Hb?k3G&@PV{B6c$~ zidSu2_)2k7EEF6O_P((s9NctAasF8yP>^WSQ)%T8(`jRJXWwv-Bo>yokgy7Ccp9WN z;x$C883rAPFxl0iI14x6OMtb27VJ9BdF9&!z1?mimjbzrtn-qMJsrh5o#-A^5x%>6 zgMNw=%jLbp1-##h>rL?b{66;=1K4UrZ&YLSj z<6MKu!XGJtZ0NS7JkYnY_8~DE^cpL+U3FE>(=|ak5i*(3$WquH^OCN0pR}35^41#I zVV&H@9~UElZVgRl^t9Cdn3sV1l!;O_#=sj__C-}-veQfw%@srq2IG^p-V84+5y+2$ zFDU_L8&sOKRk$Cm8Om;!v4hjS0w{x{>_0pFWIKj}U>(#A{$XXr=U7grLqZPQHmp`- zFMzHhm5O7{msKo)tWe-OS`N?{=mRl6YhDqBBkBWDk*elI-#b+-cVm-EgRZ zddkS`28EsW$b3G$E_(hzUdu>dQnVbIbJ(xjF`@fGq*$tSaNG{*vXBC()qi`2A*0hY zpA(g=$eH%sLGmtwc~w#3j!;^tS%Q&+_a%pi*44y)uNo9RJizv&eHj^=-@qhVA2JhB z7PxhywxYs}1;f6vORP5%$fGcj6pN5L@0T%M`Em=^2r{9(O{P|9T}k3v@YZ|i_jQ@f zhvK^$_z%=Te9=4Upar44B>wAktpSo5+a8AJ0vI7{TN4_X?hyk0&|_rLi`3EwcK$en zVcCeRbVdB4h@Qgk0y8F|8=#qcP!|HO`5XJ`{8aanP}~CNHstrvUZIi*t(aoDNaYGJ zi^qctu#t@<9f+$oVCRO|sEH&{TJ=Y=8NORkGO(>mAg&sltpO?}*@UBjD$@Fp#OzBJ z{_&6Q?PBY4$A=@o!^>afXmZxy!%IBOui&O%-tPaacX8km;nVJE z?<`K)Nbbg-pKrE5b;8aKr*!_HN4el+k>F19D!VU6Yy~Mu^bjGnwqyn`NHY`bmeewH zTQOrP;6fe^q)2QI6Pkio-hKF{YM-qntja;JuH9F-YB|u;Go+QJj^OjP9?z;UGJ463 z9~{5DpqvPQm@q*4h`*ssuAKHbzoeS_XFEiTxHn9QsCi)~WP>+clotwH^65_*Fg>Sj zWh0~;?^-v}b1fbM%Jo&L6!EAj>7`$Htgo5Gqi74#r`5~9&j;qzWOG{XqyZV*yY3c4Cr=|Y@eg0Ll@as7k{m0pV0fc_1FMC-fh0$5tMR*8y!C->Y zG~7ws?73H{5_CN7k@xF*(t5_;3KPY@bjR#q#_VbHKn= zEl%1h*AEHg5gH@%=IsmH6Pj?4;5dSAb|$i)4yU5KjE;tN#}&6|$}DT(9VToGWpOCV zh`D~R{0uwC?3Yw__y$-g-Usgc;Vy1KYG|KOpj5kS{OAD>K5*l-C=998D$#Cp&4|ha z6Ae8b44#=6fSaka@JZmdwy_qMoC1Q?QEm<)bh0I*U}&n_4_<_{nf5!WhUkkUb6g8n z_FO3+wl=<8UemM!(*`2c)E=W96P?9J4I9;;(b`^-xp9eQd4If)GqAzzCA7ltlF?7G z{Mw2nE|^Fl2m!UY@_WxNHM1Y+H$W)lmli?#i$dkky7gb&QbnzIP6(PeEXMI140DKo zI6jU7iz2yb>te#WktJrjonhEoN(IN)xC*T$*sbx4Aq<;Ns9eI=4?YPOW?f&u1-dz` z#c-cpaqn~OU+-6*@qD1v7Yhn+LXs!VrweZ(TLE}|6ha21D9Fm^1UhSXf%qNMXo+|; zu1Z@-u(uUndk#BYOV6VW97GeeIW3=i9YRHxqqlQ{$)C&OKB*XKuDfd?t5$rGUUn$2 z-@9wpnun@wGqM_wds0`eK6P#umSt7ZL=jb_?1#*P>Ip9(9-LdPH%$J%JD^@U9k*6L zS(7@bqBUKW9&8eHVMdSrQx!=A?HiMZkrz6n^U8~o&n$E?uWrpkpz|tIINKfEpcTfoaqJEIZcP*H?PyZk3)n4xj@JS6l1*n19mE&c#aCNdC6mSP z2dS1tr;WEy&Ss2{^#cj6!fr^W+9EE7I|yM(ZpjjVFuR+;n&IYmkkW#35|R5Iw`Tbz zZvCsf)W3m}{{d3Iv)ukL*7p%u;vjVJUJA$7S`^^7r$VwcpSqT_@{5r`b3VfPhd8># zCNPGHIrr*ZZGhd&>3=7!x!J&68{srwOLz0|ar zZeCFvK8{M2F51Y`oK$$LJe#ZZOlbXC(`~uYKJ!fWvLQnil_us4ymy4TeM_PsmG)5% zXSpE$!Vpg*)j*WABT-;G<3_I5B-5jv8J7LU@Q_jOOvag&F8D=t7@ig0S;d%N;tJP# z52|A7D&);^^aDR!={IgYkQc?9s)sQ2@Pv28`apFe4EOJs5onT|-OYCo4te+U|9c}^ z^%p+=UoRYgME>8zo&WWi-`>p7#ORO5Tqi#)4WNa`yfQT9e* z4Mo(rfSptB%fL3?2{>QhK+?6LtIk;*B>Is>h-Q;tQpIYbqY2Bl>`TTL&hpPQ!r2># zV4-qMB7s4!hjSl(so(;fpy*nO=;j${i&S;2wcYxDrDbkwC(-Kzl-eT`8KF{z=BR-{ z7A)E$rt@r`cx#p;oI zc?krctZR|?chdSAstG^`paJV+3R4R~5s>Yz-+qiNRP9j>-xlLO%@#F)V4y>ZwUyRq zPxkqp*k@U^9;5vJUZZ{)zr}y?ZvVoG`b#eV%WwZPi^s43i}lyRZXDD!DCn*B(Fvsf zJ03SH(~z&>ASmJpevDDu2GOe3v~zq%Z--yXJ$@MR<0+02=SdnYT@>g2gom`u_=C6g zc+=f}W#u{$XI~!hhR2QVcX;W;s&tPoDTr+fPl9hdM zNp!5I$pp{NsmAt1u4swD9dxNhBfe{lKl)qSIhrvGbWBIJm)>wWUb1&`3--fFy8&IX z6N*(JAGywr2pn0?;V4VhNx3ZA3Rlg38T0OY@L#xc&#?J}#Gk2QW>qlzA zwoKD|S;SU45$dHkU`aHX5f+nKZM^PMd|JJjU&v7=WWO<3zQ&|Zi1u-;adB>P9bK1} zQjnA=$8k%la2RZ~pFz5p&UfrRbvqPcf&?33l}nu0h;LA4m>AW@oa*tZNzN|y1B56Yv4RIhH$#P(8W-? zkcUt^c)FpUfl6<_TLt_*biK5h4pdayT{MMgx?Qv%v7v)R2HKyE18CD#dh)B#$XB{x zYKSh8($v4Qv-3GKCBOYHq|0!~t<`%^0;2z-|NZxg8pOXaY`woK(*H7FnR3;#yZSbU-Coz>KfkyP~Y-QjteY@naIEzywH zEvlWFpUfMNb$h$Gz$~CoQ;JSVOpf$=j4QAx1mQ6UzpRL6JXG_Vs%y>KSN= zD@^bq*x)OLLHd2uC6YZF{dp=|WNDoI68f0kIziC|<>SjE&iO-)UrN2oyJ{pO)nSkG zEHyRrjHlfj#1{1!M$vY)7Y3C@s}}qwWa+>%M0U{}4t&-ciL{$v2jd1G$DQ1bsx6jF z;$^6iqo05uU+EXElRVn=FcH(Z3>2qejMB)pTh^0_ zMe(sh8Dq9d2YU|M(adh72lC+QB7O=3RE;r6ESyv&`>N@?D0W4}cwL*B7G8L=NtRU2SYzHCPJ#X7 zSZ9mFha?6C(>w7w<34PuuV{cFS;U`8ZTuE>!UUT&WxHLQb)I(I%c1oHOoq{LLv#@R z142XijTDJBN?5sFm1J$6PzKG`bITU62|V?dJcP%{T!qWQG*Y=&;GU%ESm2&6dNR}! zvL=-o+yXnZ@&l_-i`GO^|0H@^K%%~LR0B-K)TRxx32VpDt+LlC$#0i<2Lpc0)ptuf z{AGD;_0KHrZ&B^ih-8V(>`us#END&behE4mPMm-<*HkVt5 zDL!DRO5yn(BGIRycTsW^(FL-7z9QykAjCjMH_s95jIF@DWNzT9IJ!Ms%CWsx3GPW! zl=m?R-M>`&l(+M)60F5fTrguY$G-c#b?ck zw4_-0&o?|U+VAIHV6XyT4X)yX7maC1!g?wasI$q0CP8||tp6D$Jz zGT|1yz0yKu-Tr1AMZ|s%n#m2rv&4L*S@JKD>t9y^<#mN^#dqmxfmAxo#7XxM+`Hh-N^O;NOD1;J z`U|c^z235{u5rKQlJVTng#79JVz{yU@0G6~xgWLyDj4VoB7J;)61JPn$gqZrDevPP z#@W_i#+vrmJgi@zkMDRsv_@DV7^sTrA!Ta=V9LTpH?VP5QDgTnff`83kiJ2xe#X-m z>TCuN8-@Y;0#IyMcJS{kZ-!xc7U!~Ul`Z|anQLdQrY3w}J+T$kaY;O14s^w$3EVpVLoaR2%;%PPwTn*W)_cZMO z8{G&!Ah4;SKpk0XwVF9HT)k!{whDUWhigk=Y78Aaj9JK?hj-oQSuwp&r?S>Nc8n*TBe|Wr;}otmoO*3M z@BsStB9GZANe;Dkb|G-+J5XZ<3YIMC(JE*8o$#8Bj}eL+hHY{Xt{0!nt?!=;4$ z!rMFTYgdpB;a%FAuZRTf3+4{6>8pP>XfT60xrG2|lh`BP1VLi!z}u(}q$fsqu54YuX5U#$RLFKz44xgfW5{GZy6`&`j@yl0z?4~X646c>R+H07C4B|5wgE9euEKjnl9Aq40(X`Nk& zusm2`!#VRHQb(4Sf5Aam9{M<4t!>}}y!TNxA^!Nh7c~A_4i3^-6R&=uOm;Q@uYv2$jL3O{INi=ScZRpOpNzGGx z48!#H$VOhVshH;qg%V!(1jZv~$@Kx8D2~7iM^Bv&-8F%5G~QQwe#r3*Aq09wo-7H( z$}=c!>fUde^S{(> z>i>V9cK(%-{@&h9KDM5weV=1vF~A>KLDW{HKha`Li<~pvY}+M0RRZ z+PF|%&ajBagib!m;z^@fO}SB}SxtpeCm1sr1?UShGl@51f$@O@gJ9D>GRWXnHnWJ3 zS$n?5t5)4fNuM#grkj)&d9ILnIUhcEXptf*h4;0bq7sibIJW7@f$2fUxfgr%H+|<7kKyW~znJm{=3l`K)vOHDMs{T1*tv;&= zQf=9w;xcHuZ!+h7m~Rso1we`^J9$`9XTnd7N?#<#J!3C&+&b1f9OY1Q(7ToDwbwkA z=!cB~2J{e1<|(9)a45|qImjC=ex_Itgc^dv;;4>5Ja*oj-L(5eD%8NZGDjDMj`bsD zn$EX{{gaXdq5%7tzF&1|<0Li3s^swgU+aPYaR~6aEWrz{0+=bT= zxrbfnq6&ISXBzbc9cS4~Ii9H3gcIJ%Vn+JnqSnBL1(qrr#!y|jx(nS)yvJ|$%DBGX zKZ~+9Kfs2h7a!RHE#*fLIV`5NGzQV(9sW{YM% zg)bm(Wvk250MF=Z75j;Auu6j-hkf{xbFmRFTaHXV2UBn@+2%hMIN@rp3(;1Nts&Lp z8h~a4zs(w?w`9k@;1aw?x*xK0e4USvEJ1}g33;2wk8bh)9~z@E3J{cHCZ3EnYi6%l zO1JP?Q3ta7xsUoyy}^j9QQNNN7{df61*_n?fKq#4(mDimgi>A4e&4;RKx_cedUtmD zUp5yF{{M3Jzu#!ulyrVuwee=1X>n5qv%Dt+0}D!+nmsQ zto07t#2tuSVBCL``GWKO!JBk@1t%xrQpeTQ?kr1)DPGCJJ=!*HNpVDc~+I)mpL@qVnE;du1uJhVI0 zwP6aoeJ&$GMKL>!9W4w1IS0k+O|#aPcqNz-L?GBiWS<;IN>EE|_*{;(eU%crZFxK1!L(1R z?oIu9M2_iYbw=ExvZcnVwOY2zzs&D&) z0qG30o@Q?yFHUZ+>8cBO?V>>5%{IFj&WTxI;R|nBU3U;MrxldUa`!Z?FUF1?!#PI2 zjX)#vCU=;tMM%p`&x-X5yj6-a0h-lGcSTzE*<=trMeuT$HT1sNtpbsqq-?|I1N_Bu zqLTg3DL*Bje}`|UD2qYo-%&1vUru^6{AYe9ZDVNk=i=JGU*`Vj>~CqcE=A1|NeC&} z#v~Pj0Si}8j?r8(f!7yEQC5vnl2@2OIDpUAPg=%giuMq@*@Iu!uIr;qkCxrKi*0~! z_*47Y)8jX|-+%U9TN_%8#R}rKLTzDvR8(ePj^@gjm6+VFvaObZbm^br{mg7lKu`T! z#7^;=l6Sb{p2V z1a4_N=y4`u_3@k{mz;x8gfwxjf|vAz6oi#=t-_b=gBXN1ajim^%!3?+XK}3}m)wJ3 zgg9}Yf|qoIB!t<*y5ye1mu_h7;DBVvK;x98!%_KfwbquUz(#f)W~8+FgSC+li(||H zwQqF=)rHN2b9IiTdnwmrJf>KF}8gL~90wbFuoY78;eLMpFlskx*s|hD*zAQyS7Ws|C zV-`4XSwZ)EsCu#41?IH)u5sGUh-2BHDyqC+C!7{ScRv3(&q?hoqWzoct{K7 z+1_nn=95@}PNC9H_Ih0s2o=};4hYO9%=y^^X@sYX5X)+v)4V#r{Tw&SYM`+j*4h91c=Y~wwm{CvAQiDZ)V7m^57lXq%pR= z=qlw<;|pKKu~dB6Ls^?VKdXgt}5P4c=BV4GJR90F6*& z{fI&~Q8k_J54qmyqIa(yq_Bx{xn+!;G==-6w&Ytpp%}D1u}}f5Ig?&0ijn))M2p2F zm?(}s(pmGx(KpqWkQ@0?F!e&vSaS}9D4lhAQ>flZsNT+40yY7@p&d-zZCkGWV&EGH zL!msu$VEs`8=o)PHl4>^02}1q1KNrfnlbh*8Bxn)8a63S4<3L0(5q}k&w8Ypo1|0s*JfCwY@oT4Wb-VXHoL*|(!2 zyyjxCna8wpSv5!vU0R%lV0?F{EbYu96#!z=2Zl%RWVj@uczLstm;Lo;+Ss$UgRQayArv%e8hvIw3byx&oRHVOU*Sd1D~*8=}Y z7N>C(Xgq?xx$B5JgmDo357d|@Nq@V*ls2~!cZfb@{o75tc`;g7Eu)lfvj7qUs`14t zK#_j3E5Um5`hZP;)H5uZ6VdWOOYh8!>k*e|n@dQED+lT-)a`x=9R3X+*$p)7bWsJ0 zg2>fOl;|p9K~`_^{SzOOABgKwyr`GBI2Y>N{mfNQJNAG=1HMywf;;!s4RCd(f$NcT zJ~v5$_JF4r#cf@5QG4J_a=}bQN+oJUiGkbEv8b1Tcw3+x4YET2#f(erV?}~HdY(3Q z;oQ{>+}Fpi3GN^BDn3`vxn@10%kw15R~nYqX?t>Hbt-fSAF;?EDgAWztYZcK>~;hv z`XVRZ7A&-(PRG=z;KP43E`Q_{>E4#Y9#-_~KciDHgMoG8YRsC_ z@v4OEhB_9|ZA)=Q4c#y)79>jWwtZqPQ`BPU;yn9(DiBta$O>^#TJIzt^#!0{iem2lH3$f7^dnWBd&-{tqs|e}Rnu zN((5DuKr`1rPx@DE3vK(31Y1sf+r9cJn*Lo-=I}P8`e8kmzdSjuu9|2_eqvpec}io18)2a70;g|*PhRdQ-1JXESrx3eCR;@`G z(-52YU>%0TQTIb!H<+mG84aAaeq=dD8XhL*$jn}6o{E5iFOi%Suilm3FU~c?Cv$kVtbx1Hv z3d2QO7$?d8?V&$t1L4oVKhmUkVD(SV#lPzT{$XMFw}<}sy6*32$^Q&cXj77V?+LwQ z^I=UpoKos_#d4)|bJyw~<@fmEssa4C<#1F8;i2g*M2ohIP%g~^xJsFNo=taKC8(wU7 z4bhI|oRYIMEeMKKeW0s`7OcOUNwerO&8iVvNNVtcjD6p!lwxI}lU?Ah)_M@&GrTE8 z+E&lnf;UR1jU*C0NLeEOV1>zUy^{FX^@D*H5v!7~|u&)aGF7_Zh$rDMhOigj|JAX?y-{_8*KZv2l%jgdP?2T~mc& zKz?pPMk(W?R{54rh(O=0q8Z1RG1;!YMk*dc(RX|pG0YRATqDGVA^^~ZCW$xl(VDGD zwl@U{r7<*$%4Lw0NZMmFk4R=YGB=IOIp+0udQqA<@ciD=6xUglpnt!d7`z9MKW+Hm z+vlSHEO5vh8Q9qWzL-$Y@jY(*(Htm9igB0`MC#Z1cHKxIODgM4jtg!El>zKOrEXe@ zBprjYB<~%nPn}%v$H*3^=@SY`PWpd7Ij* zq~TE8i8yb2fo(Wm7auNtv9IgJ`?7bc1GM&;Ww0`|W9=#?N*nJNi!ml<=Y#>=TLnyR9=P=s+{F|7!;RGdsO z_`|Gj-KiN(0_)-|+8)~%hQ-e~tpufPRshP6E`NMl-zCYGxdb;>h!G~DMuQtQb+Hvr zw0$HhF_470iHmbHVeLn;&B#T*@H>okt6@bqV3mG*JnvrSXM{i%a|SE0QiLj~8kX7A zk4m1Wrm5^VcVUz>1zFyL9eOf7=1ol^Z#9}>a(rKYap2kLk_ESRh>QuR9!{s$$0O#N zB>a%KDxHO&%%xVtaR03-CjiCnDJBDh-Jb7i@3{;xh~^-WSj=HdpO~|^zurql`nm$S z+R2bLjfW%lMi$}PAW{7+>hvj!)1tW46YnEFfIoEj99%O_ck`Rx>7x|xyb*d5fQ4SM zyMbQuYZ&6`e}b2oCWNbLzFu1E5=SWc<_K z-n4oc0H>v(5eZW>ZRKRA7V1qZ<34DhK0+IH%EEI42ogwFwm}*6xL>*RJLsSL^T8mnyAipFJflfl zAg7Edp#kf0AA-EM9aRI#_c=QCRsF|F3ty%>Iahr^WGBD?)SC*BZYK9!$sf5;KLR2t zpAdrY__~OU^A~fz3U}w7r|?YK7jHax==BKo@x&Zg2KGO^@;g7Ga*GL_E{Y>2%%9hd$@2zCl*-0M&VRjEwR0-c%w+@uXq-V9o20 zT*m0VjB37_f9(jpvDtc@Kfmfg?W?(3AG#sgg!tKe5o7Q&ZvQOv=p{d(Mv*2$#}Na~ z67Bxghx6EG{}IF1r|}h{Y<=f(KH~YanorWP4bp>zYDfPKOW1R~st?QjI_0H|@yocw z3&uC1a~iCl+4Qys{pS!>VEE^V$<+uBjN`?9Su-r+k!EeQi9tVwmbr1{JX10ML6PESbGnf-87H6+ntO$tc1uI1wC?jRe3M zD1yldTc;*=A_y*$zSAI@rM48tRgK%B9C)2|A2lYaHCs|CPYJiAA#9HW>KFniNA>NB z5L|?Pm$3yK1khGNC{HQ3#vp8?_#6vqn-?ihIk%|6ZL#LH^%MeTVZ z?{ord#NJ#=tZ7__caiY*pjK_sa!tN;sgLY^iEiWpZA9G=!fDW8U9?BJBiVXI+_=4u zPdtw%9v^=Do$gwH(j>$8zQ?2;`B!O6J%vuUvzcIeN;)$8Xq%`D7gHS|s zc;mt=zGaZ|hXad##U}?k@-nC#QZBJtlqP-a0irIM5$H{g;U8I2CMDyCP`w;;bzWOe zWjQ|MksklRDVzt4(Lml9#ldXeXPvTJ5#_IEpqv}>u$esCN#@S! zQml5&r|kWrspkRy>T+W$ZH+RdF&?iWARJLVa!IvkB?iFNXo)Ue zxc0LaR>4Y75zouP16@Q3Rx2)5pnT`oFm!&gzGZ8|*P`)mYDC{K9f{y!gbtNq%rICEA{E0+6UYhvHrgL+45YZML z%5s>~zW{0j0WzDU^SHyW@>`mJvK?qIc5W0t*5;Lj<6sHe`HB?k$}8xVd9X z>9iL)Bv^3IH^69gHmkV%-4zKHM@I$uc*`;Y9^Z`W*b)R^^EHVsYgjy7^C-13b&RdN z7Nzpzu&5i|Cww5@3*S|cqMHLe@NgI%#(r1 zaV`spiABNNS@AZ@`%KA+(`W8ww*zFP8?(EQgA< z(}Sh-z6w%M6}>PYs}uPx3+PIr7O!dV;O!1_8?y3S+j{T7BaNdJe{1HNxVZJH+=7ER zi<%%wcivzb7Z;4Xh;)w<*JPrE`lW;t6z&Gbi9HX6_LgsG3nydwSEbKA?#nZ4U{s}D zE@hdhN9y}GEyJzd_nQZld9GSXr|E<7ndL8%=oGDt&Fl8TqaLDAS4@+PO;QIx6mya* zgU5d^S44o*pn1QpU!2i+ksSMHHpZ9Wv44V}PNv}$c;uar+XNJoD18VKJI?z2D)@!8Ay+EUaSeT@R3rf?~la5 z90j-d{oR?Leo4{P`)AhkKMj?Cap?a>g8HAcKS`Pe2^#Nvu#o!4@o$BPg~-BH6a#a4 z1rcaA3Xmq)u?))s8155@C0bHW=PV&FXFbnB?sG6*SV(FoCcsVv-X1bn9R?SFJpA6t zRQ0zMPx!H?#fAZrI!A z{FHc{Ewhi-jshaH`V+jg>Q)ksuow59u&h@bG+_2bkK@_cXB+7jF>*O9XD`Thi|&N# z@rWg0jy!)@U~`$UD)#@7hXV}OH^whcC+5?AnFWD=FqJi{51dZQ>42RNGSiG~W$!dh zbSH0y$SLX&Adm0Q^_TR*!qlt5cWoP{|IVC2S5lU)$)i69OWR~1;YR2=vXoOsK5!AV zAB1P$oKKC-Y*L0eb6CHce*w1WWhV}^Tsd`SHH7uu*ibd=Lg#?TuV}^T?Z@Mk`}dUz zF3VvE;#qT7t~h%4Aq%)J5GFjH!WU*OWvvtC$SZ^A0jBNmrVY! z5&U+)UPtr+9NPC)EdZ3d6W4b#q8K7iOcY&8pzi?Q9K~KZ#Lf%!B#+kk8c~y(9o8+; zgx?6=mm<7Ue?{!N<n}CU2I2x9>B&8@3ye z8^R*LcVKJaGui{hCHD%~S=7KWGKGol-^8_WaprNs`*`P_A(>Fd0ngqg5YBrgQ> z=aP=!VmDF?e9hT=+}`>dn(*(jTlb%N-(S&$zvt3FPTp&^Kb@~G`N`!sl)|ol3Ou~*`{&#b4(Yc~f(YL^+)jYPDtM_L-g;G9rcB|+E0^4YS=p^h z6+Aq$3)U3r3K}G+vNc2$w9A@M61Q zrbW3U*7oW*BWZii=fzc@-AmP~qo#Wsa2q8`XaG@;9jEDPEZHB zH)4xWDjQ2_&H){ZLWsit1zrr%8Y^E6X`?PlW_P`k%WAAXZ~rV}&KF4!W|tZrs5!yd z_T~e;v^7(f4c3NQt392vY0pU~M}Zx@%#c*Ju-nOrIKuN;ePQD$+hDHwT7GxFuGBLJ zRRG%~w^f{du~k(zho` z+NV;-!q?R?6y*ya`vl&=HZS}qWOA=Mml2HvDtaS~!w$iLvH~x3^(203Fa;tD8 z%;wL@a;}NClq*mhbe*7U>YnhM8lvGTaDwxvlOdr`89AS+m{*F$kmdR6oY~GuhPba>5c) z>(p%qx-}bcrLvV?OS-#=AP##~?L9en2i48ql-BF)AS>_zCVzs7#4Z!m3x@_6yqS?t z1!%yt8WFLp2324ilL?qNL(GwkE8ym5%fgl_M?~Ic(7~3!$urHA&^GAsKK)b5ySqbDrZ!u%&VlnUf|-XyY2B=1}n`iGb^lUqjUh0Q0!cHDiwQd+~}N zszH5q#KcWlcCs`fSu6e55K}(@ImSNKqn+x3n2*}?-stY%o5b=}P0HcJjIfm45=M*F z5R-^7e@;o%3yr8kC-BjfGzae}`wY}i9#;4yv}?*qh?}YWt5Y^WS6O|Ki%G`?wOQ6At}s=oq2OmdT=0kV7vCD6l2Eh*FW2 zAug3EqPNn?PN3?YRdlBwY0S=bwcxVqH5QeC3kndRr;)T$4))_N#$<@7!Aeb!LcR{% z-6M#9ulVNm`eshuk-eUILm=m(=zsUJ@sfS@w!U@V<#D+FO|ctfTlSSP#LV*tj{iCQ zE^W7e7XLreZdAW7cN8MipiB*(YtiI$a^2CuXUk7WW=mxu`?E~#Y#GnbeN zCobw`d7xL^1VwhTtz!K%Q})qHXV0OJtzv^$;)I-S_tc>omoUI3Y(kss(=?Hs>u9;N z?{LW0zwfY2U`cbObMWxNxHFJD_(>Z^?%EVZ7}?hqWIYoT2`A&Fy5T% zjpcX*QalC4@QkU7LK6i>|5rp;)lFk2Ry^XfiY-y~FMAHP++B zmwg91D(srF<#5p(ZW|%Xg+uDF4XaJXtBFLHnyUJ;-^9##1xqQ~Q&R@(H*i?OIp}2W zo)sfTRJc4GzjCr4WTDr(-L!!&VzFd9w4=@1~3=#jX=Jzs$us zH%m<_alcY3(i(46B1G?^ko1lIMqZI=I_9DA`(U+{ftX;M^kPi2E7=ULTm_xrfy(K4 zd_}gNVB+vAL>s>_60TR^_)khzLi?FHnPf2&9UK^`%m%G7P8p64+4c&`T-gd4dfu#< zD*o~XM{CA-SOo!dUD>Iy!A77b$x%2>gIqpbOecxusTXhN$%Co$57FsiE(;g$I~VT; z-u+Y+MHhkE!HY=emD|09B<%6ReZ*6sP?im=dGm0^2`P4L%P^8z2ArXw-AOY7ZH1zn zkB-9Ne5t(5t+P#^eZ}Hj*H#h5GnP2i{(k05$ zT9>6AKWJ6k7ES~5RM`LMwY~Dp=VPlm;eL`*gd;Ulqv~QavJ{0^>H6Ec$PTA@d=k>Z zYmCn{*DEukLY}?Gw7JRJ?8$QMBAyqF(p_t5c@Olfxe0k_UlDU)hiEne40oP0l6c7o4!KcKmB1E=op=^ zT0q1W@jca#;X2CvDGQ6wJpIXU4^@orIeDNwPdIj7=Vz)efAA-1)Rn4L4R;&ld`E7_ z?KmN${t~ayzNjzs4cp#P-et`@Cqs~{sjvb)V)FE)44In?@kRS|U`jCTfC#c>O(}kfNOn`^pnUVZec3lG=$M#8Sb=VvH8+*RS6Y zy2Z8I#OzZxf0*ve2QjM3zvzbh-N`~!ck`BQUkL_**vJ?5gu`M?&UJuTLQN@kEf&cAnXDhLWG1)ULZxjUzsV9wChTkm>r{8ooFe-%NiU<_OkuVh zpjZW=-)fMf?qsh)nx{Szqx~E-XP&B@YT2=AX~S4zdr3cWIxG}3g+e=pPCF5G|9wfl zSPdidie@$(Uyn|mJ(8{9lWUyRyh)mYGaQ0yPzG7W z0i2kDZDbiT04r>k1t|P$Ezvs(pne=67{?j9LmHwZx#zJpz-rk#(C!W-n>lFX3MTCu ztO~1_h#E+(*Ar$3EQZ#Kg?Wr2&$xtq1VufE2vledh+PJ(3HAul1m(Iz$aw|oGX&+5 z12y$}5b($EL3Bxix=vuy9>F>!AbhfWHl0B6n|cQYdYCYKH(emsb|Bp|VIGmeUdrH? z74grhkb%gsfOkA8V0KrJH6+W2SjwL|N6?oT#O5*Zb7xeBT^RRh>}4_3a~&`nXV8~w z#Af`K_8|S8{+K1$Z_(t-c27AkXNp=7XCy#3CP2&H9o9186W9Z9@IoX&5DDO+!|PLi z!4$8(7;PN2$8hzSWfsgc1~K8hY?fx&XyFwPtyKh*<_^)Z1aF%N)12L7?*{skgy>Fu z=>e(B5Om=L;R9mm4jug|3_x2JX96JalER-G2^iaO!2*aMVUu1+Uh!SB}dHhIKX1i;f;ng&h6~d!vDg;m(l^{MPvnAv7!U zq2w~{i1*%BtrcR4k3*HfzsEms^>RO`tdyU4-NiBJ6 zWv4Ciw-bhw+@1v~RGxrYRV~?GP7H#yKd5?6i2B}`X+ZLJXQYwB0rUmyP}rR}qkR>iAz+Cu#Po?ga!@<>FkhJhYI!x@gFSCDbffXlH&Xx)St9DWf=UwWj-}=WmzG z;w{LffUStSL9v(nC)s^tmt2T9z47b@+`i^zi1w_p7dh$)f^9 zC;krRl)sZHdjr+YU*f9ec;~s(!mawc&+t!^=Wr0*2bR;;PN;^L@l3Q#>>9ZivAj-t zay|f$y=y5}Y-eRnEK^qnl`}t7IUu^`U3^%!Zb1{i=GN|-nQgma~EHs(l^XKbSY*A zgomO1dL_{J8a{o!#+xg?(wB%?&a$uNo;dux8eeI6xm#Mg0-;K@?T3^;{lo1vU59p15s70w$lAhM~m#Kog zb+gPfY%_+Ztu-;+7G-xMsNFXT%dae0sDm)2)b&!mb}&mYnM^%bTTj%y9N+^R6(mWO z(_2D@&08PL{e+n{?DC3k&1s=b6b=c9Fa%cbe_J?Kw88BT|9nfLY)G3*X*i(5lHOxE zAU}8lZHb?d{^69#iFSK=pEBopk@()O3vAZhJhWTd3v|tOrezs_4da59G?lN;_RM(a-~R@ zb$Ps^A)U)t=uc;Cyb6^Wv=6YyOeC`7;_^+Y0DQ|qMpsdF=T?}{p;KSLH(E77IYM0q zxg`wHi%(P`3q#TS!$F{Gi>L{FW;CJ3=d9gW$%Wp*C!j5#wIDNKKsQPxi%-lg`$^1N ztTst(KE$jR$}{=OK@nzi#3s^|bvmy=yqn5a#0hHpW>_TbaW6xvq$0SZv@$I;ZhZq5 zt=D%G3DO-YhjB!AahPBD5m+m`C~ZW@TJZ;cS|v=F?bK769}5@p=%971g3DNnaR2G_ z;i3(Ug8bCRCH<{t=YQBn`u8IK{{&?Jr!uZ7P1tcw4W~zVMNyAmagItsg(d~8fJXsc z+44C(A%PDOXPsl<^Kilq-^vxLrN!js2qXO4Lon3Kni9;9gVP!u_-_yYw$E%dBfqoI zh7;lW@2eO8s~7r{j&I#ha3GU`xnU5;Ry5#pecqp4CjiYfuaQ<5e}#wue`m1>nMHag znBeC`7ndg4NZx3jk%8RJpcoQq&EqH0*}dTPCY?q1H5-NF))B4$o!PUBZ!C?Fa~e z$km!_!4ZrhVRJPxDsXbz=y$2CnzlzDl{LJ0h(q!dA~WhC^IjNOT)MW6PnTyM&`lGE zO}sc`uVh^SC7XktEh5ic@Uv7+>*g~`*gL6$PYsR_8{wcj4H@jl%+mP#TQ70cmw+=% zLiL6&1nW~B)k!U*wnLhEZs*%7IkM5AX6a42g_p~vUQK3Qg+0o8Q#Nsv@`>lT6AKtP zuucBV1zei=aF)KvXjodmHY=rQWBDK8@!>leDY4uUn=qw!*W(wzzF02k^{c@{VUI{- z#=6P5*dWmX%D86F-u@Y{|_vlSS*0|~7)EEnHaPj3P@n-Zys+WveG<-xsnLo7b)bI%>#et0 zfB9geS<_vxvx%Br(Ixs;B$~==gP+J!qpfTA1(#uu1DQdkb7pQO`1OwHL+x#PU7XSdSgd286ARs?lOVaVRc469EMAJv68>Yh)~oFoPGUyY(_G ziV^@Vh`l%%gmAz*ZeYD6i{ckRr!325Us%EIEnaUce-L3EdW;Vl%O(lV@6S&%EMa8# z7rGF8LOikby`E}pX!+vRskiO{N#&|&NzOm1bAPQkLey=NGVq}X-Z1sz+~fs9vu~2) z6rti=p~uw2#2~|j;RW81_Ii?l?U@8QArYm-z;6nje`EE-5}|PmQIQgWIHEL$`*y=J zpn&W&D!d+!(g1%fjxZ$E86}AFND1qMK=d21$0FL>O=U!W9Ri(Q%=i)(q98MtS8TME z1hj(fkfz@DK}$=9%_uN=1c${>W$lpAeI$p?PE8Y()O|#U#ZOKXRM357haHm}%PTPY znEsdU*I$Whq1V-EFSp&P-@0ex~hX zI_q2%oXhd#g`Rcw9YzLKp}9|%z)<6EW^SnfJ8CVr{2P+6}p+8KW^rHva94y7}2q2tCI$}@>F9nNT5d^+8lO5fW%9VBgtNqivWdd;wdcvhkXAG;W?-e2EQXMRN5air>Z3IA8CT zmr+lzobB>J^$h)Hd4Em;i4AA&i(w!#)#$6&T(0@x|80MPqf2&?U*6#Lo^Iv-Y~>L} z)0c_PO_bWMu)`6Qe2&c59?@jQorQg$-#Bv-;Uu_%u0En+>a_lo-7*Y$M0jaJ<<$zLAc z-`lfb=;TAK=*B~cW&BkD#+&SK0-NFcFWRAePm-b8+l_GWJDTH0?=EVgl-JpCayv0_ zW728;S9I0I-Rjrja2^TkGKI<%jPtWr9B|(%QOF~c8=uD5O`V8PM?GRSI$! z9c2V~s`~hn9B&+GIaPP2Vk22^Ujws^vcQ85%6bNxDY<7;RWFjIQq24 zg?LygH5*LRVSj7{?XPe-iID7RNKhi|FY6Mp_Mn$>j_?IULoh8cb#K=?y^fTK3q#*s zg@x6_+NR&JwXqEcC7t}{LU zJ|ee5`E!-<41N9nOY9?;?L%bqTl92Yq(d`P`TpBj=sVWRpJ2}qx&p)WgKsSP-xQAH zgGe8dcizILiIbnhZ~aw|ZzMTt8^iXs;BUM z^B6!h#1SyOnEsdk4Yi!bX$u$Mwu8*j76gV(KH5k9&>J^_-yzjL(L z3tS2gTqJK38PC`y?1~9og6s+7%WaBm6GM;LaN&{IZS;d&wH+ zqo)%ED{#p^K$yCn2Pj#lc;l%)bTTBll!LvLd31V8ac$7NX>~yH&K1TPH~Cu$vuK+;y@*`P~7r)I--n zm}k;z^~@YLB5=VB)1%!xCK`D3mdXDP{uidv+$L!%_=#so;Qk#vX!bwDH2yPa|7T_O zuW$dYwn`Ut#FxYf*c$m^I`$)%$H_k+0Gw2rfEq3VLRt(>S4KpXhf3CV7p3o{F^QXZ zgSLhG<68AEs?a|l7I60BZ4W4OOB$7bE&cUt>gm%$Ju#Y>O((0y?Vou+*0Pw?rWa>9IV5E_A4(|WhRPyKP$_!0x1^W`J^j;sPis?V2T$)=GoHN zflU7a7hfkOL`Hgv%(1Nan9X!JRvV;!8s~5bP6%Xpgh4+yZk1ydL6#V6lXD_t2Oa@z zQS+CmPX&)PidFxCd2>%FcG=lVyeqv)j-)}8Ys(IRZ|bcMGR^}(SH!V$R8Kf?*|5ln zvNy+0qJ*}?C{=PZYlhIgARGORvhMWw0n$F=91=MY_PhljuAq03^In5lje}qiXz9mO z3fD~C*k|N76K!TZhKB$($Xi^vWcAF{GS_HevLo7f=M3MjYRZj2&W58RRnlbFmJu)~ zkAqbA;>Q`X$KLKFUdNWdc7^y9nFX)ESIf=To;;xbnujl9g(Tjbew$GW%H#}>uyGTY zHC;Ga6fYZ13!e;)#p5!VR4DAS$c0zPyVo_6X~YEAtZvsJ=foOZq5p$0N6{q^vr(ae zSJRZLmQexvY`kJw(e5obTF4$_BQ~9;g3CG|A&ZjHv@5$lJn*Q?okK?!k0}N2yx2#E zJV8LOO8>F$=beUI^iSC__qA2KgSuJSpEF5PlAc4cIHbXB5#~Ed3<(t7U16zdIdG){ z(Xu3VJv(u$NLf`|;Sy3~$n%j2*ZVhqpN|T$YcY9m`9g!AC`;!FB3B&YcL=4|{L^D|0 zUKQ+)t4GU4wRAinVmxJT^~-IWS&U8UzXfiu9$PfEcaSmT+K$T)PjGoHG4t4nOrKge}f0-$Tp5 zx*?`XJC0_I{3#|>$Ka8n<*8v*MK&AI(6S1JLy4=o1h5UPAu_?Y)r?7gS|ay5L+IKk z9ef?35>tht-GKgG6bgmhg9N8RYJ_a;iuQlCcfonSHYljB)VVG z6Ni!L=G=W}xd_<7ZX=lQ{?$fe@lhlUZk`l;1Afoo`-|nWvFS3zxh7DR&Spcfe)h{* zoT4nWnrH*6^8;04RdDM>Ig?*BK!7OlhO5_;3~Y}uDDa4L#4{a05V<|%1F<&(8FAD zGRyFAT!=@s%ghwEPzLt^Y|E+7dQI|RMRK*_vs|M*SYfzWxuURD^m;@?d|)ggUCnR>P;&a>!xs_u{Wgel}T^W!P`#=psA6!bC70r@FnXthE^ ztXR=B)Pay}7CJ#8b}GDkrkqU}uxk~SUWE2E%SQ32VEESje>wp8R5vd4PtZp+r_wb8 zPueMc~|PADu9AHOND+ft5SkpBuINQPKJKRzqJ z670VVBL742&%Xl2zuTTRWx#r>Enp5!<$5(7Qc?uMhH#36j2wbULagv1$lxLvWuidR zJ+XPo5Gk4uda)-gTbFzlDZ$XKj#`K!iTFiE0{BkW2p6=WX?0<&c~0lv6zw^g&7CZ> z8T5wWOKdT@@&4OmN`JG9_v7*=8H6P<)(I5)Xni~UZolg^pzcVN(YHS~(jnc++=@!zt5uu|r0-^x;m0!#dOVFNasA@1lqKrtgx6 zglr#Fcf@QTly|~xS4nL#VW45btZZ0p8rXXo6TRp58L&r$<2zxW`M)X4Ix$l84+c&- zRN9pV6dTmwnMExY25A0WR488-JWTY!2$HeFaX^Uu#aFfk`31u9U*FXXzbfwq!DB73 zx0V=0G*cL>L@O!&+N+nCZ887h5KB?}0NZBg*Df2VC8Z~whX2Tx8(0&Jh7fh832y^o zC=t>8jKx)l)i1-a{}wp7-4~|r1`jSLj-Ztw>Q91|nk>e#LLs42h^dL$KWN=0;#c!c zrkyKQ3)rrMR#{Vr$e}_t^u1W~!4VTMce8J#X*0r=!ZnE3Cufy-S z3r|M7Bp`rDD?@M10lG*u!&~zSGg0Z7M8>V(9w3bBDC@R(cqwWYEk}~mO>NK@n2JR; zD`;w()6q)$J|xK}$QL3OG}0_`i6n*4l#yxal-t=9H7S87q!`f$GMsenweM!?w=^of z;J%^kX~QVj&Mce@RUX30*eR1{%92Fg3yIAcdNSplNQ#LSFHc)fJUzCaVi<+;;&fo~ z;!I#5U1*Qm!IEw=g_><8jT;?BmCQpSK;h4eYirZnIA~-aJmSJpOR%nXh*B{tC4pZU zKQ-SY>`wNU42?Ca{>aSnXamE%95EiVk}1}WYFuV#q}hS75WuL54pnt-M;$q&(68a` z)?qD5Rp8NSSVjY9i{z^L82xx3ZMf&k!kM}d0`ZGa8Wm`K}~^u*S}mn+W@q_q$@yXDDnB*Tg`_*e;Np=Q<{{BrnC4JuqzU z_eZgGiaI>!Mqr)~OJNr;mwtD<*z-^I-SD61Uq-p|Qep@mp$leF=acTD_DY8E=P+~< zrom_6Od2-TUYvR6^`;#eJx`_Fp3fn$=s4A<@eZXYZezUQ*am;R_Y}X4o>Jg_$&B1? z;y@r00=x6nf}{3=>GOqpM@AU!r#qw?p}Gf9Qr>x@;7*C-j~$7UHDqC4Pm6l7$_}o8 z5SeO3w8Z71&*w|S=)1<;CKyX1X^b}nPER(7j`ug9@l5o4t89wU;jaLQE-4#A$2I6S z2E(p_xt-9%J_()S!)v0O^Ps4<%;eUsf|a=H@NRe9m-G7g z_-&zZ`~-;$PnZw)JmWk&xv#^;OZ6fNMF{a`;Oa)q#X%3#)^Y%IqlGU93hDJJrm_Dk zAM*@-=^WZOxt%ZN&`|Kl`f!LD1{ui)At%3hPbf2x4vO)2`0j6tC4A5i=Z*3pzip%r z^{8mOK~zJhdUF>sg7^;W41!tELZ=_#X@z-Fo~!~1G(c!Jr4r$ol^o%#)j0iR@p?3# z3AFMjMptO(REmbLPv9zJ;FT_P7Y65?w$(IT!CKA21JvDvC%}e!HZ?7+{Mq~%jE^XH-$b9jQGKU>zV<{Xo%mafYUeu zB^q5R8l4%_BEr9U(Y_bke*p#HSp?Mq>ccdzfiM&xX@nJ5@=8@{TP`FJb_W^2Edp>) z^amEsdbXaazgw=b(ilvm4+LshmY;-)dN#W((sIR5^b3NT*a$S+Uu$hc`EV+jR1hB3 z;>&)u3y!_$diCa1C{{PZ1y?lng8kh$rnQ3*)0hKUcYv8;$_wo>op0f#zLOIUk^KED zFZQ`-Tx@vLuoZbF!;9~62or^eF~ns+JgFb7d{}&uMQGwzL=R(lq)Qd4+}ejo+nSF0 zMLx&+`P?$E5e z?Y_6+UMh23an2D`&I|;zY>gstT&Z%lWLP21S+{?k^MjmJ`pC}_|Jra5m!rGG9=7J) zYObp*b!LBN=lJucnWz9VI{<)=*dqj=uiDd`9S}Poq%a5R(}Q4}f@Mn-1o`=;E=bT4 zOrsQ1w;KL20e9Jwak&PD3MW9YI_QE06u$^E*|nF56&$|`@=OBA1P){_2gGLuq}uc@ zo&*V+L%GLd+2RLnIDkA#VxBvp1D$k$;cmU|S%I-jL4u)x7lfX?uf6V>0bWf2=+E`+ zKy}MrMbc#}a3HlhP>>3cssnUs2l!0%R5k+YNS7hOfn{nyKPte24v^cmH;)UvY9^>F z6p)V4(_I{N!42A>0sk1sw9IY}5JJQ`3h|vZ19>#VJO>f5f#K=}^Aoz!OJrIWThpfh6&gchFJ+B z>~QsbwI`3+tU;2zMUfOvO1@}Lt4QRcAVe>>WBV0>R($&E1~U#4;F`Uide zAxTl>__OP+Pxg1owZZ=kq4|3?^nVJtk~jVm+=ot4)$7Mtk%|GrZlZOfhFb^FvNXy^%UH%1(FHwb@ffHm&Idnj{->w;PtHAf55YIz# z(#d73@02BKC4QOgd%Q>7N8`NgdC0_Do!~4{nH&f?i!;&nMCU!Y3pwf&b(PPIu`WDy zq;=$>tcZJju9V-FSPFK)`e)>t8G}U9O33@eOA^8h9pgLHl~Y7%^med2b#lWI!VW4E z`-4Rw5Z>g@YaP=yLmgHE)vNdsz;n&zjEbsDIrU6&&Wl-@Uq!s_$E9^NYcX+0B`-C6 zl{Ch`E7$q>TXGGJmtr8xzf<;}4@CP-)&5Y+EAY{4XUhRdBODxBodjv3BCJKtVm8F; zXg|4ho=>ND7h1SjJlgK2+HuYwb@IJB=&czXfu){D21=u_#J^g6L&__W6276 zB#T!4N<%2JaZe#VXarBqKIF=UFr4o!)$;q@ppL3zkK#OpI+jHFTG~ji9@e*-oI<~O zq+tK)%#Z;mW>6ycSijz3;2)&NE8(QnQF-_i^#Y_gm96C?$43jB9jR^>)C8-4k2t_^is-mr#@*C3q<}4DEoO|C)1JTPOorJD zaSMxGWrLrvZaTwTMbdVg`kW1j?}KnY^gXcJI&Ql>kRXgx9xtO0HcS^EP5i~s(MS9` zS{OYn`s{$+v=GecI3rtg?02X_er2}n4a;l&3}vxVmeJHModH0Rt5|hpQpu26ThrpC z`F)*ULBHE%Vsa*qL32pd+x&huI@l<`lK#|czHGP9ojF!9*_5IM;pdkn_*2(Jvg4*1 z$qSMs&v^e++wx^b*+FtkMbDLLTI9=tTg^*8)E!pt{(QuklhJR(A*r-Xe* zSxQx4hM;M$c#o!-ID{BkszBDHQ87&*r0q%HomsVo;XrAN)(}}PDcOvbO=4Q>!q0%q z0nKu5`c^jrYD=N8_avDeigAr<@I=A{isL(tFf6O!`*VD&BTKqMFlYm@G21uf%ogbl z&)9oKJk9Y2ychW)2{a>`E(nzXoEh@)rNmjFLz+9bN zQ{oXq3J>?PV_0uk$YSAtH0#B%5ZjGE`BaVn!KeD~Tt@c4Io1ChJZtA@rL2>SRtt>FQ66M5@@rwCLcu?aiX43+QZ6L~tVlX(6}SK0melK|ALFUyJPB}XWml;s(7yX+dq_8RTgPY^ zWl^P6aG+?h-jO`1RUU}uI}ebK4_$f)Jw;5lS^M2?(cPZ7;)vhG1%8M7ga1<6yqi&V znR0HiL4h+Mv3SJ=+nUDSj78F^Y-|knw-*5`4wT}a^fp5r$0RP(J12uU9m)^e%b1!= z09hE(^QLq8hcIrm+6Wz8#Iy~hVFs2qNI#Jo)a#J~0C_&Z9foSE0ZCjtL8i27ga|1bNE8WjbHzl?XzDBST87-8)K zAz~DJ&li47S*e z+4A8hx~kg}_6PHfGsopRH52F&M<>dhb_8Am`g^H@yXaHp4cRfWYM8|9YhHqkD#%lL z2)+bH#CxHrl+Dfv61jUZDCBZSpH#@>oWAHp-sQywdF=W+P^hh9I*~Qq7KsjT|B;Ps zCA_6T9oYOdL5y!Y_Ze|ynRYsjHSi>-74rGEU`L4P+3uBlhNMvJN^tYl9~GMHK#Epr zLPFu^0XxH&bjB|aqs5y?BY~G=N=cnKvI<)cek`wJo2%-V1>JpIhZ0Q0Cr0b&4kg;6 z4_gKKxi_dD^#PL5YfSbKq~RQN9#S5LWme(`fNoqgU5 z|2xwE_d9|9|I?iyBd+?-yC64J_b&$X(1$9M*%=)-Dn#{H`x42t4w$eIwHO6Fj^6v; zmJ{~@(lNz`j+(%!{b**+o8G7MaJIbMG`I_{@7b=C=i|>GhdI2|Ylc66et-bux*#r% zXahOY`bp8HxEiZ=0HOUl1ZM>s5hm<$jA8HTv#GtH;!56a!_(QSX1y$03AH@6dFdg) z^U?`)aA|3>`6Vw$uDjZn@qLqo`c(ou%#=oc9B9&G9zbf?jaACrtkBISs2*Vw?YmIF zAeeA@DA_9UPU0Vo$Q?kfJ2Bx5&M`{Jwd5PV_w-mJ6rpA#e9`vqLn?M2p;t0sbXWEa zY{cJ(HG~LnX7n{oD<%()*6c$q6Zn2SryPzo=^3kwWkthJJ@TT=e(S1Jz$6CwisXTwSD|FDVUuH`ctsjcQpa(6X1o4L;F7W3`5_9t06;H7yDSoYN6L9f5-srZ&t!RGEO)v3wyt6=CLItIrRoR|v(#B=pm9yd` z)z6RDS4faCAQuR%mTdnV2SeEoZs0uB-nf?xN#fWQx-?dZA}j(XZW;fjRj7_xlkxVb zw6bMoeVxwu3o#Q_E?onx{Lfhq>$U1Wgu@&MZ` zO!ZpbppzdLS*1XNwr08|9KmE0FSy-#wcOA&yfcVHUcrut3gnf-@Jbt=z~p+pZqMpP zwUw{WiLc$x@|@81a|*z0thsm=CCa7Pg9ojem|oj(V?mf|w_i~er{jZ}8XK6UHrtcJ zJ9RcG{18G1OjT0e-NQngcxm(AWY26D-tQpn)8gU=ngga8)o}QTF@xo($3=mHZQ@2? z>_PO)mGYd+=^vS_c~Nbpv;3ZV82&UcIj-fKPs6%6Z76M)^jA4-hKc}YEjG{V1Lo9l zi)4AO@iyv}xnENVX<(OTkMR$?NE#TCZj&sGQT~vmhN*)Qd%vqtu&ifL2YLu5!%|S` zp4Ye*Kx!vkqlXg4ZB>DG0)?KKW7%<~c54{kL2e1D(M+%NZn4t~h_8Z0LBQ%ha|kGe z(Cr~oEwU}V1bR1VS}~;l36y67DO#-OKB+k94$|4|*q-t_E~plIFIdG!z|hiBrnWPf&CI^+r^orh%|{tib6`%3xx6JqK(S{E_fHA z3tyj>Urg(p!J!GwF(&y#8MX|UwNo8<=UlXxZ@^aMucWA{ypb=+q)U@e2GE0dI7hhO z=DUfY375qCpnmh@0m#9*zK00o){4*ju_0)^T=^KdreM$C zZbp||+e<=QI&x81z=!04ljflouCxfh4A9CNgr{XTRL9NJRBy2Re;9kG=uD$#Ycw`G zcG9tJCmq|ijgGB1wr$&1$4SSwZM?Dn{{Fph&e`Maac$!L?YE`YOS!>jsI$k<% zUR$+hT~7Wg*B2i}e68)4uAP-x8PS`@Fb)@`*8)Z`=FW1`O3mxp#QIBe`WLR+oA%}O z0P{pO8I_n)>kECpY=n**R~p$|JCS5Hp55!LIf+o0r|jD{Gac#L$)D|ja3rVBOyNuI z&NC++D^@16c>8Y>JyRXlP&J7Q^_gW4t>GtU2VDe(JY7+9@ zg5Xh2BM*KZk*_o0dz6tc1-{5Yl{Sa!z0kHDfm2K=&G*GREEUrx_8gtW_Su;@ z?}AUOhK8HX;u(CMzSo_61-qr-fzxK_^9EQj@a>m=xQ{hj%nB7vyqW0Rdwxl=t8voPqbUAH5{EIQJIeia(VdFNxyPh@~FE z#0rzc6)B8Eu&w-|ZPF>TD8$m`B}n9gFUfHaOwsmWdV*Q_llC3{lL%}!I2~pGIVAurp(5wpt`o1Il8_wCb!-UU z2}mR1Tf?Kb-<%`(9b{x{k2`c9AcE49)G$ukT*KajlUbZMSD!~1edI{elhT)HvB|P_ zqvB8S>g$!>oGuDAzjGa(+$LI)<)4igfm(E|i_-$u>=5$nYZv#SiY@jqIo)&OsIhHq zEjr%vShR#4L)o`>IM6qGlE&R{gP#{b7n|N>Vsq=|?T@G1QLk-HS`j-gaU9sNEbM`L zwf$=&@-ERI-;*jJ!LS@+Dg#KR9a)CYXz;2TRnQXcS3LfI zr&qrJ#j0{<_OAbJ$X`azX8+m#5BudP>&bu#qw^OXXKLAKYia#d%I9xJM+rp9K&R8> z#};_tIFBd2xNIbPEAu*|J}n@cjf=dlHJ*hQL-rP&?m3z4X}W)V`uN9PoMkoCP-vt! zm>KFCdKjTPFK8B?MoS+mx^)c))kB=pjs#`5w+(C+ldLszZ(rK4MYxAezxAFs8Ow84 z(1pcl4!)4m$p9ZU9!r;oZL|M9gSP0hxm$U$-e}|TABA%V1mu_kbW&2&<*UHK&nt)E zO12ov5AzwqrEcv}%Jc%w+)6oeSp$Sh^P||2M06H(zf&X0d&!eukc%U-V3}^aZbKyq zZS#Uq>lgRLGlo!7hegZ)I!y{D%J@ijYUioc@2c(&mA z1F?OZk<4(#pgb{2x)coKl(@w>Z~}SL$+K`cf5vzM5#VPj4_SeC@o4@pLv#|8^X)uF zp-PxhO8lYtKvNUV$VM|B$lwr3Q0L<0p9Zp}+y571UO67fyZk%Z_Wu=Qvi~oq`=8iQ z{Eu!o+5cS?jGfJ#+{~Q*yIG+8Uv!S{I-MJ4@FCXdcSW;6HPkeUA*^sla*82wIVRt{ zMl@_?^S_sFj}95-WdAgJK9ojyC*V-!G!3q^d7W=Eyr(DQ@^*T`PfZ3#5pox~`5u)9 zS&8hb+cjP1h!^KoQe50&atdY>BGk|+7NVk7i;Y_G%%%NcB=EN*(5#-?eGIHpddaFC zQplYHEw)1 z8wwOOX_rK9QRoyZuh+S&L5D|3$+ zr<}%zz_-x_uZx8`=rg{~91JoWB+-T(gx9~ySb0Y{p{?Zn>@_q0)~&mP4s)OSUB?DJ zb4~v-H3vcKspyFBXf`arwb2XlsU!AYS(x5e7&a-!tvyh*8Fkcs-#~7Wg7m&J!dLfv zk933epNjwmo&#p=pQ~K{KM>^qyB47Q|KlqEGh}1u%EMb?xWZcj&6K!P+Dpr4pR%;5EM2wUP;Qgp~zwB8l6Ugg%emdl}xZdi@ z`jPzn{n0a-<8?flp<-d~sqp2w;tRs0tAAz=?okE(wabb9K}7iDL%gyhA~B2lm6E(o z(TSG4P05Lu+}(p|O^)39{my zFpYp{M;P@p%n>2`3n=o#A*bRVvozuJE#*rR;TO99`c&TD%y`f!X!#zubZ(r<;46Hx zcS2$s--p>39C_WmwSLi|E8x#wsL_X<^mbuu8?|TH`Zl@fzv+RUqRn@x?>=yo*FP15 zVXB`*g5_8mnaeauI!)6T+>pyIUz z?ibpTA4Ru+!G4|U$JDSt=L->G`7USYi~rd>B{{Sn*ez|kgd=f%S$wi3m*QzME7fme z*WfHC!c7zuiHI|lJCeY2RO&RcxDkStI6uyl2Gq~-QNF?`$4y*(1=`|=m&ohsZy zDJF{OK`Bp5NHLHDsBof#(M4ch3qrAv1D%H*IkP8rioZyiD8-dGsI0jnBR$zH*~m>0 zm{ZMhEyC{@nES!gq9RoD6jCHqoDljM^6!#hbcr#d5R_!u$roK&$W^GQOAa-qOO=(y zrMSr#EN5Ld&6o$t%{zza(#rFFE6~eG?S`3!nltE+728;(rD5N6NtXDek5w82aAQf6 zW6KU9q~(i4Lz3wVhZK_NOG9bKA$3V4>7Gbqs!NNZG9)0%FHqxPB1#rramZVe7d=IL zV7x^EToH-ll{SUCtVh}~hfd=)`3+*-f-k)%4zJ2Uby2$Tsh~{x8bbA zT8j?!;N`}hQl!Efll{kiv_TdJ0rKJagXU)@Ae@W_!g;XK_Mob_LNmQK|%TxIUq-s@e zj7hu-UPcP~lf=}yOzIX~X4qNS&#Rz^oK=OtImw%1uI?&M$mGvnip-mmh|egvMHTH5Oi*2jzyE|i z?s4Mo3nO1h?1W*bn|-P6r5@vaiTmrJVai-w0AfN~zDi4fVjft$*aXEaU~xj?r9tE2 zc+h-I00JY;{hi|j)zPcr{{6xT%4*>P9_%aB9KUnOzyOU7o}C>UC7zhjXtsw$bwv>` zTVLUy{B*Yv=0y7bAP9kvzz9sZW|Zg|>ro7k0gIC0pRp%+=1 zpN~_{A1(B775h?64Xipp6@LW;db^3;4GaVgP!ETjEB%)0zib>2S&)&YfwRECQqbWl zGIaV4>Bd-cEm@Kb4Dy7hjijZWh{VF2%2iX;;_=1hxbsBlOih#5(Nxt|(qgKq_b~eV zpam~8E?p*98h9o;7WBGcp}{~`UY#8R^7^|(vUcF?h!yU~*gpZ}&0l{?xK(+ZnRHhI z&>fIC_fa=73NpR?on=T14Hm7+V9mbXgiN^5vDX*pXMfn+5_bA5&K?!)Miw|TDS0n) z{;Iiq6-_Cf_u7mOb)-kMn4JO57k|AFas4`0Z1=OY<-{*rLBZbia6ll0jEVVDYi3+6 zsBe1v#^KkbcB8l7 z6qm7>b+p-{iVXRxB=6t%%H$K`Wh3334bsKPMyWBV(S~+5<7~!W? z&afzJLvNRfK7`aBorxPH!rp{2JIRQ?g+51MP?8CXgM<4H&0el4@%V3 zICijht2By=t`9Nte?S^Pq=FI;-vFj;y4-q(LqSvtA?J}qw%Je8(>!3Gl=J!FqN|&H@+r#p=)SBS7oMP63;y@P8Ciyk7T-%7mSo1 zkBMixhWMH82#Lvn)3a8{SGWy>g^bZmks0RLoH0xE?d8OX2D6+eKUF-hxIwks=#^oH z^uLg1HZ1gWSz#@{FHDSM7Gl=X>1%4MhMMxjGQ**~OLPn*&gP_4oo*LA3 z30h;7o$zvhH93%5!dw*w3ONLcR4RpKy&^7P} z?#xy(W*B#Z!0f_4Tsys3s2sM1csvgbmFqk|xKh@J>QJ@gH}LQDTVW zxAYAA*G|Frj+z5L&=wt%5w(}q`_k^bTfeHM_imN&qAJ~$hLyCLMKh{vZ$yejZXM2v z*-=vNop{^rXQBLbKsM=}Q+RLmQ$6#lXeb{jf4Smg!_0aRTC= zIkc@z3>-)i!enwV9_{%jqFC&&ByMu>xo5+rp(kcrJskCq+VsSlp+9PoziZ%zGsXJX z%>}IkOH+~!S&k5(N6*KG3BSaI6d#OSim}f{<;JW2n=5d#SeNUbHyZ6D7SJ0M^c-H4T^b?E^mZr zBGX-(7v0qjS21Y<8!IJtdH7Q87t&a-jQqn;1mBQ}xD3rz-^+6AaKuqY z#QUhGSsg&&{LEWO+%7`QCP?0@Gm@X*bQUK{Ii%Srif1bPNrOjYgi-BqA~J+fMuDG- zUn^0iKvxnR?*f@#so>$EZ;oXAyy+s_FOR4N{KYwV%6x zXE^1D%sJ*lAVV_)dI0?AKANIO;Y=}r18;`ZH7HX)df=Xd(wi1ss1ji4YRD9B?p{ArV86Fs{Ow_^3|HC*^r_>cQjh9E#r4cvaaIiGP*)nr8q(0Z_hz_f<=R@RU)*E_UCI%U=hiIwKcyw7p z=5R%{pf@+?2EZ}Ujon?1SegMls?`^04QCEdqzIY1#&gmZs>X~5k-hPbq?iM8 z+Fa^d8>vLQ@@pD<`N9+DK5vEoHdI8`EBMmUuvE)qC2q`EP8C9r5d?#9f=LC``x-sX-rE|4$D()hg3AQPJ#G&pW;>~ z+Q=YR@;LF?QV^hALJeB%X+jVxoHEua+?6NHt1fMySH@kcov3GzrNe#PJsT%PA7jm- zow>EG#p#*lreCMLWxS*<0W%~hsUBsT>NY<(PEP!)^g;M1z3@)bvKD_461}+MmJi8J zulcVE0qm}Z)->q8MRjyMHSyx2z^;N=4?tOo=$5m#@!8pJaOZXtUu+rUs%>ZM$B`g7 zb7TG4v8ZAg{?Zu!k%-B^OC_c_a_rahv@L=ahIX5}h&o&#(THD+doUq+27Q^FCTg`d z2jvK29NXaGvfz zgDW?bmS|0Mu`w9SKh?jqMDq%GRD8o_<>2Xd0PFoD5Ex^RM$n9Oh5mQiTPJ;iC8e?>A&1wtV$N~etsJg?xUdMMz8q{oIrl5_CF&j<>!E;px6 zmtYSMy74Ch0b6WXpirmDZ@Kcymbh&`j6F=Y4RYedB#6;Hf?vAUjZ)RKrWb46mR>wY zI@X9!PEW{q&_R>lDl!`jm7P0SQJ5*;$-_g-!Z)Px6!r5@RK9K&eI z-pYI-d>-i=ph!SVK`1Rl=+L-p*w_gBHpF`tu-W0VxPm6H?xtw<1n5cUt>#} zGP_5(i>2Pi_+}aQ-uF$S+vl*-Fu>8 z6#w#vpeAD3A4So>eW^%A@m_W7uKeUHVH%CTR;F$%1p~)y8Jj0-DY@ zu)ZF%>r>;yDjYzekZK65?T(_xnKK6EoleGgR#qe_F>f4QJ)rQ8B);LFU|=t{mj-BL z%n#SGm*{vQ;8;kjjAXHZVo_P$ucb7%(&#qJCc^M&m8M^EKk;EwOQq;nURaN_txYAL zHFi<6no^K!m0l8iXR_${7>?_&hF~-pi#&0d5v{Exf13Iy^^EGX9D$gkWQcemBnu>c z{{6CP6l6;1_;5+`o$57WI`xOWlcMowJNo$)Lk*n-^DQ(?tco8wFxhr@Me!%o03iKz z(_hJ|n?!GH;y7C9trgj*d(ehd6s4h@oxd3#cq+LZmen*DR9Ry?sU9fL0Xl`$Jo{y_ zLL(qTcEqHDltwD+M#4P*OZHH*82UZq(|TM9NTqW8cdpzOSt**L3^COt{L&;M=)kgV zM1>_VF(Ua;#fgo4R8d)oW=O7pD>MK1S~**jT7bP-nvPZyKXHG2lB`y6y(j9OGP9j5 zY+$}dSKQQ&WcSZkt0Gw5G|wuLBf94%|- zY-N!QT1`vvpe#2u`W$9BpmY+NPPljHE-KSM{SXqtYD>u`aS^x|lAFpq29i^pTB%RQ zIw*7^;pC@_Hd!X`#J5e2Hwg=uUl#-Zf?ZCg0nJMJ^gHOLeEau$@AvHOAF~2d$G@;sJdB8t+;Ap|%L_){P?ND7b^{*o$n4 ze@ovx{tX~XxUp~TA~GNS4JWxri*vi$jLrn24_v59FDp7_@@*Yfy;vvZjcecm5ma)e zyxnmHx8iN&A~2`pFXTLtp5pNiJ3==t$L|kcOS^^iErCtpP#x*FE#2esSQ$WLWuz?2 z69@CQbRa628;60#xJ<=`S+cJDC*v;G{FofYpCAJ(AboN6`$rEplEp*(0Sr&9;p~Nj zH#7FTfA5|l(;iInoKapE)7bkTg0<1=FGfDHixD&X@>VzP_BA-&g@(dYCgRWtJ9$wP zJxUC5`yZ^ve6u1qKj33`k?!gUGZoop59$~}=BDZjBt1V2bj!Xz3HA*-v9EY^deKIB z+0m)rl{EGk%8TevT7>iDySJXu<~zhv!TbDR^K0LV!YG-qmYV{QuB^he7_PBmvoUOy z1sJYf>*QNuT!$6Q;A;gh7$@eE;d1e-+|TV^;w; zc8VVO6Y-=?*Hhob7hT5QV#JnR#U8}u{+v?Gs*M=?t2kPQ$w&6=FNy_sHp8Iu5?x0J zy?HrHLyz#iXY_hhU@)B5xEVWVl6{{){ z-_DkFlAP_>9%1aE;trXI9N7)ljihk7Ihq)Mg%Vwp95OK$!FVXQ_w)|+)WltkX)7E*9hSwm5axl;uJUmayf!x~-!7R*Nn@^?M%% zuXXS*e-SCtg zm9PIrjRE-NIFd zo`K$+0uK#~z;Jy=-Z@r<(gSubCFR}9SyV+*ZwM2St}%U(`!g2v*o5Bjhzf>Cz(^gsj|ke^LjdF(;j|AV&ha?Y#$Hy*@{RMY`S=` zb5RL8McJ(m;Lp-h*rFA=oFJi1*QjBulCP9i`X^5-yBb$8=|jt3b1IA$g#?Sj1}Aaq zuwfA7m@dy6IQp2?l(rjNo2l}JycWjhKxuQfLdsc7TA3A8|0E!v;;bSf)Ma;DlS$p3 zv~1C6C|MTv)!L-t+XnwL8HN&yvqbgE`V3D`P6Q>&eD7>buhqwQ$I>u8>p6*gzElCl zxyC$pZI=B;!u0gShQldfSaU6lO2X#TqeVWIszhCRxQJvW z$r6fdDMdubtW31}>gcwfvAA3VBj`a<&*GGUax;F%RS|>puLKnZvM2(dk#@3a>9^Ld zsu&v$>FPntGCf4=NK`ZKr8$8b{mZ#9t<}Rb?&A#@_QStl2wbx(@ zTzIhv(+lMUILJrPENs}XVJZqplrl`;oWZSWO*~CQ118h2r7ETD7G}+s%E-mtzCoNp z>{%2A_6V}F&m)DnKkW?4nX)9x_g}RJ5lareojWuQAJgeNau1Q2pT+D=$VL+)xq~Cb z8p%a(4M^9pKXTMl7vF3ad+a`=F)yS`MXh+fi!Mj?mC~>u>pKPUd^8QpK8H}?=6vo8 z4ZYlxdy!5nQ@YL)!SotR^T`R9G1nhHCobCOu~k+)+l`FGP}zG1#Y|S@cbt+(M8OH{ zX{AiwrE1W!#154m5Ol0(wl+`e94cF;%pzQ0B{5ac!v1uMk#|{l0aEtHwknP)688}~ zco|1c*nEd8DLSphtQkZK+t}if=Kafkjp8p1ArF>e3cH5#`aC5Pe&a4^R-# z*_;?-2~ZQyhi*J`BTqIwH0q`XO~RD|zFaxSakPePA{7DLiP52C_YoT=mT8(j_s-;<3`%=&}H zXOAwQpilm}FNWqnQI2{Kj~hF~Pi^C0P?vtfUAlJuHkWVYbj7_gS2ry0I#ugkO`X_0 ziw6ej?ByR)PcP!{8>aqQb}#g{9dEUUv`g+OpL%v}dA9xv4Wl{Z%jGv#PtX7Azc;Jp z4Cec|VW8I46Mari40T?N{{?#YU(L4M9KabAogN%m2bf_rqdSq~&eqT48}JS+HliK) z&bTW`0BRcY#ir^>fo`_KCFH^i)Lk3x>4`B9#6E~7iKQbdnJ~PE5w}Yi#w^-&V6P)2 zLU2~Y3@KA0dB5dyh}{sgUQNfE=cUh^65cU`Ar_blw!W!JJ0f#&9!2!y5z;y|?DscS zX;3Vcn=pD8NhD6VF-PGlveE-_qzKBe(qvS*iyD==P7*IwY>QIy=CCaqeE@@;ovGm9 zk<7>PyC@#nTVm3Bew0G1Fbheda%g+m^h^(U_j~?@zU0If^47R7;7R07|MVJf5emnf zIH_05y8!y>iks+}%$xBc^6&Gs9YDZt)*bnQ_Ky7drp@I*ANz;i9+#w$&%Ch77ICbZ z5b6X3<3vzzH8g@LzY*7jc(=DO=mp|YHS(mz=g+=35dyq?^^2zZHjRs6H=7plf^@wR zhS4RcJ`s(YAr6-)^Ez+X)>(D)_osl227<$@UIdbF!ZB`;yNAE+Xi!e!#_KtLkAHo3 zjXQSDMScb%iphovD{zp6vNsZRS~SrQO>^z2U0U0yyEVyO%{YSXfR+~`e-TGORoLPo zV4|@ySGDxV=gh`AW!N!2fMRpVxng%ihaT*Nb6|I*HZ23{)(6G4Ok#mo0KWaAIf1}; z?ojOs-wy93I<7rfwl$OZ;CEKH-nc8kZ*OAG$RQ709?<(YHKw6jj@y|fC^YIi_`~QA zB;C!(9S$zstT=8EYJgLnNb2ZE{w8mM(9o;{w<$5?NMQd+QDjcQxg`mL;VYHCgb4Am zj1v2dX_6r!&B=il%8L^BNKLsOM&8Ky9v=rmts(2KH3wR)7^Dw$Re-l5 zsNk6SAgF5JOd-%WfdKiY5p7pRwWgV~DE^u>p{>?PCjptoh-o7fd)CkQ_;SU9J%QF-AatTup9@dSc9?eNK_j-w7DOsMo&i;&|Szl%4eC+n7%BpQ`Bd07$o}i{# z1TWc0=e01J+*AD6e34CG#6u}d_WnGJxTR-3XFAYY>R^fsBPG1^a~%k{)ITOsvp0F~af2I7puF=l4BN@K*(J^5rL z4AAe67#OQz2E61*2UvUB1NkO@&P>?>Uf+%Sb-4pqH+m2?l7w@yfmX>p{PB@} zeuEKy5|Ez;F5*Z7j={O3IMD|rp~eR}Eio3z>+9#l&=o1I0za$t1rjJ|D2>LR&CJY9 zuK@@3jy}+o0Lp+7wEn>^Argg9wp=)zO}ru&mK+?`gdz$m>(T~^WGNZZlqafVh-v?P zmCjW6eA=1wbuSQBH9%JhPn5oiLybRG=Ik>mDcJHY5zMVk#SD50NG3;_Fq(!o) z;=-t63w4e~lPfcqk})IZb?f;&N?ixWLQ*C1(Y~wmzN_(ErB!LK4zoc$+rg{Aw@|)L zwDEXR>Qi}04nRf}EljKM<7VulFwQYr|I|Hk3K61K_fPX)!Y3bl7bg015dNpelPn{h z)kT57>`P+4(+VNv6AS}4ewto$^jBsH3jO?4!~9(T#8oSsGF6|zct#*T#hwyH$R|2G z=>~afNT+D~cbiPeHjadx8`A9VkBsXyuQ7Tin)G&N;%iUO2cr)8?IO}i>F6es(w7U@ z?#6|3b0d~1W^T`Qey-Wr3cJcnX> zqY|z`CHI6SPnKqrR)hq5Awq#aq7P+^5DcJ9Joyjrnb3hy=>vF1xDG+m+XzeDkg4}6 z^^tW!>^|(-e-%P$Un(5I4Zz)R7xoDK{wc7|1Yx~sx1eGDG;aBZj=TsH4?ML~?KBOr}4B?NV|`5<7?fHg%xZVL@D`iP`_PV~^;K=WJgt z-u6L|Jq57;yt~w!9PXz<==}<*p}2nXMiSJEyp^(u>BI{y;Lzu`W2)@i1E{$K8uwT; zMvrylV!(8w0X7UDH`UE_nq}#-H-+);A9wdpeyVZii*sYKkBi^l-6v#Xjhv1sO|Sn8 zL5-FtrAzP@H!Ork5fH=g*fGX;*wKVqPP-HForvkGlotM?oYvU%KFUa+1j?TIJAl2a zUwrdN{_pX3`4!awH@H_BAKud%$4m`{H)r0-hzEYxYi&;^o2VmCrCG{*wODbawy?P5 z!SRz=vX}=u&a2}2{g#7);@rOA{XY2zrKCIIx z>H;C#Wd(OYCtl>sPrC9G44|uCA06Sop>_Klf}>uh9NGUsKHY9P677I1067kKc=ycw zu(~5}w~wFuy+8b3f(0RV?j&|8{Xo7ZhyZ{OSc;dV$z6p8uCM6zBkFdI5XXph2}JXO zwM&uVI`9PNHfsBkoMz8_PAJ#!f~H@m@Okxd=*SNeHVi$;6d$dNGlnlY+ljw z;7@(QQxA3k*2~6qYA0-p@~JVHSO?S`i1p8fmv%-rS z7X1!*C>8s2OxnY;*fG*K;V9a2Wbz@58pVKf=+CU*n9+JnJ~boV+Q;&2=g@4zF%nX#0$>wR8Szxd|iDZ5BPdrn&B>L zrg7v?uDE6aTzO46=mPdbCk^HJ-S9=s?|kP^Sc>pHvALsJ@k2WzHkFcLv43z{dyU9O=Jd-R^htW4>cMKakt=zF}ub zMS2;qyUDq_8QGFU>h@^&%qs>4Sw!(Kh-En>hnf;d5@OJ#F0m8(;lwDi$QxHt=K%Cr z{A)CHc@$nvQ-zEh#BWV_Lm+NZV@A(WftQ|pCJs@$PX*XRy*~hJ{=+|NqY5_ZefX5{ zk5&_`xm5n9f|}JYM9OQzMyqiHd|^Hp=YpXh-yzgaM%p)L1crG4Tga@7npjea^nlOR z6B>CVCWM*+3u&m){H@o2Pf@533%!cUI+0&HX{G>8V!pxNUsR`Ib4|`0vDwCXm2b1e ztBP)%*)k_lcoS!vwh#JTGjJhjP+CBbFa7qP6bwrGE#=^Hiy}cVA{uibTKiI! zf^Q9<&B|nh4i5dJD_{QAz=bzH%#%((9icUaKZ-PMBv`RTCX&#UnYUK=WWC+DlrIPe z?`!#+bI$43`)_kUvd|q@2E)lsAgb<3N!d*Zs_wK|mJ-Ha@LjpI)q0%$o6w09N`nUE z+U}h+84mAzU0`L~?v9$BqbsdPxfhY{!k(;7HZ;R;0sE1Vcp8SF?R{fxDh}b?c8FM% zw0`v8+>;m@0};k$&VFH3JTltl>G2ht(5*U6@t>gunE|TlmD7`BKRyBjvsxzMg+5hE zm`I*{TKq@vmvk>|T`&IiXzW#>tLy4xZCwcLhaTe@v=e;oL5NbrBC_zI$p|`E8KA<5 zr>PHMpzl#oCeNTTIqc=!6t|E~LhbG+d{>0qgrWObPlZ=|A&>T2g^~N78kYe?Z}q;u z=mZ!N;TwIV_Rw|h;PXR-r_}4l9{G8!c7op81P3*M!;vCm3wa0;NmOQjDjnWMJ<+fO zw%?pyg3kdun|Nq6Ubd%+_u*}nP6-ftj=wj2NK6DT(KFJU`&+0IyKZkfK`2(Bfy{Qp zfAGp*;sSu*lxvs^Rae18zNU?EQax?`F;ca5w@62)=}XG%Gl}!!*StbIS&7l?p7yjW z^%OH`G4Bo|js{CAm){C>@n3maKJZ6;2_^wLClouWSYiI9D@km&cpkT`Cf?_>xd(~@1jT2vx?+sW zk(O3tQ0QSZrQuwmUX~$319jjPmo1}otj79Ul_e#E3SmY)OM@uS+Yb^%ONC-Cv10mw z^dZUO%kipPsbc8~>gwF`-@CluYPrp|L z2-wdBX7^e|(!?nk8gB{GM?M;bk*e`Cr1Pn*=BnuJ8VWZW*)i#F*t z;~q+-Uc6m-2371vT>B$bXTo+20(nH|-}QJRlzJh=aYZWD`BhlR4n~$o1G-=zV1pfa zI%qfm{Of*Q)XA`ulo3Z^W}kA1;Q+puB@VxG#~SrkbaL$$V@dr}{7pM&r0do@uJ>Jg zJkFFuiw0KDZWb4S7O8?_s6AQLQ3TCu;==-=4urx8%UL@e566>naTh144W7%LnW>VH zwEz2V`)MnA5=+wEZmLxL&n}@-*$Aa%t=LVAJ5ye$@qOx6mht@xIP!5%4C!pYI&%q; zsdlPn-g;_M8>QUT4vf?aY=Q%)j_`e_I;AKjPCwjbPsHe?Eo`T<4lM5$S8N(@AAMxV z`{gASZMfus|2uhlYu9hY@P4$8!*kHezuNgI_%2 zgURQcpl42#A ztA%o-*sKY;+c*zVmg;7TZm)l?pX>{;I!sM-L>K9dk?e4+Gdc4eFRPxM@OC#!R13g~n8Z#PlR zY{7|ihtfbaYuDi&ke@GZ*?1g)sk1-DHhGVFc5N41u0Vf>OG~|Q5`KqLFG!#0gDmvq zcB^JW3+f}sZ1zyih}^fU1|Hwd@>Q_KP1rzHTVL9(_sBU!a{4LhkFP_d<_5p2Iz)y9 zaxR_ELAkdMkP%@l&k&{gfB;pwRC=lSaYRDMJ@xB0F2q2B2aSlUs%Ht&lVS(7?2}y<(biq7Y5cG}M@=4_N!t$Y6X4IP$aUolc2v2{VJQU>t z5{C#LV$(q@X5=3d)1h@oe5o{8_4YunRAL1~{-X%neBCi9DZwnNs#Ru|v2io@ELxFE z(T?QTBbHX}tRWfceu4B}IJCskSo|(dxZLy@9XmO7hgs~3&Cc&XPa;#W0ZXC$V=l>Klu4# zLb6G66!3Icp%yZZ53j?5zj(qqKWsB#)0qJw<)oj>Q^08oQjvtSzX6DPm`iKaxp|2l zO*Bn1@)^=GkNgGOd)%jLHhc|Mi_lD$SCi#4;m|r%{?m% z-f6w99>qFr%<7l0fqEtns5cBRfBP^J5n<^qacA|(JCBu`v*b?c!&`t zLh{N0QwMz7LZ#wNTWQR8-z{GY407gct~Cm*rPof|46pNxWlK0h^zvvvA!QG5YFtyD zT2y^VdPFYcW)sG?>vU<|#6q&y(o#ZG0eXWB0iA~)ZvnkiwNM$HbkRu3DD{ktLx>Dj z)nbLzbn)x6m+C|m23E(o?W;4P23eD2N+ql+H*LsKTVrrza4a`V8CLtx$O6 z3&)T$)8`80ZWrza@ji-j{Nn?OG`6zq`N~66#<^=3{2b_mC6MwRM0E6i8}<>gdXe0S7jT!I9|;U&F$RItR4-=QV5Sn zQ|X>?teP?mFftAN|5)Rd3bALkSg73dngNu86FjTmT%0z7d|8yvJyd8jF>~{$2#>Gl z%wCzjM42Plm0uyuwQQa0UJaS&n*w=~3!J4^cni9C8@nY_vr8v33z(N7C;uqsdf8U= zJ(M=H#R(;zUDNDB1gyj}@&4eD#VE`ZXcD-MZlCj1V}My*ORl^w9#yD6WSkZ6VnDpI zJPv+T!7QfHJ`LJna?9HB*6Q=9l9boP+i$gqS?lzRN$Av}rL>kq6DbIW7CjH*Nea;r z?{Zc!d%3=mtfS7#I@(0U7m|x`(nB^jtDe?)8s#rzgj!iSCZ{45wGcAtU%)0c>nj#N z23BYbQmsJ!0&u`-Hy0VUaNx=>jr42V@nx6E1xz$YIJTrBcQqI7aoC~q6=U?XYBP5i zr}WXTqrZsqV)ibU4!CRMd=#dz&XzWD*>T9sVK&%K%{Vm=+qPpGnzUG_)-BclvV+a4 zq&Hh=F{@UtGP`dPck6D=er;K@jc}uZx3O7ux^ih1*QrbIz}2dOYz3O2!`Fqtdj=Zs z-5c_=u;L_67$SP3IrHcu(!P*Q;?%PSF9aN@xq2^Hc1$$?Lr`zP|8Snpp2O*|M3_4?>!fmx#w@1 z&1BFz3)svVR})z^UC}a*i3aRWJ`ug27c#J8(GmNRneT5j?y;}OAn^8Zqb9dmD zFumZHdZMGdLHK1)8am$hc9^rwt`cZyru(n2Z#WtYb3vM3b-6;90wh(V@o(Ck>eQ!hD1bwRbK?JS-DZjT!WEAj^ptg)^ zabCeLi+CqKF60^mn#XPUUz;j6{HVIPMk$|?1YC~fK*5!Wv+Nv;sx zN}eb-N_vLv=eNz>t#2B=Tb|cg*P$<6uXZjC*26BruS}jQo?zd#-;+NJd`I-wvH0}g z6Wtej_xYRaJO!`t1a#k1<5qf4Le`PHb>9<+%04Fn>vcXAPk41JekV4~f-gAh!hW$x zK1tIr7(6E$XF)rKcs_C0`_HHLU-8#_3KzdWxcLMBR8sxJ7F7f_W553-e zWExyHZR^J$)3>tXr*YlwfHUb!O^5{XBL42riYua-Vx=~AP4N4obG8VN*P6Z-% z;!wC?1AN9&kb)q5F+co=y_7>LAb)&;JsQ~`Xk}aL{I8f(o3E6bI?8)>9fLIB#fz9QygdF(Z_+Kw(?(7Vkhe}v zZ4WH7yVa`Iuqj;^yVzgfb;dm?QLL;+?EQJtf))D-e2C~kNAWYGMhW%^1#ggr)NgQn zeB!9XD4t|>O8QgUQLb~(Q#W_1R=0^suXM&^v%l?PZLhGw>$a|bYFu-K2OsFbVyCva zrxmwN#O4cv{U*fZ`VTG}ZYB^jX%oRExgN6nLg1Ih1(Ux~SFNw#`ZJzkz5s54=c-5;&+ z&5B0xs$FkGR5V%9gYhGmz_kTrRH}t-F(nYy1z~NmmFGUq$A&G*h02B(6;Qqz zyy2NTx)6Jdy#7^WN{Me{?kg4~QnbWkd5BvN-0>wdVvW-P;Wses5+Z1`H(13AMyLG` zCG3{>CF8DwNL+J*`a#S0mIDy%=!SGxQRWSlh_$Ci9D~WKOLBqOX!)Mw)oVd7IZLgb zMqOkB-61rMT-EL?sTOS|QM`|OB0s)T3k4Lcu-;h(7ps>C!-x9Y8w-P(o?Ds404+hg zxvT^;?Y^4200>|5T33u(XZD9Bg;3(R3ddwW+~_5RuwS1D>6Lk~3y)2XQ#QOEqXh6; ze0`}0+Hk~T)Fgpba6ApiN|IPEjC*WOSfrG+e=4K2h8pNNM=I*&ZXEC^wHYA2s8YS- zZ}2@=1wzFOEa-E1yxAWZ*d7=V@-!4!S(?WD9Sb;#%_usM4-pJ!Z`EC*DE|*-?-*oh zyKM`mZQHggZQH7}ZQHhOqtdo*yV9<--TCc(PVduw`&P$Y5i8=2cSWo}b3SuSJ!8xP zR{H?beF>fv*alR;lFn-OptL+WBxO^-yw55pF+{vq_L%e2 zAki;|I>)Rz`oRjRrjNOX#0zqSKauGWYcg;@iE>3B`Cc(g8c?xd&xp-NXf%FiwPoDa*)65AXK_r^SoSL* zi^r|SdtQUz=VWkG+KFq4)xCM3c5?JeK5mE>yeZq(0vB2MpmV^z=o9O#VueuF7ufjO z`Xs+65XBo^^yEwM#T&l-)%KX83H<)3shNz*?nFh83m)sN8fd85K5pdv4|Ac%aoMxm z8(p{9B;j!WF*>eJ)Hyz(3x1&37s}H+^I}PEVaf|rh%yh3Vz9eV=+i7p(|b-`w9flw z@HW@x`}ww?)EC;`1D}Bo0vEad2~bXvtO?1-xlc0&sGaF4n7!I%;Pr?JJKwh?n_*VW z>sOCi#m3GIw_5veFmOugf!@je2QAQir>%2yLk2pyAJKIbxLv_wuKeyry{oe9V1a0< zRM)r+^OO)CL-Baz4F$l!b2)&{y#e`m8Z&*%focWja^q7Lzh`F6oV^~Za*8qV7^2w2 z*rdf7_XxMd{j7)~9imY%jEnlg3bCyQZ-+u2jC_7nJEBagl*ZYQl20tIC4=wj)7l=< zwlY)J4m(H_;%cuQN*)>wNSuxPADE{KjcI!Ye-s&`>zaQ(o2Gh1QY&8?XBN7wnVLj> zuJy|5G&4v01gC5a-wUmj36GvEyiB`iy=gbv8G=46kCCPh}Y!hwTh>PuO$Gn`y}%vIbiy{rRBnaOh() zf0an0VkBCpqJ}jrzcb8WhdrR5#)u?}U8t884-PtPM!_^Jn9c}D6%}sR84nM*Z>Da6 zGO{|YVd~uUc*ah1X!#4@YmTqp%+bpK11#cqd6qWss4GuJibH%)?$8^Uabq0_)Hru!&J^3`=S7%+dROwpjp#hJ-9wk-N&o0H)Xit)pfB6Hy3OtSS9)g{()A zeNAwu)rY1G=j3FXOU!)jifz(}3Is~I6ElOHM$)e%6xPj@#4Eye#Or-k_tbF4c=luE zw1Wzsf0IxST_X_vZqtcOZ%nOp@%&G#m zz+3eDNonb?Vm^0*U&>@L1S#r4#Nzz)g3&*Mw1|$1uW5pvx4=mIeF_=O$)hZvU`(O! zRcaJ2MvBcDW2N@zqc~Q~CN=R%DJWU4i<&DHd`GE9qr2w<^xl-0nhHJm-m501{)LJ>FghgQ{5U? zQ|e11a5!BH|A&6>n8LZ3Sj|fQY+P8xtvX_Py>pbWrguiHLbsQ`u?)_Y7-z5h z#;Gm;glj(Z(s;MAFQRqrp4GmKG`jpAXs)EnlHsfv#(CjJIf?&_yk*$t8pD%dY&9}- z8;mE)n_+P^cHN5&YLthOwWCsl{o}d0Mx#~!N}1Rdv5qauVwVvbYuiQuJ)gES&9?r> z1PsD>@vjygTO%_|R-uB?R1Ef0ls9rWXUx&#vmv(>&dG)5J`(({C^`~6#fj+~1(OeU z*Y-?2nhwM7FB4c(owV_u9ZlD)InVdy-2KW_reVWTl33zIZhy%=i4y~E(5)y_f(bV? z%p;By!%l#}X!Ko*ebO|uPMD!#YK9RvR5Z+*J_D=DAHQ96#!39daN{9@EWg2*%v0c6 zDp`gqghoWf1D{!4VJ=v;ZqS1ZW-j;Ca9WirsUu3any4+E^}s64MCcM&$$FHNrmp4` z^CPmUnbU2R*DPInF`MGHfyvc?JyE1FvB){z1=uU;C!eg;3LO7a+O{}JQQ=7)%>)<1 zC`asYoJ^Y+7^lkchOud(uO3=BJ4yA1XjTKK(e{98UB$Or;M49)(DEYhSq4ijAN2!i1zsf9 zMhgaF?j3UF;C1%D%>wWk^W?r0fjZ~$W~=S^iBDp`y=Aw)^$Y*#^$P6j0uJNwy&tH^ z86f^=M1XfT($)DI|2v(C0)+{%I^6A;`ui)wmg7xk`r*dz42!2VDC;Ec$JhHe8^tJL5fw5nSgch=Ugf6?n`+Hv^+3IPB#`Tvbx zhw3k^8vmkQ5iruXbNX-AmF(mVC2SRp-X3H@8yZ>}|A68Q3wj9(5Q|83s)*frGwC=0 z$*x{9PpAzEm;E&v3)6e}SyS&vd^^!>g|s^6vj+N4z)!^PH~Ek0wZy4Dj&<3##o3acSKEdlrHUt`9dd{XEK9`kJwTaP>}n=)h)*V-Pdv@}cM z9kI^?p$U5Qo@f7Y4@_DaiH5h{7*H7{vt|A2EiJOkEfXiJq@n77_yndOVtU}eVV z$XolJw}5l$Fo@!r(*?j~N@HQm(3Q_Lrgmva`)fd!a~OCM^3RLk69^J@aG$A*V)l3F1LhGFPjaEkrL(I>g z@W(Kx@b_jqq0ljSW@-O?AP-DL>&QH%+hK}LI;ngsw)&e}WAa#2VZh;!(r#5pnsd#; zpRiQU%4sZ?-Q1H&T9R@r<8hX6QCy2ai9BH3 zUZN1#@q!3ft-HXy?5}}8kObseTjK}y=7#%~`UYD%u#MdvQobt91}Cf?njK=#Wn84x zWYY%Z39FL0h2$DEkb1+FI0(N0;G@W})tVW^hyiXTuo-boROWw88G@pM?=p?2N*fq9HXb?5E@oI+{^n7jEPf3lItQs4#z-PRa4is1So^Ms`l zmp@p~C-@98{mhvWC72ZD9&jcrji*Xt_;Xq~i`kB5`jrqFfjAK_b&rlD=4mnmpyp{X z`q^G7tm9r8r!Su3x#55wZR~zjQB`XL(654*jJ=LH%L1G#~ zO~44h4Kkf^)#>&6?A};G@hpCvXTW!gK`VnBeWVOv@R`Z!>9;=~F5TW=x2V0in-%@T zhg7U~Lur-6TFfR3!?4(F)+~_AJjE&=x$;)c9zlv+_oVu5m5rO&uy0I0bgzJ>7F%`% z0XbpGKTXFJUqwlShA#;asC^Q4nzCEHPVvznQEm@tuPo_vK$~ zm?8=oWVdd#U4I>DKC$t0;fNA#y`#H@*4}EEe-S8iCfAjEzr@u3eh;5vcI`|NqmV*I zRy|%pOeuglLk<&}&X3v9puwTZ zw=kR#fHlR@VlY9qgu_yAsFOK)&n0t!pR%_hxXiheGaOTjy+mk8aXddiW?*Gt@tES3hzN!S8#ar z+Mq+uvxR^GU@F>l$UU-KboqC{}ShzlaUSF`X;=p`F5@UJ@XyKU)<_H zH0uAwc$u=Tw6BaZ($hkl6e17R+6zSVtE~6T1o3(V%{5qYRopy&x%w9$XMS{Ab{vDm zdiK`M3OnyxcWn1bBz-kAQ!Dc~FhjqVmhHEhQxuz{*?Do0KOJSw@I2)>%H6*4^?ZWq z!Qn$=^m@R2v~ES~k7{)e41u9z(y_LUI>hfAP=~nI;fm<^g*{L}0qtq^kQ4MdGe&U1 zcso$(E-t=NI)3rnAAA633nak`H7T|mY*b9esq30Io#-l9J=`0iTrm%NhqM`ZbR@}H zA2X%TI~+~l%I5fw!SGNn)Zz}=MMUVR3^a-{M6qd3SQabOX^NXd|CY|tWQ1y4qslA+ z^hGoX8_m?j40(w%P^M9;6nA;p>21U*ZzfoxU^pddFJJH{!{kh55puL)3Mw^?7?ey$ zP0nX4#I$H`@2S)PHC8K4|BU*ndS75*?)67TG)wLWXL-5fFKH=lDr(VgtMqoCjmE~% zl3j}SPd|OL_T6ne$#G`a(?JUuU~3(wL8^@66x=D6(SknOij>0S^TlqSrLiUD%1@Jp zt!2HAWD^Qn@8)fG0vgVu)-?l86D-Xm0TneI=Qb@>Yt4-%rz!Wi>hh3*I2$oeo??~` z2_NxJ)=B<6c-BY+%y<2xh+x4vs|Pl#`#K^mP;1b z@Wn4bLyQuuoO*Q?XXn_2fm-`~p2lDnku4Q-KlCr;AOtDntqy;M3aX7NGeTR{s`~bl z8>wYahw$nxC5wnK%1(kks2p^ARQkx)=^-2)RK{3P#F|}aM7CCO1BpS}PTV1FM0|rJ z{aqZ|0vXdRzR=7bO}dZ}W~t#th0BI@Csq>V1IDnI?p{txjgGiPP@+ooGNxXoMgN*J2tYGn&IVMu+g+z6>ei##p5jR>lKTU{^ z=B-%73|t;T_9%SpG})C4O>{j4H8v=+gneIJB;~lY~ZK9Uj3&`1ip3T)k{Zr zM2J7Pz+XfkPni7eoq)Y*@S|h|wPOG~nysfm<%l2vpnUQ~I}ZRmOsWuzB5C^(U021< zH;nCx#Np4b;@tkNj$t{uh_8Ui#NlaNsHNpPt`*0SNGvCXePi?cVF~Y1RPT;Z`_M4E zQtiUJ3f{glFJC1Jn21y}Dv~5mQXLMksp2#9)(EFeyh43L67UJKad<>L(ytDt=pWdZ zJHUgV{{pW__q=U{?+By+SBl-gvX=gTQS6fZL$#^;|NP~Dg)l`Kxs7kS|3rEr3#g*T zMt*Ziekk20Dtq9ES@CbtADoB+hfQ|s)g8C02QpM&sBq|Hj}!QlSiz{o*$M>||G~aH zli|DHf2OB%J9K*hy@o{*-8-obAScRy+Si`#1O_tY+vO1%_aM|SO@R@rEznA&RaJ`P zxpK*v%Ze}`NS9XwBeDi5m~%@X{Rx(YsMHl9!e(0ZuU9J-1xcM}DUwKvd6&gO(&CFK zDi0=-AX&j*5m{u?XzDD&9$8nfX*qsKdf$x>TXZ5vbMdZ!=RMDrdi%%^s8#*g(91gO z?r|5l`$Mp|FIP<_J)$9^iq#`R;5&QyN4H~-ZR>|WxALiY%COF^lIDF7G`JGnvesn& z-X1I!+%n`0CrrQA`Z%hEm)6BQ4Vk@qtCI z79I-BSh-q%W&RG34s+l$>?h6_{rqrLqYdmJJL!kYU{zaaDvYhAN zcp>HAa5Vo{{zav~z|sFP&$)!Poz=gQt4nD@7Ml@;Cxpd6Nz9GK`jx;>odjF7Y|D6w ztQd>3QH7Y_z|_PBDX=cIj=b!n@jEy9D65!m6ks7yh(OSJ9_{Wv#(8`Fe1G@=_v7gt z%})~w*Vmj|+8`h>94tq9v6Dawn?tMU)76kZf`v_S{U=a_*EXHAPv)8mMG! zyD~s@3G1t2X@fGP6`UbaC^9^M`0d6cp!StS86!t5XyMsI9 zKps>ZYMRn$w8aStypg`>?K2aZ_r*q+CG;2u+|?{TJHC#uOCtk8^E$bpm9CA^2m@+> z&D6~sob9KdKG+9iu6~*bnB6nP8j53@ls(=H3cexX&`{4gDNU(*uVh5!hxbTJ%JjM0 z5yKS8m;nwm*S7o8yc~dUw#(JZl=EE4Lmlp$NHL#53AuHd~#s zG=wY@*`4b$8>4X>cAmo%+aaCz$Yl3A&q!o5hRydIUDnBD4UTgiuXsVwu3z>S+8n3T znN9J&Cesh^HFG^9!c?!{J{kkN_e!(xQSr;rv*hqekj`{@?p65OPA!?-=Y({h3}3LB zUP^G>CCY5azWI*3_e-zlpWg3n@9VFmwjGyV_pB(wRM0>91d>6jhJFJ>?&{eh(V0}*WzaD14+)uwg7Dan* z7m!|?U~I>)v3EbJYCopsY!QpD^niw7PPKwzT}F43`bYyc!D3-_jzfMQ_x$M%J}||9 z!^IBnud|3RfRuu5-0>T&OwNZ>bLg@HF@}J+gEcXL-pF2b9v|MRf&RdCsbxWr{=oC@sE8>I(h-R^Qy(&t}&X||2_vobJtg-kL0-J_((cj6our7 ztK-m_O*z$Es^nKbS$h8tT(&4B*OxOcW92x=%$X-(LN+b^SvOf|7A{^ zv!?N%Y2_gE!+VVs*5SKwBU3dzhtQF`qdarW9psWrCni|%4yQkYJtdA=0-a7JDGk*V z8Ew<~V^QW##tp9|t3X7p)QOA_zj7k_+-hiG$ZP=1F^|G{b7+2Rrh-e+6+$=vA&u29 z0SJ;{XUYx$@}PmkD0U@N6!idtTp(-js3K0TF6ZyDlDKA8t#@-0nmGv8@UnSw{prQN z9w+;dw!DV-uAO7K^eInu#9W~5plZhO1Y8|`=VjR zp30M$&t_=mo&Kr&(nL{-PCjGVBr@jl>Pm4ZHBG06tUHrGKUR}D3bUS>C=9r0(VqvS zML0o!vT7r*Boc2O^7zUojOHktIt}pqpZ*XdkZmyCpkaHxIF;u#Bz6UWF41RPNkO;r znF{f~AyOzSE6;E;RDyXNA517{Y@YEP!m?bW23y5BT~t+?i8hrvOf0)C-qenA$?o@? zG8Adyt$r-|^+Lm{lK%eKouQ7c3b&5EJjj+Jj!J*C+RfjEGd!yi@n)tvGl*QLRT{rU zUXFS8o4@KvfLI%SAu=#I+wo^Y*MkW@Werv4sJTu{t zOv>}!Vyw`%tpQvt`u#&J*V`einw=*zuW)Jl!wHrch^7SI@*ApG|#PL52U(i&zH0FubDJ691Y|a;2bHQ1< zLqb`8Pp?=ME!2uW%_+cN&0h!9EKo=oV(E^oSb70(m)*#y@AEL7Fu_b9B=#ZjMVaBC zvyq+@iBe`2z3nozt4aT!cw^}W9Y`DfaC}W0rsPOZAsk@yWUsWBjX5^PIR3zryY}Ih z(i|#aq(;{uLwa$G%&}mEKdF@w(A@{-KHb%1=~gl7N|j}zZR4k5oZ>!DUQ3XPHJv6b zI2c;SV=l>2f9tpx1ndMlJf;zG$tg8bb3#DhWUi7g#;z$MlpI)XZopR=nrD|O^(Q{v z?aUE}JtCjGOKt~4d4{zs+pbt0Nt zaFwdCaNqZkZQ5{4-6)Wn%k?1Kx|C+o%;P}?!-4eACuby+k>dOj$F4_K)vsagsL2dp zZ^cArEW1c6;U9|D($#>x*ez*PZeTaqf9D9uSTL1@1`VsUIMAY!gv6+p+;tHqUhPc5 zl@lSMNkFfDI9OL+Q+iuE+<3gK-o2J2O~%Awe7V;Q%})Bevad~mn?pkB)oipJf-I{+ z59>Hd-Ol-r1Je%}HBXDIT+Zf)e>cYRk=m$AYt}eGxyUaIQ;D@R)fuas{JZN5H}N+m(<=t!QhMA&g9ll$ z73rEycQ~dg|Mm|`%@1a0Z+yvdmQ3k$dg(1!?ket04?;XzvQ~?sAE-+TN=u@UG-nHq z+HE7#w!4`zQpqR~O_@V}3A%HU%PHu89_9U1tVS%xJ1V%UsAp=#kMB+y;qia@`Hual zQqt%@qik4b^v>ll9%5Ft;x>&ftv@PjW!NY@c2++5+|5egqSw(-jsaVD^H!h92hSQe z;z3-HMA+}3_5}&&XeJA}$a{?_EBYsq-#$>rfhAocEkYbKQMuyLNO|HYQ`@P7OnCvK>vPrRqXL{TN(43zNOr zGR9gsyyE=1^VmQj3~dH}K~iE#$?tdq){b~DBdjdq$xr~Q%#vaJ@Z;ht>_eoZy8C2x z$u*Zptgx(`@&Q&-DCP>sympBV{Cf+{eR72Q=&c-!#ltBoQGJac?iZ4K|Akk;v;L4% z1 z+6(!k`+t%Fv$p->ZwM;qZ-8bY--BZf-t22@!H;rVM9nEM$}IB<%7A8(GfFPw4-ZIs zsWY(f<-sKwQ}Ut$;N z*{V76L_h9`xqBMo!gPIP>OPU+C3`$QFF+^U3ln(BWGG`5gCQn?FJ_b$tP*QK1|yFZ zr9SqbLKVA5ZFzfM+9JN-a2577X-gN`Py|Zffv#I7PwFJ<0TlZQo^;cZ6y|pLr?VnRxum)@KY3-17nPS=7$; zwS#w%C4I4TGY2eCj~R>7UPspY6C^+imkO?o+8CkP6C`H?xx0^IX3q#ypyhNR?TDTU z7#Kw0Q?Hci2R>5w~fMJ5k`s1$ztXM zh>PK52;w`@`ruX+9mk>;gKp?cy>|j`+#tR)fy5~QS?CM81x2``K)3>m@2I`L^k)|% zh!=7IF;9Vawzc0TqW$EFB5@DED^R+pEE*|2t-O!Dmm!I6eesw`CS~gZS+hTUO;t{+ zw0korvBZ&upKEA+z#HU=E#mwI@F{>51O{{+J5RO=;b*872@(dpL&JAy9rR1h7|lPp zjyfh!IXADmz6vnm%fT0=R6+Vw7!;&2!R8zEG?%+}&^~`$K$vzU1FMzIYB;q3TyiLv zM66ry)`R=41+7}s>EenuBYVS{@Wf2y2xoAMG@M}af1>n$(me9XA-)35A8-__Mmjr7 zRQ<4f+mc-RVfVRp7TlK0Yu-cF()R7nAh}n&vz#RI(b9NaBfYhiTprfS8$a#4DyVU_`wgfdKfww~EHiz zA~Bo5ns|L0v{oGAxu7bcT;%a=_S|{&J1P5l0ksy0sQgoYI^@?lKZEa3oS=L2MlxjQ zmmeG%g|i6Wh^pI|+A{;sW7e-^dOFz%T?hBW+;E``fh#{6J}6onK0w<=^=*@rdU+6Y zq1Y{SjY*UCj!^`a*vsL}ha}aBfLSpez*fAofOg}k59nr_4Ov!1wTncTJM%N*0q94#|1hjN#Ba*o6h}g|by3A>~D6x5#$9gwrS5glD zaC@>e4>&&o){?{D#k^;{2NMGf0dI?!y|n@j?%1gX-#r*nCfI3LT;>aj&_^8~k!RbR zod{;&&gWN%*5fVbSBPDZqm*YUAmgs$pMxtGL!n67uksZ2HvrY?Rf0mHzQN#w)_Ybk za4id<%3G~~ssIbu&GW5~+yUG?7;fpKa%mcZ3KiaT6V*R7xyQXT10(RMq#8QY8cP9F zZe}!=0{&>I7b&Bs(hPb{_FfK{p5FPcx7F-Czf8X` zUFa2uYL4~b)aR<(GJvq5mo3q-S^8;iwP{pxKSw)bU7Oxf_mdaK}Chw86jX zbuHbP;AM4B^6L55_(g9^bu<70004yWZ{wH0H8=hvO(Se4 zjSCp73z)1ZSoO~Pe8GWo%Vd_aC|Gyx&ZL6(NtBPh z`GA1;0;CTn#E*Pa7{eVM4Z_A0v2%KvDRhz=4sG3=6%!j?jK&oM3nunv8^` z9eZ3n1cq5g(_&{%>luJI$)19OfS@jtV`}oH$C2}NkqDffW^Yb7fGg z0v$fpsS+U=NN@ue)kgK6HS^A&C9AVygXA2@M<23`$%i(cM^moJ8t_Tn+|5c>;!*l< z^)Ty4v?*$!Ob{_ijfA%>C|;w~<|*#jme!rmJT!(=W2u2%HFM>1+$;di^m&-4VnhAoe*=>pF zSPCXST*YY7{JW=*b5{>%_}!Od|7)vO_%C++Z-w6fp_}#Jul|=VezCHQ?6xdKnw(JhgkDyVMQtLX36nM3WWxu8fzcT8Sc!4`*fo8BMTgC*lM)8! zLj9%h$(7#9F0aI@zNBedA9UAU^(EKwCh?f>*W0Tc0J=R-Aj`Ccy2QQ~7-ZHAztB$| zI5TNu#v#ju?D9^3~l(d6Mv3TGW9{jY34W zl8O{VP&Ukj;$mE|;!79Mf&7reuqpi>+RnF5Yb6oeR$QAayP1Ty)-sS-nq2)))~6xu zo4Z%>xU_4p3FpbrUX#&`7~;qp&7 z`IuHW40%rU`9ykHa1U_Wj4@w zY*spd*UELB^EnUlx0jM{A8m=?t$4fFCk#PmP3}R&ShNn?-QDHD3EAu!R(_-z7`EPo zXW<`azM1{dwUBnw@Hge?QhB+|`DCG_nFmh-=BeuT@3E5 zSChbeSu83!T=K@5vV2Q>0S?E%KaisEMZGN_Y}i7t=TxDPRCi-!t(6wMg*OCC9UU~r z8BnQL>ielhd=FdE>W`&r5565j)0T?M4Y8jK$I^Wh6X_@bdO3HyjuC3F|QO} zD2TPd^30fI$1R;S=1Pf|j2w8N&R}vQ3THy$sY2{Z7(~_gvvVtZl@DiFi(JVvX&Y>A zJO6%i_G~wdVp^#{&@6cuB^qNa`lOSkz252_PQ^hjx*(zBM`V}UJ-8{#k``SHMxX9p z68RZtG?P|x zhcq7l;#%KyPNN?Jb2`E~_2~r<7E~W_|HNjdG24KDMAh;BDl<#-moxp3@STK{vGu>t z^nbP5l{XZT1rT_I1X)vu27@ZycFB-N`w#@{jKwJuBnML$$<`8vjM$R5f^LD| z5pun51g`xBE53yU$Lmeof|Hetz0z}-yy>j9QhWVi8hdsaULGf7{p(GWhLQZfg$*4WQUc7Xmh=d0*wJ&KPUR~d`(iDjSu^>~V`RwZ&(BTgRo&((|mW^%ISGoCtS+To||;g#nJ7u=H-Zp9-Qaf|0uA z9h_#*XE7_P%_&sK&0_#25O=cp&t+a=#bDrKaVhApr*{zHa%?(7XX1JtwTWUzi%ym< zN3dOwl;3WdsR@|tTe2ez&+tI6lwfw~0%ubQSIkbxm*)LlL2ns$e7pW+Ua_-!$lY=< z?Nq-L8%5c~_8Iay>Z7cV4j`IGVioPd@ur8ie`l8RGiO8TwCB^X~!oxA@?{ zkJEp0oT{Y%5+z@tCK#7NKt)7IE~rm%3ojR_(=+3r)yp{9aN^pFJC*(~O>#Lh-tVm} z`Qe6!COyqnL9bPwhU2U++v%LP_t%eoazA5k9Dbzl8p_z7FA^Lk{aA5AFPN|`j+(Iu zs)Pt5q`3%fl{f-XR@(-K3&tMTR9T_=u$p0}L7q z+RlKzZhDI2Q?wP-nbC;-70Vy{ptAe%3k1=QBw1Gn2bja!)vnqz6ixcv?q*KC$((wv z^|OM_>@NnF#K4veQO=M|_i9DtoBUWZKSjnZ>ZJOii?HLx9))Wc5Oh%74Y`=+tebHN zMc=0nkR_qtG@Bq3Ni>|bci-x1l40PMgOEJA^JY!hF2bkIp~BZw;g8QN8+&7CK=sp@ zUaQKNP+vYJv}_@88Ghwz)HvVlGnFD6P|HMy9cL*_FjzaqKh_>d;W2MvJXk0krvW4s- z>{C=fWo6&;W}}82;>lm6NnOOWxj~r3OkYHVco+;Kh7HMAVrn@oS?7&}(VVXh<*k+V4Uz~Tc)!e;2d;;!ZaJd&LJl5{& zFASgwJ!(;z(__ppX=D;At?=~KZYD@>#)RVPN#LZ7rDW$qg=XEO#U9j6k^sKh2AC4N0D>R)Kx0UU4nD+I{K1`dH^jBXo!lR_05DEUXpW zr)^5m{)j%O;z$}gt$uB&gdMWQcW57kGL^wA94I$Ors;Hdf+BAM>2MA9c$VXc(g25} z7hk|f%a11|ex%uZ8|5Xl6s}db$EU4I6?l5fMems1=^0ygGi#n)`oLUupgagwUaa7d zrSeN*T5sz?=6RCQZ}VUCbD#VGb=`mR^uMZ~s{h4m|K}?ehK~P)DiX$j8@WmKU)u4X z&9qgp@rnpH0Rb9yC=3N|RRv*XO92q0QPa-|-|OxnMXcty4@2nQvpsu}%s|YX```BF z$dQ4zkfC<5VCLyqb9{W0oi?FNkwrYsM=ww7H354Qvuc2N@Pn^ z)JipwjKEfu7PDgx&_p!CNUFCXgYm4&>wqzBXiuhICo1JA7VM*I*KpUOhW^ybWP!P4 zH~5*>?`xH93o=M_mBj%K;rHr7&7i`v0izxonTjI+(^O0QK;{=gM z=C=z(^#*u!eze+Xqc4aFyOk#9c;l?h&F$BVTpO2Ss#!`xMqSP-jD&8rO4l#T5}PWs zrxdj|1zOPEi<4I+sSiWVXr_5v&Aa!X;arzMEe<|tvEJ{kDveUXPL>XBHF(^{^P|wr zBeg5#`j2c9m*`vGK3q(c>?rVNeU&y znawZ@E97CBJK z?R1A2z*M62l!qvz$}v)5?y9b=L%#QiBlNmCsDK_!J9JRmP$S;@Le|?<`EUmaBl))L zuL~zxoDWJv-!&N1l9Js^Ah|oa>dxJA)`@LiiLF1Hi?>BobbOfJ;>T<__V-3Gs?o z&{fg5K@JO2P|QWZz+2-#5rXZn2xXA3s~{R(L&SNwhl(K-;7YSvq_Sv6Yq}`ag^Koz zrhwuS<@Gm0bqBQ0@!k~r47eVMml0;ItxV}HK1uc^46{WZY=PXv5F97QsJ`-OM0+7^ z`?MITUZ8Ba@Cc*J>k8m*2P|e?uHji#9(D} z)(h}1nrangLlFex!Oe|Z1q4`8AxUN#EI}asf!s1JiJT>WSQ{FaK56hl_5=BP@r^r` zB$p^iO%mQ19gjCXXSUX^__}?7Yh#Yhi)zgV6|vy!zDZDG=hgcxpje@XsWd2lQZTDC zZKCSrVXSK2az;EAY=&Gxt6beOpd2h`Y}!`xH*YbVJpdJNeTHnL?ge@Z z8|1nMjeo(_PSvVlY~KLsLhE)o7xJB5+z1pIuWZd}jHwn4`Oj{Y_)Kuc8jfm1^~D`H z#Y$#o`ib7#g$(t`CqZlt2*@?MChY-3N#TRU-ws-uW2ECfDSOYnZT7KHiB4*iaoQ;~ z)L!l|=BTgQ)rObC;b^(m2EzzFe1+9tN9n)pO|xG|Llx+J&cu^IM~CE_o{qyT!as2% zhP*szM3ZSz;4P>zc|xYbtmPHA-NWDKNhN6^h*RpKlEry$Qs~J1=|6y|Xe4$Dj0rJ@ z*DO+`CGGS#HHrUV`=MR{o=_l$0xYnD^>Y$NzmYi_W(@nlnn)H+FWI1uxmIq#FOy`U zNWbP=GH3|qW!fFXhL8)S&_0&nReTl*P15p^d2o4L7lBX2phh`7QPyWz`4DB9{(vUo z`E#8rZgxW?Z{gn zMV}YpD23rvb&>#i&KDXa%g z6j)$U8X3JTR)w`==5AK0>oVnGkS78(jA^Go-BiuwWHF}6LPa{ghKEkGRB(Wb)4`O2 zyV5bd@l03ZvZ#PFn1#bWOuUf=Rb85nx=hJ@`RSlwL_V>%?v}E#(2tdawN3A8jsx_~ zG>WsAAJem5+HB6pU_wexX5mbkjkhctImu=SMN?O1`RcTs_BK9nRLV1* z0CY_k5}NK;!>phg>NI8iwK(-)2fHNE5kRAs^mg&B zQ#l5stt`LZZX@sCthEw#iwkF5rKqs4 z)ZUuax2TF}3Dq<_0X4y)tkAn|t zuhvf`igAQhs*hUNwZ&j05>M(CArMkI0+3jSw@ez69s!E^kUXM5;yP_F0FRdCzbG(`CDBqVwqh6LXcFY>`%Rwdx0<&Lv2#V3{?Z> ztFiCz2{PL%DohX5(;5YB2_HFH%xN1)Q8?x~F?u!a`^zO^Bmp9Ut_DHevS^6%JXR1g zmLMzq=!9<>)Qd14z)0&p^bLVT`0f6$G!yR=a%?jE9k%BiF4;Z!PElfWS9Gvz&cS0M zunFOGjXJb2Z{V*L&o~p;+Zpdn;KQk?B+If+_n3e%J^gVbX#d?DT$Z;R?qr0fUR|?+ zZMt&T6;JwT1_KP2Q+=&|2zTZL;Wn_3dANY3d*86nKf(8GCl9K#H9HpRIn^aC&o@hb z$a=V9WURS4;Y2YE-V*&%QD;8R-Fg>YYx zx#hV9aDP30;T$Urrn4ZIRoalbNIyb*MZag{6*%wtCsMh!ybTC_M=2H9{|TxRp?3V= z!Ri10aDPAP+ZZ|6nj6tOI@{UVIylk0u+Y1FSN8P(ul@CZSNh_%HpcFTw$=v!4p!L; zQc{8fD4#JknJzL7Om`xSqS{W7L_~$r1dxi6!WJAl;MfaX4!E#i%CjYTB7AxHs8cG` z2=;TmlkVQfT(K|j9$x@pNMmC&KO zv9Gk3tm>7eJmKw)KWoOccGnzD`FX+!cYjT1HB*ahV8ws|pHf+R8AOBn)@O8*Tj!I( zrmn?qR*io8l~bQyhts}QG4=hibAAdW!a?yVPdQAr9HQ!zXO4{1jV22MS7eXqXkzuZ zXmEJ)JtS&zI9Ttb>ePo*WAj`zHZ&mu8Q@z#GI#&wY%p?iK=srA9*|32k68+iaw&7la&O4s&pr&lD!Wxkg}&XdR;* z?}}M&D4ns+uBXnZtXxAPGl)T$+(c+?cu-8}15wKDGrGG2wL8Ry();wkiY47_f@$OL zVyXGB#S+8c%=vB8f0wxbv_U0oOl<#c!f5#kN#O70@|GI&^VQ{7m*O44NDL6yh|B;6 zd1d)_JHny`b)^!CG3j&HHYBZQK%XQxW?nQK7CYVJ-;NH6El;Ux0Ai~Zg<(qo=YXsb zs9YG8?Ee>K-xys9wyj$gRBYR}ZQHi9V;fb;j#aU3R&3k0Z6}qakmRMS``mN8?>lwh z3&zOWW8}}8{=PZqBB}Q_;YucsH7BKaJ_jmhuBuwBVuOeQwXu29NmnDAJI!uO*|~X0 zP#+(D0F{^E(Q;Sj#P!C>K@aTq36=EM-j=`06#G7dC)Gk^-l8cLk;k?)0U4RaTkMGE zR2`3UC?=-0_)t%5xEttE?o61LPL=Lg^hRQx+O|wL8~ki7=+%!_BRKrT;h*b-=W7rR z`H>Ap^qNB<^^{kPx$OcaX!FS3wuvUhd()2M#2 zh5zlW;^J!jZ%D0B`7MD}LgcHX)2mQ}u0$8@7bOXTu#@$mnfWXZS@a2*#{c=+!TqtQ zW8&emmVoVr!oY|4riWlVhIrDhdkLr;E~|nrg29kNRlmYU5(Wq zS8jcIl>8y?lL^DnC7u%e)Mc?exAbY4VKm^Sk=8Z^K7aBc*Ka{5Kvs9dB>S#fF!Q>9*JyfEIiR%1=87__d`kCaO*rx~4j9?=~GUIlVL0wvNO#Q9yW#S5m zL($A)YWdbt5AUk-T}__VrIBO=6Eoyqt-F}&vFI|X{N1Eopc98AiN?j5&G)aVM79VAn(B-paZwn+$Hxq|+%zO;gS1Xg zx8Tu(Bpr9sOvJ@1F^-pb6b7T4huhT~Y8{^^g_XK*WXsCIHhLdB4|Y5|Ww@T85uU1K zkrf9?8i7NaDd_$`<+Ofst9&E>vYrMlPhF>h7U8+xi)`gdgMRAKJE7%~<_)(9Dna+6 ze*Kv%!e&YOuIraZAgp}=WWQ-c_-`pk&fjRnAN0gu%J9dZ|AS@>l54Z;6-3N9FbaGx zq<~RK2uFd4hbAmsn-B^TM!Ydl5fJNZv5`5^RX7Z>pO0vPj9NH~-j=`O>7&Qb|9+ei zat3OK1eUICLn&LsEwNdZrq|<^cA@9^p0F%oYBxv?i)S+VW{!qv^<Z z;NhY_jD+{60qQ&8htpj2+^{4GxO#gY!F~IEG$7(ej`Od%E4dC1G{WChT z{Vx~xZ$9TAVJT60?Tu+iTSKU^ZIR=@1&Rj9cYUmT zDFzvMJ`jzhPQ-G`pYsQ1an^ih1|sSTrA>{8ex#c0_ODC9#~@%?+f7-gYwxbT*2`x1 z__T%^HMkoZg0&wRMrzJ9L`}81)T>$fy|D2yASGzXz>b|b;Wjczgcj@Nmg0|mL}7O{ zEapwxl)p6*?%zoKzp)QeA}aqr{o7MkCg`cm38DsX1$blg#JTX;`4h{t?qOFH8BX=Rx-6MX0AsuIqw{*n z@up1qI-W!5o)dIv5siC*1J6oXrd7vnQ55o#Y5U{F=$+-F768xga3k*qD+1PsT~}kg zcv*@~mTG1Z`)*PZRt8)Ni>0m_xziGWL9K3Pq3pJZdB||* zfq6KI^k7xVR95b3S<4ohWTE+d6u?3WuCLR;kTpCUI48S7ZTZF7``+#3^WG;ld*so$ zEU$MK39`!K5gH;bt%&-=+5FR0&DVtX8-c%x`Z)|kG+ox^b19v85mBlrm> zGdzGFixX&(+l$kAWX@kG^YN()tsMm)XAtmdtIg*Xa(oo>=a0l`mO_%%uOl?}CUJaR z7j3O$+cnSRR1rGn?8DSDjJf?jAIBZA#_!6SvN|K#5Neg3&K-P%?OHA#B|qgO)(ldJ zbWRBJ2*#-mA#t`%d1=@Sp|ER*$f+z;kEBa}EhKZz*DtjZ3?$$S(y!&)AE*{(7sjxG zQb76!_lLFAookH`zNLDXzhyLjhco=!K1d~m|9$wkv1G>m8b1x*k`l&^BtoRT2cJnk$eaP-g7r7?vQOS4MUf>Av;2 zk{av({BncR%~R2a*++x%?RPl|F$jk4>Zjjj|Y z5A9mM5Whie8dW>>%BJqKZdnSWug|>dZ7a5`H*u+}_xqmmpxpTd!TuW40Y5z;9$slQ z!J~JFB!%Di9)*=Xgh-se1km2`Alc4BEOBj6A!MOgCF8 zK}soUnLw8ptKN!njy)IEh?x4DsP za39pfZ7=x|c+_`-=IQ9fLaHCy@o6Zp2~4wiyVkiN$tHcG`yFk5z`Kk|!SAUXvg0^- ze#Hk*=|+`c-Wwe!;5r;%ndEcg5IMYB5^Q3kN7eAXfD60@k{MBi86Bm^ zLEBE_adyf!S09aQY8nTI$<1=7BIDUS3{gt?BbTMXK zyhkI$sx%Xw3l%DZ zEM-y8?(98ig06>q9PVlC#7Z^6NcL@>K9v3w=uf3WXLcb;R@L_9RIaO3PjffUC4Yu5 z?|k~tc$qyew}S$c;q{Sf;R@A$rhZ#NOsLXQ=)kPv~ zHmNcbt)MDO>5mlqliqbU)~EpF0xp<8cN;+0m&A}K>7<-b=OO^Bx)l|-!w6~Oem{ao z#pNbfh%qKag9$74^AKJ$lOo*I#{)SsU8xSEPIh^{lW5ZLRR1DHLIoF@8pa9IGuG+B zwHL6Dq~6j8+}KeSwnp+-XCL{x+ROex8I*WMxr+rKNP0f(!iZh9<=_fMtr#*wc_`cY&tjOC- z5SPhMLAYx+)cA&}wnlLudDT}?@N+roHcjju)DLZ{ZgsS^GS}s4|BaDQ zF>*6ARdF$LG5Z(om>F;T#z-Izz0TpgJE-^P-NkRLO*}vo~$l z{&o{@f1X$XB+$}8;yc?xlt9IBDo)m}n z#36_F(yUjkuQoMGHLPmx#HmD{ZSl!8R1Hib)h%i1^qE<)m;5Tt%DXwT7*KS5D%|3g zX@fmeg@Y_OZ1cTSRZRhyyPv5n_e6)*Oh<8!iwj2W)EbjfMT3yZji8f9x6s>oJi6#c zO5CDYg;ipaHhFSyU7M!s>Pc z*9K|lX2nZ1<@F1v5g9~bsD&52?>6S410L&}=y9M9seV{8)G@llntZOigBUt@mR*mAd{ZyK1`I2iFtAeb4j#qX+vgSkcp1t=VABo(*3& zE4kmV;L)i>x?u5^M-+hn4yq9NP4M_5b@&rDQn34L&z*>+k)4H^EWp{t%x-cUou=g;(Q;@aWB{Qk;%v70jAS#g4RdRv7veg%Uq!SUal_ccP{7UY zOVb^AHzFJc8U~v%jnbTA5!Ts?ei>c}SXWa5^}G}+w6pA-s0A@W81HVp<_O*yhQPO& zQwT8oOx&+YM@r*BbHh{$X3k;V))JJ#E%lC-s#nh=W@ap!Mc~7dly7duq%i;%=f^3D ze18(s%VGCg!Js~)tZxsrrAtXqikZrimU_!8?^B*AKu7)nE9;( z#HNB3-S6WY4OzEqogWl`?wgU}{IHptyi{!Qj;umU(s(2wumXw;B}qk7$|#Yjg~qq^ z5mOv`09(YzfDK%vmDtyD_3o6I^XMx((Hz>s_60UV!AXdruE7|+NNMl0d5-Da#RE4U znq^bh!9!uQW6Z#I4uem}mv;CiiBXKe;M-H4{iE{kJzGy2dElYiM8geDLWEf`(-n;I z{FLv5M7wuh+O2Z<$Cq@14e3{yZxjlQw(=QMaE{>_?`cXTWVrDmS$oK2lpmXePlfY>_aNEf1p{}n#mjV%ku$}FzwQr4 z1;7Y&#~@RbhBl(v5VEvb9A>2AUjD)z@Dgr^4T)mJP~m8?G|BfL>ht@2g}w6E2~FYk zWRS?uR+WlpY*oG`*8*6WRovwO90gzY?l4lI>|F$x%VdUb^=!ikZ+Vvx#4I#i>RU13 z;Dk7aa!+OD{hz2{#j zd8U$_OyAqGp1H-rLCXd|H3a3GSuaM&0hs}rTt!Oi`uk3~MHjM(VAj&7>~;^O>pxn; z!s00d2vi-6jxO3WJ==}+_1~8L^+;t1IGDxgErJvOL?C z_LL$uE(~nU0uo<}ephdTR8tuAO-j5hyjImj(FqR)@l|1FD^WX&zRjIET=7SF zn&E>hGFNTB^3jjG3_QJv)avSwd7o@y(93{_{^Fz+Maqo~AXA-~dNLH`=M(N}ws{fQ zx9#Qk`%krKh_HSukqOKpF*!fZy<<(7NXjW4>o;Kxjl*jCzum?hLhN65vR(NB^o3Gm zA%@EqH-BZ?D*&TKzBeI>{AG1-3zy!${+VfGzkT+9;iKFErWR%{4FBI?`(OX3VrJxI zV)^ePnDn0?{JScyRP2%N6GY8e8Z4-^t!#ka1lK{(1GDGzD1i(Tj+I8(Tr@4EIw2=b z7xkYAB-oY+YeS9vj6@r&(!BJR$z5jOPX2x_VSwi}z%mI!pKSBcj9kTxg_0{x3cP!v z_ibX)M8v*hjM8&Fg9vcYg=J;MhL8ZB3AE=er&Q#BoTjmN53hmbc)c+JMr^Guzsm)w zD?`tPhM8^-`ThZCc_|(`ExjWsZLq4!rhZ4$wbC}1W(GF$Vw5dHz$3 z{~@9HOB^DuPR{mDec_$L!|beCb{aCzIV?aPT8Z zf*?vUYt%d(wQzu{WFiP{c)WUVVp_2wMmT; zqo^3=BN9f|8bF>|3e&RIsZ`2!$dEjYC*s)7AnNx(i<5z(%VMsyh#M=hbn@I61j&F7 zEV%hbAIwN|kMnQ0FifcM@_mk?{Tp?sAxtgPpcP9N6q80i3Z&O4y`fHig zlx65bs#9u{T(3?rT<-%Q>%~$K@<~1XmUm3kk7Do$w5UdX7@T|*hF!mUa55Vm18%Y` zICT!j4eh|_7-r;h#2?EC>%pWfPOP^Z^^qb0k{#)P8 zKVAHP=w1I0Uf{nnvwziP4GFgP$bZyjFbSYu4fWdf{EC&9p4EIf);>!4VWRmX029lw zn++!$!*mmbEA;BE5e{QwkF3XFTZRF_<<}3#Senm znSbuD6f39!q^+StqhD$+8(05WXLw?s6%Lyg4l~*l?6?TK-dFUQzm!yhsw`mSp^-5LUC1qxhs{crXQn}YQ?vvKz5dMM6IaptY3P|7p%5zu2(^)f%7}?S zu|GpE)_|PRqISl{t;|w^wqde)6g{OjR(hgXw&-`l*VhYS0?5Epd4(x!loEs2mWCZq zAyR?y%B9ek+;jIUli~Rwdn2HcMIZ|1N9V$0sZF_7vYi!$YJ(%Q+*lKw5#_p%i9doi6e=Nt^O%^mD#m%5g!43Q^k;ip&8LL)v%! z>@?l=;s_$CLYq%!b>`V8ouPA0Kp0ZJqD_3`dyrJ80~*b5IL6rkRFNcY7L?^)=!S>t z>>g_R9$fgc1f-pl&}z*W5cO}-9uXAc7*EIkkfCSI&@0ayAi>w>u@DMR{?R@a zUfzl0scVPG5DU*IertiDzB}H?n^jJPA>Zci7>sMyJ^Asz2+ke_f$QB6Kh_i?xPGVlTzb#EO?Q-(a?zT{PY!wW?KU@>Cn~x)>0arK)Ad@O%>s#D&lV57A zXUZd6?sXaps8kmXuPv#V&=Kchkqi31g$6Vws5N25V3i7UN+TYxU;N zd%n=q@#b!PxOcC-x(t1A6U7=L+tw;RNn4?I5ONsgo?M2A>HxWPBgs|P;$hRkddOSDogF?IY)Q)4y&iPVB;N(W$O=wxTb}Z)`AIK;$AZMJ z(#nB_QvkW1!Ct1P>$jGq&SEN(6G=RLkv-5exZ;K=gRP}b4aGh?cs(oJuNnf5{(&UQ zQ|$t~Sv%^^?zS*8a?q^FL6cK>V~6}^qJAmZIMptG4p+*wU`8)#Mqlo&_=FM1wHLQ6 zOik7)Vz8CQSy7cSLDHh`4($%eGdNU22UxFYW~tKB%_#}Q`&wb%+K8-5+A@l}%8Zql z=P#qg?}(3;*9i?1>AlINnHOuAI~ zC1&6ePI>aH=#-*%6j{Lt7OI8w3V{W`>=0{tpgHi6?=#7DaC--P&_}7N#aNo{b7ym9 zYSW9=aaeTq4@bryX<7~KnVzLZ?3-GLxmS^$w&I$*)`pArsI^SUGlj#BoP<$D9YX^A zbW3xJDqa=k(+rUsLW;;ey&lx(ZOAPr%Z11lMw zv#_u@B+;z^Feeur#cTG>ogF>!u0C&QSyMFJZXs}zpDuJ&D%alh!vvg-0!}vmyGsIl z@C+|`+svZv9tJ%S%}PDuHhG)rYe9jTuZleeH3~gj)iK<5(PTN9TGOd#fOswfobbj< zSMluH(WL9!pUDpex9m5#Ds!ASrhvEunAGNn-XjlF+bz3 z9X1w|(BeAl(sHN_e9sv_h&upjUq|w`T=Sz2nbYvB^Ji<^eQcc6?5Z4}B*}gn+kP6G ze%(l!aKa54f61NAr#GE3Z#Ce zjEG{(d|o-*7uX=LNadfj4?A21*Sxiy@^Ifk8V11d`=L^Y?<%)5Me*H`J(s2SjoM7d zB)t&|=jFCcFss)JQq7YX$m}>&sYwp~$;qry6XTyXLk_}p-VV}lhiD_xJW$QSPE zhFWL>#QP9US&l4mA0XpP7DU(RCxe4-1G&V8?eufj*7f+4TIlPTcGi{*Rx&0V>rMqB z7PkyM|95u^hDHXi7mo8CyeB_EHAlxN6t~=Q;T9qWYdmf^CozYRldQe)U_?mIje^wn zG+UG0B8mZ(JoDR=bTND5!R?!z5N50|@o7&6CB0o#bFW1DuM7=quO$Eh@r3pb5}->* zBFIU>=ozD_@2))Yt{d?am6(CvEkIzF6pySeDOhk2%55FKaiMHh%yU8!Q_OIX@|OG1 z=7};L-y7Hzr3B5&fW9Q`IoD#K~B5rytSHXOE%9Qakh4Po?F00K-PDgUdmq z$%?siQ4D8%oqqM5C}RwTFMRma$|is0>e|{-&UB@Ip00>RQc!I=smGF9Q12e6HH@Q? z)*RR*x@RP5Q_O#q&^ctUlX79;6y4$@DQ=VNm$k2uuHv8 z_^?gR^f@^`Ju_oHl3_SQRdM%Alk=_Ex*>%>--K???e*EU+816B1~ei~O-qs(vQfOT z1Gh%-P9To0_XfMvf)X>fLS9DkHJC6xZu;m(9ALn9eyK^tU@H|u@#x+B&RDY)h)+;& z^*i>l=MVGoT(>O&lO5#zpc+x1U=Zm=o6`|5b4@psC6vFyOKk|dr<_R0ni&0zLFF^H z8?bG3D#vRGNiV3`mL>{?JjTs}o!$dG?zwPLD=>h0!oIAwy@*2t3D}n$A)>9be1Sb{ z=GH(|Q4g+2HWz|Z*LKiDW-y8M0H+zS_T-1K6~(wr*jsYUEkNhUoH&}H8w~lz>Yvtm z?VqMAdWXIHBiqC0_1AWq6Ix#`=C^5~Z&<&hHKczFCjAowB2}BcFNyU>br&V;NXbO@gqbGbtnZYN!%7PS2qTsQF;T5@gqF955bb z*PE{2HG-qu!Qve4B<;-F>|tVCXpm#9--*V}zm+C^v$S1kkv_pm7e8g}<&kr(s$Pr1 z`j{79fXa;Lt4DWNoc;!&zKNHSLQP@PmDmz>AQPouSm_a0hnW-sJ|NaU!HZ7;0l_iT z0@Gz5A_gN+LE8hOmniq(=n-qlwfYmCze3Y1pk+^MsI&wBxWShn+s?MYYV?pQ^u4_9($ieFl5&PUw)%qwyn{yFbMrJ9m)^9Fb6{uXy&`K_Xf zyLE}PAM;jSd$wUstS10Lx zV{V(Nd+w?IX<(;eUf9hFff+B0jgE9_7->do=JTZc+jr{o^U)85cX!JXLIcKNGE89A zwBzB73PGOa<7JH2y%jKZNIm9YIF>)p!clbbulAmQg<2m~;KYUN{B|0ydW~ zhRtWNf#?GK{(X$e-1&?)V|z)(2mRk?bQCZUz9v;IxR#f7(BmOz{3H=SqU-BVj0QTj zG}-$Vi4G|zYe9n(aSpc4VqRj<$V#_kgVHEASgltqLSnIO@S;st-Z1w`56)As*sg3OnSr*WydWhw z-`FTqb&>FWh$@tG2K~!g@QH6;Q4-#B3eg&b7kbg@aa0+^x z$+ztUlh;1{2J4CiR|`pXfN)pTrI7l)N0l)ygX;P~W4L!{xumhg07=alVX-lM8>`kU zTN|l*^|Q=v-C(&HM!3Gp7z+X4yUL4ecA1V}&>&ju6UpSSPkn+s8L+>)|}u1C3xtM|>V3QtxDIg@FxOJh!0nTR8?FrW5$ikA^mz{&dF zX)!w@DJ_;u^Fl+=)6ns?JY*@mZWD?PP{J!a=yUhMe5Ew4yrnxMAo#Gd!m_4fy?7jg zw$O4d4z;Qa7&=0$H)Dxi7}~OMd3p>fT_lD&;(Mkk=H(OOa`z%A<1)TA(y0o-ws+zU z^cf*qdEfWqdq2aJUbGKC&@PDd=Edz9Fz)2AA-RDT7S1-BoJ06Fa|nrNSf%@*SFfXz z+l-qcW4Di&dJ&9W?_o)->i0u2*H=%pQweHQ?6iMn_>n=?-(V0HTkV@me?GUF=pv_t zCLlwMJ7s~jX-WDOHKTg1RloOZ=t+jv{hIH5F`E7bnB(!N$yEKMc4sGOU&+pgQS>at z3@%n^BQApvJI4Y*m6?T;9ge$ZnBJGIv79gv0Smv7pN$jSeS4_`SrbXJrmrx^EIi<< zPX_V?}m=?cO+||9WjPF`7^Q zjh7Ygg6bhBhir9t2Tf@hlp_;FOH3ptZA4#v6Fn|&zBFDnu|diCE=0Ir6A2DgmPsJZ zGyn1C+XMVIwgeTtGA?qo%oTUi({Tjlq8c#$uFy@qoo)2EWQn_aR0m4`5MwOrvc9aL zT=k`(a636kZ^T(x{YhO~e>n}RILOz|3crLWKYiF~A)l%U_+#V8G<#HXNvqCRE4cxs z=iW@Xxk&~B7cIy-0ggF{Bh6dwknT?owt>6Ge_n?@X?S00Mt zMm7=uc;h(P&R@VYW0>Fa=G&$HG5z(QZTCNnO8f;pE1NmnyE>Wt{bK*}gKB?`$0yOx zYga>4=Ad^`ifJnGR~kqTu$HDN&Osm#6yiBbwdpj~85+pmV*rTLo(z2+B(Sc=8)1lL zsy|PzziqW`DSph#eEI(T_P_{$H<~CqosaXAj#KK%pbe~Z%2G99Hip?#qi!vesG~%m z9^4~ly>^e~YL%g(XYi)>WX&0u_mGvhx0UCa>jL7s5vKuTMYu^in(x{^PiyilyaiUC zyX+V$7GugdaOBc#YJ3%RC?=mB{u7O)-@P+A+3~a1fTW^?nPyLfhQ)5r& z(V%!D??`|idl^|D(}D9C7Lxt=hrW7*6^Ap|HKgwkk)0;$>@Cw8o_%ha%#T*IYLTrH zYBb(Hn3Lejg$$~;k45bQH-yf?n$b&m`bGSzl#UAxK&?HpIwRyMva#AABH184l-$xZ#U)9288%TW`CyTWN+m577ipx9)S?hT5 z&LgkJnt`#~2JT%iPVLi7TvhF9ZO}aW;ya;JZ64X|Sfy6pJ(s>|?)Pz$mAP0zC0Og6^=atRLu@sraHB(yACIOKhZ%*ph;%d# z`=grB_qV3JsIa@VITg;BdD64kMc;|cq7?m7+`hTT@+*x>&d_5;%~E4^P!!Ows`Qr# z+`?@P?W+oMWPN^4_?%58&w?L4MRnjT{VhY8PJXr(I0htb(S_Qcb(W=P2pyBDT?!j3 zk8ai_%iec_Gohm|Tr5CNWtcuj$%6G4JC8TuDw}S6kR#dAka=1_mQ;!QTsug`AiEjX zjh!oEy^J^C96h2Q<&7F!V`-kU+Pu_~a@(2D&xZE$G5KImLM{840#qf$BnaNvm#`w{ zy`~q;Sui)t^RE&WTj*P{>RUrf3gqw1_Wy+_7YEq9xjWK7-Qb@G5gUM+oy(sF{O6`3 z)Qk`sn9$oDIX-Mq44iz7J~?C*6lCKh4~8~|F=9Z?n($9+{Gab%C{474_X+#)x$Rd| zJ-YZqv(S0qPY`r#mROt&+ zq!PnlMAJgIaLZG?fh>FTbtZTGstC+}a#3#7SaPsq1)=Lk=a=VVB}Fz%K!?qh;))@Z z>~Wl%fh>%6M5|ag^1~sULEz%|UX-yzFE+DK_8mWz&u!myacI!pjZ?||h-++{*U6u? z21k)s{qYvF6SSF9k1|lwx?Y)Z6}TS4%U>I4L4nBXG?0)GkUm13qAlHsi6G@Ao?=iE z1NstkCKEhRLzW3NzuI`QqD?k1-rOeCTdMh=UFYwsH6OfVpRpA28qVDeAXYvskoaT|J?rvWHllEhcZX%qFpXU#{wo-mib`Dmm^B zw0pWiL_oN^4wdf9_qoRO=b!9TLQ~it@p9QI_2P}P(DhL7ahCOv?YZOa%GnkJ-%_24 zOjd1=)KF~?x4$`R(({-Iy1Kq~MT@uz4(GVhLyLOK3b-owfU&SSC$HoZGqpygn3webU*S zHG+u+eg#!R!nCJoKaRV%*V%pmWTg_M>ATEmo)n99%8DYvR7f3P z?CeNN)`G;>NTkuTi&COn!2%P}?wMp<7BFlG3?fF~_(+>lVa=yUsjh~eYMDf) zJnWC#&>BVdGcyUd_p!!n@yS35mn5q_<&2BWe&}S3kxpKW8u|(~dxTvG>0AK*eGI$E zlBBEEDdA~={=P6tfH2iP!8|#51&s9(8al*){J1u9z>`L*sUkC$X7b(H=}Ow$1L0qE0d|@vMmot7mGi9PcW1k?-CkW^ zC(0q!whFTFnJ*`>%;&e|8RY>fkAxUl{!zVzEbPvb3SZHx)`G_Xwe;Q(^*z*i@uY3X zfzTTqnlcR)ucwO4$Ja&}1N0Z&|hx!d)@SmV$icMW6buwf(P&C9I|ckFyb4ArpwNbxzy@1GDCT=QMn zq=Xtib+l+^jDnJ6s9fg;vv8JgX9UMw%fKDoFkxLqnEA{T`LxCu@9w4Tj>(^D8K`pj z<_9Yz%|+VZhsONK`V#EUs%r=^(p9~tfy>@jf$JLNOMX|SC4)L{{RG=JbFJf3wd4NO z5e)ZK8_dd#{qivr`(?PdNRa*s<%M?IRV+DRG>7dOpM)e|f1u_Lro{~;y{fSu#>GI6 z=8$*OgDvv*1FilvNojs0hPL}v>@cRmsiee&y7oCQ$+zCF%UWB%+=pkh$GlV3ZawO+ zAow2?^LJE6C}7e?DFX8$qA1z$Ff^R}l;%jf{iSYp<#uf*y z5}BVoXAZt7?V{VDT@o<0){fK?HI(n|hX0ULO5fIXD7<$W48684Aje^ABfssUMH`4D z?+b@#quO{VI;m8lIDo7ug=!0Lr`YN&obK=@K92+^h1Dd=^YJ&>X*z|AqgRS74iO=i zaRoX(UmJRPFlX-TmeU@A*d_zuNGyv9wD9hor=cL0bhNpe3FmC~PH8QuNt}xEU|;gJ zb{M@3hs?iw#Xe=Qet`yCYds_cX~bFv*=)sH2Gt*e#buq$y-Yx@4;oaL>V$(X zU8v6VBfJ!Z@p6RwUY#i4csPJG;8yB_miZVbt^XB?F;NR=JYlk0J^ zE)oFytrHRIQ5_rWGF>!xdVSJ?Z<34iRigXHHhDMPa!etoptpN>2oKrzOM5r+_QQYw z0Ws8vL&WRrfNQ%G9B6aNn^_s?22dJ7L_OSXxU#nN%D+I;JlG`<9hCA!HwEes>}m7B zxG!J0PVH%VH=VkUo0Kl?xVn30y10EP1KlW-@CCiumG6Mt@v?umP)uXfZe}o^In%_w zZe=ixZSq5hn_Hf2iyGJv-?U8=-^NA1;t4tQb=KT2Qo3d$4x9vM!Vc8u#Z*k0fLiZ)8hz$fZcsZpifP0+-B{655BDi2%E zPaOq)1M+m>XT-VKNjzEPquqVd{891 zd`>|A_7i$$e3nyrbR_OXzQt2lc;OZd95O&~OUrOmdLFhL3vyDBibp=NjR0{kB{kcC!3FBrheJ;Ow;vaF(fPP8RnNQ*Eye8;XmM^o`GH@h=+6YD%9=*wzGSKMHi z)VEGvwID7LRC^+gK=9J|(1q*jiw{AiUYEk_V@ zIKm6HT=dB_!8pkUXjgAEM4=hPF}jcIGGeJ{b9YsQG+Lk0ObPESx2Zfn7}V~N{up)x zf%7qq*LX1{Cso@7x|fCfi&xI z!P^KvWq&}-h7eVJN3#`xt@$mM8#LKhW^Hs&vuDj#7T2cj26l0o(PLiRb(dhy0URtg z(HDu(UeE=lUQTg94va3+tp>}l2HYsEDZ=&`Wo?GROUAP`joIr9Z7a3f`_4MI6}WJ6 zQPXc`uQT4whP{$R3hB#iOB?8f4(so^$KfqUaP&$Dvw+2D^pKYx(AFG^7F-XWs@E@PAbJ%;eV zb^HDoQuyz--v%{*`&mU)J_OA%YEH3`bnet}ECF(~rc_!M8Pqz)Kpj|^uosQdvBkpr z0=jMf5ChRy?3w;QMzS_F((H_?ZIIHhFTJm_oG(4^H|`&^3A)}T+(--IDxmU;sSmP% zD^-Mw$-{bLiD8w)&RX{|x7!9=(r+1ts-2@-q?BW!Z=E%!uX9bOkzW*8HCCm%Qx*sPVGtKNNqixcN0y_55q_ViQ$Q-Zi$An20 zjEW=6JEn8lVpzQSgUuA)KIzN6pa`B_c&f{m$5?C&-O@EZi6friX3@L+7wLf5Zdz0< zD-f#nTppH6ns_=E!O$*A!l(u?d}FCWZV^{)G)(GOLbb=%Pous2^KF23o8X4>F(8HM zlUF5;uji|LDHk>~J0>#+djg2=!wWusnCTr4H%|<*O@N7RsxyIU zs^(GhuuW)c)+vF&<_wc=A+8HHYj%cpPJQjhS}}#hxL~%h)M29SJhe@3UY?dFpD56< z(PQCYGm;cYfo($zG-XX!WnKX7$!xW~aN6NNh_-0Od1$49wT>W4*7D4~umV@9YlOBg z#L%TMc2yY&Ctv(V6QjdT(Oz)>D?^eaM6?(Z8xZ?xYT+yN} z{*IOcFa2hP7EIcUlgx6K+GYu4Rif{3SL4CRWms$^agSd+UQhFzz_MEmas7wi=tzAvwKCJY0RP{T5iDqVeKoRs@k515kx||L%LJC zyBp~SX^`#^Dd{fh?nW9ZK{}*S8kJI%P(Tn6zjKYJ*T?1k{%d{bE!I0+yfd?>_w2oA z7e`i70Zjlf=3yRxcq^2oyiWJFm{qcwo<)J5EzI=#*5LbcsWky_sMT#M^{GH1eRvjJ z=uvfd)Zw?>K`7{&`?Vp2ALrNK@2hgQV==$Ms0t1LG7Ggw)F<;IQD}us7fawGaIhXm zLd#`!GHslQy#H~mr%-cjFI>Z>_>}SbdhE2JWK=()j7cD^^3;-|em4^aS1_xB>8*O9 zB3n+K=qbumRuT1t2DUIVvG=5v#`JPw z(YhDfc1?lYO6hKW{7R|2>V`IYF;jhml?Bn3`ah_Vyi4`g*P3!1?(L4|_7B;i_y}EEFDZvC{?Mu_F-Jxl2$G|n9TqzpFiha;##|#GntGvAiWjGGE>`?e5^MRqJN2 zF5ooO4e_ss(&u8C1}98LEo_Y6dc?Y6Lbg=yL>u#$Q?jm?W%}`k8-G(Eau+kgo0cRs z>5IrWc=4?O4Qb(BhhtV}F(Rinv??{l$&Cqx^@7{>Q7eqh37;{lh(!rxr*yx-E9mhC z7CsRDKoL&4ZWRSIJx1~(n4RQ{9?|xHYm~oteG@?k*vH+d95Y)7n0kFU0cVb!e2xu{W2RKtw!lYOx znu{0~n4A9@y7#7GT%<`Jd)B*B3KY~R)NJZdE(IArS>1{8^@l?9AG?9f(s?P-6)`cH zt^jq}WysIZ(qo(&*ygcy8w#~HtDj0h$rM(((2M>#yLGmFJ^k~W)-MWmv>uneDhkv> z8giTF`l=Y>$<;g%N6Qy-7q2R{$#Ytu9Pt>YMz7Z^ctx)2P{Cnt1NBGt>6=7H7-T ziHCZoc(r{W-wE}oHp$NrE#fxSU~hQ7so9_pZSUiZT;$xbJBnLhuX+1gE##dGk?aP7 z-^+8cGDY+IbQ^VyD!Q2n6w`Z^WOAc5Ko_IgX68}dtR(Ac29>goRu(+!(+QWeLLqp{ zE%>+lg{Dt!bk$dfgaQ*JqVS%5`qK37%hC%vbWNb2=ZE8G7)u3i!?n%j*=w3-?86E1 z_Yg)V6~D0?)V}^?8nPiYWlv@cIaJRPxQ?{s@q0(H}Jgy@lqqI*_xMPve9hBf&D z+{h;hY>Jl3SB(*}3S})Zg!J~WnONetFN!K#Rf)DF*4AyRen>hXN{6nGuQsg zZQ|c{YgxOMC(6WR&`3YlYw-594!=VkykZiKSj_^CX)7}hlnp=s2PLvdre=xVn?n6V z<#|x}3KN57L;PbE9R2I{VuZC0=v%P?2WGTyc}C_r72qtViriS*!YD)wR8!OJD0~Sd z`5Z4KM2NdSy&OOFoflDHX2h)hciD0W2KR?a348>v%$%2-^ zJj;X~F1M7GztnE3*Bm}Lm@8p2uF{{KguXS?jC+xCmqMOc!QCt8)+5@-tfVa7%HGf! z))d$qConL2NYw#DDb=AZbN!AFRZk-?sB(ht!|O1gr=DT<3^WR({5U7wRXu)PKEK*x&mz6m?yy zCu`;?^biI{G$nC6wq~wb!-#Iz$t2L92DnqiUPacC`tt;h*V*B5=g#+|)eM>QrHedd zrSMI=!D=ru6y~%s^_y;}2ZgWaaVLwh=LrgL)bBRIxwPgRO7zXN5k8Mzp?ZuxEq?I~ zKD}ynOLW4Q((%c*OZRM4@b#lxRbM6UDza^$b{A@^F5NB&%Z;ZvoDNICLS-~jPqV&7 zXD=;BUEm`u^VKAEf>V@c>wF8d7$Mryr&1s%0$XFgboUN(BJ2BuL5^|<-|>{l)NLgK zi?yUzoBR??YueNcdItAx>|J;4jQCcu0wryUb(WcvV92!JNUPHo!{1w^z|=o~ngxr7 z6~|m6^Y|h8-IkjS1;{j}2=@JgN$i@P+ZrD%D_?g}VmYSnNLvxKQm4JbLmv6^{m67l zMtQ9)urdOgfj|!G5YE8=oPGQ)wI5>2(v){0ltjCbN^VSOPq_6kD-?OG2cZt*J{dv~ zDL+{2L>ze;q)RvF&%Rd_~Zs-4t!ws??Pw!Vq@!o>cgWR4Dcmz2#sM zW9V%zz^CeQ(sSV!Jt;P>!Oy1pFoBPJ56A8PYizj!U95UH(w>jmjdzm9AJ!c)WliPG zx~b?1i{9Y8QLHA+X>T_JPj3B1^4{~BkM!b3?w^=f0S;1 zs@v_@gd}{Mfu*Rkx%;7U5TEJ7>mte#`yO_$Nepo!A)Y#GQABLhR8=SF0{Ud2gBxB< z1yAH6y|cE8(=~JKX>AQBmDH5|ClRQRi(koOd&HZkdI_GkQ=WWaDD+6kAv^wdfPVwk z_Xd)dQN~x-HI&trvouAbPspxk(6JW$9ZYg49`oqe-)@(j#54QK4%-tZ+@Ch!LV2AX zSc4bSvE=c=VE3~5c&4A5k zqQ&C5?25f-e8cne?Cq{?;~XnRMtrR8k)f_BZ$IqrRZAK?+aQ*V=8FHoxV=g&*>O8` z{4ILx6inhq%pCuTdgd@(q;f&MbM&a`yGfUcQ51C#f|8USEoKmYBmrzNpq! zP(eE41Ak zY3KE5;En}`YDej6)To?bj{XUrYW#Hep^2>0ys~0ZY3KC@hUUy#vjElN=j4`FWA|R@YlsT>cieZ%V)89uiLl0g>-M^!4z_TultsfborI6K4C**Y1Yo`JvV9 z*v3_SIK>ocd!EHkFt|_W6!J~-p3knfCl%5>jdpXUEK}*r!BGQ66S;~~`sSUJ{e2d3 zzWDaQJcO7EZ#Ua>ggq^B07ZT35M6a{gj8s`dx)V zxTzMYPp{Xv+C1vKJ~N4ICi7JoQG7k#rj9o=nKD##x?+K?$jm(ssFs;kI6Ln0=Um zMc*o}8B9P$ve~~IP=xa8*tjI*<=dS~Pi@t{5pgK8!`JTE#wfnGm3t-eP6E2F5uBG= znoq)*-5CKRUY^Yt_9&%0I;g_ul5~3nt}b*yF<;u z26W|)N20|vXOqorldm+P9Y$mYDLHLEW0;r~2|er?*O1a;yc^A^(wm%5l6P(*3YCZ5 zK7H8B51b`(=uSzyzqjPFnfc}9Bv8S!JJoKj~+J5;gJyHETu8HFcwT8dQ+XFn)u^v(gF2 zW%r{``GW(P(y#a{BjQOYD!v1fs5D;suW%-`Mkl$04zpZXl5W9AYCEs2>{SUj6(gc#j{1OO%TK1CBV0S($*OxKtKr4@C7wgt&1l-Nad+zF4`7l!#Tn7;vCwt}uXPsBO;8*ks>IUj~CS7%ty zz$h>|L;&%y$jU-s4&aa^z(>fFF!3vS`1d62djrznAO2B@QdvnwMFgMOo| zKGTFqBc+I?-k|DIED93>x7y?(e}8?>X17W5YlhDR4JOGs%y~gn{e49;531P{$L4w< z#2q|eTKeAEM7!@RMqP_qm-96SSZ$;)Zpz-<_~K3a^X7xI&{`}CoC0)Bt#FFyom+}I z0_@RUuP>NU1t0A3yOi%pV%;C2sD0PeySb9>nDk)cBR;h)ouu}G7Z$=Z0SCu-Apu*u zD$UafV&+H0+M&c$bnj^!ac;S`=*k^bu`0+pd9IsjGo#XGiO`ch(wN?oZq&5?a6sJb z!>spWaZ{+9?&+&um5^XeOayF%#BCLgEJ9`p3dYnk0_HhhH|^`% zi6ys}D)q!HJTp%vdS>`V%}2Lxe9funGtI>JxlA&RNDv7}aJ7v3k>N;ZM7HP|MpN?cw0CnU6qt|f^v+pCGj z==i6Y)~$znG1KCLQ&l3#k~GuV+a8Y{%*^f-daUr|;O$Ewi6A7pCSWKWC6Y|0P4a%Q zA$M6e8|$0&Ep94y>c`6Lo)-#Lj7@$--)T%+?&K)a?TAuOt#lUTrRM)yK&SO7tiFug z$OGB^7fNwB2W=pI(viJ^2117f=hbCTV9L(cY z7iGxR+C^J!^VvtKHwdauCgAh*C@v8UHSs6xa19@h<(k-QEU-@)T8$fIk{92~I{wrr z4AOQ(UCsRmKz|lHtom$#y#<_$joZcpkI$*O zZ|Uk(dQp4mE;VoXwBTVo0GlCaTNXbHwpGCyH^2WPCcHA+A>!BlVA|R2t-pA@s9TkX zI6Wd4eiEi3oTViGZmHt{Ys(id+D&Dy7)i^}fFw@im_Ynp#f%U#_G-}@{;Aj9NvTB^ z_1ekj1fTOpB;R6c*37gU@xK|+_h`9ax`!{FOc$|XJ&Rv>5-LGKe6Q!`&~b0FWD!kX zGbIBu{U+8r=Ta{dY%`~=ak%lZ?T0#3N~+|s$4*ThNM*7*SWCv*Uu~=roA8sBg9VW6 zO0w((R~*!jw_UIK#S+A3cT~T~K&q6JJ~eU{KGLtS^d?&Sa-k$1bfYJ8=SE=Lv(3N; zMkT)|?@AQD)rw7-)-hI+N!0|LTXe07NWOCY*I z6b&biJfn-3Q6=cQXRyp_%9o%t^-qFtf^UblpNG7?MmFY4s$3Y$ql`019^;nokS+F{ zR1vv_rKwKgvq{c4Vx#=mN%LVw9mz1QsJJ)Uy*I`N)(KN6io?GbII}YPH0S}nJ-_k@ zD1?;h5*-wGHaB*4a27YVwKXv|wfWUL^3IR`f_gU7FKV*S;l#Y1(0d=o2VO@M6Ulqx zl};%yyKK?3Tl~i8;1%lISo&SAlYkcwZ}yC3q;VfuyvTUT4TZ~t;E44q)i}vUR8yf* zS14{Q*eo>&K_ghxy!+8>I}SOV26E=I^aJe#*6@6>w^rv&zBx=1_7oxAm6=**giLrR ze(6PJrpF8)eJVIRSTaLA)&>kPp>yUmL%H7_?L`bRPdOG7aQo`YmAtQfB$kF%tNX&t z)DN!6G=Z()d?@n_1xbr!TT$($oL*<^x$e3{6aNQgSiu`1DCh58@jp+vd9G3%-a?u9 z)(c}<#ogQeS=i_MPWh4t30cR0*|%O91|hU>yoAA&KWzOcpMO>*e-#}HoiN&PbqRFk z=u##&#Z2YI)#K3u$TcFcoNQ)rv$?`G^g+$;B{wQWpz}U;!R@Pr>Cm{jV3vQnJML+@ zl{xRCd2w+3(gEr|Ej3!7(k&O6ia}BaBVO1x#81F&5R`lx_Bx6yr|WFDW}YnL#LT}~ zHf(+muPn^)Hm2g~M!varyEVQ>$@GSy2Huj3#DtDX7)crp*Rl7ryPm8R3Qs0!HQ*U^ zx$LGZSdymdQIdEq!ql_46zjFQ?bh6Cra2_U-B}o4X5lo)JYm^5zRUXN0f%hh7j%&u zRThgOrMgRQ$n>~1ym#(hm)f3gP2lAn_FL)5%;Ug3Ewa6BsZoJSN~L#iD8Y#~Q|W2A z@fISdKfU>O$$8X!oMz=i?&oR+8?uY~CDIxpkLZYyY}5k6(#b3cbBqwW$7M5InGTEz zyn8vHqH$HQ(mKbkmR=W{9f{sbw0ksEG(=X&QpU=JDSe@XqG&y`-Qw>RUM|{`9fJ1; zG5enOKxatXoa&vsw4-k*hJ=Nx0xgj;zGbD7WhTGCwmitrV|$)LwtU`Zhv~UUSpn`XDVKx@#;Q^wkWU4drLDKCBxkQsN6Y?`m#*DLo3GP!w)A*H} zSOEqSaWvPn;{470xb1;U(k&~5yqyc(8A{8p9T1qmZxMX;KzYu&Ir(eMofresTm3Kn zb;eLWpt9b(RuF=`gwJyCLddRbZfm}fvy}rT+vDLuS%T#nbo|8i=iO-01QVr{Z@!`v zxCL!qGd`@xBL3n;$bgkXm(gPy!J1$+&|`H@0*Y}`4%@t@fUn>Kt5N)2K7)|aU82fA zb@-nOnxZNj=#=FToi0){aF}Mn60~m%F$<3F?mxk^?ds`=L7yh}Px^!wAVN=)KU*ls zSH>xz+UA@o`&DZZ6GH}QR)_V5&qjrx42>+orKVA|&jt;;L!DSW`z$y-y}mGB3b4< z^61eLB5DTG%Jq>6@2Vsgr~U)|J5>*N{1|W!809@r>0;e|smAj5l>KLdbCkM~Vk?r3 zwKG!g38W7nRgzn_t;X_IKg||UU&0A#>EF4S-bEMQH13}@K4Ip?D2wLPnB`^oh#Ow~ z;JTo}#De@gu12?P@6Vnjttfg{&rCg4za;PkG9~Oiwa4%IXHvER5)bXrUoWR`{5kr~ecmq!$^fc&ymiNC|O_uBT9?4(wlZY&?Q`#NoFREEy$VaQ8!CJKeb9}9`UGKJL|G|Y;uOg7h_x8ZmJI$$_-Bj`yGzxKV;9|tD$DAw~k z0H>#VWn_V{boRe#%TMo@^t0joVsi3lr7m(NL^+itp&gm4e@-9*CCUywo6cY)(E&Wnu@p|DK#%Q*-G=Ss8_~(3Rx_-*bm9m zsMO5h$JSemF+WSA3OVWYwQ?@8u|XQ|PnxfODQ`X%VgJ5(Hwj;E-s$Er5sL)9W@33- z^}FKF<%{>M+zrfFC~cC5qhajdo9q!uWybO~=DPB}xj8c9T|RrSeQ6QR;Ldn@8bR^l zLP=rPNAwb+1^i~#2iGRE@HJW*3>NPlCE{t0t3N1Ht#lhlaSvqY`lQ?34CtxAs@TjA|NlRL~kjuq(~jpO5X8OLvj>SmYD zNF56c{PI#vH9}IuoG=q%bPaRx*UPP^G(9;80zDRwN(uruCJQFZ`zWfT$d#l?YaQI^ zY_)JH8htL7;X*AGyUYeXX-zRc;&GW5*3wcuG!LOMMWuK?@x`WhKaA|`sg^O&gNxQa zntKGvC4jQ^<@E=LZ_D1MU}+U|iW>ExQA!LZI|YBB3w zZ*;l=RKtUD&)6)iQnsbn=-)kSv@kfb5%1((rTMasrDHvMe@So(XVUqCUkuhg&=Jq* zL0u`4Ha_XFkV?PX(tcPSAAx>Yta_;;i-xO&h7b)E34@#HlN+?y!tTmy4PP%t@WAGg9F7gjv3`x`Teqd?{%_w8 z3$r>`k;Vd^^4GHQ<+B02%Vz^_uGY3!%m-+$_?Ei0ow)|^^`B{1u;z|Sk{0&2WdVn_ zab{x#FRK+Ue>yIN`RjV8rDRM~wyHSdGO}D86I^W{gvd~rn|I~uIO&xNlFQv(7Z%(X z?&#fW$Y*a*dKacf#P{`$@GCgW^L4ndZ=%?Jar};VH(`;XdsbWkC+jixW!Yt{hveJW zMP(?TYhHwl``TzsJoX}ACoe2a3D^mGO>u3v6F*sM>So4Gt#NHSPCcU|G!`&klNF$v zJ-AlLSjd!#JB^A?KH}T1p zG8Kh0h=+@AT86}A3B5Njhr9kkBk`V2?z@>%f8IB^=i5Edx(=(A{zAH=-q&;XZ?2dz z`7JPv7b-@nJf3S5KwJ_j8>ZqnJUd@!;*_y`M@eMU}oQTJuP&4%mjZJgBS)5LjE zD-?)*jE@92RtIx(_7Sg-axo$}2Q9+B)6Git5>839*2BK7#%MDm_*U8UAKrI#AeJ01- z6l0n{9Zn3oevdxOl-J%{X@-9(2{+t(OT1$^)I2Ged=iEtO}`-QgEd>+$NmR8M9*z% zglV0g%>^$TyPB2+E8yvP8ZU*eu-st9lETe57Jz^GObpNR@!+#>dr?&W4zF$Tlqqog zDLj+j^_ev5ykIc6du+gv@UhB>KfQXYpf-*HN49B&a4+)ak_+6hd3>^*VZcVcnx;wq z-Zz05jb>8IMQ*Z0Ar0e$aPMz$K?G{`N9^(42S_R+udO6*Sq@O$*R$3ZabzFwJzwZUx(`=*f z8~lRr~4(|82TFc>iFtz9S9Ysu*pg~ZLOOXggAbCJ(_Rq zrG?{%X9R)-BnK|U2-sn ztzJ<5?yUpqTfEIScy}qb6!e`aGneXF61U31&Y9T6k@UBPsRpAl9177MwT)`jSgxaQ zsl2*Z)&rm4{3>8mdRk!M9A0h7SDk$*Dik2E`E=0o3&xR=#d%NE2^1%)F$SvX4Y)5( zUl3J!f+VBzgLvridU(kNVtXRuX~L#@e2zcn^buC5;=%eZ>V63vO+VjJ_hF*j3`#it z=>NthJW95+kdk-i5hX9jAemm>Se|=Csovnms<6m3jR9rVdyoE5t2g#_{3pX1ZzuI1 zJb9N~aKL!W<#4ci{gK|cglCvYcoi>qS0Yr=sfw9|L3q?Ur9Wm= zluXCwLS;AAz!c|>skZ$f0`IkDgdw(eXan|A1v)!NwW=p&FIcwksg;+_s>vxs>zc$U z0=I(J-ontb|N2l*Iz?~5T|BBVW<7xhC)IQ$7rL6r1v*|*cR}f`YELi1^jzs0L*!jb zKir$_s!t+=$yi;>?Cs~$2$gnZsD{>Nyt`M-RIXv#<`r1=C!N<|(2Q}w^&l;B4T)u% zcMFUKrkm0U4^2IYFi#hQwq6;Y8eX%%CN$F0yXwLd=|C15Ik+axIMZDE0CoJ;gQSmy zGmcp}-21nLdc4lliY}CW6t%T6_&>VU!mXm+|ES0scGE;SxsRojiTIgEA6YvswWCUA zue=GuDkj58=Lw82=|x~lpV3G0S;iWd7T3CEv+noj{#Le)aI_CX$k*&8Wsj)Od6qbtO0`Fz&yv$Q#qZD)GEsE)jG7-Xh2hS%R;`cV;?8wd*-pKG zp#i-IJiJltOO8G9yer%;57E{CybeCTIM z$Q-u4LU!D~epReX8&C=N~8u|d~8z`f8w!kJwH7K+YNhCfs%Z-YSR&EEBlxD+E#Px3-;xMj4P`J zcg|*8S?iiTPq{{{gJ%rMk@qSUPb|g;TF6s`UXl~8_sW-sbCwcIsj(7U_j<-1uOrpB z!VSKBaipADRjqGMiUrx|PLf*HC)eWPXcm*VPk!3Z)HcM?U2Q{G$8f)jBn*yau{>g-MTm!NDq3}`* zGeM75dPDTRTUO+svtswn{OGYfC-4F;0$7hnBt)JZstWv-!UI0qtN5gZj45HVXF=S^z4rrAKDT&h72_yrPu6{hm+rs zD}HcMN)!;YUERatZb$2!Q`7bw!-!j=_;tNPLSQ~=6g+m9v!dEFHQ4ce{qCFXN&9l5 zh}H--*o>x<-GqhM?S18eNH2n3^CP@RTrm|F)9j{xfmi&^NF-#ij{{GtCSLitoSVS6 zXmfVtQCb_>&I+P~0cua&g7w>nW@w#ItcRFXt4_E*+Z#}2E~k2UWgJg5oXUNVC!^)o zOE<_Rva>2RHl_JShSb;!9e@6#dXndv~a>nN+=E-i=pQRanXQl-s6=gX`@lX2gWN$?OTFdUSqs_S+~F4zaaP5Jg4ij7!D0R_vC@X zMbTQ*6nN89u!~F>0M&kyT`92Hv*s&+{%Y7*M}RzXHDz-MDx<{gu)$V;RIpeM}Nx0uhSh_1!HPo)BEKQ+8p)qzj-0!rkRhs>m^G8wBlvS`R!s zZrJRHa5xOF=;eR3Q~4G{LbJZq=G#ugPxJL87VR?W#otJOt|*y^Zwrm{>a<8Ki)eIMx$+*Lk%b>}|r#DSp>Zk zY$w9bgg@^=$-xt*G9R{*Tnowq8{XyY+3m!BnK2ovohN=vwD6kBc=MHlpWX3fDPBtM zy!M<%1FpGLDX%I)_cro1$;rl_`ApS<@j)@#Dx8eTba2Vgb*n!b*q!LzxNb#l9@S)a z@XnP!(I%~Z_A^!DYJPOodk()zeOR@3feC{zvzeju?kSYa+C0NCL}225;ry}_?l4~e zzS? zk@1!Z#8)8bRqJqHcg8n0VKHZYKT_0$7%TwXyf`v}%$cpQoyLT~lrih|`7}S(xUiC~ zsMOr6=!hJ?1OEXzC!PKKx_aPZ9(t>rd)cuz*qP=_C!>y-oWu6ssd89dhcriUJH~dN z&E~;8qwIz^-A}V-Kfix-WN?OEnMKm&G#zg%M0U+ULXtD5)VZU9W_hSm?M)G z>Z`%3q&;vW9blnnGk2on%s7xTQ6DcoK`ME`Q(!`%gAKGEAZ;B(Om$Qn91LM=#Ujmy z#~+GUXR|SL>18p4)pak5h`J>ZdLX2K=cA3!ZjU5(isO9fDy_CQ*++H#ZgXs{q9;ao z-eO3j+|871itXQuk`<(+ZV2wpUu6s6gFRlQ(>A*69jmCnwLMAOL(kAL<0xoT^P%wD zt!l#uIf1Z6*VcPDv5j6G3!RJhrJNK5PQ1j{$4PPY^2y*OjB!}Bqi&C1=8GRwO!K0m z8-He-O#O_7hIdr*hSP%~42^x69Io!gc3r+I8XWzzf+mgkOyZb5@-#%t>r?Y35f2VU~mwWE3RK1Z5QO4u69h@=Zt$|08Ul#_E zlKNlXxqq$t_1#&dr4;gL%U9w0z{LUdZ(YW_{BF$Vs5T(3n%sv$g=TVGvmgL&cz-axluaqBBa$1=Y;s*_aTrc6#Nc9Y_HExx|=P_X>+jP~@V@zWWX@Ttc5@=d(p zn%L}!{ani%ktQWpZfGhPP`xsdheSbor=kL(1s`HRC7zT%ToGb4UX(oK%8ki}d$J(2 zq=}YDO^!ps){L3LRJq}_Jr`rp63OX<)>G-&#u{@!{*@Kt{f|hE9bqc%NJI_?*gW0K zv3FA$B|Kp!UA0f}I%bOYt4-go4NC6taXOS3EW50)#rQm>c-j-cD(c06=8+)W$<5`b z$mIu@QPYjfgeod)1+m2(v4x0`7zT0CS*$T?_A7a>AmvRpnb+^gPf+;wi5gxf zPBLS5ay?^Fhh7zh>wL)NlYEXM?Tgdd_NA>PE_%2=Xnf$P9!LB7Yixa*2?f)4P8=i8 zoGM%W2}5q@I(gn0A)nS3iY6?nCC4*ikAB47@-AVJGy85Sj*?~ue2ngaLuLn`S$XR?&~MC zGt5I{c!#vF2<;A^aR?1cEzbmsVvKnWU!7BtyJk++u;m(2DAy#WaPL#Dtncb&c09i; z6fo_Xa{Kr`HAC|!SLkLI?A;C80g3mVPk2VlTSnFc;rTMNnCJzb#kzeG+A@3U;vDKS zMK6DhJB)E5W{rBbTf-=?s45#MDasUvtyNHp-Kf&XNHjxecD=e}>?kVe2t^3}f-t4Q zWepQ2WSrJ5eaIsu*qGqGl^)?3X8p~EkNM$}gAGp>KQ=AO?x*6XFi-F4l(kUI?#amN z<0Q;88AUd|ls>_%c{E?X$NBKfI12KTYqe%>Ee74@Ai0DT1s542!;E`*jv{6st8}gP z<8=?7PyDo&_{A;_t9hn09+C@1ne??EY9t+EM`}mSA5IBc-roIi#6P~RbaC?BcJ+YF z0b5>=LNz_TE%|9gqvR0o`Wr-NlAG0Q{)wqsej~!pqloE<7yXqN{P*@zg;KvUmNQnx zgjXq7Mt(8i|FS9X{+Qg-;hqwu zT~@~2NVYV+ez<%@iA+|BLe_xFOGA~c5_N-RfmyrK(&}pU<)Z`j=Gy$$Q&qQ-t+ML+ z{MHavw~EcO>Vo`Ms-o?5;IoSSR++L^z0ES_;0B_TWr5rG%Y8cK!m~x?WvlKiL^PC} z>uTs#U@yRxW%Dpjszxwc(w=B6sypXa1*glUfXOefc7uk$)%f-x04gP2zF%F;_%W`E0hO0`f3J#W9&es2QXpC0< zxx3b*`R04)N6zsX{=@E1Y~xk2(DJCLDWh+rmA?)yT2AI1v9Dk5(;_)1>c_*(R=gfX zeF62&|C>}+JyRciDXQhzXxa$(!SgSl*ABFxT;}df+%V`*3dAec%QFdy)CmsiURBi} zb#gN57MRl4i;Iu19V8{T&%h4ZJ--Q@u(>r|g{K$b(DukYB)P}dil1u460c?5p@o*Q z(*!8C4986{Ysv1SNsTe+AQ5>jHfD!}o+UrE+vUfRK8h%=H$CNQCX6+4y!#T5`cttU za01k9=Gnd31DG{hyP$v_Jg*wn{TaOhsMTi8UiNi|T{JCdTu_kei%S)To z$B)UgZV_dSQTOeInup9W3-)z5Ke2#qPsVL^tx$PHeQh`y_f+zI_;Wp5hbER0IL*mu z(U49X+Bds!^n8;=`9wtY)U2g%$dc88DOn#y<*;X^3G#dTx?GF?3tqk>w7mIjrwh`1 zlTl9fx{XU1J#oqM7j`x=8=8R+!#MKusrI+gkpveu1mxZdIBd6LzKOd_U88-9z3NZ; zhH9ktL@b0;cc8J#I7-N#@WBq=M=2jkf_UlX>!<83vUQ=AmS{sThwm-#p5pdqd%T{- z55f#Pok1u>Rj~*(HatD#a{gL0@sx(I^C7KnH**IjlO}ZugWL( zv)@R)9yh2$7k=Lm&3M%Ekxk%Vbj=HmN#QJq(TD4oF64DNel(`(RJP*_m#z<+ms zUK0-d>HC8K1soNLBpo#|gS7{=%dZdAW4*J0Y`x-yngv`2Pb`(LYhC{XivaZ{hIsSR$l9gLgjuQ_dhM(dvzv zZ2&}E0NSApMj8e1$t4`Sbij~s{~+z3p7MVp2l0hp4%ZL|P*53QM}jRF@?hYT|3Ann zA<+hQ68dk-kRKX&!UNJu2JUfC0Hcf(XaMy8hq52b-+yDaA{Af&T}8SG@Wl}fb2G~S zFU$~mqG|5z0$ecrdt)w$x?e{9w}IUPqb>ok)=T(e23((MV(bEL6Cg;mQeHwSz!--B zH3Rhr{{4*p2PDvqoFI_jE2)2OoxF-z+~4qlw|>AmTtcj|qa&E@g6Q1q+Z;v)+xn5OJs*yVwBmO>M;7tZji! z@05Rk0MZjNKoXbZTrkgNc?;<5Ab<@)wH$YzIIrLH2Yo3cxi8uoGNd-axZf2wL?w zzJE4N{Tl~JC3G_{0`Y;MsQ{=L=+?&H&pp6a{zn{_gT_8UHVOgA?+3^SwTSustN@W6 z0%$Qn3BGHBs=29yvl*qb}=%XoF`Ga9#wI@ce#WNI(Ssi)>L- zS8I15_){u@Zsz{unr>Np7gu9@AU(JosjJ;aLOO7zG!}q@>NiH<4Hlr`KX`RHNH#mq zA{&57mO$hJbpMa%lktCo{L|(*FNc3oUhYQ?@F)a`&a8hxhl1)b`(NRIcUb=h53;O4 z)XfSCfcyqP#^A12%nBm-U+n)82>!#a8cY|swgBdoAVERVgAHKd9z+bk1Py_5kakY- z9==ln6v!N)9<Bgp9^5)`Hcp6>vo2SNzKaG z`R5@6%0J%0!~Cy=6*QhqQ{>N-z*sl{Nq{R7qANr!zxed0BC-El(jfQ}Llq?KzzD_w z9H70^zn>U)i15Ke!_Tnr*GBcfy@q|$FdI3btjEZID=eKSL_EL7adlJ$<*%&UQeJcgSGuAwfO)DGYLSR0iJ^?M?(brhXMhb>k8x`s^*}WeL0G$&&3qWgiugnfSAD} z+3jSAD1K4>%eMaw8pQnr+sE<^KsFu%1W5)krdZZN1pN=UudwEWut+geI3R+d|LvJw z+aUs0GIsxCY5z|}1kqY5WFY1Mc+Q6ax4{D=eJ@0izu53oQ~u2bkSWdNiM~Ss^v@ai z0e5K!0}vtqoIU=XJpbteQqSP{5CMu|07d|L%^#2Y5Jb324r<2k=HFQh5N~I$VC?1K z_VZxVpDY0pF3zZwr#!=TT2bAtLy<^8L(B|IHi_+s&U(z;^?rGXl(!{$mJG zP`JR^#{Y=1zYJ8_{vW0Jah}-h*A0CCMhY6(OFL&|F2J!t0f7cwB8u}6k%Bq5%i*HK zn5Q-aaH#-^uz(F_5y*f4V=(H@#`Z48K&k?17Lsnf{p1b6rb0mG;4Xh3xZveKp#SOe z*)FG^ClDQb1%Q7G2r1wplI=A___8-W}^r<%R0OV)?(gOJth{%6&?N2S>_%{ndwjmh8Wd^Ewl>s=w=Y&0{ z{|WzBIH3H6P~F(_ax_B8)+C^UXf7ZrS};>-JBNto4+Xm%sy(Uj2@gPcE}#S8iiHP# z4U%I08HFh!*v58R!{8hM_#HqpxMi1JhY0u=6DUD0*9<~9135vZ9_WED0MDNSG6g4l z6%HcwU%b95MBoncsj~xQO8{icfpM7?5hCD!t0e?Yy}Ymhvcu!8kXtaoU`_$N-~(Dh zf{6DI>B@?$T`u6~Pi9j008SYK0tV0Lcz`qyg6H@H@ZZTbL`1d9aHXt3>f(*@cZi6? zfQU#&T+Pwg*~MJc+1VIEo~Z7CEO!HtNCGebc`%9K-+~DD@7lzrwUGbt8wEkKzR!WU zbQj1Zxxo;p5<^6Mi7G&h<{;-$lc=r}0f?s?7$JCw8m5MbQJ>70JHyZtjG0T~Ln z3Ka)G1xN=kkIXXtC;VTw4hi_ts#`({!1o*fav%XL5P^gFy-OGl5-1%9rr-z;U~9l^ zA3Q4#V1o$wABO*^MO|*m+2P3ZlOj-1s4;(g)MB8sAOuVP#qU2<<#Ol-4VFHQK$Y5q z1`3K9OxtGx{e%D=EW?95^|p@8_Xa89D?pOqdG}@pMA&~(ex)k11q|=L1c-hLNEEyj zY?}=c!6l~p?~lL-1!>s;$jbhBnsYA2Y3lHOX8EsbJnwF;`5HjD?!WSr{i^{fIsj8t zdr=oJdsC3VqlA#3^xk=I49Ze10Qlf8fIa_8LVwJJuZotL={U-#0BC4Hup)nO09g70 z))p>NnqNHlp*WOM4nVsI&^u6M1kJj^yBhuJ%Rr{(Tpzsa4yaKg;BdgP%@kjW?Qceb z2Ig#UY%2p$XbWkUZ*?lE3u;56J$pB;fW=H(Ll(e2JVbiD3X6J1{hGS5w-3CALf1NBNJQT%{N6!=+82 zn$`@Ei2&~Tk7pAY_!T4kVP==J4R%@2*b@Ls0ssYf3oWBp0{ziu|0>Ytx5p=30ckt| zvM2D&AaeFf(0_{`LaDV`+mZhSfG-53o8U7pqxmc0|0Cg_{s;6wkn8--AxIvc_z$K- zL3J))iN?|HUrzpj(<=-IP#e%h5xg>V`k%0V4;wI#9q>$5*KO zUt@rn_a%O&-Ueg|S{eg2OZ)va?_UZ3r^YDTgI+)}(bnA6{OY1vJYCyfH82)Upy~lW z7UWM?;`)0m5T@ATTE1^T0v5~x6pO*9*n~${!vD(*e#;w@_T&9#6YzHL;!^1D*5)4n z()%RC?d4hEJB|RqmB66nUI9hZ+}6PqSbqd6e5Qc^|B-@F{`Kjl8$15AhXa!J-Bp19 zS^)pSQ}K`AAdf)a+)L6O$VDLKJ)qgTx&%T9IpBb>fLbrO7SY2%U#6wsc>}gJUMxhBP)Az+HNNf?~+iCU$Kvn@U z0Nl7bQ2rONS5=rm$>wnJT*M|oZWus1cy0F)CS>@E=Jsx~uI6?>H5T;if2!$%e0)nI zfF%}?ffyKrHn1RL`puvp7v?J4gV^<>n18ifQX|W^(gw`p8jwbV2Yy7{tMN%YJGePQ zvI`K?V7*X|J^(`iW_5ohM;-W(LH|ovDgXH7KiQXTC@BquHz+hf8Nl;&BElyQ7bAuMYu{b>LZO#l%RU_@ZhTusCu3je)P>L0&D+-eE3 zgPSciAxi)zRRAlv42kHj#`weM{qM%CkrT*v|1Trvfzq5BY_ny1={JYsGPMq1@ne(0ZfA0Cue}B_IC*GkaDcI_Y z^jV!WHZS@|dz#O92v|`j6iv&43rRQ>O`cTPx(Mr&@~f@q0reanRHrhyCx*yyiXhGF9T<6oo!f ziMreuj?!vw_4&0^0aXq~PG@jG!3>^cO@&8M$!7a=bW!%~xk9gEOYye7Sy1ITti61D z=^`{Naba2xJ^n-+Nv3aY-AF3tLz(n%p@lEuW|^n+S!qui{nNVj_fui_8f4)cGg@?@ zG?@ZDtukFiMMYffjP8qvX6+LEF@p({8ygg`sZ8c3b^jnAHlS4h{x`{+sCrukX7U&_ z^>INl!?eKJareM^iKi>e*X4<|nk``k`{3?pQ6lqoFLZoRteNy=kC+=`nHH9*4m2>3z$uCdcj@-NFTj=L=IA|8*sY`xPJb8oGv(VcHJKvNv ztLUrJ`oa=;XCHLzG8sGNzYU5Vvv+b0>A#4%g&ZhpeQ5e>Jea(K{ZamCzP_~0+}Qi< zc|&v?el$18$jg}JL3wGH(*qPihM zDv+SKM^1e@C|G|kAg_3ox%M9#GkXM#5!>5Z@ivzS#SRY;Q4O6ldX(*f8n*Yu5eBCH zH#YtNxZD-IJjtq{aJar9arD=4hV=WaN&MR8AAlwMfSB3vLG$-Wnj@f#nM@DK)j@%p zRt~NN_YcZ^5Z&Wvhi4%o_Cg79jF;0Go@{FZ_m!XgZ}j&ky3c|9>yZ@r?8z%NGN%5J zOgC7J&CbWEy;-cgwJbT;d#G)t#cJtyA9iirpIK{Y@PzIiExpP6QL- z@LF!Qe;<&Uuryax&qkjuORlVk3(~&SB<6~hhZO)qU#0aum8*pp6CVfmK?un^gx8M9 z_(o5~-O355Ssn7zp?9AI(iQk8@AB3im61pyy%*{Jzj`p|vBF1}w2RTSeao!#3Af)4 z0Kz`5ws(xJyn=GK41MrH%tDheC@UokH@C`T!*vOX%c*-n=Q$@bx|{Bn>6)4?9V+?zr(KFHL>BL(UB#&sNg^X;?e%o?(|%83AW1s|`*KfC?V_M+yTeLt zB})+bl}h_~@SY98KMu)KnLeKkk?~udFXWmxvvKOlGE6SE|D|<5Ywgcv2HcKf?8eyZ zw%f|&=nBa6A^!MQS*}ASZNyG29WAye-S^s_&Q7Rp7|a&Q)RyspnzYCN48M?76LG4b zj27H}SR`T@$bbPdh<6S00FhI8zw+x12R9Ad_%GzNdw>|m5N{2V5h;-w+otvkOPrn= zfoB-47{w$XWFPggOxe(qsrgOox~#M%Tg#?rkYxb&AQPc?xEe@Ln8LKi;PAwg%VCQg zOz7|i^oJ2L+1AI2vzSD)YhO?f`er0#Ue2UmhP2&e-TcV5Yr2B#bSkx%QPA_eGe1d2GxClxt|Mi( zZCeLL{25xygiL(q_}7yHK}gE?l#P6Y#WyFAbI0ZGn70%_wGfFrw<}c!^V-I5+98N3 zzdzBl7!cG#;%R5>6d8gvW=agU?Gd8hkeyXO0@Pz501usB)6~>$W8HS>yjmJLB zr;kpz#qr#EY@PkzQRi5P<6hcR;FqV#>qy)n$In_ForFsnE%8B+?AOJR$LN1z00Mn8_1 zw2L!TK>F0T^2zGK$rW4v0~@75VH23GJLRZITV-%0<)F1 z0xo|pQb~!csO80_)|l@T)m%Lp*{=dy2t3I)El>gD_(s7394unczOF;HUGFu{#-5O? zBix6_c+El;gibmxI~UA*gwj964-_i9#%gMU!(%FQKUUUV%57b z*mcr=hkA$)TVGtE1aRTn-g!oe;^QDKjw@%&!E1`DP}gZmz~&Erf5q&Q>?LMR+Dmm~ zO>Or#;CJ8Qk560XtoBdYld+_9Sa<4+MC`n4&mngtGv_XORz=(z+G>?a=$X#`iyMJX zm$F4O^l_L5mX&7BTn^(4Wdpu?^Xt2DR#YP=;qJb1L$G+Z`(G4Wd2f9-?Jztd9pTFh zn0UODEO6m`y@HZ{hw`eW7{k*#LUp_*cUGvVTMgIZ+P*L(MB2#m`Tqn=INX{G@!uC( z17lD9E9b8LG`m~^HSveom={&lS!Q$S1*^J6yM5lA-<*SojR6`zvG6R$M`dH!}6X(y2}%90reIUWN7oP z(>#BDC)2RK?c7Y6Jk@;NGBbw=>d(F!D3U|hJ-f`=j6)Kc z?@{Fj-uA~pag;ahS^>?MSMt1dx~2_)Mo>!L)XU^cIAOZW7HoljJ0|xh^VM1d7?nGA z%Tz44Wt#Os1~%VW#CH%b9F&P#@945pHNSyAl(E`)M5fAbKeEy^zrve~?>eEN$=_t3 X9Df(GyQW>hf43jgw9-?^liL3Qe0D + + + 4.0.0 + + com.actionbarsherlock + library + ActionBarSherlock + apklib + + + com.actionbarsherlock + parent + 4.1.0 + ../pom.xml + + + + + com.google.android + android + provided + + + com.google.android + support-v4 + + + + com.pivotallabs + robolectric + test + + + junit + junit + test + + + + + src + test + + + + com.jayway.maven.plugins.android.generation2 + android-maven-plugin + true + + + + 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.1,) + + replace + + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/proguard-project.txt b/libs/ActionBarSherlock/proguard-project.txt new file mode 100644 index 000000000..f2fe1559a --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/project.properties b/libs/ActionBarSherlock/project.properties new file mode 100755 index 000000000..f28bc833e --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_dark.xml b/libs/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_dark.xml new file mode 100755 index 000000000..ea7459aaf --- /dev/null +++ b/libs/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_dark.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/libs/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_light.xml b/libs/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_light.xml new file mode 100755 index 000000000..0edb33b4b --- /dev/null +++ b/libs/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_light.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/libs/ActionBarSherlock/res/color/abs__primary_text_holo_dark.xml b/libs/ActionBarSherlock/res/color/abs__primary_text_holo_dark.xml new file mode 100755 index 000000000..2bcfd0b63 --- /dev/null +++ b/libs/ActionBarSherlock/res/color/abs__primary_text_holo_dark.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/color/abs__primary_text_holo_light.xml b/libs/ActionBarSherlock/res/color/abs__primary_text_holo_light.xml new file mode 100755 index 000000000..198384fed --- /dev/null +++ b/libs/ActionBarSherlock/res/color/abs__primary_text_holo_light.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_dark_holo.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_dark_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..769463b369a5185ba2d2fdf26abf058086ebcd08 GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^S!aZFaLn02py|Iz^fCC51!MDwC ze!c%VXGhEQ)_dJsezHtpNMD|7Dac@a*_ZJyulFV2w{5C|YCbaz5)ZX-3ak0tIJ#lm tp_aeGY*)k&iW*FhzahTiJD1r}=BlLiI{(TJ=>e@^@O1TaS?83{1OUpzopr0A6)2vH$=8 literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..73050476e77aa798919b829a5566973e231f9d49 GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^S!aZFaLn02py|Iz^fCC51!MFRR zxpqaJ>-4UOe6iPKwm$=BLD{Wo!i)yScSSDT-Jo*!N?wFe;-MB!VKtu_20%tEPqwzt q4f{lgTEQ5`;-9UxjMeKCf^DQ_uhd?;-Btm#g2B_(&t;ucLK6V6dokDm literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_transparent_dark_holo.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_transparent_dark_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..712a551ece87b2544433ac982382a087e7f1731d GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^S{5)M8Ln02py}Xf^L4oIp!}}xu zHrx6hVG;aws8xB@i!KMDZA_cCWy>wsRQS4KRST!En$HY_#6vK~{lt8cLjun}a%VT6 c)z9eScC>M27UGHi05qAw)78&qol`;+0QSr+Bme*a literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_transparent_light_holo.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_transparent_light_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..bf3b9438b16543294498ba27e51d4e878c8ead5a GIT binary patch literal 134 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^Sd_7$pLn02py}Xh4fCCS+;oB*H z(}QQZt5vXQMa=PTZk@YrXXvE3+ofW5$)(cU#3WF_jrSY!c@8l>`*HAE!gKCvr?`99 W=bm|#aNiDSFoUP7pUXO@geCw#94y)Z literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_share_pack_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_share_pack_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..1424fdda2fb4df0bf13ee0ad9079d78fb0ba3406 GIT binary patch literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI43?#3pEQtkDJOMr-uK&RR$O8h_eNGpE6mvzopr044V<`Tzg` literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_share_pack_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_share_pack_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..d1971b0ad316e167a7e066ab935248f9543498e8 GIT binary patch literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI43?#3pEQtkDJOMr-uK)l42QnexM-P)Pki}dQ z-^V;Vpy{TG>rs2jRbt|WpWjh#Ji#?9Gi^wblTEgJz>gTe~DWM4fZg4bX literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_transparent_dark_holo.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_transparent_dark_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..1e39572224b24a81ed4d73923280ba2724dbaf6e GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^Sf;?RuLn02py|Iy(L4oIp!})_Q zZ}wlfoYKNk`|wbZR*CB+2cd0Do4#G+S+1#YsD)El&1Z%|BAjt`!_S2)TNKYc{m-!5 f_v_(jT(cfAPEEXE)c4^Y$T|j3S3j3^P6 literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_transparent_light_holo.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_transparent_light_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..a16db853e94af78c0739d9b89b578e2a8021c856 GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^Sd^}woLn02py|j?`fP(zopr08IHUH2?qr literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_transparent_dark_holo.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_transparent_dark_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..0eff695d82911a73874d871f3a7b23b71dd8ab44 GIT binary patch literal 155 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^Sl001;Ln02py?l}LfC5j;!{`N< zEvnsL$t-6rWU%$psDGg1|4DLDj`q~RzPqNgR|nl=*Rt6dx_ol~p-I||JQ4;82O1ce y*`SQyBHQ|!3>bf({j~je;Zla%ZD->HG+$+yO5M3zoXrBXjlt8^&t;ucLK6T4R5w)s literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_transparent_light_holo.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_transparent_light_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..219b170fa67aa2ef8e0b11ebff90c1629ba7e97a GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^SB0OCjLn02py?l|AL4l*?;q8?- zrB_<6lUu~-<@8GYocm9fskyGHQx2!G9uAwU_k&&MlS%_4GaHYDLBatTBRp}nY76HL mkjg^D5fnx&0glSHJj>3%>1Jqp^q#PeS;bnL@oAJ;NJ=s*Cb_P#ZKbLh* G2~7YBfgFPX literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..66adffed632f0f6267afe6dc2f518adb6a83ca4a GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^EI_Qo!3HFq_#{<Lb literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_bottom_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_bottom_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..214d51e7279be41f1079d6046d55f0edc4af8572 GIT binary patch literal 148 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$S3?#1xGui?vo&cW^*M^1$8_^z48FMSrhX z11ZLmAirP+hi5m^fE+VV7srr_xVL8vMHv)0Trbx5N$NOq24857@o`b$S1tK@POtCI qOJ9D89m^)|4W83fCCT9!M7)Q zv literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_top_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_top_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..564fb34b4308750b6922f320e9e114b080ecd538 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^SqC8z3Ln02py}XdO!9jra;!2L@ z<=I|~`ucZlc5hPnsi>TGwM)Is^N?0TPy5{UQpwgqqAoKG5)ZX-3ak0R7;B{1mWZ}( p*lxaUe)ue*Z}vC-G%ee~_}P|!&DqTD7lF1gc)I$ztaD0e0st(KG%NrB literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_top_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_top_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..ae21b760fb1ebecac3389164251b0fa14f580f5a GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^SqC8z3Ln02py>gKAfB^?4dC zy{Qwe+I<(;Zrk3}qFy7o$Hqotreyxi(D>i`gF-*@tj&^T;Xws!Omj@dZ?l#4Y_?*} ivA^-x{8q}NM@;fbHo20HF^oW47(8A5T-G@yGywny6)bE3 literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__dialog_full_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__dialog_full_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..79e56f522b2837bd9f579b28f037ad5eafaaee8e GIT binary patch literal 1414 zcmV;11$p|3P)cSnqT^=WQ7*OFqW%ysfqGq3fLYCng$eebY1E zq0h)!qMt>^T{!1KLrdv5=!(bMwnfb7mMF4BgVA@!3k##^X|etG%o8VLfn)&kp1$5* zAHx|3EF9@?)w)=}!OothSF(h}LSVs2n}u~V8rR2gA&m?6GFm9LT5i!5ovh2$`aOLf zDMwpCv(UIN+s(qhT71rU;E=VYR%2(wIy9)U9BqNMU?EvKj~0!zxmxOREjkZzh+iq_n}2*GaEP(@ILD!5 zWBguqUH^M`clY;tz5a@wsI&tR0g7j89WEv$&WS`s3jmAj>+65+@9$R+4-crSDsl+t zoXD~ai^by4tE;Qe>-G9CHN@f2j4rXz#$jzQA^^+B$H$Mix3`1-5a+ZsO@p#5Q53}= z0G4QnFC7vyL_!;@%<@Jv>_k)mSUx>Hz18n=PE3d}%kYsC@*I)N0NNv^lI%s;wg9j! z%knLLgEIoQ+bysY63^&#(^_eOc(NT6QxTdoZI(#S<~ro`ne!tS(gH7T&zUX-UZRk7 zy|5!F77`FowzZg?X(M)KAw}<4SRP51e8qZ>m)V;$Z3?tS#CiaZ^hhEi?U_=yRh(%c zcGk9kwMUBGYjYt@pHyCSltyExNUHHFevMy@-l#r~QEjQ;(4?Q~r4samHh{FF{bU7StYxgeBAZQ`6E7N`VLYGS_ z;oj>aHT`D+d1$*FJhVBd!G+`uf(ywR1Q(Js2reXN5G)~C&xiARXWFDHmGF81Z6U>K z;#g%)oXa8RP+ENtZO$MZBP0)Pm&4Jv?h_KbHF>VdnKo?a-t;CVB(9^gh389`0#_~3 z>r7%ovdX{d8amFjk%uF>Vp~*9sp-GKg_vAO(?{&AZLdjA|Mdo3?oA8H>)1>mc|==# zB~EQxV&cD40$n%8&w#s-rjOWJ+ddiIQXYxR!^y#)tE&3)24CJ80l~J_MCXPu>Ml3AI+9BHcJ4%0r(Kw z@2dYHj4#nGKH!fqjRVUuh**Aw_CvMrs{b>Lm!YwzO4di>6N@*}Phg5Tw&B1fy2S@v zNaMh=r`Sx3>1SxA8q+hl==5I;NtblfC(XkF^KkH7ZKuDGcq0~@mC!ryi=K7WXgde? zOwz>rq{P&!A!(hIr~D!&pe-W#G7`?LQYh|`P7hzs(O!weT#2|xih3m$l64(P7wFd8 z$(YnkvI-Blh{l8EAha{kf+|%%mTX6hk;YCcRo{6`6g>|oKBz>Tb24JLyVyI4p!izS z!8wv9b%$RIiIZ@y?B&nGjRPyvaF~-xlb-U|=!P~^8?n&FNw@wIvQA!p;xDy1AK0dR zu~CaAl^btMM>A?9->NQ&|AnVmzdt!!WF>xutSl`;$84s2a_=ari#ecl- zo|KSyJXTD=1$2JI1Ql!6el7Y8-dCZ-i%gs^mMBu<;eLzP8a;YIXA&Hxi>7JtAFa#2 UvCQ(L;{X5v07*qoM6N<$g1g$KMgRZ+ literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__dialog_full_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__dialog_full_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..e029f210b9a81ed4765d31e90b6e49dc8aa37bed GIT binary patch literal 1537 zcmV+c2LAbpP)P*hY@RCILOyI+)4*Y%ioMH6eaT8-v42qXrI z0|4iGEk=d4Xm52^#H?WUz%bBygG}I|;L!cJM*+u%I9T*&&%x@0sx#0Qk0mTmeRX1h$MIxNMY&% zsz{Hz01C^N@8p~dFgXbIRub%{CUOKKzAQ3rfl0r3M&j^DZL~BnC0R+zNTfH*0>l>% z6xe!^4oO*vg0sbY%(i4Znw@-;nUbs&1zN~PIe02zy)Qcz&8uA?+OU=`?e2Zs5}uJN0M*6C#a{)YX?>^k&iO8v%U{pV&i+9YXUL@sAe1fQs10%V6ikBo z^7Qod_s{$xhgN}Tci-i5`3pX{;N4;1Au(Bm7x@)z#1I9LmdL~RrF4uC5rsHs>tE+w zbjvJ)LKv+{MYbuBrU=TBLj=6Xm=@t7;baF+QjNr0K02$09MPPz_Q^dZ5CLh8#Oov4 z0!TsP8|Jz<^->fRi@r;w5OrSCkw6L?N(J|#V;?nA78HxV{Q97DD=gWeOHxKs4U)p3 zSoD>;waz;geHn?ahed6Tq%7rduPgn zV$nDE$&ajtWS5-`4=n>hvFJNE&bxxNGnt2!k)T-g9UO0!_0BFb6cm%bLuC>qYeBK* zyN@VHxuTkNE#cFa@PaJz!! z*)p0eo;YjAyoc}Lcn`@)ql2XA#Le!l%y)3C`#|D`qk*LC#Leoh&{v9irz@IDt(4j|E-ey3YJCdAoj z17Q35`nvale)TKB+I=4%AAxk%w!OMGAf++t+Mb@Cemp-vW4j&J%V|A%#TR&cd&9%S z1Mctdf1tZ-6W>Id5JKwGXhvG!+}wOSKR^Gos;Zyq!S}5X*>+05G4|7V9G{hQ?!VjH z+iw8YkTX)YMrymYx~{`?w$U_yh$q-O1#m*zu<(hQDx}lJ70s)V5Io{ZN7v2rMwVC7 zhTm_2@1*&pTK&uP8_~o&lye*5+om^+1{h zqafFH?O0X{OUO1M^YAPb=F`bd6<0LB=s!LE=ilN39Gach?DYt$CL6UhXTc;$2zpT2dqqQrIZWT*_yH->_|IcgE|Y&S_AJ4lX{3 zlIkT6RWeyS^JtZ1q^3@yK=Cm?J&*-y*C;dpYgTSF&bztX%>V7a(kL+ra#JUT-|=or zs-McUNbE?uO3e#!@v#wf-Fu%ryG^`~69lNs`%MmnTRbrM$471B$+eE{K4hK$mCQ9h-Z$E`tJc<=E)8`p?U8s&`700000NkvXXu0mjfb2aWW literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_ab_back_holo_dark.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_ab_back_holo_dark.png new file mode 100755 index 0000000000000000000000000000000000000000..41b0fc5f26f6cdf34d34641634e537d4ada86a8d GIT binary patch literal 545 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaA9SiUYaRt)NPQx~J=gKOL-mXZeo{|J#4{do%-cUhTmHK>6wOo#H z7l{ThzA|gpoEBZ7M1O_upwM;MK+{fmx;TbZ#I;VkzNy)O$3;0=+CX*h_j}dn-~Rs} z94IH$Tafnd+y@2O?$)Wn{)`O=o`)Jw$ZuP!Q*=+<;6v2csEbStvXK*GSHE}Izo7XO z3*(2+FMCQtJJ=Nr4MNtPS;Ba9u6yp{)T>eg7lQX)Phitnqio-I_XV#+bzR=6xq(b+ z2k+=iW8i#n;8e!udjbmmy8E)&`*^?o`fyI>3{rJ_tk&j8{N46oR8rb50h!JS{u+$44$rjF6*2UngG6PN{Ijf literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_ab_back_holo_light.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_ab_back_holo_light.png new file mode 100755 index 0000000000000000000000000000000000000000..64952591f2e7d7cc7b2c35d665779ab7351be11c GIT binary patch literal 432 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAO%Cu0aRt(Y4OCSsmjc~nSQ6wH z%)l7n9pn?}uc_+mDdFe9%qPbhEG%gj8lukOsOKi7#HDR4>TD&>WWulGt}M-MZQ#x< zrv}tK+tbA{q#~|&(s7}~3Op{Y4mbGn3g@IP{}%V}|LaMTeYc$Ng?^qU%lWJOp^f+P ztb`R`H(xYSU)-Y19ycNIP}fop>7Y5@+w_ i?L9K*TIv4dnV}zjpFM2uvA^b^aQAfeb6Mw<&;$Ux@L-Mr literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_cab_done_holo_dark.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_cab_done_holo_dark.png new file mode 100755 index 0000000000000000000000000000000000000000..d8662e3f0fdae62cdee68c184a30fa9e421dc338 GIT binary patch literal 713 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}trX+877l!}s{b%+Ad7K3vk;OpT z1B~5HX4`=T%L*LRfizez!?|}o;S3Cn7d>4ZLn02pop#z!+EApeUwz>goe2TrwSq2J zirU3kvaXhOUTIG}?do{Paly7dd_m%&JI?q_-onB2%KO8{j^-Q>wzNlYp6R5&dH;Rm z|KbvtPbZZ7*R_9QUBqkhp({MdL+G`PeiKVkcC&4Y!{nAd6Z!S-oqj3U!mzMkIqyf? zf)_G(nEp@p{`aZ-I!9g5h7aq1927O&FPT&GvG@NuQA5$TGve!iOqW^IxiY{>!{pzO z!{3V<)Sk6$zkBfg;!caFvF0E2Z#WAV9{aHQL)vPusEmizHw=GVH2J}B^#kKc_ZNxM zzDF#RGe0;TaIo&I-m~b0*sP8To$2@Yde+QKc8kg_`XHU5q@;HFk@ug+O{X5aZ~nk~ zjknIKwfM~0Z^b864VUMM@zwGAdYt)v<(Nz4IlImB@8#qJ%zn(A@Z29bd}4MpKn$sTM506307QmPCTFYr(@#O zpZ-(V%YE!?30QE(DXivDMdI0PFM+AKOT1>hnX-6tX;O6(d#2d)iSBZ_kN7m7WzTRr zzEA`h)2bz|5hW>!C8<`)MX5lF!N|bSP}jgh*T6i)(9Fuf$jaCl$hI;t2>JhQGm3`X z{FKbJN)!#IR;K1a1kqq?mJtlpAPKS|I6tkVJh3R1!7(L2DOJHUH!(dmC^a#qvhZZ8 Q4Nwt-r>mdKI;Vst0K3d3fB*mh literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_cab_done_holo_light.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_cab_done_holo_light.png new file mode 100755 index 0000000000000000000000000000000000000000..6bbe91d7b77fb66ecedd3d696b56e670fced6467 GIT binary patch literal 592 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-s2LgOTT!HkE2He*A0zeNqlmz(& zGcYnWwAV8?);6)!b+9(8MJcOnfa9K>312Ju2ebk258oQPZ!6KinzB|ZU!|w2(VtrT-L6t8OtD`F#k+skyfcVd74UjxFuTn{c2=eJc~(aEt^4I&|E;o)@evLAdOMjX zP29+SCU=c=^M>_%R_!ZnvaCEakt5qI{Cp&*dgtbbmOeDc^0@J;;M1%jlp25nb-=TAahBO zUob-&!^w*r|AAaJPZ!6KinzDuHu5qsa4K Ym(N8V8IJ#m2kKz(boFyt=akR{08_Xq761SM literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_share_holo_dark.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_share_holo_dark.png new file mode 100755 index 0000000000000000000000000000000000000000..6f747c8f065940a8844587c682fb3c9443ad1ca2 GIT binary patch literal 467 zcmV;^0WAKBP)SO)@i{}u0qYYqHziBfu2L*;{L?D;GxhmUqDjP ziMkTn=nm+BxmDP>FsP2fGnnz3AAnfGAiD%ptMDlguG*u-2|uL;kN%Xenfoz6Wm9neQuJ_f08buH`WL4SA>8mzG9{4uwy+$BT^9a|eO3@D0j4;gS>^u3i8D002ov JPDHLkV1mHE(I@}_ literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_share_holo_light.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_share_holo_light.png new file mode 100755 index 0000000000000000000000000000000000000000..682b2fdec4cdcffe042a0eaba5574fcb553c6fab GIT binary patch literal 505 zcmVavhwv6%LZS|`6cHU3BI+s;(j}rxR}U>ALI;t;n}mlvh$!J-yZtua#I%j$ zOwQ&T8Q}wUMSkBn``+6%%Cao-nJ5SZfglhBcnTy*QWjP42;RUuXn|w-Gn@r3!4%9C z+;SJ#1s`+53r;u-JS+&G)8Q=eVjwW)EYS5?U=Q?t+(O%+X%L*ae6USp(Wf44I z{VTBf*DdH$;99e18zd%PPify*mOc4h3DgW)zaXzPJFd#ED}jzd@IKXer+vefz{o(L zSH{%(p8{RZ0^V<-*kS}|(%8UCAfi$^)3kopWmIldEo%J}dR~)`A1WGIol7IL;ao9F zT=*mq(WtgX@b^ z8v#5LPB(Y&Y|7|Y_!S6e^A;p&8Kn`0O ZNo|$kIQ!-!vj|WNgQu&X%Q~loCIGZbCtd&m literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__list_longpressed_holo.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__list_longpressed_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..89c5a5b3ba7d81b92bfa5f6cddec970712d794ab GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-v3?#1{n!goD@dWsUxH8N<17t!-(Y;LDfo$fI zAirRsXwu=;zkpnIPZ!6Kin!1d8wD90SQrj!Kj{8tWU@@|)FcTbw+$y3Y1Z8PPe^A;p&8Kn`e^A;p&8Kn`)FR9pq#%;Bdb9fTe6Q2VcU%-{(VYwrF=eux9zE pyJ2b>-@*!&RY^*}_RRb6`6E+Zsl>AICZ#Tr`JS$RF6*2Ung9f$E#d$G literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__menu_dropdown_panel_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__menu_dropdown_panel_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..4d3d208578c61662986fdc16bd15c69759b48d6a GIT binary patch literal 922 zcmV;L17-Y)P)!ytdud6iMT7O&4bD*mP3eKws)UN7D2k#e z%DbYt@4NEYnkjlINEK7=*RV3zwml#Px9r!q%}Y$QM*NqHEZ-wscn^ zf4p9=Kg#ZZAKY%YZvc!aCbL}F6eGxtBSzy=smEUmc6C{?q1YCQrid~5o$!73+BeU$Ee{DI^Yo4#-o87 zao*xiE9Z<+s}ttlWw19HGlEA1nW09+*~|#}a;CkeJh%bU1g9CP5h0^O2}4Hs%y=Lc z5q!OY8j*^uk~rBBK?ljmh#jL84Ev;rDo>_H#K|6e%Mo=CgLzw$rI$Y4Z-kUy6U47Z zMI2w^tmuV~pH%tJq!^(yWY-hbFl2;G2g)+VPqud2Sicc+jL>MvBTKK;HbN#jlrS<8 zl+;FqdPULzJ~Kh_!?j8>!xs^*nQAm$#290~ue;BBnY1w&wMw4#m(pwEDZd_oY1{Ux z>$>N)H(eWD*FCpw`-IsDKF28-6~1bz!JGs-W6Z&R1n>#KR{&q8us99Q}8ID#(NJr3oa@N-D4Rc$qhA--bT2} z_Wpf@Z`4749}#V+fwWJz^oyZKO1~J&exY-1*Kg?D$hu!fixvAiNfpmG;mo&f5BSEA wlE;@gYed_N;JFm#Y)Zw{1W}kQU9GkF3xsHEK4Myn7XSbN07*qoM6N<$g18=|djJ3c literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__menu_dropdown_panel_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__menu_dropdown_panel_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..924a99d173082ba58ca7527822359f228bb14dec GIT binary patch literal 1061 zcmV+=1ls$FP)#d)-&;4-At916hopygyW1WQa|lkCWZ4-q&QLYwjy>%Jd>?UvK6*fy zdt!`Ep~K-gOUxeEG;B7TCA&L&Y`5FR`f3tF5S^VH!lJsKJqkfK0&PN7nPcRS?`4Ev z7V~vPKouy~L@5%v*=&-(hy0cgM*ETwjWz&8F3NVhH7#V4$3+kVgi2lPw-adOTXm|2 z;f`P0rx+seIw-H@dwPaOx@-q(hY$i2BS4u1XM~+%LI5%mN(D;0XOsl63U@~43e6EU z5mrW96C~;L2O}H%p$jmGc5)7LM&^oyAacV=14{s_wp`K^YIpyDFEM02mXk3OSEw-p zdORS~SGTvf|Cjl{`o#71_3u-2KzcK(ZU7La5JAil5&>9olp#5yVJhG_L?OnQ2x7(v zIfg2+Q2z$cZ7gkJK~)>%8)`3CJSNBlBGKwWz;6QnLDPCN+(q9 zAJ*09wM4d_hZLYxWa z2;mvCbu@F-B1H21)loz{?U-+?@nqk8kw`Vm31QkJFg_V%{OMfZr8mlq(-<|uJGVoHpVClAlh&-bsduQ(hI zi(GsU`1tt1hf_X{d`3<68b|S{tXYwd2YXzXl8Izg4y^21 zp<@H92ON{kB3P1HizBE$;EP~xzks74y{^`3Ur7(FmPO0@t2+WXW`VN|Gc9|AJ43ds z=_5ZJGT6$YN8bIa6HYZ^jFIn`&>W#9La9l$?6@3`ET&WsUkjFK0@?|yO)(6`*1l_s~_L1z$~& z@jZM)qqe)$B+Pe}79nG2sji9uR0#8Z){~%#a(p4yPG;@1CipK8G=YCTO^$u*mj`Me f=rGa5Ym5<}3Pv>t$oVA_00000NkvXXu0mjf+qu}} literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_bg_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_bg_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..d2948f033b3261a4d623a4b9e296f6232a2a921f GIT binary patch literal 154 zcmeAS@N?(olHy`uVBq!ia0vp^96&6^!VDxU>=`tG6n}tEh%1o(4+B7U|DPXKK#HX# z$S;^dikCsW$F2~_H}iCH45^5#J-d;UL4k+iVE-KbF5qmEWKtE%)5amFnGH9xvX=`tG6n}tEh%1nWfdBvh0~z~Q_u2p{ zmXaX9UcR3v3Y*J2PIQYhXa)PSnrI^o0 rA3LTW{;|L%Bz@V;wc**HPAH#aCDOJO>Ym5d24oA2hD9A9kkZ#C-ocYxQulY$%dG^s1`jD zWrBMf{hI5z?Ps6d&cAMwxpu9tAQSLC;sV+l?CI!atlqGv_K~X}S&7D6#irA$G%zcr zGa^}SvXODrvrsWB#ZkB-c}-Y}>Q0!#L#9IQCX$ZI#7DnF9fJ8(R=8iR{NJvIR3GfdBS0fGXvSYugM@B@Fy~s_l`Jy)|ccqHxmVzApHz{x4N{y z#Xj0V$LH=@zsWY)$4Z)6--edG^VYGh&b$}$F3XeLt5=Uu0`p37@FaypB&t$KuAm9R zq*|yt7WOdr(m%g{dU1bs0-qy(nh~3JDc<<|K^(;cR4G!8UrKd|p;N zi5zZftW#Kk#vM}fdS+hLQ#;Dq?H?_`8P1nhqmR=j>Au${MMR)hh&5HCA z?cjx6pHV1ZFFx`ra1m|YMALE$u3RAxZp(6Eus@DJwzn!G7Cw2Mm%K&AN5Ia`DuuPd r`dvHM#yxB2nYDo;0!e2cs)*3P7gB8$K+RT%00000NkvXXu0mjfr&_ZI literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_primary_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_primary_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..1c269205e874bc6addc308efe5be4fb7c5da0edc GIT binary patch literal 917 zcmV;G18V$CDOJO>Ym5d24oA2hD9A9kkZ#C-ocYxQulY$%dG^s1`jD zWrBMf{hI5z?Ps6d&cAMwxpu9tAQSLC;sV+l?CI!atlqGv_K~X}S&7D6#irA$G%zcr zGa^}SvXODrvrsWB#ZkB-c}-Y}>Q0!#L#9IQCX$ZI#7DnF9fJ8(R=8iR{NJvIR3GfdBS0fGXvSYugM@B@Fy~s_l`Jy)|ccqHxmVzApHz{x4N{y z#Xj0V$LH=@zsWY)$4Z)6--edG^VYGh&b$}$F3XeLt5=Uu0`p37@FaypB&t$KuAm9R zq*|yt7WOdr(m%g{dU1bs0-qy(nh~3JDc<<|K^(;cR4G!8UrKd|p;N zi5zZftW#Kk#vM}fdS+hLQ#;Dq?H?_`8P1nhqmR=j>Au${MMR)hh&5HCA z?cjx6pHV1ZFFx`ra1m|YMALE$u3RAxZp(6Eus@DJwzn!G7Cw2Mm%K&AN5Ia`DuuPd r`dvHM#yxB2nYDo;0!e2cs)*3P7gB8$K+RT%00000NkvXXu0mjfr&_ZI literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_secondary_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_secondary_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..a608c1ca6d09f4970e2c06f52a314ab4a5633815 GIT binary patch literal 154 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 literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_48_outer_holo.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_48_outer_holo.png new file mode 100755 index 0000000000000000000000000000000000000000..f62f74bb38e8818fd970b7ec1f7862e543cc9d07 GIT binary patch literal 1811 zcmV+u2kiKXP)Q(x@RlU0R z*6*F+o^#JDHf2*b#j2Gf_PMBSbuGRn) zDJUkGcinCw!4`)d@_D~EMJnC5dvGXBz z^GjW6Gg4BPB{MBwxqQWN<0fP#?6J=Sj@WNJ!AH@8Kl`>9L$xnJF(@mqT0WPnt1qF1 z=Fx+;deGw@bGK5OQ+L*P{Upq+6DX1yGQbK>T~%EkI?XGa@}$o>ppa%>^8?Sie*2l7 zfHK3%D>%7|N++0Avr7*8vd@}G^Jo0fcfyUHfnsLZ&jU?a6Rn7I58e-AIzUHA66jy!AvGyBgcAIA_8&ya%2-o5w@+x^Frq z7x5uqjDu?ciVPZ-34&-=b^T>U$i3owu8N|PCmm@W9a)>Z@0e@OLVt>;Q1iTBsK`Y- zJ#Dm0pva)1q@Sv}wFUZZ<(6Yki*muoJ=kjDtmVDYq#tH=k$<|@5vTpkf+%-~CzQJb zDj5=FGBxvks&SW*YJTnCqA2*7ySf013>jGQpITpe;Jn|eigLRhZQhTXHIx~a$pm$C z{nZbIsz13R7nL1tHd-@KF*$Um(og?cC1;!x<)VYeI|B_UB@HdgHw5UWlj@?}cDp+O zMal|-j9w2i9i`?Cb8^v;J)JZZ3@FG_LfsGv{MVdZZorO4w{8Y134%<%0TH$=m)%OW z_@u(SziK>nET6ihG`KOcU?%zJh~m2+ycN)r5;qQq+h%%Xy;f)Ejn0a3UnsuS^+vZqWP#0th z#x}@k!|oI0qU+rNRV>J4GUFR$v|a9z%jMoM-5IE6CMAy{ll?bZ%p5f-7cDwzt|L%b z^86*Y-fp1pM3X+OAj(~JqWJ*PYCpbZCY5MgZSAj)V(>vz$zUg(ZywPKsOqx1Or~s4 ztCPVxWShrQgx&N<^IZW2u9-7hOofKj5BzxpoW= zdO}GqH|+&2+j}j6LUh@cv}2FCzaLvtWZ?^TwHb8;v&L-C`+P^-e$fT!yBz5Da{oL7hj@h;h3%R$P7X_J;dmT2? z8&V8*_<~22M7c%3b3D}BjlNwczv7fZds1ebGU8;svesTmESU0X4=BlHYF_eOt8iaY zL0I~yq8N;M$m4dU73%)xM=srNe&@#dxWo1;CZw8+{^f0pouFbc zV#*`lZz%0)HGlQf<_c&{Ktx7;z}?CzodgvZopY&OUp7)Q;eaFVO?})iuy4L9j zyLs2K^qSz1EvYP7>Ws{|~_-QddT;IiL%she&}m+lY?b~t3u zik_CPT+O0+6?K`iA!CLOr{nnY$OW(ay)*42s|TRe$lC9I+YPJ$b-IG8H~q!myML>= zroa>ruDyrb zV`&jHgSNQCgmI$=lu=hRYuXKOo3_xM&-4jsmA;l<3@^Ph&(-?zXM#=Hludb0$^QZH zs3&Vcm*{c;001R)MObuXVRU6WV{&C-bY%cCFflPLFgPtTGgL7)Ix{mmGBYhOH##sd z?sz9s0000bbVXQnWMOn=I&E)cX=Zr$HaasiIx;gYFgH3dFrOu# z?f?J)8FWQhbW?9;ba!ELWdK2BZ(?O2No`?gWm08fWO;GPWjp`?002ovPDHLkV1l}_ BPJaLZ literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_default_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_default_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..bc53fb9638c0bc2ed793ba2c4a1dc99758271df5 GIT binary patch literal 277 zcmeAS@N?(olHy`uVBq!ia0vp^%0O(y!3-oH_D%`~QZ@lTA+A9B3nC1X!_e#Vu&raI|L&;MWl!I+rS8s-6F=MYx9R8X%oVdt@=PbsQ$4}( zauLtOb{-)~6(gUN87CMVm7CmJdO4&8&6ILnZggyz>3Ts&b>Fsc@@f3}U)>+a-r16G a_l~7)6&s^@b1f6l0Sun5elF{r5}E+krFk3x literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_default_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_default_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..1c381ba2f5eb8816c6bd98b89d79d97ea309bd54 GIT binary patch literal 277 zcmeAS@N?(olHy`uVBq!ia0vp^%0O(y!3-oH_D%`~QZ@lTA+A8$7z6zO{~tpPT>z-@ zXyNK)ASGN9N{|EZ2n-B6%BT{Mx#DYfAOIynsz{pLb=Xz2SUjn!0*c;aMYxWvV9_W-jKL z=*ZT|BP6M2M&g#$6?k&HVS#I}^KlCMQ VXoAd{NkH2fJYD@<);T3K0RTOFX{7)F literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_disabled_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_disabled_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..d65d3b7fa95e9107e482f345a9f207e1d9e154ea GIT binary patch literal 262 zcmeAS@N?(olHy`uVBq!ia0vp^%0O(y!3-oH_D%`~QpN#3A+8`A1N{I0A43c(08~Fy zS*rs`36uo+1v414=`u?iFt9Lc3NwhQvrfvFW&+9;dAc};RK&f#YRK1YAi{F7pZ5ma zq(8ssn+cuex^O}yZ^MjVTUT#QseYFiuqp2Iu8g!doX<>CSI;UuYviy@^#sGr#XJ)o z**bZIB-M<3QW_QrIw~J>Yw6{X7Bo}Jak-HqXfj__?swc-{W;pbDH$}18>|t?+K*jOM?7@eP>PxXV?+7I;3gi#Q5YCh8fb+mh?xKg@yXhuL<|} z+huu19H?}&r;B4qMcmt~C;6HUcw8^u*x?{8#Nm8g@&Etny*H*VPY?@T>b3Xv=B@KH zQ}vE%%#2b#X!tZ){7A>^%(eGajf2uHr@zhcX**%&y6%|J**5OUs~@aYTcuWa=7j!? z{X11hnkcr0y1;=I?>gu8IHv literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_focused_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_focused_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..2e81cb91355972d04f06ede1d8430fbec808493d GIT binary patch literal 387 zcmeAS@N?(olHy`uVBq!ia0vp^%0O(y!3-oH_D%`~QV9V*A+E+-pTZGP%-A!~c-DSn z|48Fy7mYJ(jeSDlvOvcF|Njx(;5ZO_&SB%yUgL$QjF+4)p-6f|!X666=`J99!f!;YxcAx#@6#wVvR%#fb8q(8DOEYyE~O}M|`F3U6G zK&6{KT^vIy;@)08$=7Va<9hMN4hLx=4(H>F|Nl?#y)kupf>`KMuf4A~Z=Ii+s&`Cd zW|Z7sOM><|-uDz#f9F%T3{cVO%+X*w*b;pFxwsB8h{a~%yDz&;ZC-i6R-)R#X zEbl*S<#*?u1|EE(5(?G^9tRW{n^|~-Ogt_qI5xBJ!Wk3hSgJ3Yc{^Zl`P>6{PP<-y z{Cc}`bnm~zkvEzAj%m++G@0|>I)T^CIX8ERbBYVEUvSwk3+Q|XPgg&ebxsLQ00cRr AFaQ7m literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_pressed_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_pressed_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..d42639ab54f8fd6a619aa36d48aa2991c262fefd GIT binary patch literal 350 zcmeAS@N?(olHy`uVBq!ia0vp^%0O(y!3-oH_D%`~QXv68A+E+-p8}Z+j$U@%@o3tS ztHt|o+iZI>`^XibI94?A@HOY{k4&~cwcGwUXWyNkL)Xl=J#pOr*mUdDw7qv7wm$}% zR`;B30g#d|3Gxe`$#8Aqx$Sc=O_OFwnULH+@$`b3bGMwBHGkHId5f}4w*r;T^K@|x zsfc@f^<>{60|D2IT0%}Tw!b=h42Hqel-pIXzl;{@crw3(g05?YgM&Drv%7 zrxGoxYR#+1bKEq|)0D%$`WX4HO8X*x=6p>5vX}q<>0W;OJT29OPnYKeUmFVwY-s~S zwk0WkGb5haY+oNMHY0j-*3SAG>!|ZA%`G{5K5E)YKVQx?bI;{+yLXI|++1c)zju8B Px|G4w)z4*}Q$iB}8@-|> literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_pressed_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_pressed_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..7b2930752645c4b32a2ea83d888eb89d27936b8d GIT binary patch literal 350 zcmeAS@N?(olHy`uVBq!ia0vp^%0O(y!3-oH_D%`~QXv68A+E+-p8}c25oyNDE*K{k z8Q0G?UVF(nFTZ5G<%#i{o5rn+jWcVFH$607dBu41Bjc`B#;dLYO_N@; z;Q@#$3Gxe`$#8Aqx$Sc=O_OFwnULH+@$`b3bGMwBHGkHId5f}4w*r;T^K@|xsfc@f z^<>{60|D2IT0%}Tw!b=h42Hqel-pIXzl;{@crw3(g05?YgM&Drv%7rxGox zYR#+1bKEq|)0D%$`WX4HO8X*x=6p>5vX}q<>0W;OJT29OPnYKeUmFVwY-s~Swk0Wk zGb5haY+oNMHY0j-*3SAG>!|ZA%`G{5K5E)YKVQx?bI;{+yLXI|++1c)zju8Bx|G4w L)z4*}Q$iB}?KGK} literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_focused_holo.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_focused_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..b830f1fdae8bd50f449211241560d66a21ec7442 GIT binary patch literal 137 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1i!VDxu+fIuDDgFST5Le@^Pk~H0X|+=RF_6z( z666! Z=h?=@cC~iVl188o22WQ%mvv4FO#r__C+PqH literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_holo.9.png b/libs/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..fd73a033b034b1fc6fb933fdfda45e8648ac7211 GIT binary patch literal 126 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1i#0(^79kbgCq<8{+LR^8g@z$q61Z0G5zBwOA zF_i@Q1v4;|O+IS@KMu%M_jGX#sfhDE>CFi==evwJLPDoBk{4*AEnmdKI;Vst0H2gA*Z=?k literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..0706c8af658bde9602634950dfe3d5fa5886163f GIT binary patch literal 134 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%zMd|QAs)xyUfsxfz<`JK;_Z5A z-Y@ApO*&r-OgQM+#9X>wkYiu(tId138dhyGKCm}H$eLN@qysZ=3D*)nlM7YC@0xyy hn}nCMmBhQI$CPR`ipOiMnG7_T!PC{xWt~$(698&aE5rZ* literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_transparent_dark_holo.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_transparent_dark_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..d814d02d31183b8f00f475a05c124004983d9eff GIT binary patch literal 123 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%PM$7~As)w*fBgS%&%9EC|%_%783w8jlt8^&t;ucLK6TeKPwmj literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_transparent_light_holo.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_transparent_light_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..b139c8e49168e4404df0a46b30a4b30e90c1ccff GIT binary patch literal 123 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%PM$7~As)w*fBgS%&%9ECY>s_{MVWOZ(=mjBT1_sGr W&p6(H%v1uJ#^CAd=d#Wzp$P!PC@9VV literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_share_pack_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_share_pack_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..5a5e976bbc4527949f12389044c70952447ca8dc GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh<3?#J|q#XfLJOMr-uK&RR$O8h_eNGpE6mvz?tX q@GM&5R93(IF~_X{3%(%HTdWVWZPpUXO@geCxz0x~fG literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_share_pack_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_share_pack_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..8f10bd5222239e82e49c5120888fc9a07a324b42 GIT binary patch literal 122 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|M^6{W5R22vKmPx>XSQNv;&5z| z@wwFK#IyY<~E UNZ9H+7ibuRr>mdKI;Vst0LZr`*8l(j literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_dark_holo.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_dark_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..743d00b6cd7e446c7badca9dd11d1579404569cc GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%KAtX)As)xyUQ!e~V8Fq8aW6aH z`M>?!MSK0VJ5DH+1)MRKpSquM-S5BdOIN+&sn~jE%jQNlsf-1UY_}X6mMgq#dKVMx ha^q%Y#@a>3az|3`-r6;1y%^A322WQ%mvv4FO#tkfF|q&v literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_light_holo.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_light_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..17c1fb921f9b7b46aaeefe7afb8302874fb0abd1 GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%KAtX)As)xyURua|z<`JK;_Z5y z;?s*`91mY+Vs~LvSAIRQ|I~ek>wo_(4hk(}+Y^;`>!t%UugL`m=C=w5f(6PQ%h%~C hy?JA^CG4Uk|5eN5b2qlEYyz6g;OXk;vd$@?2>`YpFM0p~ literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_shadow_holo.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_shadow_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..ddfc8e3d5c4131f2460254f183938477fc5a0679 GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rhed`}n05R21qr&#kfCPg`@kT=WR_nY-)l@u)2m z9YjR(TNrC5J8zlf@MOx-MX#^q?zd~tP;1Ok`WbQLQ#YGy{=Yfu7pEGW;JmVaai5Cr S@!LSF89ZJ6T-G@yGywoUXh2*5 literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_solid_dark_holo.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_solid_dark_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..007a4b239244212339b817f8de9474a4dc34fde0 GIT binary patch literal 134 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%zMd|QAs)xyURua|z<`JK;_L&H z*gyPBj&nSGnTg$nOT)YjBQU;+O3-o%)BNS9GKrK90(RDcPwA0 ir}XBH&6co>lKfV`a~Jh`>I4G~X7F_Nb6Mw<&;$VSc`p(G literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_solid_light_holo.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_solid_light_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..ad6e1a4d9f3c81e20676f979a53cea2084ce903d GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%KAtX)As)xyUQ!e~V8Fq8aqs`F z7bNtyi1zwxcbrft3piseKXpIjy5E1@m#%ulQ?d2Tmd%Z9QW*;x*={*DELV8f^e!gW h<;Km*jJ1o5{)rFFF7K literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_transparent_dark_holo.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_transparent_dark_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..0ad6c888b4c7e436e7d7c78432dbfdaecc95a7ac GIT binary patch literal 127 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%Zk{fVAs)w*fBgS%&%9ECxB?U?=uVx Y$^4u51wHGE0Gi0)>FVdQ&MBb@0N@ZURR910 literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_transparent_light_holo.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_transparent_light_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..19b50abcb536602cf2cd36d5a19805464988bd20 GIT binary patch literal 123 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%PM$7~As)w*fBgS%&%9ECmdKI;Vst03^XH%m4rY literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_default_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_default_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..5461b9c00fd3fc513aa4465682e70e87cca36a6d GIT binary patch literal 101 zcmeAS@N?(olHy`uVBq!ia0vp^EI_Ql!3HEv&)kdyQaYY4jv*T7lQS|h5*V8PD@dJb zGfWV2j%0YY@&Et-(u-Ft=sZxsVYp-gbGr;f^5z}?TUWk$0o2Ff>FVdQ&MBb@07WJt A3jhEB literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_default_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_default_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..5dc6f804aea8ca344275ac6eb497b6bfe0f117f3 GIT binary patch literal 99 zcmeAS@N?(olHy`uVBq!ia0vp^EI_Ql!3HEv&)kdyQd*uajv*T7lQS|h5*V8PD@dJb xGfWV2j%0Xds4&4P&{4SYp+J&{BRiiZ!)425)ejdW>;mdy@O1TaS?83{1ORBM8sz{0 literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_focused_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_focused_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..a70b53c59af769e3c98973ad9718670ce27259ff GIT binary patch literal 109 zcmeAS@N?(olHy`uVBq!ia0vp^EI_Ql!3HEv&)kdyQYM}*jv*T7lYjjGZ_h07hy7xL zS%<;BUsuG{45zRSgeB^>bP0l+XkK D`1l&; literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..85d7aadd4dfb619883f68f1cc63e629698b5dab5 GIT binary patch literal 107 zcmeAS@N?(olHy`uVBq!ia0vp^EI_Ql!3HEv&)kdyQbwLGjv*T7lQS|h5*V8PD@dJT z6Fe3@|6GeFPb#ATqtK84|Mweqan0i3X%}$jvMQLwz#trO`SaWQ^{znO44$rjF6*2U FngE5o9^U`} literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..f7b01e012f895bfe2c4241e1d48771fc372b35cb GIT binary patch literal 105 zcmeAS@N?(olHy`uVBq!ia0vp^EI_Ql!3HEv&)kdyQU;zbjv*T7lQS|h5*V8PD@dJT z6Fe3@|6GeFPb#ATqY%TTAPubyB?B2J9?cgAJee4pt{u!zH{AaisF%Uh)z4*}Q$iB} D0X-aq literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_bottom_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_bottom_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..d8f1c8bd54f4f091e79389603095c99cf825cb6c GIT binary patch literal 127 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%Zk{fVAs)w*Gcqy~4zxBtu(zuW zGMHg-@7EW5UBeRx4_{GYv)%pQnG{Mujhr&UNqv-pZ`Il{xB Xs2c0@Tz2whppguou6{1-oD!Md5lfPH{+~ zs&ZK!$GJ#`+OXn)bEl7O^s7vMLr%~_eO6@BP#xDsWzJIxbKZ~=QHwoEqiCN;6|k~9&?6VGu8wVeg}kjlqBUkXd|o%rApJ7Xx?SmelE6^*`N?mP%?P7H@yM z@176Te{31>^aeVqCP$2D`iM8b&G*loT1#z02OAN3)TGUD#xbYXQgqexFre>@9yw_< zoN>t9>xRrEIA?_IE1YrUo=P<2hNadJXB=4rbb%a&IXb5^sO2P4AoU2`8OJ_N3+RuU z?3bPMlGWJT{|mv5xDeck3qc!^Tkv+yM^@uWqQKxnw{tZ6G_7EN(=@mR#5tHjEptZL zT?o!N^e&F18P*7EfGlP>S;He0o_$19Sz!q7up>N&hdzT z+KgUQr1}UkLQL4KR;yolWSqh_YPX?lMz7!1&pz~~fd%z~_Z{#V_yT+aKE`}mgO%s( z*Zy2en({y`_&;C`{0061zkwyNiuqzpAev$qeWYoU3d{J#NzC8IACaX$H=;%xu?3!~ z#a`2j|4di>P9KpnLiZ0Fl^vDJdHW{hJgNDYHbk!VsHwq51hhtobGl;RmY>GHkg{)E zesWUhFRGl6)Eh$fMU__)%@X*lTj%8LuWk+fh;ux9b*`$4|A#h$uKO0E`Tzg`07*qo IM6N<$f=1Pyg8%>k literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__dialog_full_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__dialog_full_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..f18050ea589eaa31233bc08e4f8a4e361747bc94 GIT binary patch literal 1003 zcmV%vbFc60S)hq5aquQtN zO>~Q-PMt}eDy?tfo9G5^U3x~HVu{=o-l^#5`3AXbFq#7CZ= zx~R)Z%-PngELWXE=jG+4wH{HY%RHtL$z+_dj#xsD@Y&isDVlJd1j7%A; z{S3ob2!NCVl}{yrl8B^V**9f-6IC==^T()w$-W%VAY54pg(^F>UjitZxrB-eCn8Lf zB*stQ-rl}17K`6RG#lbS(&{_eIc1V`#=6_}di{5~T>e4x!ZPZXM|a{QRmt`XV*pbC z)5T)(D?{j2cRHWXf1>hHw<=z1GC@s9Ro2lk0Wc$?r`}K3Qx5<^GzKt(+iwt@SwZ#Y z1K3Z>bSEt!roEr8rydA`NJoVD>nPSV+2(05UE>4T005_GAOuI89vS#iG4B18N9tN{ z^Z@tNBTj+gCOB47*w+ z+2Hb~>!f2H1$Hq!D=_Sb+i%D|qnZ-1^bbIwX<}v&QHjdz0&e>kOOQ$(hHgzP5DjJz z0f>y6M3En>w55Wi4nf3bKUf4p>o0~xvY>6}4YBjHqH^B)8ba{Lja`ks{5OurR;$m< ZjQ=n8%sUGgc=rGR002ovPDHLkV1nUn!$<%C literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_ab_back_holo_dark.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_ab_back_holo_dark.png new file mode 100755 index 0000000000000000000000000000000000000000..ce87586fdad7c3f56dbade73a1815d87f5271182 GIT binary patch literal 375 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}V}MVHE0F#_pdj|X638*yB|&~b znvtoUxkHLWAhsr@tGSM=p4+RfrG%|hH`*`HEKS|W!Z1T4M%rILpJlT+3nx%pm8Xki zNJU(1|8>5m1RgibxB4p@x|)jLfBeUv;d43e!tQxbqHq3Z2s?a}!N|Jh;=VOZ4VzY9 zX8RhiG0VhF;eeTMH^a<=Sx>k{oQh)fpLe8dv~ROs%<^E;uGNgw@5DS>_I;lwe}CzR soG5LvtMR4(w!N}@7aSHp`OCBa42#yV283Sp0RmdKI;Vst07>c9i2wiq literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_ab_back_holo_light.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_ab_back_holo_light.png new file mode 100755 index 0000000000000000000000000000000000000000..5fa39648eea9a7bfeac8ddb5bf6e85c09db103a4 GIT binary patch literal 308 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}a)3{WE0Cr&NXsa>2ed=6B*-tA zfiWKl|?65kxRhK#++Zmg5Ax|Nm^e{*4IGBx#W4|d7$EWPZ!6Kinvt&<3g9LzX);eE75ncY#QV#~j-JOA}RF8Ig0cLH;we88+Ukl#IB{an^L HB{Ts5xCTW> literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_cab_done_holo_dark.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_cab_done_holo_dark.png new file mode 100755 index 0000000000000000000000000000000000000000..34b773981837801c637779325082e9a27d4a3e96 GIT binary patch literal 530 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyy8?VdT!Hlep$R_8s2l{k-mWCb zFPMRWk(ues=TE!Zq;`vU@7dA3?QKh=jGT1Cz5`tK3j32RBzNj{g#VD0=#*y_+uGB* zrO$BPxBf#nCr@HwkL0_|RyhM`(n(Jj$B>G+tCx=UF&hf7Jq(d^pZEJjov*qfJHua-|~5!X>F`c%QyOO;a=_6)mVRDW~VS`9NW=IZIQO_X}d)P zFB}#Y?>CV6)Tf>v>jL9$Zu~FJYQN_<^Ns^tCOI5=*MOd4@O1TaS?83{1OR*&M`Zv2 literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_cab_done_holo_light.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_cab_done_holo_light.png new file mode 100755 index 0000000000000000000000000000000000000000..e3ce1cd9c7a5c4277c53c80e48b11a8cc83ddefc GIT binary patch literal 446 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dylLLH0T!Hjp167sEr9d|smIV0) zGcYnu=+6@n%H_}T=kxQ)5ArVX?GaQImM&xqFrH);$jqA^oE0LQJ~6bYAv}!5ghN@) zR|=?krKgKyNJZS;bH{}a83-^3tlj%^?!D^w|NocH@Kj^=%sIE<_2e?KdO4x^IcM|? zX3W@I|47c`{kaLoO$Vdr9DHzB?6|_E>O*h+)fbs>*X`4)He{OmF4Abl8^h1Js;#?= zVr~jwTKQVDy7I+o%d7vnm!vvBnP+D4w3l_7_RhQl0mVm09Cu8-edu-XwvzJShXbd? z?Z0SQuE%t$w0Fk(s`g2@|4!V!&&k03f%|8cQVq7)+sQx7L9yWJ>gTe~DWM4f`JZ;p literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png new file mode 100755 index 0000000000000000000000000000000000000000..f04cd8e2bb936bd39c0e3b32207a34da673c1e25 GIT binary patch literal 119 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnL3?x0byx0z;*aCb)T>pbXXwBCgpa@e*kY6xE z!)$Fo2_R3-)5S5QB5vy`MMfa6dBg7S`5sf$9qO1|V=l_r8S<^U@BPIX< literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_light.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_light.png new file mode 100755 index 0000000000000000000000000000000000000000..1076f75c68a78924980c9f7d112bc272f93eac48 GIT binary patch literal 130 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH3?%tPCZz)@&H$ef*Z=?j8-u}AGqDvwLFSSm zzhH(mhLaaL{sXzHo-U3d6>&$;C^9lI@Gu>m8o~KVVA71@&j}T!ua4)=WT={ApT_?8 W2G?pIkq!Ak4Gf;HelF{r5}E+*;3b6s literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_share_holo_dark.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_share_holo_dark.png new file mode 100755 index 0000000000000000000000000000000000000000..6bf21e307ed392bf00fe80b162a6ce9115e62c84 GIT binary patch literal 332 zcmV-S0ki&zP)h$s=>Sb2o({EW84x$2Sq@4Qvb1mj$j~;VIQBr2GX>%-APxay9!he7 z8W7h*gD?q**@0LXh%12j6cFzN;xHg)rA0Yl3bh0|IM)O5QD`2|BGz~0I$#-|1SL$5 zlJGbV2jtPKD8?58g7k0zp)`;V#9XwljI@z*K`Om-0V(CcdbDyt4~XM{*o)XuqGd@4 zDx{8=fp<=lEG6&%R0000-rS^s>8d@5ni2hppU0iV;YH8gwNa0%) z{Cz&}Jx|SbT`f+uL?986Pnf0|l@f5k0jz*^zeXXD13Pe$Xi*7R9^s5FmB7$P;6x!% z=kMH0V6IZ20v0@I38qpwTN6bU0_>(U;T<=GYX-1E8)QG}f-;!!681nE=wJ-aU=KPV z^-i_I3K%>@EawIsd5_!T)2R?x1}rE&N|%HcIQa;SqE5+gRv=Fy@M;YVKt`niqbhVk zpSlae%z=$G2Wp@TnqrmWnuKgf72^IDIDhdq{E7X5-;T%%62M`q*b}mOv3|8DyoG;R z;O#l^2#-VHDcnmy=|6xRPAT9SP9@+QP9YE&4z7Z?SGPkvc&h*a002ovPDHLkV1j;z BjTis` literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__list_activated_holo.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__list_activated_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..775d9cfe990280415f5f92216e36680ea01252aa GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^93afZ3?z3ZhDiV^o&cW^SB9BqfJ_J}x|eA?kj-2Y zEakt5vO}BkP~Qt%SLvw%}WbSS_}Cs1-=<(o!qdo{xyTs4A#B8 S8{AibjPi8#b6Mw<&;$T7F(Nzw literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__list_divider_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__list_divider_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..986ab0b9746301f2dd9401829da09e00995621b3 GIT binary patch literal 78 zcmeAS@N?(olHy`uVBq!ia0vp^%plCc1|-8Yw(bW~qMj~}Asp9}6B-)+^BAxRtXRm= az`@Yw&#rLZUbzUUfWgz%&t;ucLK6T(%Mo}0 literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__list_divider_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__list_divider_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..0279e17a123f8cbb3c7e3a9ce5c5af8e693b6977 GIT binary patch literal 76 zcmeAS@N?(olHy`uVBq!ia0vp^%plCc1|-8Yw(bW~!k#XUAsp9}6B-)+^LX%RmN2q0 Ycy4A9FVZ~13zTN?boFyt=akR{01+Y(GXMYp literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__list_focused_holo.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__list_focused_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..f25b4458e3b2646cf7ba2a04d26946c444e77e17 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^93afZ3?z3ZhDiV^o&cW^SL3ZuflLS~q}cKs$YCxC z@(cE5U}!SA1tb}iJzX3_D&lmH1#$unaM{Q%ws~oxNoygWrNB4Ctdkoy*1u+On!&o4 TcZ2&1pmGLJS3j3^P6Eakt5vO}BkP~Qt%SLvw%}WbSS_}Cs1-=<(o!qdo{xyTs4A#B8 S8{AibjPi8#b6Mw<&;$T7F(Nzw literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__list_pressed_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__list_pressed_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..0920cd70cf7d2a254eb75dd88718d182473840e2 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^93afZ3?z3ZhDiV^o&cW^SL3ZuflLS~q}cKs$YCxC z@(Z2`RJ_G~A&{%=>Eakt5vO}BkP|58vXNbE^U^|-)Eakt5vO}BkP|58vXNbE^U^|-)P-$X_AX1LW&_x;TbZ#9cjckdr}y=jerg;TgVl3z!1&^00004b3#c}2nYxW zdZ_-Wf*5)VugS&t+23J}V*z%DOHb#W|`Z#l>;cJ*L_P;jGoKD8nX4Z`xH*Q>s zTvTl`b;%gqS1znaOc|u@tz3t7=|UK<9Hr>BJ|D1%4AOYzE>c>YASL1(e+Y^`_iG?7 z1UL~EK(|`0e*W#*UXt~C{TfUweyI5sSBLVgO?u)p&e9+4(C=i^T1GuQJ|j^LdEE0 zfZFjzm@Nd117_`XxKDqSoJXh_wG-}tW_yHE!!B|TSvyio<6k9eTgoG9O0aTdZJ81x zmbul8Z%fpkT#Wc{ND1L@No&VX#iPVF7hx`c0JdkE;3e2%ZBQYi%Oe#dj=&z+_I>|? zOJi$dCv)FoZJG3n&>Qp|;vSo*JOkf=A5uR{`;uV-QwsX>Ho!a31HXV*se5TxFFA=4 zo=3!%#5;D2+DO|6R;c8b^-B2j{s4XhZw!t1m&l3O!HmBwHn=!)kb6yF?kI1MVX*Vu z<6hz)DH{^YBPWDzO$|0isCW3@P>L98oO;C$E5=4jGEPjLS?XZ=13IvJLaC?O;nLn? z=e?6_T^b`&$N3sK+o1N3^-DyB&=+_N>geY)_rHdJwBJ%s{^4(_WBZ5MLQM53U4RfX laXn(LVYMe-Njr@(d;;;gyUim^Wn%yU002ovPDHLkV1m`SB-{W1 literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__menu_dropdown_panel_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__menu_dropdown_panel_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..e84adf2d41604323cdad8b15e7034b6137e02425 GIT binary patch literal 720 zcmV;>0x$iEP)!1&^00004b3#c}2nYxW zd09!jyup9SJyH%;E_KZI5Ed^B*iNJmRSj zBG=jNc2B+cMg+dzb=~X>SwfDye{PH^oqQ4$1^~*55|NVv2=ckIR8XoEqC|oOgc2g; zEPA&fNb-^8#Mr1#v(VQn5UdVR@QAWs>EP1 zTHM9Zaim5tPqqk+y4R~ii**RWgPIHWkr2Q-rv~$oD_;OFg!*vQOJ?oR%RhjSvm}}N zU}~@)A@q_TOphE%y@J$^Y;2GXG*T-_?U?-%UNYKi5n>WcgjmBq)+~pV5UB*Fc4!_E zr<_OV^tE=(@|l_%K9#lBPBbeU!!Q72CYl{ozqWE(s>}kg*Xy-fB`%jskbX;(+jyxw zA`k(lb8|^3?6_RfL_?Is~gyeh`O%xX?U(f{QJR|o`nX{VXzT_{UE(T9mKbLh*2~7Y>#wCvc literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_primary_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_primary_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..ab8ec69844173ccda09e52b81ad27a5df76d4cd6 GIT binary patch literal 572 zcmV-C0>k}@P)jR6%Q#K@^;MZ+DwSq^+h% zP_!xtf_Ukr^ybl{hx{k~3HlSfd-tNJf;W#6l*URbZIYUN$+x@DLlQ_#DhP=KbJ@4d z?3>3vm>B@rlxxLQd;P-m9-THgzpAxGG!1yW5D)4ffO-cU4S%t?r)YdGWOO4jhM%lT z$gSE=x_Gve0j_HeH8J^wOM08!a}xvEw9Ce~Da{Pz+J>k}@P)jR6%Q#K@^;MZ+DwSq^+h% zP_!xtf_Ukr^ybl{hx{k~3HlSfd-tNJf;W#6l*URbZIYUN$+x@DLlQ_#DhP=KbJ@4d z?3>3vm>B@rlxxLQd;P-m9-THgzpAxGG!1yW5D)4ffO-cU4S%t?r)YdGWOO4jhM%lT z$gSE=x_Gve0j_HeH8J^wOM08!a}xvEw9Ce~Da{Pz+J>LjV8( literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_secondary_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_secondary_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..c154c9fa4b1afefd1427415950cc95644e96b27b GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^Y(Ol;#0(@^pSp(uDV_kI5LX~=yyeON|NnuE^2NV1 zffRE|kY6x^FT?(YU#9`NTAnVBAr*0LjV8( literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_48_inner_holo.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_48_inner_holo.png new file mode 100755 index 0000000000000000000000000000000000000000..d36ea1538368f308d8587cd2db5e04a79a4229ff GIT binary patch literal 1105 zcmV-X1g`suP)^h zC@4dwTJu@2)y$`fYJC5=tQsC*5zn!f>6?NT1=5~cS;aCn;|Fp|a+Py-ic@scOUfJ& zv=)1Y6`LD3TbY5Opcb)`7WokUbh3+We9X@F-EZx-9c*VCdo0Ev5U{DNv6=7q%6Q1B zkS(#f_zg?hagJL&=(xj1pEcM#+AU;QmhbGy>mwxv27d5$X_gv=lG4BSe zz%pPHnY5+wFIf?0;o&$&FJ*a^_b*tCc@|)50cz^;3j46CxqA6niC6rmBws(YNMz)QIg&eD!&6R0ASFHp}YVF~`A zLUxToi%>@-e;`T#6t@f&@=_#;5TceF`2w{7B*r@`HEb!sPc`HVQ~{8tV&?vuK9o1$ zjQ{9GwyMaIsbK)BsMtrQDgcJ_2L=GB`_(3h$^>i_kYgx+ppTq<)Dt32MZ9Wi1zD2$ z191%zA*yJl0lSYHLrDhmuE|R3x^N-2UZ4`&J+ICDhIiG{`6n3Bd0J&mQzwIz&FYzG z=oaUnSKbL)QMXG7*Py9qjWT`W*(|Tlu;1-U;q{wT^L;PHAs@mXhs^Xr&$$y2P&aLj@-QCGEF5&aNJ#})Qq&9q-uJQWe=ot+zSrR+$EKE z=0Y0n>Te$*;gHjB>d=K1uFM_IoROcpbXjwluF)7_!ZiJRcsaXCmrmHgvlQZ^psj38 zztE#@;+XvS@xw>eNrWg-Y86Ce=_g@vQp!UK%rs{7HZyAaH@aKEfTQc z(CR%$S?96abl05G-F(|Si_LUV;?$6kux)>%v=)2DUfpwT%PQ8|6^&m$u}%%W*3rdf zl`oQNvN&>WhAa0khnps!G6EK_TCpZ6=uP_{^)H>%wyt5X^53?*IxN|0o#E77R`PdD zm%Wa|cNB9$z_AP5mH=4J^LP6!o!<(RyDvF+#7%B*F3D>^@) zG3au=^KjWsn^#jyew;NrnOPLU6Z$MNVeT*c)9TLijM8T&&Ec75wYjFybd#W68%QTW}f06Axoa$IU9~?Nqc5iru{em z%^e?{s+j1J=mX5HswJ)wB`Jv|saDBFsX&Us$iUE0*T7QO&@#l()XLb*%EUs~z}(8f zfYD-4D2j&M{FKbJO57R>QdwRCHAsSN2+mI{DNig)WpGT%PfAtr%uP&B4N6T+sVqF1 RY6Dcn;OXk;vd$@?2>^cl_{IPL literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_default_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_default_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..367d45d4af6872ffd5610807de967a1ebba39981 GIT binary patch literal 244 zcmeAS@N?(olHy`uVBq!ia0vp^5IV@O5Z)yv*|4GKK27r8W8 z>+k;0GL=~KF|b`_-$UEYYD+zyyS&)yVXNl0t??gUX1?=`>1r*8yO}Pi$20scVEo2) zLEC{{BbIS@QyIfsj_hxx()NvaZT;@2zc=_?$Y*?)c@oRh%}aoGF?hQAxvXQ0jPEi}VtKlG3D7PEPgg&ebxsLQ0R5gy A2><{9 literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_disabled_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_disabled_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..baf71dd9c54f03e8466f1fdb094f3ea79bf1cf19 GIT binary patch literal 244 zcmeAS@N?(olHy`uVBq!ia0vp^5Sb@f1_d71i(DG4 z^>_bgnM$nr7}&0|@1gBxwWS`)Z@9!i>)5EYHr&a|M6w!JI|P|)?&Du>4JJZ!|wvdZ(J9& z9oRKu8Fx38F}&r-{#Gh&-+0&7?|%AwgU^M0#&?+~u{_mdKI;Vst000C? AEdT%j literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_focused_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_focused_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..f9ff30e48885e33a9618bc62d90abad3939bcc2c GIT binary patch literal 324 zcmeAS@N?(olHy`uVBq!ia0vp^5DH%h2d^(ZcFBJGW1u+D zkS57{vw)OzNswQ#@651?3_Bv%1U7Byn=~WVlff@yMOe9C&7#2YM;XeMKxHkSE{-7; zaaS+zWMWd}aSfbiVa#>r-K_ur{WnfHG1;VS-=}$#64Fj3tKG=S;QE%GsN}5Z%HOS- zvZ1hKgW)aliu8nY=?}~{@O^o|A?B9BobPHp$Jc5Hd=1tQ_{!|?lHn5L5@rwf3498& z58|v2l)k>dd+q!WFHT*1y8L^9e~5k7SLqe<&pA8Za_{q=cqJF;9tKZWKbLh*2~7Z! C5RyIs literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_focused_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_focused_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..3da9670c7ba25f727a1a78946f880b6db4b2b4b3 GIT binary patch literal 324 zcmeAS@N?(olHy`uVBq!ia0vp^57#@@kjSqS6*|Nq9D9vaU-W*iW0yzrE9akug2$HsXr#vuvDtFIf+KMpix zVt!*QkdiJ5@(cEz88(q&N93BorY(JwX2g0j_(iM;EBC8e6d3*}L%9;Dti{vCF{C2y z>gAnGOo}|NfzvFExz4PPeJxU zoYjHS*Y|g?o&Vv*scTP{e-H2vvCsM{y+ZyuXUALaecltV`*L BhM)id literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_pressed_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_pressed_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..c713eed3424254113cfc96ccff05333dbd8b9aa2 GIT binary patch literal 279 zcmeAS@N?(olHy`uVBq!ia0vp^5DBb z70Yc;iuc`~d-MuW9D*FTKQ`U^bjsnYCR?AT@4X9Dkh||r|DkIR+aH58dKqv7DUp&O zzu=h+*A|?gz5PlbgXjENXJ>Alv*qNxm?ObAfbzwjE{-7;aaS*U@*OeYa0%=%woW$x zZlA3a$rs4F$6xpVZ6@D2YKJy-R>-~FTA23ePN2|)om?HfkC^Lar=4xf`#<}_*X$Dw zwHe$F`CJL%4eJB TXWwB1TF>C=>gTe~DWM4f;IMU2 literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_pressed_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_pressed_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..ceae889ec2a748a7401fcbe4ffb9608fb6bedad4 GIT binary patch literal 279 zcmeAS@N?(olHy`uVBq!ia0vp^5X@rHZGEsKoPtBhA&18U?^fAk1QiIfES z1dInEdKbLh*2~7ZIv~7U^ literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_focused_holo.9.png b/libs/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_focused_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..0864db113200b5442f0d998d3cbc096ab39f1e61 GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^EI`b~!VDx6uRgK`Qv3lvA+A8$c)&j_r_H=O!sfhDEXebEOa_E4@>iIWT6iMuK-bKs4<*B?URVho&-vIflo-U3d6>*}+9R-1st_RBn9GMp% + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_dark_holo.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_dark_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..575334699663b221b5a2b3251572a7c7a23ddb4a GIT binary patch literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nET98VX=kc@k8Z|>$jV8FrZ@U7ta z-*T0WQ@><>I%v71J%hiqt0P@2{q!E)%~9Ermd_VC*s;IdoBnhS>rHk6>|lcSgpVF9 tjEp}SQ%vGx1w)$%c)I$ztaD0e0sua~Gttc^?peW`&R>iA3bc>G)78&qol`;+04`QPF8}}l literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..8155fe840532e1d0fc25450729892ea73c4e007a GIT binary patch literal 166 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nETTu&Frkc@k8Z|>$jV8FrZ@J;?q zbcL8uwC8*^`8m#29p5Ib=%`p$wC&7oqt#odO)b{rdQv>$UUk^Hs0pFVdQ&MBb@0G6aYR{#J2 literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_transparent_light_holo.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_transparent_light_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..fa4d76af93de31de153c6a7d41c05496bb14d2c0 GIT binary patch literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nETcuyC{kc@k8Z*AmdP~c#3C@cDZ z^MXtGfey<_%m2nWx~HUtq@RtOD!V+x#J|ag^QD{sZZM%<(R`m!o+Hzd-iC%RO>bP0l+XkKAqp+G literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_share_pack_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_share_pack_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..e7439c73a8bd9cf1c6b910e2ae57f7c67883eef8 GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^N+8U{3?$t(9|r;{o&cW^*Z*JuEakt5qI{2Auof10L#It4$bT;7Zx(fdpI&pn()C-;fYWjQ;zA= wrwq(S0;{uF*d|y6*=NL+UjH2WFDmksnaO#DOuwItj)83OboFyt=akR{0LVWt&j0`b literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_share_pack_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_share_pack_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..e856a8ae22649d6d1c35a87b8a6a6b158d1b4bfa GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^N+8U{3?$t(9|r;{o&cW^*Z=?j1DO!;qld{C$YL%D z@(X5gVBq5Kz76Esd%8G=RK%UVV93j$Ai#2PszWoo%7ukY@*a*%lO}xdQ+Og2$CP9G x^eF?gk-+LK7Pbi%LG~GOrPn`){)>t{WoB|-A=B^YqGKRCJYD@<);T3K0RVbrEaCtF literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_dark_holo.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_dark_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..6622cbad34409b2e09f69e305455482ee107baa6 GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nETEKe85kc@k8FK-lVFc4sN{QB_B z&fog}EEk$2&*T)$|Ch61%h`96?xa8O-xitu^~t0=vn)PNZ(|U lO#br3!H&B!tiEU`L+e4N?emv^?*rP(;OXk;vd$@?2>`X4Gjad` literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_light_holo.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_light_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..c4272978338a232aa445ed5190abab61afcedb16 GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nETEKe85kc@k8FK^^+Fc4rl_yt@$W?82D^ReIqPjo#d{bLmpkiN+x lnf&F6gB^EeSbfn>hW*nwPPt#_849$O!PC{xWt~$(697sdG9dr} literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_shadow_holo.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_shadow_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..d0df29d8b3fef9f71cda9b7a0975c68dcfb05685 GIT binary patch literal 290 zcmV+-0p0$IP)(^RAa&-b-qmfdL= zKY^DZ_=R_1v^+>Y{!mW=MF!v|2#Q>Q>z_&4i6T94j-Y4q`P>I~g?cE`S~^QhBCU5$ z-8a`dJ38hrt)qwm8XJx0+%VAuW=z7Jg@`1pBAUrf#Ef=wZl|UqH7RO)u1OwK(@xK~ zuV&5*5lN0GQV~igR!o15*n1TfDTP6ilQ72>QBJOyK^2jQYHlAzlrUMuFCtAA$s$=K oi)4{qME3aCu~lSF#Q4a(0o}otDK%H_Q2+n{07*qoM6N<$f*T@rc>n+a literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_solid_dark_holo.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_solid_dark_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..a0d9c1b957ea4a6ce62abd120668610d0cb2bd96 GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nETEKe85kc@k8FK^^+Fc4rlxca~( z?t=fRa*SINS}wBuewp3mefy2x$=b4i8MC*B`RkorJG1!P69>HDUX#kpcm>9d6MZKb ky7}`x**qcrtNL{AY0h>p*ZiBQ0JN3C)78&qol`;+0O3wK)&Kwi literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_solid_light_holo.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_solid_light_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..d36f99fecf223779432fb843b823c04d739f05cb GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nETEKe85kc@k8FK-lVFc4sN{Ca;{ zvU;8e%Y`P%GdTtG|K%*$a`xS%JL%8+w?$@ueKP6JEX#C%J{El7iLS?_f2=|R(l=Qo klfOK1u;Z=_t1sHgP`H~Vw5GX>6KE@gr>mdKI;Vst03NU~l>h($ literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_transparent_dark_holo.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_transparent_dark_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..5ad475dc3f478734be31bc5763ff494e5f120914 GIT binary patch literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nETR8JSjkc@k8Z!P3yFyvu&IDfHo z*MFzV3!h#&I<8{wJG6*7%`^0A*`zgvpO3}4Jy+2bzzZhGPxw0F1DlG16;n~mnRAu= eo1*Vq{$#i=98+;OAz%&ALIzJ)KbLh*2~7aXVK40f literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_transparent_light_holo.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_transparent_light_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..6ade5eeb37d8388813cee512f8adaad0f6c15397 GIT binary patch literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nETcuyC{kc@k8FE8XhU?9M9Fk3-& z>Hkf73eN%?4n{M7dy#!$d2ZSCCv)eTUt5@6!ODmi{A7$QW>Hb7V|gN6#Q)yLqkTHN Y-hRf#&+ODnfL1Yhy85}Sb4q9e0PNo==Kufz literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_transparent_dark_holo.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_transparent_dark_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..5f68476796fe86207f9337e1b192527f26bdad8d GIT binary patch literal 166 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_>3?$zOPHh5G`~f~8uK&RR$TQyh6iBL@$+!V2 z=8_=4UX$}ugZGZD`!ijB{nbZ8m@5dbq^8^~l;OXk;vd$@? F2>`TEIEw%P literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_transparent_light_holo.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_transparent_light_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..bae33139e1c3ad50c54f3466a6d6eea22bdf2e4f GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_@3?!2S6O@1yPk>K|>;M1%flMGU-uiTUv1lKV z#Z(gH7tFv=HunDyu1;UEQuS2t(=%(&d{0o2dn>FVdQ I&MBb@0Ph$gIRF3v literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_default_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_default_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..2283b4c01f31c24c241101989a028a28e662ff2d GIT binary patch literal 108 zcmeAS@N?(olHy`uVBq!ia0vp^tUzqR!3HEvyN#v+DPvC;#}JFt$r%|L2@FmD6{OlE zCs-P=E^ay;FECA@!GWvmzw+t@{SPuqdTbmsKKbP0l+XkKVJ9JJ literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..3c909b51306d684dc9fc4deb674ab1e1feb7004e GIT binary patch literal 113 zcmeAS@N?(olHy`uVBq!ia0vp^tUzqR!3HEvyN#xSIG!$!Ar_~TGcqy~7@GboNVOeo z`FL&lz5>PtO@Rj=>T@VLG&w|ZbqQ*{RI`whRrVK{_2=K=E+vL!&S{*=@1z8QW-xfV L`njxgN@xNAaNr>U literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..131d1030c9d5b447ef62fc8e336d9d3950ff7519 GIT binary patch literal 115 zcmeAS@N?(olHy`uVBq!ia0vp^tUzqR!3HEvyN#v+DJxGG#}JFt$r%|L2@FmD6{Ok@ zwtT#{-1s4r1MA|Z!|?(x4J`lv|6k8`NJHzTnuV0CvcJHrKmQJQDKT8Re5&^Sxg#LM O7(8A5T-G@yGywoD=q9NE literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..3e7dcdfdbaf66d51a90633e6f601bfe71b0c5069 GIT binary patch literal 113 zcmeAS@N?(olHy`uVBq!ia0vp^tUzqR!3HEvyN#xSIG!$!Ar_~TGcqy~7@GboNVOeo z`FL%)@k1sD*2PVS;{{wASQ@v8h$x3#@+~m2a+{}M^7C)Is4GMKie-X3Pa7xz&0z3! L^>bP0l+XkKapWP} literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_bottom_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_bottom_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..57b4bee7a242cc9bbf43e65ad2ceedc71d36aa0e GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_@3?!2S6O@1yPk>K|YePeW@z$pddUim8qQBRx zffQp&kY6x^!?PP{K#rxSi(^Pd+}pE;ybKCFtQY4mU=%iOWF&B=K+SXhAV6YrFz uFK_2r#jyInHJRh3w)Vi()cgFoljXuaYT{#-xpo2#VeoYIb6Mw<&;$T0ZY+EN literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_bottom_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_bottom_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..7c306202921cefcb3d372e80a7dcff6ab94245b5 GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_@3?!2S6O@1yPk>K|YePeW@z$sR|NjRHyop_G z3#1rJg8YIR9G=}s19B`qT^vIy;@+M$KBUjCWZcfgt!NLM;pLnM% ueR(_2Du&hnt;rlOwY3MPrrzhzoh%pbQ4=4t%(W9}2!p4qpUXO@geCy{l`eGv literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_top_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_top_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..6333e4509b91f95d8ce66ae523418a12c9d26fb3 GIT binary patch literal 162 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 literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__dialog_full_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__dialog_full_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..172fc3b5e3caf3357e706be2a1f0d91f357c8e5f GIT binary patch literal 2302 zcmV2ok^m97efLZMJ76bgmHN3TVPgynL1iZF^o zv0AMLkI|bD5@Z+S7;_RWD$X2Q#-Gi>; z=tN2`078B~PH0>efZ`B@1i||(&~g>qMGRTwTrQXXbnj39hPAQB*)bIg_=HbA_WGzO zR;yL=j9HSDjSEl*;KYv!A@o|O_=;IUA}l9+ObGitX|Dzg#M7mV*iktr2nZlVbl%^^ zH53Ym36TIhGR!|u7{3PC~!L=Y2z z32ZT8B;zY)i(nCqF!62X25hwu6@!!-aVKznAwf{$S4ay%(<6lhDJ|l}=KVE%xD;tO zjxokGV%Vil6ww-?fi&aiwtz1TI;KLw!36;9iSC?$w}w5ZTIo^H5hBZ^Apum+pFjWp*|TRqUSD7TX^iIhX;RaeQQ zE(9uq^MWzvYwRam(2Co38gn;jAmP?U& z&MGvUhjxmcRMYbSg3kR zZt4g}LPgLEq&hYm(J$si0u;4;IwvxY6?fp6l#|X8fH;pmq-q5ISi1H|(Ge;Lg>w@fkyi)P5d7;2~0$MDQ@!DE$`cg61AQt41xJiqTo9Dlsio7bTA}sD9BZvH#!%wZ zmJ%ND@1^DnZ%X(Ld?$$A6k0J~NE59ixD&!XX{j@~=b>7mwKZvxCyp*B)cAo?C*Y0% z?1kZeY?X^e-9W)XxdpU^5OahW`IAUpYa}@W?W5#&t4q8l2swcYLBS!VH7P;dBP@_? zQAk2?GdMjs_n0oBI`TV#(4X2ifxe2hDg*@upKxQ&K(mtAk|`HJPz)_1HaAZ&&GHf7 zsz6IO?f{EsSe^=LZJ-DtRGZRNi3)(P24xANDG)6Gj(#dNGOP_9D0K*+?F8HR`Nj#&1(CYoj<+oYNw{1t4Y0L^ri-RBp`1VD6hPVuR0IkNiMUb`EeV02O|F6c zauGd$;Lv5&C{!~b&}Udb7lOKhLPE$D=wt2>b>Q`^nt&r(EH@_=La)06a5Fwnfw+Sp z&{C)%=8X_pC077s0o1}imRwSiS1dcYq8ac~BtElCsZ+`Ul=zAmLOFfPAwvBY@Kn$$ zk{zOiq+$R>LAX{rQ>;#)$VHAb@IJ99w*HlMq6mNtA~I*7`@~Y7+@e^8prDZQ`Pk}@ zSYXq1DD{)3Sw!SFBRAo~AvKngUU3Lg#cu0$<^+@bU0;P_H1st~XDv}F6a(QX9$Rbi YKbh+rFQXz7g8%>k07*qoM6N<$g2q=E^8f$< literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_ab_back_holo_dark.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_ab_back_holo_dark.png new file mode 100755 index 0000000000000000000000000000000000000000..fae962bd20b2c584b6a561349d008b3dfa9b4e56 GIT binary patch literal 619 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyX99deT!Hle;R38CJ>&p+uO!GX zn1O+@zOJFYsg<>@v4u@Ip}C6L$%xChGniMdx-_q>f>YU|CXI=|TrI~|x;8?uz`#*7 zRm{$%vdEM@T_al7U(g|+`+H!3I?x<0PZ!6Kin!Xz;rWLRcv_r;Htk7PFSEL=a<}@u z?Z5x^TCySht+mf4H~cQJ@_O*s>Z5PeL^#WpZzPF!Z_R7u$*2p=I25{md*f<{B^t#H2C?Vos7Wx# zHs0RE_)fsV=lfYve#>(K_Ph2?@UmfL+rCol&!0!~T#3 OWd=`IKbLh*2~7Z()@&^R literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_ab_back_holo_light.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_ab_back_holo_light.png new file mode 100755 index 0000000000000000000000000000000000000000..1f3aab6ac51acb1de4bb75f7cc26a028a71d872a GIT binary patch literal 514 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyivoN?T!Hi;2MYvMt$m;c6x4mf`6~Z!|S&>j?4~HL~U^oaf`-U3}|--K>M_XEca?W?AyHOjfK$EgnR$;T z=N`Kp(hSrM@CU%evd=bf=-4w^bQmnuhmUH-jd<6WZ; zHl4_>Rk2U@t}L_t|AiwlQ891B3C|YS1B^O3M@oxo5*z)LB91b?+QOQX_r0yH)YX2= zvK_aW4}ZVF|MIp0)1X?{@CS{e_APBtKk;H~nV$PVB{N8HV+L1tOj6 z``KDz%d4l`HAyHPxAByb%b6xG^S8m<-YfXS7v>b+zlZNGcwheZcvv-P0dQ7=MviH9v^t)=yc559VF`j43j z_pMt2oAX|`goy_Fm~UMa9d%t_^^2qU)cXl;U0e%IRZSM> z>7Ksk_VioOmKv*1mN|c~ELg&~mMikt+LpAdYoES)QG3#uo404uV`0ytJ&!XS^rKz< zU6j9{ob*(;{k73gyQmwdmG2d+82l2_J(5-Z=Epp_^THpe+eb5HIvzUo?fW(va5{7P zd0GD0trC|9zjsRjleub%YeY#(Vo9o1a#1RfVlXl=G}JY)&^0g*F*LI>FtRc>2C}UT z3_|{Y+l-q7HAsSN2+mI{DNig)WpGT%PfAtr%uP&B Z4N6T+sVqF1Y6Dcn;OXk;vd$@?2>?&0tquSH literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_cab_done_holo_light.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_cab_done_holo_light.png new file mode 100755 index 0000000000000000000000000000000000000000..b7ff57c62af451c5734f75131f973d033c2106b6 GIT binary patch literal 756 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!o(K4ZxB}^s0H&>Clmq(HwRYDI@vTX#yTz2)yl~tNx+3G z)lfCkqo&g`Le|bP-7QQrgU6)HAS=X}KlkUqOalf6#(Yl~$B>G+x6|B<4;cu!m8(XY z9eZ#g+sV5@z(aBG_j&*SS1&rs?$0Ed`13%@vFiHwwYxa~eO8?wQ?w(BnQ4F5&WBfQ zyDqx#nEbM2>f)6QWiyn|-4{&$V77a`WT%wYWNu_mw0LY<+weF}^2egO znLljb{%fE2y6?F%hq&HHOJDh&|BKQpi={1kI`3ckATn#6apF$r1M~mnop`P1&Zf_A zaa=x6=dkjUI%$iL>mBpk_%BOHol=;6z}ZgsM%j()(po+i_e>KG8dZA}qt1 zvEW$n|824_rro=czRj79Pu}&Yy&dac4(S>rABK6N`tGW)q$MJLPW{$@>q>vt3618^ zCI6yt^lwXCz%wmjtsVWebir18#hxST bz(Bj7@IYM0XU^w^ptS4h>gTe~DWM4f&y(V6 literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png new file mode 100755 index 0000000000000000000000000000000000000000..84f108f964c17aa25eefcfcec5a1de1cafa0805d GIT binary patch literal 122 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0P3?wHke>@jRu?6^qxc&!&(3-C~KoO>rAirRS zhS}PF5YW2k7*8(k?>Af}__0@)dH+*|%^DMQ4gsYZ NJYD@<);T3K0RT2xCOrTE literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_light.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_light.png new file mode 100755 index 0000000000000000000000000000000000000000..c7363931426646abb1c245e06471055a823bcb57 GIT binary patch literal 140 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0L3?#3!&-4XSoB=)|uK)l4HwJ^LW@0OVg3KjB ze!&cB3@0yg{0DONJzX3_D&pRrH{@kt;9z!ikW_ocZX0^CgJGFa-kS?g-pa8DC39@b hNwhQhWdDB-o78!w$5$%MJb+pmJYD@<);T3K0RXPNDNq0a literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_share_holo_dark.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_share_holo_dark.png new file mode 100755 index 0000000000000000000000000000000000000000..45a0f1da0d01b7c0ba53830285c67d629bd0774a GIT binary patch literal 699 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU~2MoaSW-r^>)^McjrWjeSoS8SCOEBs|%|?VJ7D=T9Yz_dLJ8 zJ9zK=&-=vhubx}{vF6<8-!m`2Et8%6G=f2_fi-~)r;kY>>MAgRkm@i7CT&PKJ^T(I*&Av>d6O5U0r~;hkX|;LrY6t%e!5sze0;lgN zzhmA1v9d1mdTWRDgJXqP4j5dhQr@DSSRpJN-*(9Bjp4$N@%tt%vt{r<#9TCmVT&q5 zPY_dpf5x8W&TrHi1EzgwuzL61gP~v}`*h7_ev9YX^%66WzmeOz)Rwd1sPzfWyZX+; ziqW^(w(Z~haJj|{8OsISSA~|G@%CQ8Y_d#7=yl$KAFm&t%#H|W$kcQ2;yBguIO76W zkG?He)AQ@KipLqRU6Nb%FPk$;xS@jW=|$x{C80dl?QT)`*~FQi^@N=fie6YGbN2D| z=`W4NG-chmgqp9bsxquQQuyVroI59@gwb7QnV5NMi~&p85J-Uv+l!N+#T%5W;<7yr4}f9Ow$JGA?da_{7}kE@t}@ET5LG{B2CIDTQC8`Twj U_qmZcFi|piy85}Sb4q9e0Mf@O?f?J) literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_share_holo_light.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_share_holo_light.png new file mode 100755 index 0000000000000000000000000000000000000000..8f020971892dd351fac3de20676bbad717947e56 GIT binary patch literal 838 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!J_PuLxc>kD-xv&l0uaXVChZK) z?gV-@pd`pIn1NxY@f0T3=~IOytGd%`nkU8CMsaGhgqAnuC~$Z739x77@%kE0aMozZ z2sH6f>k)H_Nw(KiX_qdx_Tlr=_ZJbjaBDS^cMMi4kToleNM)bYc8-C8ajU0`V@O5Z z+pEXRUKt29JUnxP^>)KSfhr*{A&ay;P5wRWHD@G}piG6~ni!-A1d|ay}`zw^wXKUw2GAd&Z-1OWp%zoF5i2<|t|| zWPGA|;WEQ2F-EPN1NYr;@d|>m}f}p zqWeNzRtz!+I46q9F@0z-n>0z?te{bd(WsQcEJ1PQQbrYxf`yDrI1_G49k85q>2=Kk zP37F6r%VB77o4h{8M!iAjltY^O6&3jcTY}U{R#$$??n~s{@t4yoBCSeJcGv*LrDuO zhT3y(!JIZ`f@|H{tz-VSseD^Ehila))^|BIx}HhG&r^RM{+h*~nXcntx|O3~4Woq~ zquIZs1-didF)%SLDQSrJ_H$grEakt5tn<)kdZ-wgW+I*3d`aPM?{`93cWEoC>xPy^`*7oVM)y< byVa-omlp7bP0l+XkKvh*hd literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_divider_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_divider_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..e62f011d45a2c4c61a60b6451bec014a557a5188 GIT binary patch literal 83 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFm1|(O0oL2{=q&!_5LpZJ{Cp0wt58!#o&?v=m gn8lDKU<(7oW*(8&@UL&}fT|cgUHx3vIVCg!0O}VNC;$Ke literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_divider_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_divider_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..65061c0f45e63fe0fea0bfad9c71ee52c07ef38e GIT binary patch literal 83 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFm1|(O0oL2{=q&!_5LpZJ{Cp0wt58!$3;rOwB g4O4-x!vO{c$tv!+&aYQD0#z}1y85}Sb4q9e02-Eakt5tn<)kdZ-wgW+I*3d`aPM?{`93cWEoC>xPy^`*7oVM)y< byVa-omlp7bP0l+XkKvh*hd literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_pressed_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_pressed_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..db5191bc6d4264946a682553b022135578cebc98 GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uk3?!4(jyeG;o&cW^SL3ZuflLS~q}cKs$YCxC z@(Z5Hz`(J^eIbym?djqeQW2MX%8-#kfrH^-e+tXu3r9qrGzz^jIVc;EXZ5AE;9*J4 dCcD+A_?H&&syo-KqyV)sc)I$ztaD0e0syTDCZPZT literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_pressed_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_pressed_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..db5191bc6d4264946a682553b022135578cebc98 GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uk3?!4(jyeG;o&cW^SL3ZuflLS~q}cKs$YCxC z@(Z5Hz`(J^eIbym?djqeQW2MX%8-#kfrH^-e+tXu3r9qrGzz^jIVc;EXZ5AE;9*J4 dCcD+A_?H&&syo-KqyV)sc)I$ztaD0e0syTDCZPZT literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_selector_disabled_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_selector_disabled_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..0150f63ab752b7fa96f774ceeaa1b8cbc90bbb19 GIT binary patch literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^azGr$!VDy}M6D+PDWL$L5LX}#2mk;72eJkFiiLp` zYe|q_a05eq4a53X>$d;}EInNuLn`9lp4-UDpuppD@i3>zrY`1)CH)J2uU9&vCd|V0 wy}S9k?6n`c@s92*&W2pxBKpt&a#iwk#)k`JUv8UvToPoFr>mdKI;Vst0QY|}cK`qY literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_selector_disabled_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_selector_disabled_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..0b261f97e636fb501b7dbae27a9282098a3f8147 GIT binary patch literal 155 zcmeAS@N?(olHy`uVBq!ia0vp^azGr$!VDy}M6D+PDgFST5LX}#0ssI12Qv1r?zI6@ zEG0pH!SX;Y`77jlfP8aL7srr_xVPsHaxxfjIA83!a7i>~!2<@dyY?8OR-p?Vt(<)KXNk*dJ1<_`!jRpab6RMK;Rk47fDU#NEX#7yk4(~&Fr}L7DZp)K} zK)DDZgb+dqA%qY@2q7)3S*px^wsY!a9l?~jW2$HoDfOAoDc4?$q=mJH$Q@HesW|pH zEqI5t5pt#=^`ykNb$bDA+g_!6r>E#0Ebxb)V~E)@66{HSu%o+0#^7QK=<56_<*0g z4?VWXMFRkJB8s~XX6q}?LEeWW=Ef--P!X+fp` zQ#hbt=1@CWv@?Vrj0n$wG-wfB>s2&!$QdDJ0ulZgw-cuRiR{Y>^Hj3K6cvDZyr8vA z8lxp5*y$r9!v6G_XAF7`2PhmT)XW;J_(x39;8bKfgMu`e! zU+jWXwIOOF0-q>8C*JQsY~7_bBCuA z|Ap1-2&vJi9g(t&*dI@qVtrbEm_q)8uxlUymW^N&DQrSTF2REQdw9B(B*Fk-L?96w zfdFVX0=tCgq*<4>5rKwy4p!?>V+1b~mqyqhPm^M8N}pNlWK7Aa(;L|rtB1{dT%;u; z=)dm(?jeLfE6zhUB!2hW01y9MdY)1v=ujhBx3b+Xm&>Pd9KV)clx)K|j$bdA%O^}( zPFj=H`w_B~E-2}`eIdjBd_I2}hT(GvVW{|jkQIawhG7^!pU>wnc#H_%SRreOrJP6k z#f!~#`~_1^!^gPqw^)9R`+vop3DslHn(o!PiI0dTt@|45Za=)m1`%IwEW{fV$LVaF zE9RVsAMaWMjZlg;f(R_4iy{7r`=s=`3SdP<&^M)TPHqgUw5?z25;3$902*NlyMU*S zpa+j$RH$BIEX`7~H`b|A%pl=Pj|o!QHv-L&MvImjnCmfv9y@5Gq|A_|G$L4MNvqdg zg%(@J38}FG4N!(%uhEggMhM*%9HY#I^l zO-Wr$p$z~$ref=30GgqO)XJdORd}!BEq>NeB8p9{v^kJo2|NxCQwf^HCpGf6X;Wp4 zg!hgmb$FtdtQ>ASMriqL^@|$FBEhNWjw@zMktp~+Gzo9nO1w>OhBUKE#}ER(1L)vD UI+1b(O8@`>07*qoM6N<$f~tseq5uE@ literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..93066c8403ddaac9b19571152ef499620bcc0e02 GIT binary patch literal 1551 zcmV+q2JrcbP)ZMyJc+a5x+ehm5NR9U3;9%`Hyz-(tJnPJWK72_ZxFO$7HJ!wl=MA(TM` z5z;&LLlB#EA&f^$3`&>pe6X)LdJ&`vfROKm-xd+jMbSqLMZ|438|}{L$EEgM9M^%w zHa{i`W4qmEM~t#0eOdu-0W1NOh@<%YUknyuBI#0I?3#!O*zRI&T@|+kOaOda%IhNv zM;99T5trXGZJ+ZJPec%b)$c$hhFbzf1btKUoQ@WxJHBtqt%Djjh@==IkkXLZmr_R= zETUs(|D2YHL30h7B}jQ1@s(0WN*y++$>%9Px0_7;Q^61da}ymGVa5oGC6I`(Se|An zXj@SBzbdSFK)Um(Fw#d8Mr2~>phE(63xW_TA*G~C`G7107k~`S4FDISDe;mJcscGO zI_^QW2+SjBmZ1U=9v>gytZ(L+77q^(-%!w_2qCD0PZix`L1IaO$h(b>d0@F@G3Eaa zi55W^GL}q#AgNiNjxa=>FZdR(r+ zxE>NCSff^sd0^fBMHn)m#E-cU=7+{dF-K!9f{S1|kOWYSoDj6e26s~&lfViRc^MJj z^>Y9iF9eSfmVwbcxJ7UPa3MH8F;)otZ%)TN7|p{_AvpQV#UO5SLMWM?>!O$+C0vaD z8(N!X1GsBg3ar^sKmkXku0dO*!9}nfXnYiL)Hg=pYKs73(^zvc9aG}t;{#B5>C;Y2 z2$?l`wfXukgc)%>9s%7AsDyH^rRNDOAHyeHMgYzG`@1jfJOi4h!RzZQK)65|cOA0& z28%R3H(uBEzOL&YU%_z=>~=fscDp^gsmfTk*1@j+TejVzGC zpG{n`bp4r^(iKT*Io^&x%OHc_WZd_EkhCG`k)*sFZ|A&V23>!#)HToxrIRJ-1q1Q~ z3qHxB_z~+bgihSWK3otqlC9O%9BoMC(eAot5j0oOB3|pwbjUE4*ONZd@_Kxq z*B8(0XhE_FfV`ZZwcn~gXn8&5;sQ&h03_lPHzc@QD2L`A0DB!p$qE$r97Hn6cmgfM z$;^GEI@&qEx+o;VEJ0ZAK#dKOL6F|xU|IwgE6|`&x_D{{k^myy5)6fQcS{yR>LIlD zb0eL(UISu`k;Dp-=L_3_9uII4xG36?ZZ5uUxd1Up2yq&z=dNHjN&oIh<{D_CLJUbUG4w?002ovPDHLkV1n`$ Bwt@fv literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_bg_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_bg_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..9371dd6001e52b3db70ce536871b100e52836122 GIT binary patch literal 146 zcmeAS@N?(olHy`uVBq!ia0vp^Ahr?{Gmw;Cyr2|F@dWsUxB}__U;t!T)XDAwQp_bm ze!&b<3|`T8tU#`jr;B4qMO^DyLqP@u4wi%F6YYcyn)zk^c^~u)T5{?D&(W}OnH9VT j?Id*i&%NIK>G4T!-K8Q{{547qK-~J+vCd@8=y*q$q| z9u8hnzUjs0w*%aG2c6sn zfV+Qv1l!xZ`JJcgDayMC0JPnAJ-TzBvR-+%w&@4&OyT!8&97}F0Q6F3{NEW7_qX8s zPvA%J)5mBwh9~cRAT~FtDXYxSC2zl-&F40J8}R|+0lc>Urmntqow^PE z$#ZeoXsr`u&UT!od)Tg5vLbcX1G}725 zs#9)MoC>=uNGc^tW`v=H24OY!5I72oaB;DTP&TrFVsP4d9LlK%_K5N9Pw#H+HJryI zc;heV#un_2H+i{0fKM=h0_fR*>2<-Nt6^J zXdDq?n#3Vgu!~Rwl4P7tM3`tI)Vo*!0{Fon9=^vMoryVxIRWU5F7g2MB0V=>)GYLj zk0apTpWprVkAu&LPuHgcz+WIZo5R`TtO7rL`Q>n7Qm0J6P*05sAb~j5my2tjL8t2W z|8|q5Q*mM6Ib+V&FQ$8G;}_geFBzOuFY>dd?_K=m+_l(6KVL?q>R2PDMe;+hG?_F$We;O$X<<#G~j}Xz>Pv-yO4xb8V6F+CJtdqO3bm* z)W!dH(;VOX+1BIWJgxx8aL_?{W&mbsYM(CEuGxCX#tIWHMA{kEQEU~%5RNRC(*hGR z$ikLoBpW4?Etw!~Y_O$7q{+c{59$KVoa!%LRkkt9@W&1_TG!4exE?;`a;i3# zG;El1GS+m~Yuc5_CbcoLhH*+NqDoYkwzNt`3`&h_Mq(vxFmDTDh&s3=R)GS6ElXvv zI3ly_H9q=h(QvWxTLNqhfZzWWf~)YGkF@>iDp!uL>39QX4eAdMHB=2*p6F_h#AL?M z52{0v)vA_}Jex&mP)@BZP*)T~m6m$Pph49{B9(Md0%@>d=*3i#{U=L9eZF=r0mK)= zi~aR^Ywun3`WV(9pjiiOLVo&O-Rp(nsdRI{sMBY)io86b?;iC6hR3}dvjsk9OQkOm!K=ZKocr@ev-JRq%=9ZX) z#n*<#qr=RRcmclrS^lbdrH%U=JmZ(fFOADKlFTcFZT$y5!(0+=La12)0000J+vCd@8=y*q$q| z9u8hnzUjs0w*%aG2c6sn zfV+Qv1l!xZ`JJcgDayMC0JPnAJ-TzBvR-+%w&@4&OyT!8&97}F0Q6F3{NEW7_qX8s zPvA%J)5mBwh9~cRAT~FtDXYxSC2zl-&F40J8}R|+0lc>Urmntqow^PE z$#ZeoXsr`u&UT!od)Tg5vLbcX1G}725 zs#9)MoC>=uNGc^tW`v=H24OY!5I72oaB;DTP&TrFVsP4d9LlK%_K5N9Pw#H+HJryI zc;heV#un_2H+i{0fKM=h0_fR*>2<-Nt6^J zXdDq?n#3Vgu!~Rwl4P7tM3`tI)Vo*!0{Fon9=^vMoryVxIRWU5F7g2MB0V=>)GYLj zk0apTpWprVkAu&LPuHgcz+WIZo5R`TtO7rL`Q>n7Qm0J6P*05sAb~j5my2tjL8t2W z|8|q5Q*mM6Ib+V&FQ$8G;}_geFBzOuFY>dd?_K=m+_l(6KVL?q>R2PDMe;+hG?_F$We;O$X<<#G~j}Xz>Pv-yO4xb8V6F+CJtdqO3bm* z)W!dH(;VOX+1BIWJgxx8aL_?{W&mbsYM(CEuGxCX#tIWHMA{kEQEU~%5RNRC(*hGR z$ikLoBpW4?Etw!~Y_O$7q{+c{59$KVoa!%LRkkt9@W&1_TG!4exE?;`a;i3# zG;El1GS+m~Yuc5_CbcoLhH*+NqDoYkwzNt`3`&h_Mq(vxFmDTDh&s3=R)GS6ElXvv zI3ly_H9q=h(QvWxTLNqhfZzWWf~)YGkF@>iDp!uL>39QX4eAdMHB=2*p6F_h#AL?M z52{0v)vA_}Jex&mP)@BZP*)T~m6m$Pph49{B9(Md0%@>d=*3i#{U=L9eZF=r0mK)= zi~aR^Ywun3`WV(9pjiiOLVo&O-Rp(nsdRI{sMBY)io86b?;iC6hR3}dvjsk9OQkOm!K=ZKocr@ev-JRq%=9ZX) z#n*<#qr=RRcmclrS^lbdrH%U=JmZ(fFOADKlFTcFZT$y5!(0+=La12)0000Xc{|{u8FaDhg zq?k*B{DK*L8TK#yIt|D*@^oXc{|{u8FaDhg zq?k*B{DK*L8TK#yIt|D*@^o zTWnnA*@of!T{CMYwquWzF^L_gaW=$(Y#N5TK})dcX+>9JCZFO4VQe zg+u|hMWqLos?w9HY9&fi5Qv7BQbBQNZYGgh4ax2#`Qw{-r@|w9c z+VyNVTi|Eme_h4acuHvrfrDx_7NS{hlg)Iex|*TnT2o&Ta-qct(jR? z!+#F9$8p<9#}8D(0HatfH}F}iClxPC(8)17L!X=EQ#=ke%(4Y6u{dGKdL_}zTh{() zvP1z!A;!(NjA&NzSdz0GwiAp+d~)<-a4p7ime_2ZNu3yCCwqek#SCx}8tfLXA(U0T zPZxWvow2CM+Y^q>WChn*Y*L5`+sXUiA1`u%5v-2eSYqL<)f%VGcGEx2r$A3a#G1L0 z`4$dJ!N%BSyC3n28=#gAwvtd#yy&xk(pHt=vOQIAYgjXBuEuzS{f{Jzwv7%)S;umP z00bb;@v07xpa^V`cX-1FxWu5Eo2>O|D^drB!(7iMrUfhH+i70se2plYpM+?&FVTPn z)6m09*6}?Rp#z%BTBZSjK%CTT#VsEm^=apIdV}(z!PZfC*&P_fVtkE-!OEmLYk#J9 zx+uDzI@Zc<)SH0;c)V+GKAgI|0Abd$A#3mHwjG?S6(#YrkgehtYJ&Y}h?hA1unG<^ z04=tKnL*Brwu7T}X-eklC^y=Af=$)hdF7#z!e=1DD%ArBL5h7GFJ<{a!am?b(!ngW zu;dvlumkSkIyh)P zfMG@x^KlC5Kwk@YTOFC*=KwoqBq@VuAkN)3Cn)eFFFtrN?+)}Di>Vd@K%+WHlCpRR z`mG%g3q*P03E^}Ud0P#+9QLZ`11WoF}a&|;bTnu*}$(E-iI1puKD^-^}<#yTu0*=AaG z9v$e0S#rtfBta!yu!~d$mvjR?IR_xD7!DW!^pc_iY=Qx<0x$?OBcBG6Mu=D*0Td;gwGQL6sR*AT;}VCOU)cl7HGoS53;-oaP!X191ON;mN*FKa86X4# zfD|bzLzcuR6kJ5P2atn9MjD@rcz|E0vjR9c`MeVw8RE)dSnfkC(vX$BLd+&z3F({z z;Nu6&A0|vi{072=lvPvi9q{nTh$uoun4@aUxWvQDJwSpa8BwBVXrTfet0g1NxbnD! zCKwNdPYla2^jA1Tcdb7b!by3MzmR8_q94k4C8m5JZ`GvSShD^;?$~BEfasK{BuV z0~)782m!dvCrY`+X~GP{z~>{pyzUR2a=4z&!Q05NwsZDs%I9|>t>G&;Ab@_h-Pe`h z45Zb?AaMXeEeq61it?$lRvZBa;)41iuK)`<*{6=oNfa5&Im&s;$=^;+8QP%hP(^8f}gV5cDO0E^&^&Iu3#SIcNTYLvkr99v9_1$X7_Aj1U& z0F7}-$sj^ptBFzuHiOj`HNY_Fg zk7jeZ$*P0$CwPxOC^!Hp!Cv)>Oa!jxMye=@=gk_}U=5HNUryR_(wBKw;)ugaZKMVS z2t4g(*NQQUY`ZDWCbh}{1gMKwzS(ivXCj($&gD$w-ptT38_7}N);%sCQW?V96 zJLxXUX_YpJX)}xD1YJmKpLQ~|QWV7tj+)t^1vtSnC3shFf5R)vd7-6fn77mwl#e1> z#cgWb>rezQhH16iS&&tW&mrE+rF<}Qa>!wxZfBM(6GNg~yVXu&jVRblrdq9GwQ94L zC&dxH#>l4Gr=Lc58_?~@qvr0>;#sEM7Mnn;6nGbl3lOjnP z2;<@s#We(gA+n?yun*L>b+Bv?K&(!G&_zFISjjv^6hz44;_@jBfoxRAZLc~lML|Lo z^owC!$JoF-bBLme(596p{;iX{DXaE5LhzC%~Yb7X7%T^H2L zQKB@^NSx`^5G9P^n&cspBp79gUb-1HPu?B6Vjo3(8-=M+L@p*5$&-|1LgSm#{7;!O fWy+K(Q>OeIZ~fSFheH$>00000NkvXXu0mjf&!$dN literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_48_outer_holo.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_48_outer_holo.png new file mode 100755 index 0000000000000000000000000000000000000000..14143c51c351a56446b86b18ffa534c4543ec4f1 GIT binary patch literal 2432 zcmZ{mS5(u962||5zy>53*w9%B3P=?aA_NH%FrjGZC`F`)4nioBUC=~8AW8|nWPuBU z$_Ax~QpE+Ngl3~F#l&Etsjw_Xv$*Hnhx>3JzWJRg->crS6J%m+QIvUZxhV?Jo-M#nxzF{J^|38LuZ7p2A{3`Uq`=6kIe>?XjoG zr?GU9y(f-uD6ci)?1uQVR4SRTa}|zrSmC&X%{3Uu#s!v3xTlSKYl2xXRH=XldiG8v z(NTpiD;+Y~m>M*QdTK>xZWq^1Gi8i~5S7o}q&SbO8eBpO0}glmjSVvH7iqi26bB%| ze)qmu=vH{`#f$>>(djQ0^Nd<2H1NqlRYSvb%bfxVSE-6hyvT5}iWKJBsJYUTMmHlb zv{AxVJ_-jqG)Isz@a^K$cM^+{e$!&jnX5OO8E8XRNKBd8M%eMRN2h}U8b(CNyXF_g zuNUp7s?z=QD$}|Yd9ZAQ6_1dXY%HHkm_JujD3E@{yoa-`U00;ZS$y^Kch)kpoM#-L z_hl0hcTHdzz3P<)L4tEt9m4YU94$VspFs>kyx$G43>YTSqKXzilVwxd7qomBM?e)0 zk70Hc`|MK3$79>mw09q37_K`7rCCb2Je8#;R&l3wIoUvgp*l01y`!k-Fb_5 zLjfJM*_>#E9is$M8{y8UF zc`)}6IgHUUW3OdBIJ8Hp{C;Gz79e0KH~zPbyGtCwB=d&F=mttg3`f7;P(54xp{|RR zuCSG=W?@N(vW>lt2EpQ^z#T{`#xM4!mdgzNHx)<1u}$v?^g+=6*9ecRo#a#lbymZ)6-p6z^Oni3 zsLEXV)_}Iwfa=*b4{k&Dd=wxu4wfdbo>!(imlp)*j4pv5ge6;)9owM4=9St^swSv{ zskok>E#MxCe6UUVusSMJNGR_6U4pG5H1hlHX%(t~>nKmo-|d0-{MbUO&bU~mKcE&# zK_zv4|CD1!H8jAfY`vSSl6kj5Wt9loIBf}POwl~4S?>?$T&{Mawq7O5qkqy#o-A4N zE|eq~B+4&zVcuWC$vrg@L14-|z}_N{7ai6X+LIyj5pcb|jwUl|0>Ek~Th%i#NI{1_ zBm3pUiwMhzLuXI=foW(FgEeV4z(3A`*yzwk~f+h&vY#xmxsf?5OY1hsd ztM%beiwI=D(k-wWDn9)VJJk!~4z@|bN-=_~*}T5DfK5!C3E@eSEsWaR*+?aH|7J32 zC+By&%_SPM+AS<3m|xNxz3pqO+_l<53yw$fu1oeLjY&kkfdiF=Q*Wdc`etyM*rdK( zp22kBzFv%4ur^fuXEGM-pX|Fkd%Nq2l6dRQ2XKwJF@_oc*%do|>Y6Qv~!9EN3 zF&dggK*AU2RyOj#FqbZdzB>B~aA?KNhqMjCP zxg%^}e6-VnyS`t)yIHRQ48~-EkCwOT+tHm-FNve|j%NMGANQwdwt$uv_8B$5dQ4qt zta;CYDP&5a>BeK^j6`R(AiCG7VaMyHMlZyJ5(>(;W*&VnHGV2AuFnNIz}wiAT7{P6 zzLG`Ao;5|df63pAJnK+9#KTd^>$h)+arJbu4vNUF`f^QCm+L^AP{SFHl{TDz|}ZV(SF{inp3522LyJ zSw}w{j~U+S-eU%!n4i!>i<1qPc*uvvdn0Equ&#u7KRLHmgc+YBp73B#_8nO(jI!6a zd|^=Eg{Z*a-EZraT;7p!4p4Bd9(wNu7coSR1qusz_AQ_YwQYlP_s|#mVMCws=x<_3 zw)$>kM#p7l7$^lrSc}QL@p{n?})PR^#Ne#?) zCgB#K1aGkR_pfe{pyO!Rvn|^H>G<#YXY-5pW!C!5cE@?0?1Se-EtcRG8{ivDI_Xa) z9RNV-BayoL#=1yj7vzbP28Jim7+rm%lluA~Oy{}(F+^Mn2nnS8e?t@r{NcbL_hWF!{4*;x%LlxuUCHn literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_default_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_default_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..1849054209800299a4b9f37fa28b8ee99a72a8c8 GIT binary patch literal 313 zcmeAS@N?(olHy`uVBq!ia0vp^dO+;N!3-qj14UGTlzo6th%1mjg9H5k{~w18R2Zl? zXOGQEASF@~4xs978JN-d;P))odW( z`q1;rw$5+&zt>+Y3*6zv=C>)x>)o%%I_(=%_OHD!sdLlrb@c2H8+ZJ*e!u7Q^R2D^ z+jln3KdyT1bEXaNqdWUpK6aJeD9X#X+|JFH`z%Xb=JuJZY3s^P?mQa<UkLqUsTvNuoU%z>~`?sIkyH3APo_8ho+Vvmr>KWDubIhGk`fVf7fefCm KelF{r5}E)I<(h~9 literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_default_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_default_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..a368065eb02cba20bbb19cfd39e86b638c58c708 GIT binary patch literal 313 zcmeAS@N?(olHy`uVBq!ia0vp^dO+;N!3-qj14UGTlzo6th%1mb#sU8S|BpikDh$;7 z_fgy)ASF@~4xs978JN-d;P))odW( z`q1;rw$5+&zt>+Y3*6zv=C>)x>)o%%I_(=%_OHD!sdLlrb@c2H8+ZJ*e!u7Q^R2D^ z+jln3KdyT1bEXaNqdWUpK6aJeD9X#X+|JFH`z%Xb=JuJZY3s^P?mQa<UkLqUsTvNuoU%z>~`?sIkyH3APo_8ho+Vvmr>KWDubIhGk`fVf7fefCm KelF{r5}E*5=X(wS literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..ae73baa789aadfc5eec9d2f21bfd815ffe7c507e GIT binary patch literal 302 zcmeAS@N?(olHy`uVBq!ia0vp^dO+;N!3-qj14UGTlx2WVh%1mjg9H5k{~w3U8KAD| zuA-ShN~k2rFPOoYO;?hcMc;ryia4Jrb(VIjv*CsZ?B!^YIfjpdss0u z^J-k}|Mpz**7y?#47Ha&iLalN+Tr`{_OH1I)vVKYnzo!j`u2Y1rzhLPTc%fYey}?C zw=VA3X@zrdv(@wNyxTVCcJ#U2;|tHmgzdcvHHU7i_L+zzc;_K(6cKCj~{cG+)HS4sUrY+}>zP(@h>B;u+mg&`;AFR&( zt&2N$TH)N=Z1uc5@3zgk9epnM_`xyLsGiDy~W=YWK9`0Smx&eVd4t7+@ZitYo6 y;`?d;X0%_M=RN!SzMW5N&rPm6QFO5UKeJUD2g}X$$6JA}WAJqKb6Mw<&;$U_v~ubI literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_focused_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_focused_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..60d063d8b7c4e7f759bd93e8a6c5c1778b8c4bd5 GIT binary patch literal 474 zcmeAS@N?(olHy`uVBq!ia0vp^dO+;N!3-qj14UGTRCRz)h^z6|r$Dsn#03ZgC>*f! z!J6Y2dk$T5-|@(K`=fQoFRq8l0@eKg{~w|fnOt$~5;7OU_TBl=Wyd489gmhCyJWuY zN!!8eHrt+Urkr;uum9_x9S!zGee~mc#=lo*qp%PwqR< z^ZtKJrl#^$){{@Uy97%A{dg+;N@4o{uU3UR{7ZDpOFh=yo4S@I$^Z6*Ti;TqhAZy% z+IjK4(c_q9`39#B=CbwdW3aS5-g#c3|Ea_SJtf&hsvwz1gz5oCK literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_focused_holo_light.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_focused_holo_light.9.png new file mode 100755 index 0000000000000000000000000000000000000000..89b66fd271d0c92bd9ec086d22451b8d8e5732bd GIT binary patch literal 474 zcmeAS@N?(olHy`uVBq!ia0vp^dO+;N!3-qj14UGTRCRz)h^z6|ry%4R2w?z)jprOT z_KPsitTkS8)_BEbW8W}ipHQf>txx~|{|{#)7=bYeHpD<6IscgPvWvz`&l`s%7;k!L zoY!K!_O|in$Ht4#8W(p1En;0Y>oSniC<*cl_MN$7Vi-f@nx@d;4Vz|6GGvG|$eTQc zWm#^1J8N4(L`cXYznTEQ(DF3Jo6Y#`8*c)-Ndqv__!edl@J z|8L3ERKCi3@+o(hKEQ}L_(^Mlo?(?8h6 zt`pDe`=+L3aZB*<#WagsJnpj}W*E!lwkK_PdW>csD)q-)x?D zpJp78k8peNCGkal**oW|>Zjih%sa!r`cKAr-o>*w+jc$Q`JavHiM*b7UZXP5#|)mX KelF{r5}E+>)Y`QG literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_dark.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_dark.9.png new file mode 100755 index 0000000000000000000000000000000000000000..51ea7311b264de727c1e20b941e19b498325327e GIT binary patch literal 430 zcmeAS@N?(olHy`uVBq!ia0vp^dO+;N!3-qj14UGTRDOU@h^z6|rwfi=2J)P@Kbn5z zO7Z^NcH1A%I&#H&+mks*t^j3mp-G3YqDeV!e+(3{*!IL`+mrr7*MR1jY<+6F^=bOv zySCdO=kB`$w8d(!=o%oUQWE4BJo6gEg7e$wUYv8;kYUC|gZzTFyfZA5b6Ka%Z=XH) za_O4@W^?eY_`V8Y%{dm{h=~C=KDGR zx(}}}x4OUYXSQ$Mdj7M94f|`Ex83dqTWs4FpDoxT*=Kh6Vw%M*9{1S~GmK?&aS48w zzQyzZ+QCcj9vjvF=$4MR-f>`;>brks&uq@?nhXCn@;~&Z4{Mfj2h4Gr3 z#x0A0w%n0-Py$jaB|(0{Gp{i$IKO@F#W|M^8D>m0$S-KiJHs+Lmvzeg_Sth!&YL-F zK~D-sQW>Q8yPIEGZjy}h=buh~JM*VwIrGB{f z#((CwU!F?3sP;Na@mg8r;zi4RTB3raPHQg=kDT|(W^0VhHbcAJA1b3`zMtc-`|$d5 ztNZ(YX8YEy=Ra%Mu)mgh+wESk#kOtn*@7*SeP)L*rdizLai9G#!&oL4m*8jVTRi`- z9lZ4Ju~GexZs~aI9S3%)zWZ19%;vnVx$tiz|6`x-i7+p2KlJ6*k9YqWm?Z@r4?0v` Q2KtJ@)78&qol`;+01i~cHvj+t literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_focused_holo.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_focused_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..0030a8f78084c463f81c9398a47f740edd5d9fb3 GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^Y(Ol;!VDz4mTvk6r1%4TLR^ivJ_R!2q}59K$3Q-F zNswQ#F$2S$==tJ6uDYj-V@O5Z)H54-85md?4*IW2VCLpf-tgaNQHa($TODS1zPG<_ aBr_;jaw_g9n7s_Bg~8L+&t;ucLK6TBx+V_* literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_holo.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..2dda426407e62f1bd2568f8b620c4f0a7deceba8 GIT binary patch literal 130 zcmeAS@N?(olHy`uVBq!ia0vp^Y(Ol;#0(@^pSp(uDV_kI5LX~=y!9!BY*Ohp1hSY) zg8YIR7|JG}wE*&zJzX3_D&krX8S*kPa4;YAI>=cU$10??F-Stv(=Q@;`RBvjdu|H8 UeEegM3Q#$Nr>mdKI;Vst0P#;D&j0`b literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_pressed_holo.9.png b/libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_pressed_holo.9.png new file mode 100755 index 0000000000000000000000000000000000000000..0ecde65cfe6ce7a4288c07af4bf9fdb2886d0c3d GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^Y(Ol;!VDz4mTvk6r1%4TLR^ivJ_R!2q}59K$3Q-F zNswP~0|SG!^Zhs=SKZUaF{C1H>Y0ta3=Av`2mRM1FmrP#Z}{)CC`4;BdGYFLUsnY|ev?jAb0}_IC+!uD5;W j+jMb_|F(s7UUS(a_*t%Ir>I{6YGm+q^>bP0l+XkKvT!T> literal 0 HcmV?d00001 diff --git a/libs/ActionBarSherlock/res/drawable/abs__activated_background_holo_dark.xml b/libs/ActionBarSherlock/res/drawable/abs__activated_background_holo_dark.xml new file mode 100755 index 000000000..85c2c0212 --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__activated_background_holo_dark.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/libs/ActionBarSherlock/res/drawable/abs__activated_background_holo_light.xml b/libs/ActionBarSherlock/res/drawable/abs__activated_background_holo_light.xml new file mode 100755 index 000000000..85c2c0212 --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__activated_background_holo_light.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/libs/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_dark.xml b/libs/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_dark.xml new file mode 100755 index 000000000..cab896283 --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_dark.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/libs/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_light.xml b/libs/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_light.xml new file mode 100755 index 000000000..42ba8a0df --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_light.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/libs/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_dark.xml b/libs/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_dark.xml new file mode 100755 index 000000000..2588a492d --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_dark.xml @@ -0,0 +1,18 @@ + + + + + diff --git a/libs/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_light.xml b/libs/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_light.xml new file mode 100755 index 000000000..e2078c967 --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_light.xml @@ -0,0 +1,18 @@ + + + + + diff --git a/libs/ActionBarSherlock/res/drawable/abs__item_background_holo_dark.xml b/libs/ActionBarSherlock/res/drawable/abs__item_background_holo_dark.xml new file mode 100755 index 000000000..d99b7a426 --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__item_background_holo_dark.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/drawable/abs__item_background_holo_light.xml b/libs/ActionBarSherlock/res/drawable/abs__item_background_holo_light.xml new file mode 100755 index 000000000..da5fb2e86 --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__item_background_holo_light.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_dark.xml b/libs/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_dark.xml new file mode 100755 index 000000000..b2ce4f0f7 --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_dark.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/libs/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_light.xml b/libs/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_light.xml new file mode 100755 index 000000000..d7e31b1d1 --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_light.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/libs/ActionBarSherlock/res/drawable/abs__list_selector_holo_dark.xml b/libs/ActionBarSherlock/res/drawable/abs__list_selector_holo_dark.xml new file mode 100755 index 000000000..08b8b12f3 --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__list_selector_holo_dark.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/drawable/abs__list_selector_holo_light.xml b/libs/ActionBarSherlock/res/drawable/abs__list_selector_holo_light.xml new file mode 100755 index 000000000..ada490bf9 --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__list_selector_holo_light.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_dark.xml b/libs/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_dark.xml new file mode 100755 index 000000000..bd19140ab --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_dark.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_light.xml b/libs/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_light.xml new file mode 100755 index 000000000..321f07c8b --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_light.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/drawable/abs__progress_medium_holo.xml b/libs/ActionBarSherlock/res/drawable/abs__progress_medium_holo.xml new file mode 100755 index 000000000..6d4814f86 --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__progress_medium_holo.xml @@ -0,0 +1,34 @@ + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_dark.xml b/libs/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_dark.xml new file mode 100755 index 000000000..4af5e22a9 --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_dark.xml @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_light.xml b/libs/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_light.xml new file mode 100755 index 000000000..b78508478 --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_light.xml @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/drawable/abs__tab_indicator_ab_holo.xml b/libs/ActionBarSherlock/res/drawable/abs__tab_indicator_ab_holo.xml new file mode 100755 index 000000000..d34e20811 --- /dev/null +++ b/libs/ActionBarSherlock/res/drawable/abs__tab_indicator_ab_holo.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/layout-large/abs__action_mode_close_item.xml b/libs/ActionBarSherlock/res/layout-large/abs__action_mode_close_item.xml new file mode 100755 index 000000000..8811dad8d --- /dev/null +++ b/libs/ActionBarSherlock/res/layout-large/abs__action_mode_close_item.xml @@ -0,0 +1,40 @@ + + + + + + + diff --git a/libs/ActionBarSherlock/res/layout-v14/sherlock_spinner_dropdown_item.xml b/libs/ActionBarSherlock/res/layout-v14/sherlock_spinner_dropdown_item.xml new file mode 100755 index 000000000..6c183c059 --- /dev/null +++ b/libs/ActionBarSherlock/res/layout-v14/sherlock_spinner_dropdown_item.xml @@ -0,0 +1,26 @@ + + + diff --git a/libs/ActionBarSherlock/res/layout-v14/sherlock_spinner_item.xml b/libs/ActionBarSherlock/res/layout-v14/sherlock_spinner_item.xml new file mode 100755 index 000000000..61dc02527 --- /dev/null +++ b/libs/ActionBarSherlock/res/layout-v14/sherlock_spinner_item.xml @@ -0,0 +1,26 @@ + + + diff --git a/libs/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar.xml b/libs/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar.xml new file mode 100755 index 000000000..040df44ab --- /dev/null +++ b/libs/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar_overlay.xml b/libs/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar_overlay.xml new file mode 100755 index 000000000..c64ef141b --- /dev/null +++ b/libs/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar_overlay.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__action_bar_home.xml b/libs/ActionBarSherlock/res/layout/abs__action_bar_home.xml new file mode 100755 index 000000000..5c1e9ec4b --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__action_bar_home.xml @@ -0,0 +1,38 @@ + + + + + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__action_bar_tab.xml b/libs/ActionBarSherlock/res/layout/abs__action_bar_tab.xml new file mode 100755 index 000000000..f46f7a044 --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__action_bar_tab.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/libs/ActionBarSherlock/res/layout/abs__action_bar_tab_bar_view.xml b/libs/ActionBarSherlock/res/layout/abs__action_bar_tab_bar_view.xml new file mode 100755 index 000000000..0d51220c9 --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__action_bar_tab_bar_view.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/libs/ActionBarSherlock/res/layout/abs__action_bar_title_item.xml b/libs/ActionBarSherlock/res/layout/abs__action_bar_title_item.xml new file mode 100755 index 000000000..dd69acada --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__action_bar_title_item.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__action_menu_item_layout.xml b/libs/ActionBarSherlock/res/layout/abs__action_menu_item_layout.xml new file mode 100755 index 000000000..13149fd63 --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__action_menu_item_layout.xml @@ -0,0 +1,56 @@ + + + + + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__action_menu_layout.xml b/libs/ActionBarSherlock/res/layout/abs__action_menu_layout.xml new file mode 100755 index 000000000..a6f8e53f8 --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__action_menu_layout.xml @@ -0,0 +1,23 @@ + + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__action_mode_bar.xml b/libs/ActionBarSherlock/res/layout/abs__action_mode_bar.xml new file mode 100755 index 000000000..7168dc77f --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__action_mode_bar.xml @@ -0,0 +1,24 @@ + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__action_mode_close_item.xml b/libs/ActionBarSherlock/res/layout/abs__action_mode_close_item.xml new file mode 100755 index 000000000..875ec3e1b --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__action_mode_close_item.xml @@ -0,0 +1,31 @@ + + + + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__activity_chooser_view.xml b/libs/ActionBarSherlock/res/layout/abs__activity_chooser_view.xml new file mode 100755 index 000000000..019d14ef4 --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__activity_chooser_view.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__activity_chooser_view_list_item.xml b/libs/ActionBarSherlock/res/layout/abs__activity_chooser_view_list_item.xml new file mode 100755 index 000000000..b430032a1 --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__activity_chooser_view_list_item.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__dialog_title_holo.xml b/libs/ActionBarSherlock/res/layout/abs__dialog_title_holo.xml new file mode 100755 index 000000000..6402f28be --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__dialog_title_holo.xml @@ -0,0 +1,46 @@ + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__list_menu_item_checkbox.xml b/libs/ActionBarSherlock/res/layout/abs__list_menu_item_checkbox.xml new file mode 100755 index 000000000..39aca3a8d --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__list_menu_item_checkbox.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__list_menu_item_icon.xml b/libs/ActionBarSherlock/res/layout/abs__list_menu_item_icon.xml new file mode 100755 index 000000000..55ab28a24 --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__list_menu_item_icon.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__list_menu_item_layout.xml b/libs/ActionBarSherlock/res/layout/abs__list_menu_item_layout.xml new file mode 100755 index 000000000..147f36fe8 --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__list_menu_item_layout.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__list_menu_item_radio.xml b/libs/ActionBarSherlock/res/layout/abs__list_menu_item_radio.xml new file mode 100755 index 000000000..ff54bbecd --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__list_menu_item_radio.xml @@ -0,0 +1,24 @@ + + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__popup_menu_item_layout.xml b/libs/ActionBarSherlock/res/layout/abs__popup_menu_item_layout.xml new file mode 100755 index 000000000..d42425ad3 --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__popup_menu_item_layout.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__screen_action_bar.xml b/libs/ActionBarSherlock/res/layout/abs__screen_action_bar.xml new file mode 100755 index 000000000..1fb82fe9a --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__screen_action_bar.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__screen_action_bar_overlay.xml b/libs/ActionBarSherlock/res/layout/abs__screen_action_bar_overlay.xml new file mode 100755 index 000000000..0961ef561 --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__screen_action_bar_overlay.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__screen_simple.xml b/libs/ActionBarSherlock/res/layout/abs__screen_simple.xml new file mode 100755 index 000000000..33e2dea0d --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__screen_simple.xml @@ -0,0 +1,38 @@ + + + + + + + diff --git a/libs/ActionBarSherlock/res/layout/abs__screen_simple_overlay_action_mode.xml b/libs/ActionBarSherlock/res/layout/abs__screen_simple_overlay_action_mode.xml new file mode 100755 index 000000000..f8b9fb185 --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/abs__screen_simple_overlay_action_mode.xml @@ -0,0 +1,38 @@ + + + + + + + diff --git a/libs/ActionBarSherlock/res/layout/sherlock_spinner_dropdown_item.xml b/libs/ActionBarSherlock/res/layout/sherlock_spinner_dropdown_item.xml new file mode 100755 index 000000000..a6c6252d2 --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/sherlock_spinner_dropdown_item.xml @@ -0,0 +1,26 @@ + + + diff --git a/libs/ActionBarSherlock/res/layout/sherlock_spinner_item.xml b/libs/ActionBarSherlock/res/layout/sherlock_spinner_item.xml new file mode 100755 index 000000000..bea740178 --- /dev/null +++ b/libs/ActionBarSherlock/res/layout/sherlock_spinner_item.xml @@ -0,0 +1,26 @@ + + + diff --git a/libs/ActionBarSherlock/res/values-land/abs__dimens.xml b/libs/ActionBarSherlock/res/values-land/abs__dimens.xml new file mode 100755 index 000000000..502cc16a3 --- /dev/null +++ b/libs/ActionBarSherlock/res/values-land/abs__dimens.xml @@ -0,0 +1,33 @@ + + + + + 40dip + + 4dip + + 16dp + + 12dp + + -2dp + + 4dip + diff --git a/libs/ActionBarSherlock/res/values-large-hdpi-1024x600/abs__dimens.xml b/libs/ActionBarSherlock/res/values-large-hdpi-1024x600/abs__dimens.xml new file mode 100755 index 000000000..3312cfa7f --- /dev/null +++ b/libs/ActionBarSherlock/res/values-large-hdpi-1024x600/abs__dimens.xml @@ -0,0 +1,33 @@ + + + + + 48dip + + 8dip + + 18dp + + 14dp + + -3dp + + 5dip + diff --git a/libs/ActionBarSherlock/res/values-large-land-hdpi-1024x600/abs__dimens.xml b/libs/ActionBarSherlock/res/values-large-land-hdpi-1024x600/abs__dimens.xml new file mode 100755 index 000000000..502cc16a3 --- /dev/null +++ b/libs/ActionBarSherlock/res/values-large-land-hdpi-1024x600/abs__dimens.xml @@ -0,0 +1,33 @@ + + + + + 40dip + + 4dip + + 16dp + + 12dp + + -2dp + + 4dip + diff --git a/libs/ActionBarSherlock/res/values-large-land-mdpi-1024x600/abs__dimens.xml b/libs/ActionBarSherlock/res/values-large-land-mdpi-1024x600/abs__dimens.xml new file mode 100755 index 000000000..3312cfa7f --- /dev/null +++ b/libs/ActionBarSherlock/res/values-large-land-mdpi-1024x600/abs__dimens.xml @@ -0,0 +1,33 @@ + + + + + 48dip + + 8dip + + 18dp + + 14dp + + -3dp + + 5dip + diff --git a/libs/ActionBarSherlock/res/values-large-mdpi-1024x600/abs__dimens.xml b/libs/ActionBarSherlock/res/values-large-mdpi-1024x600/abs__dimens.xml new file mode 100755 index 000000000..35910333b --- /dev/null +++ b/libs/ActionBarSherlock/res/values-large-mdpi-1024x600/abs__dimens.xml @@ -0,0 +1,36 @@ + + + + + 56dip + + 4dip + + 18dp + + 14dp + + -3dp + + 9dip + + + 64dip + diff --git a/libs/ActionBarSherlock/res/values-large/abs__dimens.xml b/libs/ActionBarSherlock/res/values-large/abs__dimens.xml new file mode 100755 index 000000000..63b12f7f3 --- /dev/null +++ b/libs/ActionBarSherlock/res/values-large/abs__dimens.xml @@ -0,0 +1,29 @@ + + + + + 55% + + 80% + diff --git a/libs/ActionBarSherlock/res/values-sw600dp/abs__bools.xml b/libs/ActionBarSherlock/res/values-sw600dp/abs__bools.xml new file mode 100755 index 000000000..7a48e1542 --- /dev/null +++ b/libs/ActionBarSherlock/res/values-sw600dp/abs__bools.xml @@ -0,0 +1,19 @@ + + + + + false + diff --git a/libs/ActionBarSherlock/res/values-sw600dp/abs__dimens.xml b/libs/ActionBarSherlock/res/values-sw600dp/abs__dimens.xml new file mode 100755 index 000000000..f67853817 --- /dev/null +++ b/libs/ActionBarSherlock/res/values-sw600dp/abs__dimens.xml @@ -0,0 +1,38 @@ + + + + + 56dip + + 4dip + + 18dp + + 14dp + + -3dp + + 9dip + + 5 + + + 64dip + diff --git a/libs/ActionBarSherlock/res/values-v11/abs__themes.xml b/libs/ActionBarSherlock/res/values-v11/abs__themes.xml new file mode 100755 index 000000000..03473572c --- /dev/null +++ b/libs/ActionBarSherlock/res/values-v11/abs__themes.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/libs/ActionBarSherlock/res/values-v14/abs__styles.xml b/libs/ActionBarSherlock/res/values-v14/abs__styles.xml new file mode 100755 index 000000000..f2aa64d2d --- /dev/null +++ b/libs/ActionBarSherlock/res/values-v14/abs__styles.xml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/values-v14/abs__themes.xml b/libs/ActionBarSherlock/res/values-v14/abs__themes.xml new file mode 100755 index 000000000..ceb960737 --- /dev/null +++ b/libs/ActionBarSherlock/res/values-v14/abs__themes.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/values-w360dp/abs__dimens.xml b/libs/ActionBarSherlock/res/values-w360dp/abs__dimens.xml new file mode 100755 index 000000000..6f49d7e47 --- /dev/null +++ b/libs/ActionBarSherlock/res/values-w360dp/abs__dimens.xml @@ -0,0 +1,22 @@ + + + + 3 + diff --git a/libs/ActionBarSherlock/res/values-w480dp/abs__bools.xml b/libs/ActionBarSherlock/res/values-w480dp/abs__bools.xml new file mode 100755 index 000000000..3eaf4aee9 --- /dev/null +++ b/libs/ActionBarSherlock/res/values-w480dp/abs__bools.xml @@ -0,0 +1,22 @@ + + + + true + false + diff --git a/libs/ActionBarSherlock/res/values-w480dp/abs__config.xml b/libs/ActionBarSherlock/res/values-w480dp/abs__config.xml new file mode 100755 index 000000000..88357b0a7 --- /dev/null +++ b/libs/ActionBarSherlock/res/values-w480dp/abs__config.xml @@ -0,0 +1,29 @@ + + + + + + + + true + + diff --git a/libs/ActionBarSherlock/res/values-w500dp/abs__dimens.xml b/libs/ActionBarSherlock/res/values-w500dp/abs__dimens.xml new file mode 100755 index 000000000..2fd4deea2 --- /dev/null +++ b/libs/ActionBarSherlock/res/values-w500dp/abs__dimens.xml @@ -0,0 +1,22 @@ + + + + 4 + diff --git a/libs/ActionBarSherlock/res/values-w600dp/abs__dimens.xml b/libs/ActionBarSherlock/res/values-w600dp/abs__dimens.xml new file mode 100755 index 000000000..b085952d3 --- /dev/null +++ b/libs/ActionBarSherlock/res/values-w600dp/abs__dimens.xml @@ -0,0 +1,22 @@ + + + + 5 + diff --git a/libs/ActionBarSherlock/res/values-xlarge/abs__dimens.xml b/libs/ActionBarSherlock/res/values-xlarge/abs__dimens.xml new file mode 100755 index 000000000..bfc535de1 --- /dev/null +++ b/libs/ActionBarSherlock/res/values-xlarge/abs__dimens.xml @@ -0,0 +1,45 @@ + + + + + 56dip + + 4dip + + 18dp + + 14dp + + -3dp + + 9dip + + + 64dip + + + 45% + + 72% + diff --git a/libs/ActionBarSherlock/res/values/abs__attrs.xml b/libs/ActionBarSherlock/res/values/abs__attrs.xml new file mode 100755 index 000000000..81c347108 --- /dev/null +++ b/libs/ActionBarSherlock/res/values/abs__attrs.xml @@ -0,0 +1,380 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/values/abs__bools.xml b/libs/ActionBarSherlock/res/values/abs__bools.xml new file mode 100755 index 000000000..0b432448d --- /dev/null +++ b/libs/ActionBarSherlock/res/values/abs__bools.xml @@ -0,0 +1,22 @@ + + + + + false + true + true + + diff --git a/libs/ActionBarSherlock/res/values/abs__colors.xml b/libs/ActionBarSherlock/res/values/abs__colors.xml new file mode 100755 index 000000000..625c632ff --- /dev/null +++ b/libs/ActionBarSherlock/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/libs/ActionBarSherlock/res/values/abs__config.xml b/libs/ActionBarSherlock/res/values/abs__config.xml new file mode 100755 index 000000000..4c7b5d459 --- /dev/null +++ b/libs/ActionBarSherlock/res/values/abs__config.xml @@ -0,0 +1,43 @@ + + + + + + + + 320dp + + + false + + + true + + + false + + diff --git a/libs/ActionBarSherlock/res/values/abs__dimens.xml b/libs/ActionBarSherlock/res/values/abs__dimens.xml new file mode 100755 index 000000000..0a409756c --- /dev/null +++ b/libs/ActionBarSherlock/res/values/abs__dimens.xml @@ -0,0 +1,50 @@ + + + + + 48dip + + 8dip + + 18dp + + 14dp + + -3dp + + 5dip + + 2 + + + 56dip + + + 64dip + + + 65% + + 95% + diff --git a/libs/ActionBarSherlock/res/values/abs__ids.xml b/libs/ActionBarSherlock/res/values/abs__ids.xml new file mode 100755 index 000000000..f9f56045b --- /dev/null +++ b/libs/ActionBarSherlock/res/values/abs__ids.xml @@ -0,0 +1,26 @@ + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/values/abs__strings.xml b/libs/ActionBarSherlock/res/values/abs__strings.xml new file mode 100755 index 000000000..1e1c7022c --- /dev/null +++ b/libs/ActionBarSherlock/res/values/abs__strings.xml @@ -0,0 +1,42 @@ + + + + + Navigate home + + Navigate up + + More options + + + Done + + + See all... + + Select activity + + Share with... + + Choose an application + + Share with + + Share with %s + diff --git a/libs/ActionBarSherlock/res/values/abs__styles.xml b/libs/ActionBarSherlock/res/values/abs__styles.xml new file mode 100755 index 000000000..8cbd36484 --- /dev/null +++ b/libs/ActionBarSherlock/res/values/abs__styles.xml @@ -0,0 +1,384 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/res/values/abs__themes.xml b/libs/ActionBarSherlock/res/values/abs__themes.xml new file mode 100755 index 000000000..5300dedd6 --- /dev/null +++ b/libs/ActionBarSherlock/res/values/abs__themes.xml @@ -0,0 +1,226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/ActionBarSherlock/src/android/support/v4/app/_ActionBarSherlockTrojanHorse.java b/libs/ActionBarSherlock/src/android/support/v4/app/_ActionBarSherlockTrojanHorse.java new file mode 100755 index 000000000..3e3db62b7 --- /dev/null +++ b/libs/ActionBarSherlock/src/android/support/v4/app/_ActionBarSherlockTrojanHorse.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 _ActionBarSherlockTrojanHorse extends FragmentActivity implements OnCreatePanelMenuListener, OnPreparePanelListener, OnMenuItemSelectedListener { + private static final boolean DEBUG = false; + private static final String TAG = "_ActionBarSherlockTrojanHorse"; + + /** 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.mActive != 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.mActive != 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.mActive != 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/libs/ActionBarSherlock/src/com/actionbarsherlock/ActionBarSherlock.java b/libs/ActionBarSherlock/src/com/actionbarsherlock/ActionBarSherlock.java new file mode 100755 index 000000000..8340fb591 --- /dev/null +++ b/libs/ActionBarSherlock/src/com/actionbarsherlock/ActionBarSherlock.java @@ -0,0 +1,791 @@ +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; + +/** + *