From fb5e574e7953a646b95652632d0c4c87a47f4d5e Mon Sep 17 00:00:00 2001 From: Alexander Lucas Date: Thu, 10 Apr 2014 15:44:01 -0700 Subject: [PATCH] Updated browseable samples for april push Change-Id: Idd8cc6b7c43ab93f05f0a5d69d5379631721d185 --- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/menu/fragment_adapter_transition.xml | 4 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values-w820dp/dimens.xml | 6 - .../res/values/base-strings.xml | 2 +- .../AdapterTransition/res/values/dimens.xml | 6 - .../res/values/template-styles.xml | 2 +- .../AdapterTransitionFragment.java | 64 +++- .../MeatAdapter.java | 16 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/layout-sw600dp-land/activity_main.xml | 37 +++ .../res/layout-sw600dp/activity_main.xml | 37 +++ .../res/layout/activity_main.xml | 3 +- .../res/values-sw600dp/template-styles.xml | 12 +- .../res/values-v11/template-styles.xml | 22 ++ .../MainActivity.java | 4 + .../res/layout-sw600dp-land/activity_main.xml | 37 +++ .../res/layout-sw600dp/activity_main.xml | 37 +++ .../res/layout/activity_main.xml | 3 +- .../res/values-sw600dp/template-styles.xml | 12 +- .../res/values-v11/template-styles.xml | 22 ++ .../MainActivity.java | 4 + .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../BasicRenderScript/src/rs/saturation.rs | 36 ++ .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../BasicTransition/res/layout/scene3.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/layout/activity_main.xml | 2 +- .../BatchStepSensor/res/layout/card.xml | 4 +- .../res/layout/card_button_negative.xml | 2 +- .../res/layout/card_button_neutral.xml | 2 +- .../res/layout/card_button_positive.xml | 2 +- .../BatchStepSensor/res/layout/cardstream.xml | 6 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../BatchStepSensorFragment.java | 4 + .../MainActivity.java | 6 + .../{ => cardstream}/Card.java | 4 +- .../{ => cardstream}/CardActionButton.java | 2 +- .../{ => cardstream}/CardLayout.java | 2 +- .../{ => cardstream}/CardStream.java | 2 +- .../{ => cardstream}/CardStreamAnimator.java | 2 +- .../{ => cardstream}/CardStreamFragment.java | 3 +- .../CardStreamLinearLayout.java | 3 +- .../{ => cardstream}/CardStreamState.java | 4 +- .../DefaultCardStreamAnimator.java | 2 +- .../{ => cardstream}/OnCardClickListener.java | 2 +- .../StreamRetentionFragment.java | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../CardReader/res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../CustomTransition/AndroidManifest.xml | 42 +++ samples/browseable/CustomTransition/_index.jd | 12 + .../res/drawable-hdpi/ic_launcher.png | Bin 0 -> 4504 bytes .../res/drawable-hdpi/tile.9.png | Bin 0 -> 196 bytes .../res/drawable-mdpi/ic_launcher.png | Bin 0 -> 2666 bytes .../res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 6317 bytes .../res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 11539 bytes .../res/layout-w720dp/activity_main.xml | 73 ++++ .../res/layout/activity_main.xml | 65 ++++ .../res/layout/fragment_custom_transition.xml | 36 ++ .../CustomTransition/res/layout/scene1.xml | 47 +++ .../CustomTransition/res/layout/scene2.xml | 47 +++ .../CustomTransition/res/layout/scene3.xml | 47 +++ .../CustomTransition/res/menu/main.xml | 21 ++ .../res/values-sw600dp/template-dimens.xml | 24 ++ .../res/values-sw600dp/template-styles.xml | 25 ++ .../res/values-v11/template-styles.xml | 22 ++ .../res/values/base-strings.xml | 31 ++ .../CustomTransition/res/values/strings.xml | 19 ++ .../res/values/template-dimens.xml | 32 ++ .../res/values/template-styles.xml | 42 +++ .../activities/SampleActivityBase.java | 52 +++ .../logger/Log.java | 236 +++++++++++++ .../logger/LogFragment.java | 109 ++++++ .../logger/LogNode.java | 39 +++ .../logger/LogView.java | 145 ++++++++ .../logger/LogWrapper.java | 75 +++++ .../logger/MessageOnlyLogFilter.java | 60 ++++ .../ChangeColor.java | 119 +++++++ .../CustomTransitionFragment.java | 95 ++++++ .../MainActivity.java | 110 ++++++ .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../FragmentTransition/AndroidManifest.xml | 40 +++ .../browseable/FragmentTransition/_index.jd | 12 + .../res/drawable-hdpi/ic_launcher.png | Bin 0 -> 4427 bytes .../res/drawable-hdpi/tile.9.png | Bin 0 -> 196 bytes .../res/drawable-mdpi/ic_launcher.png | Bin 0 -> 2629 bytes .../res/drawable-nodpi/p1.jpg | Bin 0 -> 78391 bytes .../res/drawable-nodpi/p10.jpg | Bin 0 -> 47367 bytes .../res/drawable-nodpi/p11.jpg | Bin 0 -> 47902 bytes .../res/drawable-nodpi/p2.jpg | Bin 0 -> 44528 bytes .../res/drawable-nodpi/p3.jpg | Bin 0 -> 48710 bytes .../res/drawable-nodpi/p4.jpg | Bin 0 -> 80786 bytes .../res/drawable-nodpi/p5.jpg | Bin 0 -> 42804 bytes .../res/drawable-nodpi/p6.jpg | Bin 0 -> 95262 bytes .../res/drawable-nodpi/p7.jpg | Bin 0 -> 58397 bytes .../res/drawable-nodpi/p8.jpg | Bin 0 -> 55444 bytes .../res/drawable-nodpi/p9.jpg | Bin 0 -> 58610 bytes .../res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 6221 bytes .../res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 11345 bytes .../res/layout-w720dp/activity_main.xml | 73 ++++ .../res/layout/activity_main.xml | 65 ++++ .../res/layout/fragment_detail.xml | 21 ++ .../res/layout/fragment_detail_content.xml | 66 ++++ .../layout/fragment_fragment_transition.xml | 31 ++ .../res/layout/item_meat_grid.xml | 58 ++++ .../FragmentTransition/res/menu/main.xml | 21 ++ .../res/values-sw600dp/template-dimens.xml | 24 ++ .../res/values-sw600dp/template-styles.xml | 25 ++ .../res/values-v11/template-styles.xml | 22 ++ .../res/values/base-strings.xml | 31 ++ .../FragmentTransition/res/values/strings.xml | 19 ++ .../res/values/template-dimens.xml | 32 ++ .../res/values/template-styles.xml | 42 +++ .../activities/SampleActivityBase.java | 52 +++ .../logger/Log.java | 236 +++++++++++++ .../logger/LogFragment.java | 109 ++++++ .../logger/LogNode.java | 39 +++ .../logger/LogView.java | 145 ++++++++ .../logger/LogWrapper.java | 75 +++++ .../logger/MessageOnlyLogFilter.java | 60 ++++ .../view/SlidingTabLayout.java | 314 ++++++++++++++++++ .../view/SlidingTabStrip.java | 208 ++++++++++++ .../DetailFragment.java | 158 +++++++++ .../FragmentTransitionFragment.java | 83 +++++ .../MainActivity.java | 110 ++++++ .../Meat.java | 46 +++ .../MeatAdapter.java | 81 +++++ .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/layout-sw600dp-land/activity_main.xml | 37 +++ .../res/layout-sw600dp/activity_main.xml | 37 +++ .../res/layout/activity_main.xml | 3 +- .../res/values-sw600dp/template-styles.xml | 12 +- .../res/values-v11/template-styles.xml | 22 ++ .../MainActivity.java | 4 + .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/layout-sw600dp-land/activity_main.xml | 37 +++ .../res/layout-sw600dp/activity_main.xml | 37 +++ .../res/layout/activity_main.xml | 3 +- .../res/values-sw600dp/template-styles.xml | 12 +- .../res/values-v11/template-styles.xml | 22 ++ .../MainActivity.java | 4 + .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/layout-sw600dp-land/activity_main.xml | 37 +++ .../res/layout-sw600dp/activity_main.xml | 37 +++ .../res/layout/activity_main.xml | 3 +- .../res/values-sw600dp/template-styles.xml | 12 +- .../res/values-v11/template-styles.xml | 22 ++ .../MainActivity.java | 4 + .../res/layout-sw600dp-land/activity_main.xml | 37 +++ .../res/layout-sw600dp/activity_main.xml | 37 +++ .../res/layout/activity_main.xml | 3 +- .../res/values-sw600dp/template-styles.xml | 12 +- .../res/values-v11/template-styles.xml | 22 ++ .../MainActivity.java | 4 + .../AndroidManifest.xml | 42 +++ .../SwipeRefreshLayoutBasic/_index.jd | 14 + .../res/drawable-hdpi/ic_launcher.png | Bin 0 -> 4409 bytes .../res/drawable-hdpi/tile.9.png | Bin 0 -> 196 bytes .../res/drawable-mdpi/ic_launcher.png | Bin 0 -> 2690 bytes .../res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 6312 bytes .../res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 11364 bytes .../res/layout-w720dp/activity_main.xml | 73 ++++ .../res/layout/activity_main.xml | 65 ++++ .../res/layout/fragment_sample.xml | 29 ++ .../SwipeRefreshLayoutBasic/res/menu/main.xml | 21 ++ .../res/menu/main_menu.xml | 25 ++ .../res/values-sw600dp/template-dimens.xml | 24 ++ .../res/values-sw600dp/template-styles.xml | 25 ++ .../res/values-v11/template-styles.xml | 22 ++ .../res/values/base-strings.xml | 33 ++ .../res/values/colors.xml | 9 + .../res/values/strings.xml | 19 ++ .../res/values/template-dimens.xml | 32 ++ .../res/values/template-styles.xml | 42 +++ .../activities/SampleActivityBase.java | 52 +++ .../dummydata/Cheeses.java | 186 +++++++++++ .../logger/Log.java | 236 +++++++++++++ .../logger/LogFragment.java | 109 ++++++ .../logger/LogNode.java | 39 +++ .../logger/LogView.java | 145 ++++++++ .../logger/LogWrapper.java | 75 +++++ .../logger/MessageOnlyLogFilter.java | 60 ++++ .../view/SlidingTabLayout.java | 314 ++++++++++++++++++ .../view/SlidingTabStrip.java | 208 ++++++++++++ .../MainActivity.java | 110 ++++++ .../SwipeRefreshLayoutBasicFragment.java | 234 +++++++++++++ .../AndroidManifest.xml | 42 +++ .../SwipeRefreshListFragment/_index.jd | 15 + .../res/drawable-hdpi/ic_launcher.png | Bin 0 -> 4503 bytes .../res/drawable-hdpi/tile.9.png | Bin 0 -> 196 bytes .../res/drawable-mdpi/ic_launcher.png | Bin 0 -> 2738 bytes .../res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 6467 bytes .../res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 11559 bytes .../res/layout-w720dp/activity_main.xml | 73 ++++ .../res/layout/activity_main.xml | 65 ++++ .../res/menu/main.xml | 21 ++ .../res/menu/main_menu.xml | 59 ++++ .../res/values-sw600dp/template-dimens.xml | 24 ++ .../res/values-sw600dp/template-styles.xml | 25 ++ .../res/values-v11/template-styles.xml | 22 ++ .../res/values/base-strings.xml | 34 ++ .../res/values/colors.xml | 44 +++ .../res/values/strings.xml | 19 ++ .../res/values/template-dimens.xml | 32 ++ .../res/values/template-styles.xml | 42 +++ .../activities/SampleActivityBase.java | 52 +++ .../dummydata/Cheeses.java | 186 +++++++++++ .../logger/Log.java | 236 +++++++++++++ .../logger/LogFragment.java | 109 ++++++ .../logger/LogNode.java | 39 +++ .../logger/LogView.java | 145 ++++++++ .../logger/LogWrapper.java | 75 +++++ .../logger/MessageOnlyLogFilter.java | 60 ++++ .../view/SlidingTabLayout.java | 314 ++++++++++++++++++ .../view/SlidingTabStrip.java | 208 ++++++++++++ .../MainActivity.java | 110 ++++++ .../SwipeRefreshListFragment.java | 166 +++++++++ .../SwipeRefreshListFragmentFragment.java | 232 +++++++++++++ .../AndroidManifest.xml | 42 +++ .../SwipeRefreshMultipleViews/_index.jd | 15 + .../res/drawable-hdpi/ic_launcher.png | Bin 0 -> 4341 bytes .../res/drawable-hdpi/tile.9.png | Bin 0 -> 196 bytes .../res/drawable-mdpi/ic_launcher.png | Bin 0 -> 2612 bytes .../res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 6129 bytes .../res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 11153 bytes .../res/layout-w720dp/activity_main.xml | 73 ++++ .../res/layout/activity_main.xml | 65 ++++ .../res/layout/fragment_sample.xml | 43 +++ .../res/menu/main.xml | 21 ++ .../res/menu/main_menu.xml | 30 ++ .../res/values-sw600dp/template-dimens.xml | 24 ++ .../res/values-sw600dp/template-styles.xml | 25 ++ .../res/values-v11/template-styles.xml | 22 ++ .../res/values/base-strings.xml | 34 ++ .../res/values/color.xml | 25 ++ .../res/values/strings.xml | 19 ++ .../res/values/template-dimens.xml | 32 ++ .../res/values/template-styles.xml | 42 +++ .../activities/SampleActivityBase.java | 52 +++ .../dummydata/Cheeses.java | 186 +++++++++++ .../logger/Log.java | 236 +++++++++++++ .../logger/LogFragment.java | 109 ++++++ .../logger/LogNode.java | 39 +++ .../logger/LogView.java | 145 ++++++++ .../logger/LogWrapper.java | 75 +++++ .../logger/MessageOnlyLogFilter.java | 60 ++++ .../view/SlidingTabLayout.java | 314 ++++++++++++++++++ .../view/SlidingTabStrip.java | 208 ++++++++++++ .../MainActivity.java | 110 ++++++ .../MultiSwipeRefreshLayout.java | 107 ++++++ .../SwipeRefreshMultipleViewsFragment.java | 256 ++++++++++++++ .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- .../res/values-v11/template-styles.xml | 22 ++ .../res/values/template-styles.xml | 2 +- 309 files changed, 12685 insertions(+), 105 deletions(-) create mode 100644 samples/browseable/ActionBarCompat-Basic/res/values-v11/template-styles.xml create mode 100644 samples/browseable/ActionBarCompat-ListPopupMenu/res/values-v11/template-styles.xml create mode 100644 samples/browseable/ActionBarCompat-ShareActionProvider/res/values-v11/template-styles.xml create mode 100644 samples/browseable/ActionBarCompat-Styled/res/values-v11/template-styles.xml create mode 100644 samples/browseable/ActivityInstrumentation/res/values-v11/template-styles.xml create mode 100644 samples/browseable/AdapterTransition/res/values-v11/template-styles.xml delete mode 100644 samples/browseable/AdapterTransition/res/values-w820dp/dimens.xml delete mode 100644 samples/browseable/AdapterTransition/res/values/dimens.xml create mode 100644 samples/browseable/AdvancedImmersiveMode/res/values-v11/template-styles.xml create mode 100644 samples/browseable/AppRestrictions/res/values-v11/template-styles.xml create mode 100644 samples/browseable/BasicAccessibility/res/values-v11/template-styles.xml create mode 100644 samples/browseable/BasicAndroidKeyStore/res/values-v11/template-styles.xml create mode 100644 samples/browseable/BasicContactables/res/values-v11/template-styles.xml create mode 100755 samples/browseable/BasicGestureDetect/res/layout-sw600dp-land/activity_main.xml create mode 100755 samples/browseable/BasicGestureDetect/res/layout-sw600dp/activity_main.xml create mode 100644 samples/browseable/BasicGestureDetect/res/values-v11/template-styles.xml create mode 100755 samples/browseable/BasicImmersiveMode/res/layout-sw600dp-land/activity_main.xml create mode 100755 samples/browseable/BasicImmersiveMode/res/layout-sw600dp/activity_main.xml create mode 100644 samples/browseable/BasicImmersiveMode/res/values-v11/template-styles.xml create mode 100644 samples/browseable/BasicMediaDecoder/res/values-v11/template-styles.xml create mode 100644 samples/browseable/BasicMediaRouter/res/values-v11/template-styles.xml create mode 100644 samples/browseable/BasicMultitouch/res/values-v11/template-styles.xml create mode 100644 samples/browseable/BasicNetworking/res/values-v11/template-styles.xml create mode 100644 samples/browseable/BasicNotifications/res/values-v11/template-styles.xml create mode 100644 samples/browseable/BasicRenderScript/res/values-v11/template-styles.xml create mode 100644 samples/browseable/BasicRenderScript/src/rs/saturation.rs create mode 100644 samples/browseable/BasicSyncAdapter/res/values-v11/template-styles.xml create mode 100644 samples/browseable/BasicTransition/res/values-v11/template-styles.xml create mode 100644 samples/browseable/BatchStepSensor/res/values-v11/template-styles.xml rename samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/{ => cardstream}/Card.java (99%) rename samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/{ => cardstream}/CardActionButton.java (97%) rename samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/{ => cardstream}/CardLayout.java (97%) rename samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/{ => cardstream}/CardStream.java (92%) rename samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/{ => cardstream}/CardStreamAnimator.java (98%) rename samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/{ => cardstream}/CardStreamFragment.java (98%) rename samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/{ => cardstream}/CardStreamLinearLayout.java (99%) rename samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/{ => cardstream}/CardStreamState.java (92%) rename samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/{ => cardstream}/DefaultCardStreamAnimator.java (98%) rename samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/{ => cardstream}/OnCardClickListener.java (92%) rename samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/{ => cardstream}/StreamRetentionFragment.java (95%) create mode 100644 samples/browseable/BluetoothLeGatt/res/values-v11/template-styles.xml create mode 100644 samples/browseable/BorderlessButtons/res/values-v11/template-styles.xml create mode 100644 samples/browseable/CardEmulation/res/values-v11/template-styles.xml create mode 100644 samples/browseable/CardReader/res/values-v11/template-styles.xml create mode 100644 samples/browseable/CustomChoiceList/res/values-v11/template-styles.xml create mode 100644 samples/browseable/CustomNotifications/res/values-v11/template-styles.xml create mode 100644 samples/browseable/CustomTransition/AndroidManifest.xml create mode 100644 samples/browseable/CustomTransition/_index.jd create mode 100644 samples/browseable/CustomTransition/res/drawable-hdpi/ic_launcher.png create mode 100644 samples/browseable/CustomTransition/res/drawable-hdpi/tile.9.png create mode 100644 samples/browseable/CustomTransition/res/drawable-mdpi/ic_launcher.png create mode 100644 samples/browseable/CustomTransition/res/drawable-xhdpi/ic_launcher.png create mode 100644 samples/browseable/CustomTransition/res/drawable-xxhdpi/ic_launcher.png create mode 100755 samples/browseable/CustomTransition/res/layout-w720dp/activity_main.xml create mode 100755 samples/browseable/CustomTransition/res/layout/activity_main.xml create mode 100644 samples/browseable/CustomTransition/res/layout/fragment_custom_transition.xml create mode 100644 samples/browseable/CustomTransition/res/layout/scene1.xml create mode 100644 samples/browseable/CustomTransition/res/layout/scene2.xml create mode 100644 samples/browseable/CustomTransition/res/layout/scene3.xml create mode 100644 samples/browseable/CustomTransition/res/menu/main.xml create mode 100644 samples/browseable/CustomTransition/res/values-sw600dp/template-dimens.xml create mode 100644 samples/browseable/CustomTransition/res/values-sw600dp/template-styles.xml create mode 100644 samples/browseable/CustomTransition/res/values-v11/template-styles.xml create mode 100644 samples/browseable/CustomTransition/res/values/base-strings.xml create mode 100755 samples/browseable/CustomTransition/res/values/strings.xml create mode 100644 samples/browseable/CustomTransition/res/values/template-dimens.xml create mode 100644 samples/browseable/CustomTransition/res/values/template-styles.xml create mode 100644 samples/browseable/CustomTransition/src/com.example.android.common/activities/SampleActivityBase.java create mode 100644 samples/browseable/CustomTransition/src/com.example.android.common/logger/Log.java create mode 100644 samples/browseable/CustomTransition/src/com.example.android.common/logger/LogFragment.java create mode 100644 samples/browseable/CustomTransition/src/com.example.android.common/logger/LogNode.java create mode 100644 samples/browseable/CustomTransition/src/com.example.android.common/logger/LogView.java create mode 100644 samples/browseable/CustomTransition/src/com.example.android.common/logger/LogWrapper.java create mode 100644 samples/browseable/CustomTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java create mode 100644 samples/browseable/CustomTransition/src/com.example.android.customtransition/ChangeColor.java create mode 100644 samples/browseable/CustomTransition/src/com.example.android.customtransition/CustomTransitionFragment.java create mode 100644 samples/browseable/CustomTransition/src/com.example.android.customtransition/MainActivity.java create mode 100644 samples/browseable/DisplayingBitmaps/res/values-v11/template-styles.xml create mode 100644 samples/browseable/DoneBar/res/values-v11/template-styles.xml create mode 100644 samples/browseable/FragmentTransition/AndroidManifest.xml create mode 100644 samples/browseable/FragmentTransition/_index.jd create mode 100644 samples/browseable/FragmentTransition/res/drawable-hdpi/ic_launcher.png create mode 100644 samples/browseable/FragmentTransition/res/drawable-hdpi/tile.9.png create mode 100644 samples/browseable/FragmentTransition/res/drawable-mdpi/ic_launcher.png create mode 100644 samples/browseable/FragmentTransition/res/drawable-nodpi/p1.jpg create mode 100644 samples/browseable/FragmentTransition/res/drawable-nodpi/p10.jpg create mode 100644 samples/browseable/FragmentTransition/res/drawable-nodpi/p11.jpg create mode 100644 samples/browseable/FragmentTransition/res/drawable-nodpi/p2.jpg create mode 100644 samples/browseable/FragmentTransition/res/drawable-nodpi/p3.jpg create mode 100644 samples/browseable/FragmentTransition/res/drawable-nodpi/p4.jpg create mode 100644 samples/browseable/FragmentTransition/res/drawable-nodpi/p5.jpg create mode 100644 samples/browseable/FragmentTransition/res/drawable-nodpi/p6.jpg create mode 100644 samples/browseable/FragmentTransition/res/drawable-nodpi/p7.jpg create mode 100644 samples/browseable/FragmentTransition/res/drawable-nodpi/p8.jpg create mode 100644 samples/browseable/FragmentTransition/res/drawable-nodpi/p9.jpg create mode 100644 samples/browseable/FragmentTransition/res/drawable-xhdpi/ic_launcher.png create mode 100644 samples/browseable/FragmentTransition/res/drawable-xxhdpi/ic_launcher.png create mode 100755 samples/browseable/FragmentTransition/res/layout-w720dp/activity_main.xml create mode 100755 samples/browseable/FragmentTransition/res/layout/activity_main.xml create mode 100644 samples/browseable/FragmentTransition/res/layout/fragment_detail.xml create mode 100644 samples/browseable/FragmentTransition/res/layout/fragment_detail_content.xml create mode 100644 samples/browseable/FragmentTransition/res/layout/fragment_fragment_transition.xml create mode 100644 samples/browseable/FragmentTransition/res/layout/item_meat_grid.xml create mode 100644 samples/browseable/FragmentTransition/res/menu/main.xml create mode 100644 samples/browseable/FragmentTransition/res/values-sw600dp/template-dimens.xml create mode 100644 samples/browseable/FragmentTransition/res/values-sw600dp/template-styles.xml create mode 100644 samples/browseable/FragmentTransition/res/values-v11/template-styles.xml create mode 100644 samples/browseable/FragmentTransition/res/values/base-strings.xml create mode 100755 samples/browseable/FragmentTransition/res/values/strings.xml create mode 100644 samples/browseable/FragmentTransition/res/values/template-dimens.xml create mode 100644 samples/browseable/FragmentTransition/res/values/template-styles.xml create mode 100644 samples/browseable/FragmentTransition/src/com.example.android.common/activities/SampleActivityBase.java create mode 100644 samples/browseable/FragmentTransition/src/com.example.android.common/logger/Log.java create mode 100644 samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogFragment.java create mode 100644 samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogNode.java create mode 100644 samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogView.java create mode 100644 samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogWrapper.java create mode 100644 samples/browseable/FragmentTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java create mode 100644 samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabLayout.java create mode 100644 samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabStrip.java create mode 100644 samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/DetailFragment.java create mode 100644 samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/FragmentTransitionFragment.java create mode 100644 samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MainActivity.java create mode 100644 samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/Meat.java create mode 100644 samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MeatAdapter.java create mode 100644 samples/browseable/HorizontalPaging/res/values-v11/template-styles.xml create mode 100755 samples/browseable/ImmersiveMode/res/layout-sw600dp-land/activity_main.xml create mode 100755 samples/browseable/ImmersiveMode/res/layout-sw600dp/activity_main.xml create mode 100644 samples/browseable/ImmersiveMode/res/values-v11/template-styles.xml create mode 100644 samples/browseable/MediaRecorder/res/values-v11/template-styles.xml create mode 100644 samples/browseable/MediaRouter/res/values-v11/template-styles.xml create mode 100644 samples/browseable/NetworkConnect/res/values-v11/template-styles.xml create mode 100644 samples/browseable/RenderScriptIntrinsic/res/values-v11/template-styles.xml create mode 100755 samples/browseable/RepeatingAlarm/res/layout-sw600dp-land/activity_main.xml create mode 100755 samples/browseable/RepeatingAlarm/res/layout-sw600dp/activity_main.xml create mode 100644 samples/browseable/RepeatingAlarm/res/values-v11/template-styles.xml create mode 100644 samples/browseable/SlidingTabsBasic/res/values-v11/template-styles.xml create mode 100644 samples/browseable/SlidingTabsColors/res/values-v11/template-styles.xml create mode 100755 samples/browseable/StorageClient/res/layout-sw600dp-land/activity_main.xml create mode 100755 samples/browseable/StorageClient/res/layout-sw600dp/activity_main.xml create mode 100644 samples/browseable/StorageClient/res/values-v11/template-styles.xml create mode 100755 samples/browseable/StorageProvider/res/layout-sw600dp-land/activity_main.xml create mode 100755 samples/browseable/StorageProvider/res/layout-sw600dp/activity_main.xml create mode 100644 samples/browseable/StorageProvider/res/values-v11/template-styles.xml create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/AndroidManifest.xml create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/_index.jd create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/res/drawable-hdpi/ic_launcher.png create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/res/drawable-hdpi/tile.9.png create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/res/drawable-mdpi/ic_launcher.png create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/res/drawable-xhdpi/ic_launcher.png create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/res/drawable-xxhdpi/ic_launcher.png create mode 100755 samples/browseable/SwipeRefreshLayoutBasic/res/layout-w720dp/activity_main.xml create mode 100755 samples/browseable/SwipeRefreshLayoutBasic/res/layout/activity_main.xml create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/res/layout/fragment_sample.xml create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/res/menu/main.xml create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/res/menu/main_menu.xml create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/res/values-sw600dp/template-dimens.xml create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/res/values-sw600dp/template-styles.xml create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/res/values-v11/template-styles.xml create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/res/values/base-strings.xml create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/res/values/colors.xml create mode 100755 samples/browseable/SwipeRefreshLayoutBasic/res/values/strings.xml create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/res/values/template-dimens.xml create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/res/values/template-styles.xml create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/activities/SampleActivityBase.java create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/dummydata/Cheeses.java create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/Log.java create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogFragment.java create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogNode.java create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogView.java create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogWrapper.java create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/MessageOnlyLogFilter.java create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/view/SlidingTabLayout.java create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/view/SlidingTabStrip.java create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.swiperefreshlayoutbasic/MainActivity.java create mode 100644 samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.swiperefreshlayoutbasic/SwipeRefreshLayoutBasicFragment.java create mode 100644 samples/browseable/SwipeRefreshListFragment/AndroidManifest.xml create mode 100644 samples/browseable/SwipeRefreshListFragment/_index.jd create mode 100644 samples/browseable/SwipeRefreshListFragment/res/drawable-hdpi/ic_launcher.png create mode 100644 samples/browseable/SwipeRefreshListFragment/res/drawable-hdpi/tile.9.png create mode 100644 samples/browseable/SwipeRefreshListFragment/res/drawable-mdpi/ic_launcher.png create mode 100644 samples/browseable/SwipeRefreshListFragment/res/drawable-xhdpi/ic_launcher.png create mode 100644 samples/browseable/SwipeRefreshListFragment/res/drawable-xxhdpi/ic_launcher.png create mode 100755 samples/browseable/SwipeRefreshListFragment/res/layout-w720dp/activity_main.xml create mode 100755 samples/browseable/SwipeRefreshListFragment/res/layout/activity_main.xml create mode 100644 samples/browseable/SwipeRefreshListFragment/res/menu/main.xml create mode 100644 samples/browseable/SwipeRefreshListFragment/res/menu/main_menu.xml create mode 100644 samples/browseable/SwipeRefreshListFragment/res/values-sw600dp/template-dimens.xml create mode 100644 samples/browseable/SwipeRefreshListFragment/res/values-sw600dp/template-styles.xml create mode 100644 samples/browseable/SwipeRefreshListFragment/res/values-v11/template-styles.xml create mode 100644 samples/browseable/SwipeRefreshListFragment/res/values/base-strings.xml create mode 100644 samples/browseable/SwipeRefreshListFragment/res/values/colors.xml create mode 100755 samples/browseable/SwipeRefreshListFragment/res/values/strings.xml create mode 100644 samples/browseable/SwipeRefreshListFragment/res/values/template-dimens.xml create mode 100644 samples/browseable/SwipeRefreshListFragment/res/values/template-styles.xml create mode 100644 samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/activities/SampleActivityBase.java create mode 100644 samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/dummydata/Cheeses.java create mode 100644 samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/Log.java create mode 100644 samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogFragment.java create mode 100644 samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogNode.java create mode 100644 samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogView.java create mode 100644 samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogWrapper.java create mode 100644 samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/MessageOnlyLogFilter.java create mode 100644 samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/view/SlidingTabLayout.java create mode 100644 samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/view/SlidingTabStrip.java create mode 100644 samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/MainActivity.java create mode 100644 samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/SwipeRefreshListFragment.java create mode 100644 samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/SwipeRefreshListFragmentFragment.java create mode 100644 samples/browseable/SwipeRefreshMultipleViews/AndroidManifest.xml create mode 100644 samples/browseable/SwipeRefreshMultipleViews/_index.jd create mode 100644 samples/browseable/SwipeRefreshMultipleViews/res/drawable-hdpi/ic_launcher.png create mode 100644 samples/browseable/SwipeRefreshMultipleViews/res/drawable-hdpi/tile.9.png create mode 100644 samples/browseable/SwipeRefreshMultipleViews/res/drawable-mdpi/ic_launcher.png create mode 100644 samples/browseable/SwipeRefreshMultipleViews/res/drawable-xhdpi/ic_launcher.png create mode 100644 samples/browseable/SwipeRefreshMultipleViews/res/drawable-xxhdpi/ic_launcher.png create mode 100755 samples/browseable/SwipeRefreshMultipleViews/res/layout-w720dp/activity_main.xml create mode 100755 samples/browseable/SwipeRefreshMultipleViews/res/layout/activity_main.xml create mode 100644 samples/browseable/SwipeRefreshMultipleViews/res/layout/fragment_sample.xml create mode 100644 samples/browseable/SwipeRefreshMultipleViews/res/menu/main.xml create mode 100644 samples/browseable/SwipeRefreshMultipleViews/res/menu/main_menu.xml create mode 100644 samples/browseable/SwipeRefreshMultipleViews/res/values-sw600dp/template-dimens.xml create mode 100644 samples/browseable/SwipeRefreshMultipleViews/res/values-sw600dp/template-styles.xml create mode 100644 samples/browseable/SwipeRefreshMultipleViews/res/values-v11/template-styles.xml create mode 100644 samples/browseable/SwipeRefreshMultipleViews/res/values/base-strings.xml create mode 100644 samples/browseable/SwipeRefreshMultipleViews/res/values/color.xml create mode 100755 samples/browseable/SwipeRefreshMultipleViews/res/values/strings.xml create mode 100644 samples/browseable/SwipeRefreshMultipleViews/res/values/template-dimens.xml create mode 100644 samples/browseable/SwipeRefreshMultipleViews/res/values/template-styles.xml create mode 100644 samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/activities/SampleActivityBase.java create mode 100644 samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/dummydata/Cheeses.java create mode 100644 samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/Log.java create mode 100644 samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogFragment.java create mode 100644 samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogNode.java create mode 100644 samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogView.java create mode 100644 samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogWrapper.java create mode 100644 samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/MessageOnlyLogFilter.java create mode 100644 samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/view/SlidingTabLayout.java create mode 100644 samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/view/SlidingTabStrip.java create mode 100644 samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/MainActivity.java create mode 100644 samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/MultiSwipeRefreshLayout.java create mode 100644 samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/SwipeRefreshMultipleViewsFragment.java create mode 100644 samples/browseable/TextLinkify/res/values-v11/template-styles.xml create mode 100644 samples/browseable/TextSwitcher/res/values-v11/template-styles.xml diff --git a/samples/browseable/ActionBarCompat-Basic/res/values-v11/template-styles.xml b/samples/browseable/ActionBarCompat-Basic/res/values-v11/template-styles.xml new file mode 100644 index 000000000..8c1ea66f2 --- /dev/null +++ b/samples/browseable/ActionBarCompat-Basic/res/values-v11/template-styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/samples/browseable/BasicGestureDetect/res/values-v11/template-styles.xml b/samples/browseable/BasicGestureDetect/res/values-v11/template-styles.xml new file mode 100644 index 000000000..8c1ea66f2 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/res/values-v11/template-styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/samples/browseable/BasicImmersiveMode/res/values-v11/template-styles.xml b/samples/browseable/BasicImmersiveMode/res/values-v11/template-styles.xml new file mode 100644 index 000000000..8c1ea66f2 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/res/values-v11/template-styles.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/samples/browseable/CustomTransition/res/values-v11/template-styles.xml b/samples/browseable/CustomTransition/res/values-v11/template-styles.xml new file mode 100644 index 000000000..8c1ea66f2 --- /dev/null +++ b/samples/browseable/CustomTransition/res/values-v11/template-styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/samples/browseable/CustomTransition/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/CustomTransition/src/com.example.android.common/activities/SampleActivityBase.java new file mode 100644 index 000000000..3228927b7 --- /dev/null +++ b/samples/browseable/CustomTransition/src/com.example.android.common/activities/SampleActivityBase.java @@ -0,0 +1,52 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.example.android.common.activities; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogWrapper; + +/** + * Base launcher activity, to handle most of the common plumbing for samples. + */ +public class SampleActivityBase extends FragmentActivity { + + public static final String TAG = "SampleActivityBase"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + initializeLogging(); + } + + /** Set up targets to receive log data */ + public void initializeLogging() { + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/CustomTransition/src/com.example.android.common/logger/Log.java b/samples/browseable/CustomTransition/src/com.example.android.common/logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/CustomTransition/src/com.example.android.common/logger/Log.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + *

When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.

+ */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/CustomTransition/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/CustomTransition/src/com.example.android.common/logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/CustomTransition/src/com.example.android.common/logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +} \ No newline at end of file diff --git a/samples/browseable/CustomTransition/src/com.example.android.common/logger/LogNode.java b/samples/browseable/CustomTransition/src/com.example.android.common/logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/CustomTransition/src/com.example.android.common/logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.example.android.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/CustomTransition/src/com.example.android.common/logger/LogView.java b/samples/browseable/CustomTransition/src/com.example.android.common/logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/CustomTransition/src/com.example.android.common/logger/LogView.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/CustomTransition/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/CustomTransition/src/com.example.android.common/logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/CustomTransition/src/com.example.android.common/logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.example.android.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/CustomTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/CustomTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/CustomTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/CustomTransition/src/com.example.android.customtransition/ChangeColor.java b/samples/browseable/CustomTransition/src/com.example.android.customtransition/ChangeColor.java new file mode 100644 index 000000000..d36cfb40c --- /dev/null +++ b/samples/browseable/CustomTransition/src/com.example.android.customtransition/ChangeColor.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.customtransition; + +import android.animation.Animator; +import android.animation.ArgbEvaluator; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.animation.ValueAnimator; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.TransitionDrawable; +import android.transition.ChangeBounds; +import android.transition.Transition; +import android.transition.TransitionValues; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewPropertyAnimator; + +public class ChangeColor extends Transition { + + /** Key to store a color value in TransitionValues object */ + private static final String PROPNAME_BACKGROUND = "customtransition:change_color:background"; + + // BEGIN_INCLUDE (capture_values) + /** + * Convenience method: Add the background Drawable property value + * to the TransitionsValues.value Map for a target. + */ + private void captureValues(TransitionValues values) { + // Capture the property values of views for later use + values.values.put(PROPNAME_BACKGROUND, values.view.getBackground()); + } + + @Override + public void captureStartValues(TransitionValues transitionValues) { + captureValues(transitionValues); + } + + // Capture the value of the background drawable property for a target in the ending Scene. + @Override + public void captureEndValues(TransitionValues transitionValues) { + captureValues(transitionValues); + } + // END_INCLUDE (capture_values) + + // BEGIN_INCLUDE (create_animator) + // Create an animation for each target that is in both the starting and ending Scene. For each + // pair of targets, if their background property value is a color (rather than a graphic), + // create a ValueAnimator based on an ArgbEvaluator that interpolates between the starting and + // ending color. Also create an update listener that sets the View background color for each + // animation frame + @Override + public Animator createAnimator(ViewGroup sceneRoot, + TransitionValues startValues, TransitionValues endValues) { + // This transition can only be applied to views that are on both starting and ending scenes. + if (null == startValues || null == endValues) { + return null; + } + // Store a convenient reference to the target. Both the starting and ending layout have the + // same target. + final View view = endValues.view; + // Store the object containing the background property for both the starting and ending + // layouts. + Drawable startBackground = (Drawable) startValues.values.get(PROPNAME_BACKGROUND); + Drawable endBackground = (Drawable) endValues.values.get(PROPNAME_BACKGROUND); + // This transition changes background colors for a target. It doesn't animate any other + // background changes. If the property isn't a ColorDrawable, ignore the target. + if (startBackground instanceof ColorDrawable && endBackground instanceof ColorDrawable) { + ColorDrawable startColor = (ColorDrawable) startBackground; + ColorDrawable endColor = (ColorDrawable) endBackground; + // If the background color for the target in the starting and ending layouts is + // different, create an animation. + if (startColor.getColor() != endColor.getColor()) { + // Create a new Animator object to apply to the targets as the transitions framework + // changes from the starting to the ending layout. Use the class ValueAnimator, + // which provides a timing pulse to change property values provided to it. The + // animation runs on the UI thread. The Evaluator controls what type of + // interpolation is done. In this case, an ArgbEvaluator interpolates between two + // #argb values, which are specified as the 2nd and 3rd input arguments. + ValueAnimator animator = ValueAnimator.ofObject(new ArgbEvaluator(), + startColor.getColor(), endColor.getColor()); + // Add an update listener to the Animator object. + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + Object value = animation.getAnimatedValue(); + // Each time the ValueAnimator produces a new frame in the animation, change + // the background color of the target. Ensure that the value isn't null. + if (null != value) { + view.setBackgroundColor((Integer) value); + } + } + }); + // Return the Animator object to the transitions framework. As the framework changes + // between the starting and ending layouts, it applies the animation you've created. + return animator; + } + } + // For non-ColorDrawable backgrounds, we just return null, and no animation will take place. + return null; + } + // END_INCLUDE (create_animator) + +} diff --git a/samples/browseable/CustomTransition/src/com.example.android.customtransition/CustomTransitionFragment.java b/samples/browseable/CustomTransition/src/com.example.android.customtransition/CustomTransitionFragment.java new file mode 100644 index 000000000..8ef695aa5 --- /dev/null +++ b/samples/browseable/CustomTransition/src/com.example.android.customtransition/CustomTransitionFragment.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.customtransition; + +import com.example.android.common.logger.Log; + +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.transition.Scene; +import android.transition.Transition; +import android.transition.TransitionManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +public class CustomTransitionFragment extends Fragment implements View.OnClickListener { + + private static final String STATE_CURRENT_SCENE = "current_scene"; + + /** Tag for the logger */ + private static final String TAG = "CustomTransitionFragment"; + + /** These are the Scenes we use. */ + private Scene[] mScenes; + + /** The current index for mScenes. */ + private int mCurrentScene; + + /** This is the custom Transition we use in this sample. */ + private Transition mTransition; + + public CustomTransitionFragment() { + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_custom_transition, container, false); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + Context context = getActivity(); + FrameLayout container = (FrameLayout) view.findViewById(R.id.container); + view.findViewById(R.id.show_next_scene).setOnClickListener(this); + if (null != savedInstanceState) { + mCurrentScene = savedInstanceState.getInt(STATE_CURRENT_SCENE); + } + // We set up the Scenes here. + mScenes = new Scene[] { + Scene.getSceneForLayout(container, R.layout.scene1, context), + Scene.getSceneForLayout(container, R.layout.scene2, context), + Scene.getSceneForLayout(container, R.layout.scene3, context), + }; + // This is the custom Transition. + mTransition = new ChangeColor(); + // Show the initial Scene. + TransitionManager.go(mScenes[mCurrentScene % mScenes.length]); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(STATE_CURRENT_SCENE, mCurrentScene); + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.show_next_scene: { + mCurrentScene = (mCurrentScene + 1) % mScenes.length; + Log.i(TAG, "Transitioning to scene #" + mCurrentScene); + // Pass the custom Transition as second argument for TransitionManager.go + TransitionManager.go(mScenes[mCurrentScene], mTransition); + break; + } + } + } + +} diff --git a/samples/browseable/CustomTransition/src/com.example.android.customtransition/MainActivity.java b/samples/browseable/CustomTransition/src/com.example.android.customtransition/MainActivity.java new file mode 100644 index 000000000..04c4e4d9b --- /dev/null +++ b/samples/browseable/CustomTransition/src/com.example.android.customtransition/MainActivity.java @@ -0,0 +1,110 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + + + + +package com.example.android.customtransition; + +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.ViewAnimator; + +import com.example.android.common.activities.SampleActivityBase; +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogFragment; +import com.example.android.common.logger.LogWrapper; +import com.example.android.common.logger.MessageOnlyLogFilter; + +/** + * A simple launcher activity containing a summary sample description, sample log and a custom + * {@link android.support.v4.app.Fragment} which can display a view. + *

+ * For devices with displays with a width of 720dp or greater, the sample log is always visible, + * on other devices it's visibility is controlled by an item on the Action Bar. + */ +public class MainActivity extends SampleActivityBase { + + public static final String TAG = "MainActivity"; + + // Whether the Log Fragment is currently shown + private boolean mLogShown; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + CustomTransitionFragment fragment = new CustomTransitionFragment(); + transaction.replace(R.id.sample_content_fragment, fragment); + transaction.commit(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + MenuItem logToggle = menu.findItem(R.id.menu_toggle_log); + logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator); + logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log); + + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch(item.getItemId()) { + case R.id.menu_toggle_log: + mLogShown = !mLogShown; + ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output); + if (mLogShown) { + output.setDisplayedChild(1); + } else { + output.setDisplayedChild(0); + } + supportInvalidateOptionsMenu(); + return true; + } + return super.onOptionsItemSelected(item); + } + + /** Create a chain of targets that will receive log data */ + @Override + public void initializeLogging() { + // Wraps Android's native log framework. + LogWrapper logWrapper = new LogWrapper(); + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + Log.setLogNode(logWrapper); + + // Filter strips out everything except the message text. + MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); + logWrapper.setNext(msgFilter); + + // On screen logging via a fragment with a TextView. + LogFragment logFragment = (LogFragment) getSupportFragmentManager() + .findFragmentById(R.id.log_fragment); + msgFilter.setNext(logFragment.getLogView()); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/DisplayingBitmaps/res/values-v11/template-styles.xml b/samples/browseable/DisplayingBitmaps/res/values-v11/template-styles.xml new file mode 100644 index 000000000..8c1ea66f2 --- /dev/null +++ b/samples/browseable/DisplayingBitmaps/res/values-v11/template-styles.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/samples/browseable/FragmentTransition/res/values-v11/template-styles.xml b/samples/browseable/FragmentTransition/res/values-v11/template-styles.xml new file mode 100644 index 000000000..8c1ea66f2 --- /dev/null +++ b/samples/browseable/FragmentTransition/res/values-v11/template-styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/FragmentTransition/src/com.example.android.common/activities/SampleActivityBase.java new file mode 100644 index 000000000..3228927b7 --- /dev/null +++ b/samples/browseable/FragmentTransition/src/com.example.android.common/activities/SampleActivityBase.java @@ -0,0 +1,52 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.example.android.common.activities; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogWrapper; + +/** + * Base launcher activity, to handle most of the common plumbing for samples. + */ +public class SampleActivityBase extends FragmentActivity { + + public static final String TAG = "SampleActivityBase"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + initializeLogging(); + } + + /** Set up targets to receive log data */ + public void initializeLogging() { + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/Log.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/Log.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + *

When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.

+ */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +} \ No newline at end of file diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogNode.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.example.android.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogView.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogView.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.example.android.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabLayout.java b/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabLayout.java new file mode 100644 index 000000000..20049e335 --- /dev/null +++ b/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabLayout.java @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.view; + +import android.content.Context; +import android.graphics.Typeface; +import android.os.Build; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.HorizontalScrollView; +import android.widget.TextView; + +/** + * To be used with ViewPager to provide a tab indicator component which give constant feedback as to + * the user's scroll progress. + *

+ * To use the component, simply add it to your view hierarchy. Then in your + * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call + * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for. + *

+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors + * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The + * alternative is via the {@link TabColorizer} interface which provides you complete control over + * which color is used for any individual position. + *

+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)}, + * providing the layout ID of your custom layout. + */ +public class SlidingTabLayout extends HorizontalScrollView { + + /** + * Allows complete control over the colors drawn in the tab layout. Set with + * {@link #setCustomTabColorizer(TabColorizer)}. + */ + public interface TabColorizer { + + /** + * @return return the color of the indicator used when {@code position} is selected. + */ + int getIndicatorColor(int position); + + /** + * @return return the color of the divider drawn to the right of {@code position}. + */ + int getDividerColor(int position); + + } + + private static final int TITLE_OFFSET_DIPS = 24; + private static final int TAB_VIEW_PADDING_DIPS = 16; + private static final int TAB_VIEW_TEXT_SIZE_SP = 12; + + private int mTitleOffset; + + private int mTabViewLayoutId; + private int mTabViewTextViewId; + + private ViewPager mViewPager; + private ViewPager.OnPageChangeListener mViewPagerPageChangeListener; + + private final SlidingTabStrip mTabStrip; + + public SlidingTabLayout(Context context) { + this(context, null); + } + + public SlidingTabLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + // Disable the Scroll Bar + setHorizontalScrollBarEnabled(false); + // Make sure that the Tab Strips fills this View + setFillViewport(true); + + mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density); + + mTabStrip = new SlidingTabStrip(context); + addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + } + + /** + * Set the custom {@link TabColorizer} to be used. + * + * If you only require simple custmisation then you can use + * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve + * similar effects. + */ + public void setCustomTabColorizer(TabColorizer tabColorizer) { + mTabStrip.setCustomTabColorizer(tabColorizer); + } + + /** + * Sets the colors to be used for indicating the selected tab. These colors are treated as a + * circular array. Providing one color will mean that all tabs are indicated with the same color. + */ + public void setSelectedIndicatorColors(int... colors) { + mTabStrip.setSelectedIndicatorColors(colors); + } + + /** + * Sets the colors to be used for tab dividers. These colors are treated as a circular array. + * Providing one color will mean that all tabs are indicated with the same color. + */ + public void setDividerColors(int... colors) { + mTabStrip.setDividerColors(colors); + } + + /** + * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are + * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so + * that the layout can update it's scroll position correctly. + * + * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener) + */ + public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { + mViewPagerPageChangeListener = listener; + } + + /** + * Set the custom layout to be inflated for the tab views. + * + * @param layoutResId Layout id to be inflated + * @param textViewId id of the {@link TextView} in the inflated view + */ + public void setCustomTabView(int layoutResId, int textViewId) { + mTabViewLayoutId = layoutResId; + mTabViewTextViewId = textViewId; + } + + /** + * Sets the associated view pager. Note that the assumption here is that the pager content + * (number of tabs and tab titles) does not change after this call has been made. + */ + public void setViewPager(ViewPager viewPager) { + mTabStrip.removeAllViews(); + + mViewPager = viewPager; + if (viewPager != null) { + viewPager.setOnPageChangeListener(new InternalViewPagerListener()); + populateTabStrip(); + } + } + + /** + * Create a default view to be used for tabs. This is called if a custom tab view is not set via + * {@link #setCustomTabView(int, int)}. + */ + protected TextView createDefaultTabView(Context context) { + TextView textView = new TextView(context); + textView.setGravity(Gravity.CENTER); + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP); + textView.setTypeface(Typeface.DEFAULT_BOLD); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + // If we're running on Honeycomb or newer, then we can use the Theme's + // selectableItemBackground to ensure that the View has a pressed state + TypedValue outValue = new TypedValue(); + getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, + outValue, true); + textView.setBackgroundResource(outValue.resourceId); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style + textView.setAllCaps(true); + } + + int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density); + textView.setPadding(padding, padding, padding, padding); + + return textView; + } + + private void populateTabStrip() { + final PagerAdapter adapter = mViewPager.getAdapter(); + final View.OnClickListener tabClickListener = new TabClickListener(); + + for (int i = 0; i < adapter.getCount(); i++) { + View tabView = null; + TextView tabTitleView = null; + + if (mTabViewLayoutId != 0) { + // If there is a custom tab view layout id set, try and inflate it + tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip, + false); + tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId); + } + + if (tabView == null) { + tabView = createDefaultTabView(getContext()); + } + + if (tabTitleView == null && TextView.class.isInstance(tabView)) { + tabTitleView = (TextView) tabView; + } + + tabTitleView.setText(adapter.getPageTitle(i)); + tabView.setOnClickListener(tabClickListener); + + mTabStrip.addView(tabView); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mViewPager != null) { + scrollToTab(mViewPager.getCurrentItem(), 0); + } + } + + private void scrollToTab(int tabIndex, int positionOffset) { + final int tabStripChildCount = mTabStrip.getChildCount(); + if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) { + return; + } + + View selectedChild = mTabStrip.getChildAt(tabIndex); + if (selectedChild != null) { + int targetScrollX = selectedChild.getLeft() + positionOffset; + + if (tabIndex > 0 || positionOffset > 0) { + // If we're not at the first child and are mid-scroll, make sure we obey the offset + targetScrollX -= mTitleOffset; + } + + scrollTo(targetScrollX, 0); + } + } + + private class InternalViewPagerListener implements ViewPager.OnPageChangeListener { + private int mScrollState; + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + int tabStripChildCount = mTabStrip.getChildCount(); + if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) { + return; + } + + mTabStrip.onViewPagerPageChanged(position, positionOffset); + + View selectedTitle = mTabStrip.getChildAt(position); + int extraOffset = (selectedTitle != null) + ? (int) (positionOffset * selectedTitle.getWidth()) + : 0; + scrollToTab(position, extraOffset); + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrolled(position, positionOffset, + positionOffsetPixels); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + mScrollState = state; + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrollStateChanged(state); + } + } + + @Override + public void onPageSelected(int position) { + if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { + mTabStrip.onViewPagerPageChanged(position, 0f); + scrollToTab(position, 0); + } + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageSelected(position); + } + } + + } + + private class TabClickListener implements View.OnClickListener { + @Override + public void onClick(View v) { + for (int i = 0; i < mTabStrip.getChildCount(); i++) { + if (v == mTabStrip.getChildAt(i)) { + mViewPager.setCurrentItem(i); + return; + } + } + } + } + +} diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabStrip.java b/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabStrip.java new file mode 100644 index 000000000..d5bbbae59 --- /dev/null +++ b/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabStrip.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.view; + +import android.R; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.widget.LinearLayout; + +class SlidingTabStrip extends LinearLayout { + + private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2; + private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26; + private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8; + private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5; + + private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1; + private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20; + private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f; + + private final int mBottomBorderThickness; + private final Paint mBottomBorderPaint; + + private final int mSelectedIndicatorThickness; + private final Paint mSelectedIndicatorPaint; + + private final int mDefaultBottomBorderColor; + + private final Paint mDividerPaint; + private final float mDividerHeight; + + private int mSelectedPosition; + private float mSelectionOffset; + + private SlidingTabLayout.TabColorizer mCustomTabColorizer; + private final SimpleTabColorizer mDefaultTabColorizer; + + SlidingTabStrip(Context context) { + this(context, null); + } + + SlidingTabStrip(Context context, AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + + final float density = getResources().getDisplayMetrics().density; + + TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true); + final int themeForegroundColor = outValue.data; + + mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor, + DEFAULT_BOTTOM_BORDER_COLOR_ALPHA); + + mDefaultTabColorizer = new SimpleTabColorizer(); + mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR); + mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor, + DEFAULT_DIVIDER_COLOR_ALPHA)); + + mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density); + mBottomBorderPaint = new Paint(); + mBottomBorderPaint.setColor(mDefaultBottomBorderColor); + + mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density); + mSelectedIndicatorPaint = new Paint(); + + mDividerHeight = DEFAULT_DIVIDER_HEIGHT; + mDividerPaint = new Paint(); + mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density)); + } + + void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) { + mCustomTabColorizer = customTabColorizer; + invalidate(); + } + + void setSelectedIndicatorColors(int... colors) { + // Make sure that the custom colorizer is removed + mCustomTabColorizer = null; + mDefaultTabColorizer.setIndicatorColors(colors); + invalidate(); + } + + void setDividerColors(int... colors) { + // Make sure that the custom colorizer is removed + mCustomTabColorizer = null; + mDefaultTabColorizer.setDividerColors(colors); + invalidate(); + } + + void onViewPagerPageChanged(int position, float positionOffset) { + mSelectedPosition = position; + mSelectionOffset = positionOffset; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + final int height = getHeight(); + final int childCount = getChildCount(); + final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height); + final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null + ? mCustomTabColorizer + : mDefaultTabColorizer; + + // Thick colored underline below the current selection + if (childCount > 0) { + View selectedTitle = getChildAt(mSelectedPosition); + int left = selectedTitle.getLeft(); + int right = selectedTitle.getRight(); + int color = tabColorizer.getIndicatorColor(mSelectedPosition); + + if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) { + int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1); + if (color != nextColor) { + color = blendColors(nextColor, color, mSelectionOffset); + } + + // Draw the selection partway between the tabs + View nextTitle = getChildAt(mSelectedPosition + 1); + left = (int) (mSelectionOffset * nextTitle.getLeft() + + (1.0f - mSelectionOffset) * left); + right = (int) (mSelectionOffset * nextTitle.getRight() + + (1.0f - mSelectionOffset) * right); + } + + mSelectedIndicatorPaint.setColor(color); + + canvas.drawRect(left, height - mSelectedIndicatorThickness, right, + height, mSelectedIndicatorPaint); + } + + // Thin underline along the entire bottom edge + canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint); + + // Vertical separators between the titles + int separatorTop = (height - dividerHeightPx) / 2; + for (int i = 0; i < childCount - 1; i++) { + View child = getChildAt(i); + mDividerPaint.setColor(tabColorizer.getDividerColor(i)); + canvas.drawLine(child.getRight(), separatorTop, child.getRight(), + separatorTop + dividerHeightPx, mDividerPaint); + } + } + + /** + * Set the alpha value of the {@code color} to be the given {@code alpha} value. + */ + private static int setColorAlpha(int color, byte alpha) { + return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)); + } + + /** + * Blend {@code color1} and {@code color2} using the given ratio. + * + * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend, + * 0.0 will return {@code color2}. + */ + private static int blendColors(int color1, int color2, float ratio) { + final float inverseRation = 1f - ratio; + float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation); + float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation); + float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation); + return Color.rgb((int) r, (int) g, (int) b); + } + + private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer { + private int[] mIndicatorColors; + private int[] mDividerColors; + + @Override + public final int getIndicatorColor(int position) { + return mIndicatorColors[position % mIndicatorColors.length]; + } + + @Override + public final int getDividerColor(int position) { + return mDividerColors[position % mDividerColors.length]; + } + + void setIndicatorColors(int... colors) { + mIndicatorColors = colors; + } + + void setDividerColors(int... colors) { + mDividerColors = colors; + } + } +} \ No newline at end of file diff --git a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/DetailFragment.java b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/DetailFragment.java new file mode 100644 index 000000000..81e7b46d3 --- /dev/null +++ b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/DetailFragment.java @@ -0,0 +1,158 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.fragmenttransition; + +import com.example.android.common.logger.Log; + +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.transition.Scene; +import android.transition.TransitionManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +public class DetailFragment extends Fragment implements Animation.AnimationListener { + + private static final String TAG = "DetailFragment"; + + private static final String ARG_RESOURCE_ID = "resource_id"; + private static final String ARG_TITLE = "title"; + private static final String ARG_X = "x"; + private static final String ARG_Y = "y"; + private static final String ARG_WIDTH = "width"; + private static final String ARG_HEIGHT = "height"; + + /** + * Create a new instance of DetailFragment. + * + * @param resourceId The resource ID of the Drawable image to show + * @param title The title of the image + * @param x The horizontal position of the grid item in pixel + * @param y The vertical position of the grid item in pixel + * @param width The width of the grid item in pixel + * @param height The height of the grid item in pixel + * @return a new instance of DetailFragment + */ + public static DetailFragment newInstance(int resourceId, String title, + int x, int y, int width, int height) { + DetailFragment fragment = new DetailFragment(); + Bundle args = new Bundle(); + args.putInt(ARG_RESOURCE_ID, resourceId); + args.putString(ARG_TITLE, title); + args.putInt(ARG_X, x); + args.putInt(ARG_Y, y); + args.putInt(ARG_WIDTH, width); + args.putInt(ARG_HEIGHT, height); + fragment.setArguments(args); + return fragment; + } + + public DetailFragment() { + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_detail, container, false); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + FrameLayout root = (FrameLayout) view; + Context context = view.getContext(); + assert context != null; + // This is how the fragment looks at first. Since the transition is one-way, we don't need to make + // this a Scene. + View item = LayoutInflater.from(context).inflate(R.layout.item_meat_grid, root, false); + assert item != null; + bind(item); + // We adjust the position of the initial image with LayoutParams using the values supplied + // as the fragment arguments. + Bundle args = getArguments(); + FrameLayout.LayoutParams params = null; + if (args != null) { + params = new FrameLayout.LayoutParams( + args.getInt(ARG_WIDTH), args.getInt(ARG_HEIGHT)); + params.topMargin = args.getInt(ARG_Y); + params.leftMargin = args.getInt(ARG_X); + } + root.addView(item, params); + } + + @Override + public void onResume() { + super.onResume(); + } + + /** + * Bind the views inside of parent with the fragment arguments. + * + * @param parent The parent of views to bind. + */ + private void bind(View parent) { + Bundle args = getArguments(); + if (args == null) { + return; + } + ImageView image = (ImageView) parent.findViewById(R.id.image); + image.setImageResource(args.getInt(ARG_RESOURCE_ID)); + TextView title = (TextView) parent.findViewById(R.id.title); + title.setText(args.getString(ARG_TITLE)); + } + + @Override + public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { + Animation animation = AnimationUtils.loadAnimation(getActivity(), + enter ? android.R.anim.fade_in : android.R.anim.fade_out); + // We bind a listener for the fragment transaction. We only bind it when + // this fragment is entering. + if (animation != null && enter) { + animation.setAnimationListener(this); + } + return animation; + } + + @Override + public void onAnimationStart(Animation animation) { + // This method is called at the end of the animation for the fragment transaction. + // There is nothing we need to do in this sample. + } + + @Override + public void onAnimationEnd(Animation animation) { + // This method is called at the end of the animation for the fragment transaction, + // which is perfect time to start our Transition. + Log.i(TAG, "Fragment animation ended. Starting a Transition."); + final Scene scene = Scene.getSceneForLayout((ViewGroup) getView(), + R.layout.fragment_detail_content, getActivity()); + TransitionManager.go(scene); + // Note that we need to bind views with data after we call TransitionManager.go(). + bind(scene.getSceneRoot()); + } + + @Override + public void onAnimationRepeat(Animation animation) { + // This method is never called in this sample because the animation doesn't repeat. + } + +} diff --git a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/FragmentTransitionFragment.java b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/FragmentTransitionFragment.java new file mode 100644 index 000000000..c072eb997 --- /dev/null +++ b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/FragmentTransitionFragment.java @@ -0,0 +1,83 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.fragmenttransition; + +import com.example.android.common.logger.Log; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.AdapterView; +import android.widget.GridView; + +public class FragmentTransitionFragment extends Fragment implements AdapterView.OnItemClickListener { + + private static final String TAG = "FragmentTransitionFragment"; + + private MeatAdapter mAdapter; + + public static FragmentTransitionFragment newInstance() { + return new FragmentTransitionFragment(); + } + + public FragmentTransitionFragment() { + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + // This is the adapter we use to populate the grid. + mAdapter = new MeatAdapter(inflater, R.layout.item_meat_grid); + // Inflate the layout with a GridView in it. + return inflater.inflate(R.layout.fragment_fragment_transition, container, false); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + GridView grid = (GridView) view.findViewById(R.id.grid); + grid.setAdapter(mAdapter); + grid.setOnItemClickListener(this); + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + Meat meat = mAdapter.getItem(position); + Log.i(TAG, meat.title + " clicked. Replacing fragment."); + // We start the fragment transaction here. It is just an ordinary fragment transaction. + getActivity().getSupportFragmentManager() + .beginTransaction() + .replace(R.id.sample_content_fragment, + DetailFragment.newInstance(meat.resourceId, meat.title, + (int) view.getX(), (int) view.getY(), + view.getWidth(), view.getHeight()) + ) + // We push the fragment transaction to back stack. User can go back to the + // previous fragment by pressing back button. + .addToBackStack("detail") + .commit(); + } + + @Override + public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { + return AnimationUtils.loadAnimation(getActivity(), + enter ? android.R.anim.fade_in : android.R.anim.fade_out); + } + +} diff --git a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MainActivity.java b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MainActivity.java new file mode 100644 index 000000000..7d6ca6f4d --- /dev/null +++ b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MainActivity.java @@ -0,0 +1,110 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + + + + +package com.example.android.fragmenttransition; + +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.ViewAnimator; + +import com.example.android.common.activities.SampleActivityBase; +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogFragment; +import com.example.android.common.logger.LogWrapper; +import com.example.android.common.logger.MessageOnlyLogFilter; + +/** + * A simple launcher activity containing a summary sample description, sample log and a custom + * {@link android.support.v4.app.Fragment} which can display a view. + *

+ * For devices with displays with a width of 720dp or greater, the sample log is always visible, + * on other devices it's visibility is controlled by an item on the Action Bar. + */ +public class MainActivity extends SampleActivityBase { + + public static final String TAG = "MainActivity"; + + // Whether the Log Fragment is currently shown + private boolean mLogShown; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + FragmentTransitionFragment fragment = new FragmentTransitionFragment(); + transaction.replace(R.id.sample_content_fragment, fragment); + transaction.commit(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + MenuItem logToggle = menu.findItem(R.id.menu_toggle_log); + logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator); + logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log); + + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch(item.getItemId()) { + case R.id.menu_toggle_log: + mLogShown = !mLogShown; + ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output); + if (mLogShown) { + output.setDisplayedChild(1); + } else { + output.setDisplayedChild(0); + } + supportInvalidateOptionsMenu(); + return true; + } + return super.onOptionsItemSelected(item); + } + + /** Create a chain of targets that will receive log data */ + @Override + public void initializeLogging() { + // Wraps Android's native log framework. + LogWrapper logWrapper = new LogWrapper(); + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + Log.setLogNode(logWrapper); + + // Filter strips out everything except the message text. + MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); + logWrapper.setNext(msgFilter); + + // On screen logging via a fragment with a TextView. + LogFragment logFragment = (LogFragment) getSupportFragmentManager() + .findFragmentById(R.id.log_fragment); + msgFilter.setNext(logFragment.getLogView()); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/Meat.java b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/Meat.java new file mode 100644 index 000000000..2f2fdfa44 --- /dev/null +++ b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/Meat.java @@ -0,0 +1,46 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.fragmenttransition; + +/** + * This represents a sample data. + */ +public class Meat { + + public int resourceId; + public String title; + + public Meat(int resourceId, String title) { + this.resourceId = resourceId; + this.title = title; + } + + public static final Meat[] MEATS = { + new Meat(R.drawable.p1, "First"), + new Meat(R.drawable.p2, "Second"), + new Meat(R.drawable.p3, "Third"), + new Meat(R.drawable.p4, "Fourth"), + new Meat(R.drawable.p5, "Fifth"), + new Meat(R.drawable.p6, "Sixth"), + new Meat(R.drawable.p7, "Seventh"), + new Meat(R.drawable.p8, "Eighth"), + new Meat(R.drawable.p9, "Ninth"), + new Meat(R.drawable.p10, "Tenth"), + new Meat(R.drawable.p11, "Eleventh"), + }; + +} diff --git a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MeatAdapter.java b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MeatAdapter.java new file mode 100644 index 000000000..307fd85fa --- /dev/null +++ b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MeatAdapter.java @@ -0,0 +1,81 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.fragmenttransition; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +class MeatAdapter extends BaseAdapter { + + private final LayoutInflater mLayoutInflater; + private final int mResourceId; + + public MeatAdapter(LayoutInflater inflater, int resourceId) { + mLayoutInflater = inflater; + mResourceId = resourceId; + } + + @Override + public int getCount() { + return Meat.MEATS.length; + } + + @Override + public Meat getItem(int position) { + return Meat.MEATS[position]; + } + + @Override + public long getItemId(int position) { + return Meat.MEATS[position].resourceId; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final View view; + final ViewHolder holder; + if (null == convertView) { + view = mLayoutInflater.inflate(mResourceId, parent, false); + holder = new ViewHolder(); + assert view != null; + holder.image = (ImageView) view.findViewById(R.id.image); + holder.title = (TextView) view.findViewById(R.id.title); + view.setTag(holder); + } else { + view = convertView; + holder = (ViewHolder) view.getTag(); + } + bindView(holder, position); + return view; + } + + public void bindView(ViewHolder holder, int position) { + Meat meat = getItem(position); + holder.image.setImageResource(meat.resourceId); + holder.title.setText(meat.title); + } + + public static class ViewHolder { + public ImageView image; + public TextView title; + } + +} diff --git a/samples/browseable/HorizontalPaging/res/values-v11/template-styles.xml b/samples/browseable/HorizontalPaging/res/values-v11/template-styles.xml new file mode 100644 index 000000000..8c1ea66f2 --- /dev/null +++ b/samples/browseable/HorizontalPaging/res/values-v11/template-styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/samples/browseable/ImmersiveMode/res/values-v11/template-styles.xml b/samples/browseable/ImmersiveMode/res/values-v11/template-styles.xml new file mode 100644 index 000000000..8c1ea66f2 --- /dev/null +++ b/samples/browseable/ImmersiveMode/res/values-v11/template-styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/samples/browseable/RepeatingAlarm/res/values-v11/template-styles.xml b/samples/browseable/RepeatingAlarm/res/values-v11/template-styles.xml new file mode 100644 index 000000000..8c1ea66f2 --- /dev/null +++ b/samples/browseable/RepeatingAlarm/res/values-v11/template-styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/samples/browseable/StorageClient/res/values-v11/template-styles.xml b/samples/browseable/StorageClient/res/values-v11/template-styles.xml new file mode 100644 index 000000000..8c1ea66f2 --- /dev/null +++ b/samples/browseable/StorageClient/res/values-v11/template-styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/samples/browseable/StorageProvider/res/values-v11/template-styles.xml b/samples/browseable/StorageProvider/res/values-v11/template-styles.xml new file mode 100644 index 000000000..8c1ea66f2 --- /dev/null +++ b/samples/browseable/StorageProvider/res/values-v11/template-styles.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/samples/browseable/SwipeRefreshLayoutBasic/res/values-v11/template-styles.xml b/samples/browseable/SwipeRefreshLayoutBasic/res/values-v11/template-styles.xml new file mode 100644 index 000000000..8c1ea66f2 --- /dev/null +++ b/samples/browseable/SwipeRefreshLayoutBasic/res/values-v11/template-styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/activities/SampleActivityBase.java new file mode 100644 index 000000000..3228927b7 --- /dev/null +++ b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/activities/SampleActivityBase.java @@ -0,0 +1,52 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.example.android.common.activities; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogWrapper; + +/** + * Base launcher activity, to handle most of the common plumbing for samples. + */ +public class SampleActivityBase extends FragmentActivity { + + public static final String TAG = "SampleActivityBase"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + initializeLogging(); + } + + /** Set up targets to receive log data */ + public void initializeLogging() { + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/dummydata/Cheeses.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/dummydata/Cheeses.java new file mode 100644 index 000000000..783735ce3 --- /dev/null +++ b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/dummydata/Cheeses.java @@ -0,0 +1,186 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.dummydata; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Random; + +/** + * Dummy data. + */ +public class Cheeses { + static final String[] CHEESES = { + "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi", + "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", + "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese", + "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell", + "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc", + "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss", + "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon", + "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase", + "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese", + "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy", + "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille", + "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore", + "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)", + "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves", + "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur", + "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon", + "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin", + "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)", + "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine", + "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza", + "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)", + "Buxton Blue", "Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta", + "Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie", + "Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat", + "Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano", + "Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain", + "Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou", + "Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar", + "Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno", + "Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack", + "Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper", + "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)", + "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese", + "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza", + "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley", + "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino", + "Cypress Grove Chevre", "Danablu (Danish Blue)", "Danbo", "Danish Fontina", + "Daralagjazsky", "Dauphin", "Delice des Fiouves", "Denhany Dorset Drum", "Derby", + "Dessertnyj Belyj", "Devon Blue", "Devon Garland", "Dolcelatte", "Doolin", + "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester", "Double Worcester", + "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra", "Dunlop", "Dunsyre Blue", + "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz", + "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne", "Esbareich", + "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo", "Exmoor Blue", + "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle", + "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis", "Flor de Guia", + "Flower Marie", "Folded", "Folded cheese with mint", "Fondant de Brebis", + "Fontainebleau", "Fontal", "Fontina Val d'Aosta", "Formaggio di capra", "Fougerus", + "Four Herb Gouda", "Fourme d' Ambert", "Fourme de Haute Loire", "Fourme de Montbrison", + "Fresh Jack", "Fresh Mozzarella", "Fresh Ricotta", "Fresh Truffles", "Fribourgeois", + "Friesekaas", "Friesian", "Friesla", "Frinault", "Fromage a Raclette", "Fromage Corse", + "Fromage de Montagne de Savoie", "Fromage Frais", "Fruit Cream Cheese", + "Frying Cheese", "Fynbo", "Gabriel", "Galette du Paludier", "Galette Lyonnaise", + "Galloway Goat's Milk Gems", "Gammelost", "Gaperon a l'Ail", "Garrotxa", "Gastanberra", + "Geitost", "Gippsland Blue", "Gjetost", "Gloucester", "Golden Cross", "Gorgonzola", + "Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost", + "Grafton Village Cheddar", "Grana", "Grana Padano", "Grand Vatel", + "Grataron d' Areches", "Gratte-Paille", "Graviera", "Greuilh", "Greve", + "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi", + "Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti", + "Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve", + "Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster", + "Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg", + "Jermi Tortes", "Jibneh Arabieh", "Jindi Brie", "Jubilee Blue", "Juustoleipa", + "Kadchgall", "Kaseri", "Kashta", "Kefalotyri", "Kenafa", "Kernhem", "Kervella Affine", + "Kikorangi", "King Island Cape Wickham Brie", "King River Gold", "Klosterkaese", + "Knockalara", "Kugelkase", "L'Aveyronnais", "L'Ecir de l'Aubrac", "La Taupiniere", + "La Vache Qui Rit", "Laguiole", "Lairobell", "Lajta", "Lanark Blue", "Lancashire", + "Langres", "Lappi", "Laruns", "Lavistown", "Le Brin", "Le Fium Orbo", "Le Lacandou", + "Le Roule", "Leafield", "Lebbene", "Leerdammer", "Leicester", "Leyden", "Limburger", + "Lincolnshire Poacher", "Lingot Saint Bousquet d'Orb", "Liptauer", "Little Rydings", + "Livarot", "Llanboidy", "Llanglofan Farmhouse", "Loch Arthur Farmhouse", + "Loddiswell Avondale", "Longhorn", "Lou Palou", "Lou Pevre", "Lyonnais", "Maasdam", + "Macconais", "Mahoe Aged Gouda", "Mahon", "Malvern", "Mamirolle", "Manchego", + "Manouri", "Manur", "Marble Cheddar", "Marbled Cheeses", "Maredsous", "Margotin", + "Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)", + "Mascarpone Torta", "Matocq", "Maytag Blue", "Meira", "Menallack Farmhouse", + "Menonita", "Meredith Blue", "Mesost", "Metton (Cancoillotte)", "Meyer Vintage Gouda", + "Mihalic Peynir", "Milleens", "Mimolette", "Mine-Gabhar", "Mini Baby Bells", "Mixte", + "Molbo", "Monastery Cheeses", "Mondseer", "Mont D'or Lyonnais", "Montasio", + "Monterey Jack", "Monterey Jack Dry", "Morbier", "Morbier Cru de Montagne", + "Mothais a la Feuille", "Mozzarella", "Mozzarella (Australian)", + "Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster", + "Murol", "Mycella", "Myzithra", "Naboulsi", "Nantais", "Neufchatel", + "Neufchatel (Australian)", "Niolo", "Nokkelost", "Northumberland", "Oaxaca", + "Olde York", "Olivet au Foin", "Olivet Bleu", "Olivet Cendre", + "Orkney Extra Mature Cheddar", "Orla", "Oschtjepka", "Ossau Fermier", "Ossau-Iraty", + "Oszczypek", "Oxford Blue", "P'tit Berrichon", "Palet de Babligny", "Paneer", "Panela", + "Pannerone", "Pant ys Gawn", "Parmesan (Parmigiano)", "Parmigiano Reggiano", + "Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage", + "Patefine Fort", "Pave d'Affinois", "Pave d'Auge", "Pave de Chirac", "Pave du Berry", + "Pecorino", "Pecorino in Walnut Leaves", "Pecorino Romano", "Peekskill Pyramid", + "Pelardon des Cevennes", "Pelardon des Corbieres", "Penamellera", "Penbryn", + "Pencarreg", "Perail de Brebis", "Petit Morin", "Petit Pardou", "Petit-Suisse", + "Picodon de Chevre", "Picos de Europa", "Piora", "Pithtviers au Foin", + "Plateau de Herve", "Plymouth Cheese", "Podhalanski", "Poivre d'Ane", "Polkolbin", + "Pont l'Eveque", "Port Nicholson", "Port-Salut", "Postel", "Pouligny-Saint-Pierre", + "Pourly", "Prastost", "Pressato", "Prince-Jean", "Processed Cheddar", "Provolone", + "Provolone (Australian)", "Pyengana Cheddar", "Pyramide", "Quark", + "Quark (Australian)", "Quartirolo Lombardo", "Quatre-Vents", "Quercy Petit", + "Queso Blanco", "Queso Blanco con Frutas --Pina y Mango", "Queso de Murcia", + "Queso del Montsec", "Queso del Tietar", "Queso Fresco", "Queso Fresco (Adobera)", + "Queso Iberico", "Queso Jalapeno", "Queso Majorero", "Queso Media Luna", + "Queso Para Frier", "Queso Quesadilla", "Rabacal", "Raclette", "Ragusano", "Raschera", + "Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou", + "Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder", + "Rigotte", "Rocamadour", "Rollot", "Romano", "Romans Part Dieu", "Roncal", "Roquefort", + "Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr", + "Saanenkaese", "Saga", "Sage Derby", "Sainte Maure", "Saint-Marcellin", + "Saint-Nectaire", "Saint-Paulin", "Salers", "Samso", "San Simon", "Sancerre", + "Sap Sago", "Sardo", "Sardo Egyptian", "Sbrinz", "Scamorza", "Schabzieger", "Schloss", + "Selles sur Cher", "Selva", "Serat", "Seriously Strong Cheddar", "Serra da Estrela", + "Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda", + "Somerset Brie", "Sonoma Jack", "Sottocenare al Tartufo", "Soumaintrain", + "Sourire Lozerien", "Spenwood", "Sraffordshire Organic", "St. Agur Blue Cheese", + "Stilton", "Stinking Bishop", "String", "Sussex Slipcote", "Sveciaost", "Swaledale", + "Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie", + "Tasmania Highland Chevre Log", "Taupiniere", "Teifi", "Telemea", "Testouri", + "Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar", + "Tilsit", "Timboon Brie", "Toma", "Tomme Brulee", "Tomme d'Abondance", + "Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes", + "Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet", + "Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe", + "Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa", + "Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois", + "Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue", + "Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington", + "Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou", + "Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue", + "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano" + }; + + public static ArrayList asList() { + ArrayList items = new ArrayList(); + for (int i = 0, z = CHEESES.length ; i < z ; i++) { + items.add(CHEESES[i]); + } + return items; + } + + /** + * Return a list of random cheeses. + * + * @param count the amount of cheeses to return. + */ + public static ArrayList randomList(int count) { + Random random = new Random(); + HashSet items = new HashSet(); + + // Make sure that don't infinity loop + count = Math.min(count, CHEESES.length); + + while (items.size() < count) { + items.add(CHEESES[random.nextInt(CHEESES.length)]); + } + + return new ArrayList(items); + } +} diff --git a/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/Log.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/Log.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + *

When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.

+ */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +} \ No newline at end of file diff --git a/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogNode.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.example.android.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogView.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogView.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.example.android.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/view/SlidingTabLayout.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/view/SlidingTabLayout.java new file mode 100644 index 000000000..20049e335 --- /dev/null +++ b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/view/SlidingTabLayout.java @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.view; + +import android.content.Context; +import android.graphics.Typeface; +import android.os.Build; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.HorizontalScrollView; +import android.widget.TextView; + +/** + * To be used with ViewPager to provide a tab indicator component which give constant feedback as to + * the user's scroll progress. + *

+ * To use the component, simply add it to your view hierarchy. Then in your + * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call + * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for. + *

+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors + * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The + * alternative is via the {@link TabColorizer} interface which provides you complete control over + * which color is used for any individual position. + *

+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)}, + * providing the layout ID of your custom layout. + */ +public class SlidingTabLayout extends HorizontalScrollView { + + /** + * Allows complete control over the colors drawn in the tab layout. Set with + * {@link #setCustomTabColorizer(TabColorizer)}. + */ + public interface TabColorizer { + + /** + * @return return the color of the indicator used when {@code position} is selected. + */ + int getIndicatorColor(int position); + + /** + * @return return the color of the divider drawn to the right of {@code position}. + */ + int getDividerColor(int position); + + } + + private static final int TITLE_OFFSET_DIPS = 24; + private static final int TAB_VIEW_PADDING_DIPS = 16; + private static final int TAB_VIEW_TEXT_SIZE_SP = 12; + + private int mTitleOffset; + + private int mTabViewLayoutId; + private int mTabViewTextViewId; + + private ViewPager mViewPager; + private ViewPager.OnPageChangeListener mViewPagerPageChangeListener; + + private final SlidingTabStrip mTabStrip; + + public SlidingTabLayout(Context context) { + this(context, null); + } + + public SlidingTabLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + // Disable the Scroll Bar + setHorizontalScrollBarEnabled(false); + // Make sure that the Tab Strips fills this View + setFillViewport(true); + + mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density); + + mTabStrip = new SlidingTabStrip(context); + addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + } + + /** + * Set the custom {@link TabColorizer} to be used. + * + * If you only require simple custmisation then you can use + * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve + * similar effects. + */ + public void setCustomTabColorizer(TabColorizer tabColorizer) { + mTabStrip.setCustomTabColorizer(tabColorizer); + } + + /** + * Sets the colors to be used for indicating the selected tab. These colors are treated as a + * circular array. Providing one color will mean that all tabs are indicated with the same color. + */ + public void setSelectedIndicatorColors(int... colors) { + mTabStrip.setSelectedIndicatorColors(colors); + } + + /** + * Sets the colors to be used for tab dividers. These colors are treated as a circular array. + * Providing one color will mean that all tabs are indicated with the same color. + */ + public void setDividerColors(int... colors) { + mTabStrip.setDividerColors(colors); + } + + /** + * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are + * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so + * that the layout can update it's scroll position correctly. + * + * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener) + */ + public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { + mViewPagerPageChangeListener = listener; + } + + /** + * Set the custom layout to be inflated for the tab views. + * + * @param layoutResId Layout id to be inflated + * @param textViewId id of the {@link TextView} in the inflated view + */ + public void setCustomTabView(int layoutResId, int textViewId) { + mTabViewLayoutId = layoutResId; + mTabViewTextViewId = textViewId; + } + + /** + * Sets the associated view pager. Note that the assumption here is that the pager content + * (number of tabs and tab titles) does not change after this call has been made. + */ + public void setViewPager(ViewPager viewPager) { + mTabStrip.removeAllViews(); + + mViewPager = viewPager; + if (viewPager != null) { + viewPager.setOnPageChangeListener(new InternalViewPagerListener()); + populateTabStrip(); + } + } + + /** + * Create a default view to be used for tabs. This is called if a custom tab view is not set via + * {@link #setCustomTabView(int, int)}. + */ + protected TextView createDefaultTabView(Context context) { + TextView textView = new TextView(context); + textView.setGravity(Gravity.CENTER); + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP); + textView.setTypeface(Typeface.DEFAULT_BOLD); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + // If we're running on Honeycomb or newer, then we can use the Theme's + // selectableItemBackground to ensure that the View has a pressed state + TypedValue outValue = new TypedValue(); + getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, + outValue, true); + textView.setBackgroundResource(outValue.resourceId); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style + textView.setAllCaps(true); + } + + int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density); + textView.setPadding(padding, padding, padding, padding); + + return textView; + } + + private void populateTabStrip() { + final PagerAdapter adapter = mViewPager.getAdapter(); + final View.OnClickListener tabClickListener = new TabClickListener(); + + for (int i = 0; i < adapter.getCount(); i++) { + View tabView = null; + TextView tabTitleView = null; + + if (mTabViewLayoutId != 0) { + // If there is a custom tab view layout id set, try and inflate it + tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip, + false); + tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId); + } + + if (tabView == null) { + tabView = createDefaultTabView(getContext()); + } + + if (tabTitleView == null && TextView.class.isInstance(tabView)) { + tabTitleView = (TextView) tabView; + } + + tabTitleView.setText(adapter.getPageTitle(i)); + tabView.setOnClickListener(tabClickListener); + + mTabStrip.addView(tabView); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mViewPager != null) { + scrollToTab(mViewPager.getCurrentItem(), 0); + } + } + + private void scrollToTab(int tabIndex, int positionOffset) { + final int tabStripChildCount = mTabStrip.getChildCount(); + if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) { + return; + } + + View selectedChild = mTabStrip.getChildAt(tabIndex); + if (selectedChild != null) { + int targetScrollX = selectedChild.getLeft() + positionOffset; + + if (tabIndex > 0 || positionOffset > 0) { + // If we're not at the first child and are mid-scroll, make sure we obey the offset + targetScrollX -= mTitleOffset; + } + + scrollTo(targetScrollX, 0); + } + } + + private class InternalViewPagerListener implements ViewPager.OnPageChangeListener { + private int mScrollState; + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + int tabStripChildCount = mTabStrip.getChildCount(); + if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) { + return; + } + + mTabStrip.onViewPagerPageChanged(position, positionOffset); + + View selectedTitle = mTabStrip.getChildAt(position); + int extraOffset = (selectedTitle != null) + ? (int) (positionOffset * selectedTitle.getWidth()) + : 0; + scrollToTab(position, extraOffset); + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrolled(position, positionOffset, + positionOffsetPixels); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + mScrollState = state; + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrollStateChanged(state); + } + } + + @Override + public void onPageSelected(int position) { + if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { + mTabStrip.onViewPagerPageChanged(position, 0f); + scrollToTab(position, 0); + } + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageSelected(position); + } + } + + } + + private class TabClickListener implements View.OnClickListener { + @Override + public void onClick(View v) { + for (int i = 0; i < mTabStrip.getChildCount(); i++) { + if (v == mTabStrip.getChildAt(i)) { + mViewPager.setCurrentItem(i); + return; + } + } + } + } + +} diff --git a/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/view/SlidingTabStrip.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/view/SlidingTabStrip.java new file mode 100644 index 000000000..d5bbbae59 --- /dev/null +++ b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/view/SlidingTabStrip.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.view; + +import android.R; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.widget.LinearLayout; + +class SlidingTabStrip extends LinearLayout { + + private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2; + private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26; + private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8; + private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5; + + private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1; + private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20; + private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f; + + private final int mBottomBorderThickness; + private final Paint mBottomBorderPaint; + + private final int mSelectedIndicatorThickness; + private final Paint mSelectedIndicatorPaint; + + private final int mDefaultBottomBorderColor; + + private final Paint mDividerPaint; + private final float mDividerHeight; + + private int mSelectedPosition; + private float mSelectionOffset; + + private SlidingTabLayout.TabColorizer mCustomTabColorizer; + private final SimpleTabColorizer mDefaultTabColorizer; + + SlidingTabStrip(Context context) { + this(context, null); + } + + SlidingTabStrip(Context context, AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + + final float density = getResources().getDisplayMetrics().density; + + TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true); + final int themeForegroundColor = outValue.data; + + mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor, + DEFAULT_BOTTOM_BORDER_COLOR_ALPHA); + + mDefaultTabColorizer = new SimpleTabColorizer(); + mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR); + mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor, + DEFAULT_DIVIDER_COLOR_ALPHA)); + + mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density); + mBottomBorderPaint = new Paint(); + mBottomBorderPaint.setColor(mDefaultBottomBorderColor); + + mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density); + mSelectedIndicatorPaint = new Paint(); + + mDividerHeight = DEFAULT_DIVIDER_HEIGHT; + mDividerPaint = new Paint(); + mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density)); + } + + void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) { + mCustomTabColorizer = customTabColorizer; + invalidate(); + } + + void setSelectedIndicatorColors(int... colors) { + // Make sure that the custom colorizer is removed + mCustomTabColorizer = null; + mDefaultTabColorizer.setIndicatorColors(colors); + invalidate(); + } + + void setDividerColors(int... colors) { + // Make sure that the custom colorizer is removed + mCustomTabColorizer = null; + mDefaultTabColorizer.setDividerColors(colors); + invalidate(); + } + + void onViewPagerPageChanged(int position, float positionOffset) { + mSelectedPosition = position; + mSelectionOffset = positionOffset; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + final int height = getHeight(); + final int childCount = getChildCount(); + final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height); + final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null + ? mCustomTabColorizer + : mDefaultTabColorizer; + + // Thick colored underline below the current selection + if (childCount > 0) { + View selectedTitle = getChildAt(mSelectedPosition); + int left = selectedTitle.getLeft(); + int right = selectedTitle.getRight(); + int color = tabColorizer.getIndicatorColor(mSelectedPosition); + + if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) { + int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1); + if (color != nextColor) { + color = blendColors(nextColor, color, mSelectionOffset); + } + + // Draw the selection partway between the tabs + View nextTitle = getChildAt(mSelectedPosition + 1); + left = (int) (mSelectionOffset * nextTitle.getLeft() + + (1.0f - mSelectionOffset) * left); + right = (int) (mSelectionOffset * nextTitle.getRight() + + (1.0f - mSelectionOffset) * right); + } + + mSelectedIndicatorPaint.setColor(color); + + canvas.drawRect(left, height - mSelectedIndicatorThickness, right, + height, mSelectedIndicatorPaint); + } + + // Thin underline along the entire bottom edge + canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint); + + // Vertical separators between the titles + int separatorTop = (height - dividerHeightPx) / 2; + for (int i = 0; i < childCount - 1; i++) { + View child = getChildAt(i); + mDividerPaint.setColor(tabColorizer.getDividerColor(i)); + canvas.drawLine(child.getRight(), separatorTop, child.getRight(), + separatorTop + dividerHeightPx, mDividerPaint); + } + } + + /** + * Set the alpha value of the {@code color} to be the given {@code alpha} value. + */ + private static int setColorAlpha(int color, byte alpha) { + return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)); + } + + /** + * Blend {@code color1} and {@code color2} using the given ratio. + * + * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend, + * 0.0 will return {@code color2}. + */ + private static int blendColors(int color1, int color2, float ratio) { + final float inverseRation = 1f - ratio; + float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation); + float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation); + float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation); + return Color.rgb((int) r, (int) g, (int) b); + } + + private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer { + private int[] mIndicatorColors; + private int[] mDividerColors; + + @Override + public final int getIndicatorColor(int position) { + return mIndicatorColors[position % mIndicatorColors.length]; + } + + @Override + public final int getDividerColor(int position) { + return mDividerColors[position % mDividerColors.length]; + } + + void setIndicatorColors(int... colors) { + mIndicatorColors = colors; + } + + void setDividerColors(int... colors) { + mDividerColors = colors; + } + } +} \ No newline at end of file diff --git a/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.swiperefreshlayoutbasic/MainActivity.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.swiperefreshlayoutbasic/MainActivity.java new file mode 100644 index 000000000..f90aed1fc --- /dev/null +++ b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.swiperefreshlayoutbasic/MainActivity.java @@ -0,0 +1,110 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + + + + +package com.example.android.swiperefreshlayoutbasic; + +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.ViewAnimator; + +import com.example.android.common.activities.SampleActivityBase; +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogFragment; +import com.example.android.common.logger.LogWrapper; +import com.example.android.common.logger.MessageOnlyLogFilter; + +/** + * A simple launcher activity containing a summary sample description, sample log and a custom + * {@link android.support.v4.app.Fragment} which can display a view. + *

+ * For devices with displays with a width of 720dp or greater, the sample log is always visible, + * on other devices it's visibility is controlled by an item on the Action Bar. + */ +public class MainActivity extends SampleActivityBase { + + public static final String TAG = "MainActivity"; + + // Whether the Log Fragment is currently shown + private boolean mLogShown; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + SwipeRefreshLayoutBasicFragment fragment = new SwipeRefreshLayoutBasicFragment(); + transaction.replace(R.id.sample_content_fragment, fragment); + transaction.commit(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + MenuItem logToggle = menu.findItem(R.id.menu_toggle_log); + logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator); + logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log); + + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch(item.getItemId()) { + case R.id.menu_toggle_log: + mLogShown = !mLogShown; + ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output); + if (mLogShown) { + output.setDisplayedChild(1); + } else { + output.setDisplayedChild(0); + } + supportInvalidateOptionsMenu(); + return true; + } + return super.onOptionsItemSelected(item); + } + + /** Create a chain of targets that will receive log data */ + @Override + public void initializeLogging() { + // Wraps Android's native log framework. + LogWrapper logWrapper = new LogWrapper(); + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + Log.setLogNode(logWrapper); + + // Filter strips out everything except the message text. + MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); + logWrapper.setNext(msgFilter); + + // On screen logging via a fragment with a TextView. + LogFragment logFragment = (LogFragment) getSupportFragmentManager() + .findFragmentById(R.id.log_fragment); + msgFilter.setNext(logFragment.getLogView()); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.swiperefreshlayoutbasic/SwipeRefreshLayoutBasicFragment.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.swiperefreshlayoutbasic/SwipeRefreshLayoutBasicFragment.java new file mode 100644 index 000000000..13b22f5ca --- /dev/null +++ b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.swiperefreshlayoutbasic/SwipeRefreshLayoutBasicFragment.java @@ -0,0 +1,234 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.swiperefreshlayoutbasic; + +import com.example.android.common.dummydata.Cheeses; +import com.example.android.common.logger.Log; + +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.widget.SwipeRefreshLayout; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +import java.util.List; + +/** + * A basic sample that shows how to use {@link android.support.v4.widget.SwipeRefreshLayout} to add + * the 'swipe-to-refresh' gesture to a layout. In this sample, SwipeRefreshLayout contains a + * scrollable {@link android.widget.ListView} as its only child. + * + *

To provide an accessible way to trigger the refresh, this app also provides a refresh + * action item. + * + *

In this sample app, the refresh updates the ListView with a random set of new items. + */ +public class SwipeRefreshLayoutBasicFragment extends Fragment { + + private static final String LOG_TAG = SwipeRefreshLayoutBasicFragment.class.getSimpleName(); + + private static final int LIST_ITEM_COUNT = 20; + + /** + * The {@link android.support.v4.widget.SwipeRefreshLayout} that detects swipe gestures and + * triggers callbacks in the app. + */ + private SwipeRefreshLayout mSwipeRefreshLayout; + + /** + * The {@link android.widget.ListView} that displays the content that should be refreshed. + */ + private ListView mListView; + + /** + * The {@link android.widget.ListAdapter} used to populate the {@link android.widget.ListView} + * defined in the previous statement. + */ + private ArrayAdapter mListAdapter; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Notify the system to allow an options menu for this fragment. + setHasOptionsMenu(true); + } + + // BEGIN_INCLUDE (inflate_view) + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_sample, container, false); + + // Retrieve the SwipeRefreshLayout and ListView instances + mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swiperefresh); + + // BEGIN_INCLUDE (change_colors) + // Set the color scheme of the SwipeRefreshLayout by providing 4 color resource ids + mSwipeRefreshLayout.setColorScheme( + R.color.swipe_color_1, R.color.swipe_color_2, + R.color.swipe_color_3, R.color.swipe_color_4); + // END_INCLUDE (change_colors) + + // Retrieve the ListView + mListView = (ListView) view.findViewById(android.R.id.list); + + return view; + } + // END_INCLUDE (inflate_view) + + // BEGIN_INCLUDE (setup_views) + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + /** + * Create an ArrayAdapter to contain the data for the ListView. Each item in the ListView + * uses the system-defined simple_list_item_1 layout that contains one TextView. + */ + mListAdapter = new ArrayAdapter( + getActivity(), + android.R.layout.simple_list_item_1, + android.R.id.text1, + Cheeses.randomList(LIST_ITEM_COUNT)); + + // Set the adapter between the ListView and its backing data. + mListView.setAdapter(mListAdapter); + + // BEGIN_INCLUDE (setup_refreshlistener) + /** + * Implement {@link SwipeRefreshLayout.OnRefreshListener}. When users do the "swipe to + * refresh" gesture, SwipeRefreshLayout invokes + * {@link SwipeRefreshLayout.OnRefreshListener#onRefresh onRefresh()}. In + * {@link SwipeRefreshLayout.OnRefreshListener#onRefresh onRefresh()}, call a method that + * refreshes the content. Call the same method in response to the Refresh action from the + * action bar. + */ + mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + Log.i(LOG_TAG, "onRefresh called from SwipeRefreshLayout"); + + initiateRefresh(); + } + }); + // END_INCLUDE (setup_refreshlistener) + } + // END_INCLUDE (setup_views) + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.main_menu, menu); + } + + // BEGIN_INCLUDE (setup_refresh_menu_listener) + /** + * Respond to the user's selection of the Refresh action item. Start the SwipeRefreshLayout + * progress bar, then initiate the background task that refreshes the content. + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_refresh: + Log.i(LOG_TAG, "Refresh menu item selected"); + + // We make sure that the SwipeRefreshLayout is displaying it's refreshing indicator + if (!mSwipeRefreshLayout.isRefreshing()) { + mSwipeRefreshLayout.setRefreshing(true); + } + + // Start our refresh background task + initiateRefresh(); + + return true; + } + + return super.onOptionsItemSelected(item); + } + // END_INCLUDE (setup_refresh_menu_listener) + + // BEGIN_INCLUDE (initiate_refresh) + /** + * By abstracting the refresh process to a single method, the app allows both the + * SwipeGestureLayout onRefresh() method and the Refresh action item to refresh the content. + */ + private void initiateRefresh() { + Log.i(LOG_TAG, "initiateRefresh"); + + /** + * Execute the background task, which uses {@link android.os.AsyncTask} to load the data. + */ + new DummyBackgroundTask().execute(); + } + // END_INCLUDE (initiate_refresh) + + // BEGIN_INCLUDE (refresh_complete) + /** + * When the AsyncTask finishes, it calls onRefreshComplete(), which updates the data in the + * ListAdapter and turns off the progress bar. + */ + private void onRefreshComplete(List result) { + Log.i(LOG_TAG, "onRefreshComplete"); + + // Remove all items from the ListAdapter, and then replace them with the new items + mListAdapter.clear(); + for (String cheese : result) { + mListAdapter.add(cheese); + } + + // Stop the refreshing indicator + mSwipeRefreshLayout.setRefreshing(false); + } + // END_INCLUDE (refresh_complete) + + /** + * Dummy {@link AsyncTask} which simulates a long running task to fetch new cheeses. + */ + private class DummyBackgroundTask extends AsyncTask> { + + static final int TASK_DURATION = 3 * 1000; // 3 seconds + + @Override + protected List doInBackground(Void... params) { + // Sleep for a small amount of time to simulate a background-task + try { + Thread.sleep(TASK_DURATION); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + // Return a new random list of cheeses + return Cheeses.randomList(LIST_ITEM_COUNT); + } + + @Override + protected void onPostExecute(List result) { + super.onPostExecute(result); + + // Tell the Fragment that the refresh has completed + onRefreshComplete(result); + } + + } +} diff --git a/samples/browseable/SwipeRefreshListFragment/AndroidManifest.xml b/samples/browseable/SwipeRefreshListFragment/AndroidManifest.xml new file mode 100644 index 000000000..be449304e --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/AndroidManifest.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/browseable/SwipeRefreshListFragment/_index.jd b/samples/browseable/SwipeRefreshListFragment/_index.jd new file mode 100644 index 000000000..e790e535d --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/_index.jd @@ -0,0 +1,15 @@ + + + +page.tags="SwipeRefreshListFragment" +sample.group=UI +@jd:body + +

+ + A sample which shows how to use SwipeRefreshLayout within a ListFragment to add the + \'swipe-to-refresh\' gesture to a ListView, enabling the ability to trigger a refresh + from swiping down on that view. This is provided through the re-usable + SwipeRefreshListFragment class. + +

diff --git a/samples/browseable/SwipeRefreshListFragment/res/drawable-hdpi/ic_launcher.png b/samples/browseable/SwipeRefreshListFragment/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..b10c5c1b64640f1a8f79503fc79e22776201f923 GIT binary patch literal 4503 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 literal 0 HcmV?d00001 diff --git a/samples/browseable/SwipeRefreshListFragment/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/SwipeRefreshListFragment/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..6fe78cc59f1ca1e8056017c93607733932193952 GIT binary patch literal 6467 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 literal 0 HcmV?d00001 diff --git a/samples/browseable/SwipeRefreshListFragment/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/SwipeRefreshListFragment/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..5f89875040b44a1efccc9b7bc1f63c0bb1f575cb GIT binary patch literal 11559 zcmV+?E!fhDP)yRBX{ov}&_y zw_W+^&;pH(jq5iwZkVxp)#|Z(jv8_komx+Og9cNikz4>`@U)%u^5mcP=+^x*$wN;3 zXqE_4R>S z-=y*+I*baN?e8j^t}j@)@RFhX4EZzVTIT|h3qW9=cH_pq^z9wC+wOkJM6OQddYISD z#&n%eDkrHU?6T}~OmJ``*_~yVpH@F#eOz_nw6507Q`fu%Xad}R?yO~{wL_z5`0$gD zkKb>v<}`ev<1Io+n)0&CW+nboCBmqGM|t7xyM z9lDX+Yyg3Ms%h^$fByWL8#V;#Hr>`KKUr!Ekp3%r_UyR@C0t7TYLkU*0D*nl)82jd z?Af2DmRwG-*|b1iUEMbHunTElsaeR$KyB%4C)#_=nl)?cX45+52Tg5(zJ2@dNQoBE zzKnJ#)J_1k(*Wu0#$-0702f1W=F3lPCXg*REZWsl09G zEwFg;;`fIP8FGxlKwbbrgh?HU_FCGz(cY1EoR|KgONZZmE1U_f*hh`VrGttJrB2~| zBkX?T96KLcQ5x(4dUe`Xq@<}bLnlV7N}SW>HLCl`yfRJneB@Q2>pF=pPiL{UJx-*{ z%8L=IQn*D9U<45BL+GX>F=NlW?n87(^cQ=xOLr)s! zMe)c>yV}$PZ@@9y&q&MXRK>VjD*f*!p8R9_!%jnps^zpVqJ0kStOI#PSZhiID-3!N zz|)!b8rpFl7FwHY_J90g)J=70MbKFQjRKVdlYCI!>A^8BYY<7GQ5ptbBbR9aKm}41 zAdt3|!c+uAIFx1Rw8mwfSk=SKr;mbr^KtaA?QwIblAU305s-wa4KRiu^gri|oTqXc z2`d43bXpWIe*$259DPfa!D&tK;2f?=FljI{XjHeTddV^gh_Vhk-T5yZaWoylR0oLQ zfrjZ0>kA-){sSuv=T(zh&4Mlh+f>mGCIXmXnK6K>uOIdAiO5q?Ng&}ubSZpJf(vgD z{U^bL^zv^A7=<=~LNkU59Qib;oyjJ;!kUFbmT@s2KNW*GWHVfmV6>l{pT%HEt8I+8 zJ9>o~LzS(rp<`47MQ`L{1l55ddxY$z3pC0J(#p`W$V`Cp&U)s+?ddX`>L>*uVj#t0Vy^d4zWe9{z4I{u)Zx}|PWVdcDoDi)Vu~!q zJ~9os*3emG78%!P*71u%7LP0n0fQ>3*(k<5HL?$-T0uLQM+-N}IA&yC-3ZSZ{7U};(bn$M${IXjnV~}N(yvrrLy>0@rGY5l5U!{G9e{;_>}VaAeq^$bnZ@jE)6!wRL)Jkz zktyO#1=gjfM1d57M7};+d?|%E)OF--3{l0QC=(lz!*?|U&spF__WFDJfQ^-^7Vgk!~ zP9;&VssTai;2099B6pS&g34PtT2b7h(YQW zQMSmE$J%BJL|Ve}@sIDa5df4KAfeb}wh6$%=DYz8(~7gf!upXbjVwZg!3Q2Ea9px0 z#Ebf6v5$Ew_-*PQMivtcGHt*{M$tB?G(*Kerd2v-Fa?~r&f^4>p%d8vYaRe)1WcrJ ziJ?q|8UTnM8$KV?Qe!|Fp6;yd6M#n}Dgr89t^#mKJxLgfFpgJ@ML`ydH+7yBS`%!c zlB$>(~Hb7JxM?fb6$FD_|nx&LN6iroKy5nfH@8FhRz+ zutZlx_sIwx9|;AF6m({kNrJauW2&lirC7aKJ8>Nh;sq6hg0X1;(Iu9$0*YBM1aPC+ zcmOgO#SM_C_{yXIIK`K#&$vM7(R)mhW|2<>Cq5O(OcXGZC9+(itjCTuqITB~V#@t< z-7I7OGq_MNo#aNmiWa~v)#FU{pZ*%}9 zT3k|;aL1#!Lo0~=MyU$4tx!7@Y_G-U5nTb>Xpv^Kj|r~IwiTkXt@3=sKhjW7X7xG_ zJ%oC+2^i&^iC^ooWjg{-Z_%cvQmk4~FJ8WSMno6#S!f&x<$EL^=LAraW!5YVlm#HR zh)UL%(iLd4gJS@6={YI756Wm=T_HO4Z6`X^wiWGrwGmZaTZ?ubTg%R_Y@@)B4&G3| zLDa9>Al5EhFIF#FFILW9Csxc}Czj1xCmPmrG+P141XZUk6nNTpKzNY?N~<@B@s~{_ zK!y=;TMzL8Mv66`B3%e0c`UIO1E72gwFf|H3bsqcc^HUAS_J{4`?fWr*TD9o+tyVC zBGtKtZ1pP}#G+4Ei+S&_lz%s@4_T9SfpfSPK7MJzNQfH*v749#fQsZ#XWy`!id(m_;rEB*vqh^N_atq`*(FBh%b zRY>Nk=@GxrYuC>t=2}}E044Zye0q*o>E0v<E#30?0Ps4L?$wnhMcxpU$H1o}EM+ z5?Q6*>Q^<0hINfm<}q&i)@`%J!cSHQgS`TgmuRX}s)*@sA8Y2zpeS$N0Ph zvM&AGiGj!W7S&{qXW7=liu!o9STb#mST=L53^Sy~!caEM^Umbofi=e_dY1bk%?x6n z4~d0rzm9FD7vzoJ>~$a;AUVwV&i$*z;8SWdbto*RnXfMs^QWwoOcRbh3f&1VKm=ZA zsrk|%@%EKs@aS4m(>=RY2mNQtUkeOpc*7l=krFMLD;76vnjZ*`z}-%*la9coZSArK zG5zJGWNt4f=b-ZXMZz1#31=+KjKEWZZNJq`^dUVf&jx?do40;d95a4X_5(GSC%SFN zO0mb8eME=e$vV%%k5-9~o>(Z>E~RL=8Hi;N`393QIwE5^gcvZYtLRVefxO%DuhxlI zubUACP}-OuJ<4&@^h5_@C*NqqsFH1n4jo91gf4usFk-SnKk6;&26yoGV-?A1x^{DYp}u0v?y!+$IgbKb@*6-!)MEEKse4~$}sXgr*fTOHIjBV6Li zHmu(u#{GVps9&k%j}0Ivpb}zTvW%R;)QujEF9v{`Iv%K+6xU(@xrKlTb8TRrsRSN` z7fgMoDdU0q?9ox`J*_Y+r3u^1f1Dv!&Qr5y0>EtcAP6(`}n-vHg(% zkD6N!(TKl|_$zlhPohcvw$9P~&IX!B+=wwnbPZ#R5WR=f(kZ?)s3Jo{l7@18EyqMd zMaVE{EFZuuMLJ6AKgOBI+n`SBh4(chDX7wW`%D4anWUewXxC|3}C1`kx zvS*Z{IYKe8`Z=v*q@iK~b=OS#4^RF1TF=U`0hD&^8I_nv50YRYmTFJogncgP=VhNM zf1_X}Da}5;C-X2?-=OG>!&^8oXFxsLt>gf@|Hyac;tEaxX>s8n`yBd;{P~jutHXY}h3Gl}%NE7AckZ4e z=DxQwn6YXDBM@i$f-mmnI&b+AgPYsojTrN2*`(4Xh$;q4#vvWD*q8${`p2BZr-G!SXADKi+9 zlNODSQjf(d`l1Og6FmIZhX4r~DM|O?^w9`@N&vM0@rIY6Spnn|Luqrq(&A*M{$dW_ z*yW_&VhfM;_4;3wZzTeN1Q11VWBUcO$S`XLhKreE;IYJ1glSwNE~9m3E7MN#mgddY zZ1$^cc+@meQ}u#)WqGW<9)ox`u=WvDdexj^xSY$XTKU%yRO zm>s5YI{iGXWBY^>SSajtT6dV3XXM3O#@GiZ5NV(K@O*X65L;dlMdVjR1#*_}IvKIb z^nq=vCyFS2F-gG=9FdXLI-15lPVvUdIso)!iA9(Mki%T}inRdbd-UjbuxWJF@~X}y?1FZ2A@z;Hno**hvJ-Mc`@7Ai%lhoH{@v^00r!jB{Y#mOr@u( z>fBm%rFC}dX*G<7H4PFt^{X4z{4KqFKM&ntAjL(K4XjXv92^lrj}gAGL|V)nt-*so+kLyc*yfw~mQ8${c+GUN;3N7p zh3)fki7s8H(~)RcR?J3e>;r(E@P8!+(0zx!qk^dkEL517inCcNh&1?AT`Wa83y=&j z6@(w39;B(qXV&k2dM&LI5}ytPar4~8pNmz273Q|OOwr|ImO{l_^8+Xg3niKT9yW`a z_K|dPusFq*2_g$2sUpiXkQ_iUxg(EH2>0Fpx8HD1M#Nya=Pvp}i<*q!VbSF=(=)S= zE~oSW`qn#7e7fMjI3qJ_H+R?a^B%Yv>C}@CF^Gf z^mv#ipc!@D|6CxZK7y%8;k7*ANdrdKvYH1#MyJF-H;`2c z&Yn(nw5|U{#hmR#bb`aS%DAtx$S%9jbNYkoXmQUy#!xKJ!9JKT_Tq29lq{qG$nwRb ztGM)+4Ck`wP$xY-xU`PMdr5?O*&vH3^D#+EUMGlxvP|t*^|a|o^fHZ+4Ii|tY*QgB z^dcYYmgA$v?#-t(b86eHN3?xkvb-=M(zSi{#&S8At(4%&YQdEpK=&N-cGQvRQmBi3 zE(AoJbpRBs#oTXrXHn6bzHmqpZY&aB0Wi?9{LO#mZ#s`Ow3HD44)Ret=pgUjJBOC* zRSPl%TZ%78b-qrmf8Y=F#a(_>D=?nhwSj#mTnXiQP1Jms1*#lb#ZNKPo%BqJ83+JU z9U3*WU7{>gx{P2;0ukQqQI~J!WsIyB;L3mJ-nn8Xf|(h%m_^0`(141B*qRpm^i2dn zGK?a4^VZpggU19~Y_6F?pi6X_=S?@k$()#YzQ4ASfCmGPSVJ!D}8q2+W4(UFY znV!fzE>L)!idjV$rz<&t?mh@23=~utU|e~f?2=${xzc}=0F<`GT@o&^@Z~2@k`pgw z%i;$@5daT9b>Le70%^c@pFgw65PjjzQ^k?VJVgQzKy>nRC1xN1NOqt(piG`%#kDYC zC4wj@KeWWm^!PpMvaJNd1chvvQ}zL^<{<}gC6QQaObj0z#@dP9#db$_m%n`*E6!N3 zL2j15qZlZD-VV^F0xeCC!Y#Cyx_JOJibWWMCTygbgE1$toFMX*q0e)keQC;1Pmhl! z2~=3O`Mn3`Mf@*WK^U0Z8UR@IYFGM321XnvZ7WD*jlUcpU7>YhQv5y&NJe1Q0X8qt z%(5;0C|0gcTY3fpfQkW(RHvZZ;tKB_HQo0*r>}R(7hh*io3KQ@b2lxy%S*OoQK1|x zgn{w>M|6>^mqotL`0q+e4Xq&-&CL>|0&Ox__N=48vt@L zhDVfTPHTZv<@1X!zJ?*9dDbpmy|`YybXB<29Cv1tPtn$Y+L7|!cUUK}HLaQ0K4q0y zl)rXGgLw7Y8Db$VZXSR@Gm29*nb-1osPnLtmA2#ny6eEn5kHXZ;EeiEB8URuxWN*D z$OM>?XUC&^iXD$~N4b3rgfHvN|6rw9@Zl=@-WRP_PoH7p^`l*RpwqOkZA0G~s}Y!H z)N4?C`UFC@?~TcYuW0)9BKlHKF4l2?!Dg!5%tHmlOnWURPkEul40P84lf7oV%@(;W zt`rbO%C~^D4T+CkPjnAd_W{U0k1rBpl@ENCuzuBg0u#L)#*Sus#K zm?{x$3HsFNais>(ek{V82qKGdBI1b^aT`PqHsX9;22_cIu5^bp(3yB!JM_x;38+GV z2d4R))+ze*-;2dM`fYB0d*E z7Gw8qtHl<3bryBIcS!w6OA@~D-HW;O`KFn#Eu+6H)mNJxo5>mhFkFmdoXe{36pMMd zoKwf|R@(7EcZ_^1KAgkD5Q&PloyXFP6riF&2@Y+&NtboljoZp^z+=N#2VWtF-TbMm zY) z(fy=6r%os}1C4k~`hl7ZA`KRw*iz+tKxF1MK)3IO{Y9^x+lyDo+dJp&mAaB)w`ZA| z;YEoTJ<$v4qZRGoDdkT9mrDtE1yBP}X#o2~U>e{~d#S_#x_w`WFh|E0!bA=r*kUUW zkaj<#kNghBj8~RAMV3N+90^4j60HjLkd)0fb|-R;k{pQvK(dNDhr*l&9$ar00C_t# zhxwgXzsUk51Ty6p33+pgKp&Ij@h+zf>#*4%1c?5XlW{VE2LXF7w?OdV1vu^HQZvv# zrU-L0k=fa~7$pM$Z6K**tM^TX+ysoNlNUle9`L!8=FFhc%;gh(xy*Wt1rt5h_I)+z zng>9#*HjaE^%Ns2AL--_`GRuweG&nZ28zga4+8SAPk;<}S($YpPX~s;Q)7S>`^1?m zHEw2>pF=rsJ^=(F7gbMk zFxKf4cQ36U=(gc+hJK(@gD3!=?i`;^<6@Z@ND4rS9}|gJN@3R9;L4-#Sa})rgh)Bzl-v*LTv>9codl>!fw7!}Ssu);eZ?#behw^ReIDxpzsbm+EwMq`4 zTZX-^=Y^q%RO94Tl!MMq%O3$(ilUrVeA(R56bxms#`tzk7Gn-^Bg;;*z6!WL=CwUb zE5gR^Ini(=W&jblU`DW{={dm!Mu!mLYe^2nBoB0=T*`1Ui!~OEzjlS!s)AmMqal|yW|fm=2nb?ys(GRhvH^3mJj>tudU)s53ZT`b_miYvgWn3a(NFc<)_ z?{6#8eBB2;lVubL{oQPq?E2M%s5z13kw>zpEhmUZQZ(c8=BIt<;$Bu3oKf+XwsHjZh1OA z3WhXbP&fWEBSm260ua~-`VPR;mGL4^oNN=$AL&eDfrF z6k0ey!1AWgA&E6v00i;H0KykRdR10dcD?=f+t1v3>#c{jYuC=bO5`TcXyr#BIkk4} z%FjOgY|?3`opy6WLj#xx0K(1yimd&Y#6Zw}@I{dBw0EMtnszAGEn!2wA!^$ka>yZj zj~X>Zzxmyu%JVj70rV4(i8$UwrY!Tb_RU=~pNnlvHbh-;LVL zcs|U*hB*%rwupA|1)!21{!F zdYX<*orI2GxpL*IE3drr{24Q5(ARinW5ndQ^%Ts!?TKw4YTv$n07R5YZ>Bioj5EGZ zd7ulRA^odxs`^2XtnSyZUmp?&f9l-1a}6C&YOF24BUF)LQAKu0l%Hg@CU zB?FkDG-E~rfP)1i(T1@)CRt&4gCNQP95ek77iB+8k1apUg2?z-##Mu(tbGyq!>zFOlM@uGBj$&HidmORkNnkaTg;3uqQ>NVht6%-K@Bw(arkv(LWTv13PB9I5`fT7c<}(B z9t51ad+xdCFMay-scX}wjVu#@$4+%zL(1wQk3IGnES@x$Y>q}}wplR|op;`O#cj9U_B2%nVHe17h#k;x5`fU* zusnOc{PN3_378Q8QAdjrBSyf9$_iz;LR9>zQBSA=FgTz?_9mtD;L}e({bGnl03LM) z8v#tSX3hHN(MKPBs;*~CU?C$qmK@I5*nr3jI4K&gzWV9|s5AdbozV&`fzDtBniS`^ zPdMR(UsAd`MrWt#ET~6dc@lu$^#EuWtIVYO+tzIDefQmW<&HbiG*ONkB38kM|2<*9V;le3D_`wejSiXEY#4QIh zOWOIxDE#XLAgR=og>?GBfdl_sQ&SU&0A``rUVH6d=bd-nb(D^EA6Pe8-{@j95c($w zLepKX0FX_Bo6dYSxDUrm0h>{**-BZIq6c)?)N=Nz3@U07xp%haGlU?+Y%t zU{aSZU3!{tFvI|v#LLO&GKS3d&o%qN`hvBTRi`KgfLLdPb%(G}Z!+r#kdAgMc^*1~ zK~064%=!5@+;GF5|MZ-rf6Ql8Ad9kJB9ERX%nNk1dk2~t9qkaq)H7z}`5CyHr9(UYv z(Ba^B$qN3|jxJS#uLFS40qxE||NMc}=r56SE0^D3#To2FR?Kv2^s~v?2d5#dB=~B$ z1G2VVsQ`rgL;S*%--ULBhUy-E_~E~h71`fm)~|c=$tOqr@sEF;LTMp>v%UaXpR=gx zS~`GO5v`#{eSrKZhoHetvmG8{8oTnV`|rPhoMs<3+w&@-r38>P0BC2kzV`UzkKd2R zSN)D0mSok3W<3<@xpU_}O(P(VrSuTNY3_%j0wQl=EgeA86u$oY>kl6`Y}f-#`OH2L zVWfDzK6>=%OEq}r(_d%MFGItW5_Cnvvph>xT{4JoqBoQpK&ZCV=V%8D zNrjqpouBN#|NcjrZV6_gPe1+il9Nt4=>|%dSD)ML0VFLK(i8fP88hZ}dPBR|X8VE# z3l>wn>&Nrw&!4W@2WERyESKg0P+(C~U18Su>e;jBmJ}zuwXUwNtHZ2ca?35Z>_yfm zp0P!m^(~Qs0^iAl4?g(k0Rsk%cA4$ujlS{PYp;ET8kWs=_TU!6K6qoB13=6|P^e{~ z6)Z%$&i9Tz_Skd4Ld>pUA+YSsnKSP^=9puCO6T+Vv^Hx7lHS|j{N^`%9DMM>&ym^Q z+KzWkpFaIlYSeQzc-U-js`s|J0EEuvp)xS*d(vJTeeJ)uS5(VJ{%_eY8nE1*8A_j{~@J?+0N5VisAAP0Ceb~hrUbqm+k^Grvu03 z^UptDOaKLAo9H=YoNVWvciw@9=#Hfqrh4+^$%2M>U}!E|pI;JyhSP`xFm6j3{+jp0 zMI~iMX||KY@E7F!xda2RsIeq~paVg{Bz@@b3f0;friNz6F_K8HtT~p8CjlZkn(&SbRpcAhIa%2reN#kZ@lrwA1GgoG3#3j0|f!c z0}eRgBkDZ7k-^&!08z+d9=(BQQb*39ORPA3V!WigsM z??N(P+wv40`u+Yp@4WM0bR8aNR-B97fWeq(5~Jg&4zP}7=RuTE=PiEo%{NDC_Ct6f zX-st9uHhGm73#WOcir`B3g7Gu7J>p#Kwd|q7Ox`j^qm?&u*i%>vGd-tIO$s)fba&= zkk5hCc&AeMVlR3FzasXzj6?~BH*mbG6y_`d1YM*jS%BXo7Wg@Nzq^152|%>oz@lqu z*a#e#05C*IF#!}C{z`2yi0W_&{p~|_c%5d8-cD_Rm>(Rcrtwf5BotUmO92qSfrt^o zq=knTZzE;9y7RLA1d^hz7$TF>bhGnC*6hv3D3C zhAsl7R>c-n7y%$=05{d^?dmKuPKG+*{`d?12+s@90z}w(Vq>9Un}WdNt8~!{YeGS6 zjqsFebR3jg0E_2oqtVcDrSgH8Q@Wv#VaMB#4J-(;iQPHHw+YW~X5~8I+0h2P9sz)Y zXGa_G{flLNHmT|@g@N!83?g0`4S&&~Xgq+1SIH`s<$?Qi2jZ>g>D;IrPxA#EmU32_w1_vp#Lt|&_%eWUH(5D@*#cz%mGRJ|7AOO#NzGTz Z{Qsw^$$;aXp4 + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/browseable/SwipeRefreshListFragment/res/layout/activity_main.xml b/samples/browseable/SwipeRefreshListFragment/res/layout/activity_main.xml new file mode 100755 index 000000000..1ae4f981e --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/res/layout/activity_main.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/samples/browseable/SwipeRefreshListFragment/res/menu/main.xml b/samples/browseable/SwipeRefreshListFragment/res/menu/main.xml new file mode 100644 index 000000000..b49c2c526 --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/res/menu/main.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/samples/browseable/SwipeRefreshListFragment/res/menu/main_menu.xml b/samples/browseable/SwipeRefreshListFragment/res/menu/main_menu.xml new file mode 100644 index 000000000..c5e895486 --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/res/menu/main_menu.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/browseable/SwipeRefreshListFragment/res/values-sw600dp/template-dimens.xml b/samples/browseable/SwipeRefreshListFragment/res/values-sw600dp/template-dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/res/values-sw600dp/template-dimens.xml @@ -0,0 +1,24 @@ + + + + + + + @dimen/margin_huge + @dimen/margin_medium + + diff --git a/samples/browseable/SwipeRefreshListFragment/res/values-sw600dp/template-styles.xml b/samples/browseable/SwipeRefreshListFragment/res/values-sw600dp/template-styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/res/values-sw600dp/template-styles.xml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/samples/browseable/SwipeRefreshListFragment/res/values-v11/template-styles.xml b/samples/browseable/SwipeRefreshListFragment/res/values-v11/template-styles.xml new file mode 100644 index 000000000..8c1ea66f2 --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/res/values-v11/template-styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/activities/SampleActivityBase.java new file mode 100644 index 000000000..3228927b7 --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/activities/SampleActivityBase.java @@ -0,0 +1,52 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.example.android.common.activities; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogWrapper; + +/** + * Base launcher activity, to handle most of the common plumbing for samples. + */ +public class SampleActivityBase extends FragmentActivity { + + public static final String TAG = "SampleActivityBase"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + initializeLogging(); + } + + /** Set up targets to receive log data */ + public void initializeLogging() { + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/dummydata/Cheeses.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/dummydata/Cheeses.java new file mode 100644 index 000000000..783735ce3 --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/dummydata/Cheeses.java @@ -0,0 +1,186 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.dummydata; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Random; + +/** + * Dummy data. + */ +public class Cheeses { + static final String[] CHEESES = { + "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi", + "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", + "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese", + "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell", + "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc", + "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss", + "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon", + "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase", + "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese", + "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy", + "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille", + "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore", + "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)", + "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves", + "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur", + "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon", + "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin", + "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)", + "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine", + "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza", + "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)", + "Buxton Blue", "Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta", + "Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie", + "Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat", + "Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano", + "Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain", + "Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou", + "Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar", + "Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno", + "Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack", + "Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper", + "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)", + "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese", + "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza", + "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley", + "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino", + "Cypress Grove Chevre", "Danablu (Danish Blue)", "Danbo", "Danish Fontina", + "Daralagjazsky", "Dauphin", "Delice des Fiouves", "Denhany Dorset Drum", "Derby", + "Dessertnyj Belyj", "Devon Blue", "Devon Garland", "Dolcelatte", "Doolin", + "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester", "Double Worcester", + "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra", "Dunlop", "Dunsyre Blue", + "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz", + "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne", "Esbareich", + "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo", "Exmoor Blue", + "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle", + "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis", "Flor de Guia", + "Flower Marie", "Folded", "Folded cheese with mint", "Fondant de Brebis", + "Fontainebleau", "Fontal", "Fontina Val d'Aosta", "Formaggio di capra", "Fougerus", + "Four Herb Gouda", "Fourme d' Ambert", "Fourme de Haute Loire", "Fourme de Montbrison", + "Fresh Jack", "Fresh Mozzarella", "Fresh Ricotta", "Fresh Truffles", "Fribourgeois", + "Friesekaas", "Friesian", "Friesla", "Frinault", "Fromage a Raclette", "Fromage Corse", + "Fromage de Montagne de Savoie", "Fromage Frais", "Fruit Cream Cheese", + "Frying Cheese", "Fynbo", "Gabriel", "Galette du Paludier", "Galette Lyonnaise", + "Galloway Goat's Milk Gems", "Gammelost", "Gaperon a l'Ail", "Garrotxa", "Gastanberra", + "Geitost", "Gippsland Blue", "Gjetost", "Gloucester", "Golden Cross", "Gorgonzola", + "Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost", + "Grafton Village Cheddar", "Grana", "Grana Padano", "Grand Vatel", + "Grataron d' Areches", "Gratte-Paille", "Graviera", "Greuilh", "Greve", + "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi", + "Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti", + "Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve", + "Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster", + "Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg", + "Jermi Tortes", "Jibneh Arabieh", "Jindi Brie", "Jubilee Blue", "Juustoleipa", + "Kadchgall", "Kaseri", "Kashta", "Kefalotyri", "Kenafa", "Kernhem", "Kervella Affine", + "Kikorangi", "King Island Cape Wickham Brie", "King River Gold", "Klosterkaese", + "Knockalara", "Kugelkase", "L'Aveyronnais", "L'Ecir de l'Aubrac", "La Taupiniere", + "La Vache Qui Rit", "Laguiole", "Lairobell", "Lajta", "Lanark Blue", "Lancashire", + "Langres", "Lappi", "Laruns", "Lavistown", "Le Brin", "Le Fium Orbo", "Le Lacandou", + "Le Roule", "Leafield", "Lebbene", "Leerdammer", "Leicester", "Leyden", "Limburger", + "Lincolnshire Poacher", "Lingot Saint Bousquet d'Orb", "Liptauer", "Little Rydings", + "Livarot", "Llanboidy", "Llanglofan Farmhouse", "Loch Arthur Farmhouse", + "Loddiswell Avondale", "Longhorn", "Lou Palou", "Lou Pevre", "Lyonnais", "Maasdam", + "Macconais", "Mahoe Aged Gouda", "Mahon", "Malvern", "Mamirolle", "Manchego", + "Manouri", "Manur", "Marble Cheddar", "Marbled Cheeses", "Maredsous", "Margotin", + "Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)", + "Mascarpone Torta", "Matocq", "Maytag Blue", "Meira", "Menallack Farmhouse", + "Menonita", "Meredith Blue", "Mesost", "Metton (Cancoillotte)", "Meyer Vintage Gouda", + "Mihalic Peynir", "Milleens", "Mimolette", "Mine-Gabhar", "Mini Baby Bells", "Mixte", + "Molbo", "Monastery Cheeses", "Mondseer", "Mont D'or Lyonnais", "Montasio", + "Monterey Jack", "Monterey Jack Dry", "Morbier", "Morbier Cru de Montagne", + "Mothais a la Feuille", "Mozzarella", "Mozzarella (Australian)", + "Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster", + "Murol", "Mycella", "Myzithra", "Naboulsi", "Nantais", "Neufchatel", + "Neufchatel (Australian)", "Niolo", "Nokkelost", "Northumberland", "Oaxaca", + "Olde York", "Olivet au Foin", "Olivet Bleu", "Olivet Cendre", + "Orkney Extra Mature Cheddar", "Orla", "Oschtjepka", "Ossau Fermier", "Ossau-Iraty", + "Oszczypek", "Oxford Blue", "P'tit Berrichon", "Palet de Babligny", "Paneer", "Panela", + "Pannerone", "Pant ys Gawn", "Parmesan (Parmigiano)", "Parmigiano Reggiano", + "Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage", + "Patefine Fort", "Pave d'Affinois", "Pave d'Auge", "Pave de Chirac", "Pave du Berry", + "Pecorino", "Pecorino in Walnut Leaves", "Pecorino Romano", "Peekskill Pyramid", + "Pelardon des Cevennes", "Pelardon des Corbieres", "Penamellera", "Penbryn", + "Pencarreg", "Perail de Brebis", "Petit Morin", "Petit Pardou", "Petit-Suisse", + "Picodon de Chevre", "Picos de Europa", "Piora", "Pithtviers au Foin", + "Plateau de Herve", "Plymouth Cheese", "Podhalanski", "Poivre d'Ane", "Polkolbin", + "Pont l'Eveque", "Port Nicholson", "Port-Salut", "Postel", "Pouligny-Saint-Pierre", + "Pourly", "Prastost", "Pressato", "Prince-Jean", "Processed Cheddar", "Provolone", + "Provolone (Australian)", "Pyengana Cheddar", "Pyramide", "Quark", + "Quark (Australian)", "Quartirolo Lombardo", "Quatre-Vents", "Quercy Petit", + "Queso Blanco", "Queso Blanco con Frutas --Pina y Mango", "Queso de Murcia", + "Queso del Montsec", "Queso del Tietar", "Queso Fresco", "Queso Fresco (Adobera)", + "Queso Iberico", "Queso Jalapeno", "Queso Majorero", "Queso Media Luna", + "Queso Para Frier", "Queso Quesadilla", "Rabacal", "Raclette", "Ragusano", "Raschera", + "Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou", + "Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder", + "Rigotte", "Rocamadour", "Rollot", "Romano", "Romans Part Dieu", "Roncal", "Roquefort", + "Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr", + "Saanenkaese", "Saga", "Sage Derby", "Sainte Maure", "Saint-Marcellin", + "Saint-Nectaire", "Saint-Paulin", "Salers", "Samso", "San Simon", "Sancerre", + "Sap Sago", "Sardo", "Sardo Egyptian", "Sbrinz", "Scamorza", "Schabzieger", "Schloss", + "Selles sur Cher", "Selva", "Serat", "Seriously Strong Cheddar", "Serra da Estrela", + "Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda", + "Somerset Brie", "Sonoma Jack", "Sottocenare al Tartufo", "Soumaintrain", + "Sourire Lozerien", "Spenwood", "Sraffordshire Organic", "St. Agur Blue Cheese", + "Stilton", "Stinking Bishop", "String", "Sussex Slipcote", "Sveciaost", "Swaledale", + "Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie", + "Tasmania Highland Chevre Log", "Taupiniere", "Teifi", "Telemea", "Testouri", + "Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar", + "Tilsit", "Timboon Brie", "Toma", "Tomme Brulee", "Tomme d'Abondance", + "Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes", + "Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet", + "Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe", + "Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa", + "Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois", + "Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue", + "Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington", + "Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou", + "Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue", + "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano" + }; + + public static ArrayList asList() { + ArrayList items = new ArrayList(); + for (int i = 0, z = CHEESES.length ; i < z ; i++) { + items.add(CHEESES[i]); + } + return items; + } + + /** + * Return a list of random cheeses. + * + * @param count the amount of cheeses to return. + */ + public static ArrayList randomList(int count) { + Random random = new Random(); + HashSet items = new HashSet(); + + // Make sure that don't infinity loop + count = Math.min(count, CHEESES.length); + + while (items.size() < count) { + items.add(CHEESES[random.nextInt(CHEESES.length)]); + } + + return new ArrayList(items); + } +} diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/Log.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/Log.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + *

When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.

+ */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +} \ No newline at end of file diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogNode.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.example.android.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogView.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogView.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.example.android.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/view/SlidingTabLayout.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/view/SlidingTabLayout.java new file mode 100644 index 000000000..20049e335 --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/view/SlidingTabLayout.java @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.view; + +import android.content.Context; +import android.graphics.Typeface; +import android.os.Build; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.HorizontalScrollView; +import android.widget.TextView; + +/** + * To be used with ViewPager to provide a tab indicator component which give constant feedback as to + * the user's scroll progress. + *

+ * To use the component, simply add it to your view hierarchy. Then in your + * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call + * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for. + *

+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors + * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The + * alternative is via the {@link TabColorizer} interface which provides you complete control over + * which color is used for any individual position. + *

+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)}, + * providing the layout ID of your custom layout. + */ +public class SlidingTabLayout extends HorizontalScrollView { + + /** + * Allows complete control over the colors drawn in the tab layout. Set with + * {@link #setCustomTabColorizer(TabColorizer)}. + */ + public interface TabColorizer { + + /** + * @return return the color of the indicator used when {@code position} is selected. + */ + int getIndicatorColor(int position); + + /** + * @return return the color of the divider drawn to the right of {@code position}. + */ + int getDividerColor(int position); + + } + + private static final int TITLE_OFFSET_DIPS = 24; + private static final int TAB_VIEW_PADDING_DIPS = 16; + private static final int TAB_VIEW_TEXT_SIZE_SP = 12; + + private int mTitleOffset; + + private int mTabViewLayoutId; + private int mTabViewTextViewId; + + private ViewPager mViewPager; + private ViewPager.OnPageChangeListener mViewPagerPageChangeListener; + + private final SlidingTabStrip mTabStrip; + + public SlidingTabLayout(Context context) { + this(context, null); + } + + public SlidingTabLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + // Disable the Scroll Bar + setHorizontalScrollBarEnabled(false); + // Make sure that the Tab Strips fills this View + setFillViewport(true); + + mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density); + + mTabStrip = new SlidingTabStrip(context); + addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + } + + /** + * Set the custom {@link TabColorizer} to be used. + * + * If you only require simple custmisation then you can use + * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve + * similar effects. + */ + public void setCustomTabColorizer(TabColorizer tabColorizer) { + mTabStrip.setCustomTabColorizer(tabColorizer); + } + + /** + * Sets the colors to be used for indicating the selected tab. These colors are treated as a + * circular array. Providing one color will mean that all tabs are indicated with the same color. + */ + public void setSelectedIndicatorColors(int... colors) { + mTabStrip.setSelectedIndicatorColors(colors); + } + + /** + * Sets the colors to be used for tab dividers. These colors are treated as a circular array. + * Providing one color will mean that all tabs are indicated with the same color. + */ + public void setDividerColors(int... colors) { + mTabStrip.setDividerColors(colors); + } + + /** + * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are + * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so + * that the layout can update it's scroll position correctly. + * + * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener) + */ + public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { + mViewPagerPageChangeListener = listener; + } + + /** + * Set the custom layout to be inflated for the tab views. + * + * @param layoutResId Layout id to be inflated + * @param textViewId id of the {@link TextView} in the inflated view + */ + public void setCustomTabView(int layoutResId, int textViewId) { + mTabViewLayoutId = layoutResId; + mTabViewTextViewId = textViewId; + } + + /** + * Sets the associated view pager. Note that the assumption here is that the pager content + * (number of tabs and tab titles) does not change after this call has been made. + */ + public void setViewPager(ViewPager viewPager) { + mTabStrip.removeAllViews(); + + mViewPager = viewPager; + if (viewPager != null) { + viewPager.setOnPageChangeListener(new InternalViewPagerListener()); + populateTabStrip(); + } + } + + /** + * Create a default view to be used for tabs. This is called if a custom tab view is not set via + * {@link #setCustomTabView(int, int)}. + */ + protected TextView createDefaultTabView(Context context) { + TextView textView = new TextView(context); + textView.setGravity(Gravity.CENTER); + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP); + textView.setTypeface(Typeface.DEFAULT_BOLD); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + // If we're running on Honeycomb or newer, then we can use the Theme's + // selectableItemBackground to ensure that the View has a pressed state + TypedValue outValue = new TypedValue(); + getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, + outValue, true); + textView.setBackgroundResource(outValue.resourceId); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style + textView.setAllCaps(true); + } + + int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density); + textView.setPadding(padding, padding, padding, padding); + + return textView; + } + + private void populateTabStrip() { + final PagerAdapter adapter = mViewPager.getAdapter(); + final View.OnClickListener tabClickListener = new TabClickListener(); + + for (int i = 0; i < adapter.getCount(); i++) { + View tabView = null; + TextView tabTitleView = null; + + if (mTabViewLayoutId != 0) { + // If there is a custom tab view layout id set, try and inflate it + tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip, + false); + tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId); + } + + if (tabView == null) { + tabView = createDefaultTabView(getContext()); + } + + if (tabTitleView == null && TextView.class.isInstance(tabView)) { + tabTitleView = (TextView) tabView; + } + + tabTitleView.setText(adapter.getPageTitle(i)); + tabView.setOnClickListener(tabClickListener); + + mTabStrip.addView(tabView); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mViewPager != null) { + scrollToTab(mViewPager.getCurrentItem(), 0); + } + } + + private void scrollToTab(int tabIndex, int positionOffset) { + final int tabStripChildCount = mTabStrip.getChildCount(); + if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) { + return; + } + + View selectedChild = mTabStrip.getChildAt(tabIndex); + if (selectedChild != null) { + int targetScrollX = selectedChild.getLeft() + positionOffset; + + if (tabIndex > 0 || positionOffset > 0) { + // If we're not at the first child and are mid-scroll, make sure we obey the offset + targetScrollX -= mTitleOffset; + } + + scrollTo(targetScrollX, 0); + } + } + + private class InternalViewPagerListener implements ViewPager.OnPageChangeListener { + private int mScrollState; + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + int tabStripChildCount = mTabStrip.getChildCount(); + if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) { + return; + } + + mTabStrip.onViewPagerPageChanged(position, positionOffset); + + View selectedTitle = mTabStrip.getChildAt(position); + int extraOffset = (selectedTitle != null) + ? (int) (positionOffset * selectedTitle.getWidth()) + : 0; + scrollToTab(position, extraOffset); + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrolled(position, positionOffset, + positionOffsetPixels); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + mScrollState = state; + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrollStateChanged(state); + } + } + + @Override + public void onPageSelected(int position) { + if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { + mTabStrip.onViewPagerPageChanged(position, 0f); + scrollToTab(position, 0); + } + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageSelected(position); + } + } + + } + + private class TabClickListener implements View.OnClickListener { + @Override + public void onClick(View v) { + for (int i = 0; i < mTabStrip.getChildCount(); i++) { + if (v == mTabStrip.getChildAt(i)) { + mViewPager.setCurrentItem(i); + return; + } + } + } + } + +} diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/view/SlidingTabStrip.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/view/SlidingTabStrip.java new file mode 100644 index 000000000..d5bbbae59 --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/view/SlidingTabStrip.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.view; + +import android.R; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.widget.LinearLayout; + +class SlidingTabStrip extends LinearLayout { + + private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2; + private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26; + private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8; + private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5; + + private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1; + private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20; + private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f; + + private final int mBottomBorderThickness; + private final Paint mBottomBorderPaint; + + private final int mSelectedIndicatorThickness; + private final Paint mSelectedIndicatorPaint; + + private final int mDefaultBottomBorderColor; + + private final Paint mDividerPaint; + private final float mDividerHeight; + + private int mSelectedPosition; + private float mSelectionOffset; + + private SlidingTabLayout.TabColorizer mCustomTabColorizer; + private final SimpleTabColorizer mDefaultTabColorizer; + + SlidingTabStrip(Context context) { + this(context, null); + } + + SlidingTabStrip(Context context, AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + + final float density = getResources().getDisplayMetrics().density; + + TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true); + final int themeForegroundColor = outValue.data; + + mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor, + DEFAULT_BOTTOM_BORDER_COLOR_ALPHA); + + mDefaultTabColorizer = new SimpleTabColorizer(); + mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR); + mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor, + DEFAULT_DIVIDER_COLOR_ALPHA)); + + mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density); + mBottomBorderPaint = new Paint(); + mBottomBorderPaint.setColor(mDefaultBottomBorderColor); + + mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density); + mSelectedIndicatorPaint = new Paint(); + + mDividerHeight = DEFAULT_DIVIDER_HEIGHT; + mDividerPaint = new Paint(); + mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density)); + } + + void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) { + mCustomTabColorizer = customTabColorizer; + invalidate(); + } + + void setSelectedIndicatorColors(int... colors) { + // Make sure that the custom colorizer is removed + mCustomTabColorizer = null; + mDefaultTabColorizer.setIndicatorColors(colors); + invalidate(); + } + + void setDividerColors(int... colors) { + // Make sure that the custom colorizer is removed + mCustomTabColorizer = null; + mDefaultTabColorizer.setDividerColors(colors); + invalidate(); + } + + void onViewPagerPageChanged(int position, float positionOffset) { + mSelectedPosition = position; + mSelectionOffset = positionOffset; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + final int height = getHeight(); + final int childCount = getChildCount(); + final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height); + final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null + ? mCustomTabColorizer + : mDefaultTabColorizer; + + // Thick colored underline below the current selection + if (childCount > 0) { + View selectedTitle = getChildAt(mSelectedPosition); + int left = selectedTitle.getLeft(); + int right = selectedTitle.getRight(); + int color = tabColorizer.getIndicatorColor(mSelectedPosition); + + if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) { + int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1); + if (color != nextColor) { + color = blendColors(nextColor, color, mSelectionOffset); + } + + // Draw the selection partway between the tabs + View nextTitle = getChildAt(mSelectedPosition + 1); + left = (int) (mSelectionOffset * nextTitle.getLeft() + + (1.0f - mSelectionOffset) * left); + right = (int) (mSelectionOffset * nextTitle.getRight() + + (1.0f - mSelectionOffset) * right); + } + + mSelectedIndicatorPaint.setColor(color); + + canvas.drawRect(left, height - mSelectedIndicatorThickness, right, + height, mSelectedIndicatorPaint); + } + + // Thin underline along the entire bottom edge + canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint); + + // Vertical separators between the titles + int separatorTop = (height - dividerHeightPx) / 2; + for (int i = 0; i < childCount - 1; i++) { + View child = getChildAt(i); + mDividerPaint.setColor(tabColorizer.getDividerColor(i)); + canvas.drawLine(child.getRight(), separatorTop, child.getRight(), + separatorTop + dividerHeightPx, mDividerPaint); + } + } + + /** + * Set the alpha value of the {@code color} to be the given {@code alpha} value. + */ + private static int setColorAlpha(int color, byte alpha) { + return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)); + } + + /** + * Blend {@code color1} and {@code color2} using the given ratio. + * + * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend, + * 0.0 will return {@code color2}. + */ + private static int blendColors(int color1, int color2, float ratio) { + final float inverseRation = 1f - ratio; + float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation); + float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation); + float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation); + return Color.rgb((int) r, (int) g, (int) b); + } + + private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer { + private int[] mIndicatorColors; + private int[] mDividerColors; + + @Override + public final int getIndicatorColor(int position) { + return mIndicatorColors[position % mIndicatorColors.length]; + } + + @Override + public final int getDividerColor(int position) { + return mDividerColors[position % mDividerColors.length]; + } + + void setIndicatorColors(int... colors) { + mIndicatorColors = colors; + } + + void setDividerColors(int... colors) { + mDividerColors = colors; + } + } +} \ No newline at end of file diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/MainActivity.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/MainActivity.java new file mode 100644 index 000000000..7f7a7c8f4 --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/MainActivity.java @@ -0,0 +1,110 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + + + + +package com.example.android.swiperefreshlistfragment; + +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.ViewAnimator; + +import com.example.android.common.activities.SampleActivityBase; +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogFragment; +import com.example.android.common.logger.LogWrapper; +import com.example.android.common.logger.MessageOnlyLogFilter; + +/** + * A simple launcher activity containing a summary sample description, sample log and a custom + * {@link android.support.v4.app.Fragment} which can display a view. + *

+ * For devices with displays with a width of 720dp or greater, the sample log is always visible, + * on other devices it's visibility is controlled by an item on the Action Bar. + */ +public class MainActivity extends SampleActivityBase { + + public static final String TAG = "MainActivity"; + + // Whether the Log Fragment is currently shown + private boolean mLogShown; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + SwipeRefreshListFragmentFragment fragment = new SwipeRefreshListFragmentFragment(); + transaction.replace(R.id.sample_content_fragment, fragment); + transaction.commit(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + MenuItem logToggle = menu.findItem(R.id.menu_toggle_log); + logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator); + logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log); + + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch(item.getItemId()) { + case R.id.menu_toggle_log: + mLogShown = !mLogShown; + ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output); + if (mLogShown) { + output.setDisplayedChild(1); + } else { + output.setDisplayedChild(0); + } + supportInvalidateOptionsMenu(); + return true; + } + return super.onOptionsItemSelected(item); + } + + /** Create a chain of targets that will receive log data */ + @Override + public void initializeLogging() { + // Wraps Android's native log framework. + LogWrapper logWrapper = new LogWrapper(); + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + Log.setLogNode(logWrapper); + + // Filter strips out everything except the message text. + MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); + logWrapper.setNext(msgFilter); + + // On screen logging via a fragment with a TextView. + LogFragment logFragment = (LogFragment) getSupportFragmentManager() + .findFragmentById(R.id.log_fragment); + msgFilter.setNext(logFragment.getLogView()); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/SwipeRefreshListFragment.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/SwipeRefreshListFragment.java new file mode 100644 index 000000000..57969760d --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/SwipeRefreshListFragment.java @@ -0,0 +1,166 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.swiperefreshlistfragment; + +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.support.v4.view.ViewCompat; +import android.support.v4.widget.SwipeRefreshLayout; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ListView; + +/** + * Subclass of {@link android.support.v4.app.ListFragment} which provides automatic support for + * providing the 'swipe-to-refresh' UX gesture by wrapping the the content view in a + * {@link android.support.v4.widget.SwipeRefreshLayout}. + */ +public class SwipeRefreshListFragment extends ListFragment { + + private SwipeRefreshLayout mSwipeRefreshLayout; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + // Create the list fragment's content view by calling the super method + final View listFragmentView = super.onCreateView(inflater, container, savedInstanceState); + + // Now create a SwipeRefreshLayout to wrap the fragment's content view + mSwipeRefreshLayout = new ListFragmentSwipeRefreshLayout(container.getContext()); + + // Add the list fragment's content view to the SwipeRefreshLayout, making sure that it fills + // the SwipeRefreshLayout + mSwipeRefreshLayout.addView(listFragmentView, + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + + // Make sure that the SwipeRefreshLayout will fill the fragment + mSwipeRefreshLayout.setLayoutParams( + new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + // Now return the SwipeRefreshLayout as this fragment's content view + return mSwipeRefreshLayout; + } + + /** + * Set the {@link android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener} to listen for + * initiated refreshes. + * + * @see android.support.v4.widget.SwipeRefreshLayout#setOnRefreshListener(android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener) + */ + public void setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener listener) { + mSwipeRefreshLayout.setOnRefreshListener(listener); + } + + /** + * Returns whether the {@link android.support.v4.widget.SwipeRefreshLayout} is currently + * refreshing or not. + * + * @see android.support.v4.widget.SwipeRefreshLayout#isRefreshing() + */ + public boolean isRefreshing() { + return mSwipeRefreshLayout.isRefreshing(); + } + + /** + * Set whether the {@link android.support.v4.widget.SwipeRefreshLayout} should be displaying + * that it is refreshing or not. + * + * @see android.support.v4.widget.SwipeRefreshLayout#setRefreshing(boolean) + */ + public void setRefreshing(boolean refreshing) { + mSwipeRefreshLayout.setRefreshing(refreshing); + } + + /** + * Set the color scheme for the {@link android.support.v4.widget.SwipeRefreshLayout}. + * + * @see android.support.v4.widget.SwipeRefreshLayout#setColorScheme(int, int, int, int) + */ + public void setColorScheme(int colorRes1, int colorRes2, int colorRes3, int colorRes4) { + mSwipeRefreshLayout.setColorScheme(colorRes1, colorRes2, colorRes3, colorRes4); + } + + /** + * @return the fragment's {@link android.support.v4.widget.SwipeRefreshLayout} widget. + */ + public SwipeRefreshLayout getSwipeRefreshLayout() { + return mSwipeRefreshLayout; + } + + /** + * Sub-class of {@link android.support.v4.widget.SwipeRefreshLayout} for use in this + * {@link android.support.v4.app.ListFragment}. The reason that this is needed is because + * {@link android.support.v4.widget.SwipeRefreshLayout} only supports a single child, which it + * expects to be the one which triggers refreshes. In our case the layout's child is the content + * view returned from + * {@link android.support.v4.app.ListFragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)} + * which is a {@link android.view.ViewGroup}. + * + *

To enable 'swipe-to-refresh' support via the {@link android.widget.ListView} we need to + * override the default behavior and properly signal when a gesture is possible. This is done by + * overriding {@link #canChildScrollUp()}. + */ + private class ListFragmentSwipeRefreshLayout extends SwipeRefreshLayout { + + public ListFragmentSwipeRefreshLayout(Context context) { + super(context); + } + + /** + * As mentioned above, we need to override this method to properly signal when a + * 'swipe-to-refresh' is possible. + * + * @return true if the {@link android.widget.ListView} is visible and can scroll up. + */ + @Override + public boolean canChildScrollUp() { + final ListView listView = getListView(); + if (listView.getVisibility() == View.VISIBLE) { + return canListViewScrollUp(listView); + } else { + return false; + } + } + + } + + // BEGIN_INCLUDE (check_list_can_scroll) + /** + * Utility method to check whether a {@link ListView} can scroll up from it's current position. + * Handles platform version differences, providing backwards compatible functionality where + * needed. + */ + private static boolean canListViewScrollUp(ListView listView) { + if (android.os.Build.VERSION.SDK_INT >= 14) { + // For ICS and above we can call canScrollVertically() to determine this + return ViewCompat.canScrollVertically(listView, -1); + } else { + // Pre-ICS we need to manually check the first visible item and the child view's top + // value + return listView.getChildCount() > 0 && + (listView.getFirstVisiblePosition() > 0 + || listView.getChildAt(0).getTop() < listView.getPaddingTop()); + } + } + // END_INCLUDE (check_list_can_scroll) + +} diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/SwipeRefreshListFragmentFragment.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/SwipeRefreshListFragmentFragment.java new file mode 100644 index 000000000..1147ea819 --- /dev/null +++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/SwipeRefreshListFragmentFragment.java @@ -0,0 +1,232 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.swiperefreshlistfragment; + +import com.example.android.common.dummydata.Cheeses; +import com.example.android.common.logger.Log; + +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.widget.SwipeRefreshLayout; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListAdapter; + +import java.util.List; + +/** + * A sample which shows how to use {@link android.support.v4.widget.SwipeRefreshLayout} within a + * {@link android.support.v4.app.ListFragment} to add the 'swipe-to-refresh' gesture to a + * {@link android.widget.ListView}. This is provided through the provided re-usable + * {@link SwipeRefreshListFragment} class. + * + *

To provide an accessible way to trigger the refresh, this app also provides a refresh + * action item. This item should be displayed in the Action Bar's overflow item. + * + *

In this sample app, the refresh updates the ListView with a random set of new items. + * + *

This sample also provides the functionality to change the colors displayed in the + * {@link android.support.v4.widget.SwipeRefreshLayout} through the options menu. This is meant to + * showcase the use of color rather than being something that should be integrated into apps. + */ +public class SwipeRefreshListFragmentFragment extends SwipeRefreshListFragment { + + private static final String LOG_TAG = SwipeRefreshListFragmentFragment.class.getSimpleName(); + + private static final int LIST_ITEM_COUNT = 20; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Notify the system to allow an options menu for this fragment. + setHasOptionsMenu(true); + } + + // BEGIN_INCLUDE (setup_views) + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + /** + * Create an ArrayAdapter to contain the data for the ListView. Each item in the ListView + * uses the system-defined simple_list_item_1 layout that contains one TextView. + */ + ListAdapter adapter = new ArrayAdapter( + getActivity(), + android.R.layout.simple_list_item_1, + android.R.id.text1, + Cheeses.randomList(LIST_ITEM_COUNT)); + + // Set the adapter between the ListView and its backing data. + setListAdapter(adapter); + + // BEGIN_INCLUDE (setup_refreshlistener) + /** + * Implement {@link SwipeRefreshLayout.OnRefreshListener}. When users do the "swipe to + * refresh" gesture, SwipeRefreshLayout invokes + * {@link SwipeRefreshLayout.OnRefreshListener#onRefresh onRefresh()}. In + * {@link SwipeRefreshLayout.OnRefreshListener#onRefresh onRefresh()}, call a method that + * refreshes the content. Call the same method in response to the Refresh action from the + * action bar. + */ + setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + Log.i(LOG_TAG, "onRefresh called from SwipeRefreshLayout"); + + initiateRefresh(); + } + }); + // END_INCLUDE (setup_refreshlistener) + } + // END_INCLUDE (setup_views) + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.main_menu, menu); + } + + // BEGIN_INCLUDE (setup_refresh_menu_listener) + /** + * Respond to the user's selection of the Refresh action item. Start the SwipeRefreshLayout + * progress bar, then initiate the background task that refreshes the content. + * + *

A color scheme menu item used for demonstrating the use of SwipeRefreshLayout's color + * scheme functionality. This kind of menu item should not be incorporated into your app, + * it just to demonstrate the use of color. Instead you should choose a color scheme based + * off of your application's branding. + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_refresh: + Log.i(LOG_TAG, "Refresh menu item selected"); + + // We make sure that the SwipeRefreshLayout is displaying it's refreshing indicator + if (!isRefreshing()) { + setRefreshing(true); + } + + // Start our refresh background task + initiateRefresh(); + return true; + + case R.id.menu_color_scheme_1: + Log.i(LOG_TAG, "setColorScheme #1"); + item.setChecked(true); + + // Change the colors displayed by the SwipeRefreshLayout by providing it with 4 + // color resource ids + setColorScheme(R.color.color_scheme_1_1, R.color.color_scheme_1_2, + R.color.color_scheme_1_3, R.color.color_scheme_1_4); + return true; + + case R.id.menu_color_scheme_2: + Log.i(LOG_TAG, "setColorScheme #2"); + item.setChecked(true); + + // Change the colors displayed by the SwipeRefreshLayout by providing it with 4 + // color resource ids + setColorScheme(R.color.color_scheme_2_1, R.color.color_scheme_2_2, + R.color.color_scheme_2_3, R.color.color_scheme_2_4); + return true; + + case R.id.menu_color_scheme_3: + Log.i(LOG_TAG, "setColorScheme #3"); + item.setChecked(true); + + // Change the colors displayed by the SwipeRefreshLayout by providing it with 4 + // color resource ids + setColorScheme(R.color.color_scheme_3_1, R.color.color_scheme_3_2, + R.color.color_scheme_3_3, R.color.color_scheme_3_4); + return true; + } + + return super.onOptionsItemSelected(item); + } + // END_INCLUDE (setup_refresh_menu_listener) + + // BEGIN_INCLUDE (initiate_refresh) + /** + * By abstracting the refresh process to a single method, the app allows both the + * SwipeGestureLayout onRefresh() method and the Refresh action item to refresh the content. + */ + private void initiateRefresh() { + Log.i(LOG_TAG, "initiateRefresh"); + + /** + * Execute the background task, which uses {@link android.os.AsyncTask} to load the data. + */ + new DummyBackgroundTask().execute(); + } + // END_INCLUDE (initiate_refresh) + + // BEGIN_INCLUDE (refresh_complete) + /** + * When the AsyncTask finishes, it calls onRefreshComplete(), which updates the data in the + * ListAdapter and turns off the progress bar. + */ + private void onRefreshComplete(List result) { + Log.i(LOG_TAG, "onRefreshComplete"); + + // Remove all items from the ListAdapter, and then replace them with the new items + ArrayAdapter adapter = (ArrayAdapter) getListAdapter(); + adapter.clear(); + for (String cheese : result) { + adapter.add(cheese); + } + + // Stop the refreshing indicator + setRefreshing(false); + } + // END_INCLUDE (refresh_complete) + + /** + * Dummy {@link AsyncTask} which simulates a long running task to fetch new cheeses. + */ + private class DummyBackgroundTask extends AsyncTask> { + + static final int TASK_DURATION = 3 * 1000; // 3 seconds + + @Override + protected List doInBackground(Void... params) { + // Sleep for a small amount of time to simulate a background-task + try { + Thread.sleep(TASK_DURATION); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + // Return a new random list of cheeses + return Cheeses.randomList(LIST_ITEM_COUNT); + } + + @Override + protected void onPostExecute(List result) { + super.onPostExecute(result); + + // Tell the Fragment that the refresh has completed + onRefreshComplete(result); + } + + } + +} diff --git a/samples/browseable/SwipeRefreshMultipleViews/AndroidManifest.xml b/samples/browseable/SwipeRefreshMultipleViews/AndroidManifest.xml new file mode 100644 index 000000000..aba15e066 --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/AndroidManifest.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/browseable/SwipeRefreshMultipleViews/_index.jd b/samples/browseable/SwipeRefreshMultipleViews/_index.jd new file mode 100644 index 000000000..78d438608 --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/_index.jd @@ -0,0 +1,15 @@ + + + +page.tags="SwipeRefreshMultipleViews" +sample.group=UI +@jd:body + +

+ + A sample which shows how to use SwipeRefreshLayout to add the \'swipe-to-refresh\' + gesture to a layout with multiple children, enabling the ability to trigger a + refresh from swiping down on the visible view. In this sample, SwipeRefreshLayout + contains a scrollable GridView, along with a TextView empty view. + +

diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/drawable-hdpi/ic_launcher.png b/samples/browseable/SwipeRefreshMultipleViews/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..9fbf7ab584323a7fae599646bbcbbcf54b01c68e GIT binary patch literal 4341 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 literal 0 HcmV?d00001 diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/drawable-hdpi/tile.9.png b/samples/browseable/SwipeRefreshMultipleViews/res/drawable-hdpi/tile.9.png new file mode 100644 index 0000000000000000000000000000000000000000..135862883e26eddce2b19db021adf62e10357ad0 GIT binary patch literal 196 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 D2}h5$ literal 0 HcmV?d00001 diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/SwipeRefreshMultipleViews/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d19455cfd7b74f51a02f0c554b0a3550c8335f8a GIT binary patch literal 11153 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 + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/layout/activity_main.xml b/samples/browseable/SwipeRefreshMultipleViews/res/layout/activity_main.xml new file mode 100755 index 000000000..1ae4f981e --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/res/layout/activity_main.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/layout/fragment_sample.xml b/samples/browseable/SwipeRefreshMultipleViews/res/layout/fragment_sample.xml new file mode 100644 index 000000000..077f3f13f --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/res/layout/fragment_sample.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/menu/main.xml b/samples/browseable/SwipeRefreshMultipleViews/res/menu/main.xml new file mode 100644 index 000000000..b49c2c526 --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/res/menu/main.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/menu/main_menu.xml b/samples/browseable/SwipeRefreshMultipleViews/res/menu/main_menu.xml new file mode 100644 index 000000000..d8fa6e6e9 --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/res/menu/main_menu.xml @@ -0,0 +1,30 @@ + + + + + + + + + + \ No newline at end of file diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/values-sw600dp/template-dimens.xml b/samples/browseable/SwipeRefreshMultipleViews/res/values-sw600dp/template-dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/res/values-sw600dp/template-dimens.xml @@ -0,0 +1,24 @@ + + + + + + + @dimen/margin_huge + @dimen/margin_medium + + diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/values-sw600dp/template-styles.xml b/samples/browseable/SwipeRefreshMultipleViews/res/values-sw600dp/template-styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/res/values-sw600dp/template-styles.xml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/values-v11/template-styles.xml b/samples/browseable/SwipeRefreshMultipleViews/res/values-v11/template-styles.xml new file mode 100644 index 000000000..8c1ea66f2 --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/res/values-v11/template-styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/activities/SampleActivityBase.java new file mode 100644 index 000000000..3228927b7 --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/activities/SampleActivityBase.java @@ -0,0 +1,52 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.example.android.common.activities; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogWrapper; + +/** + * Base launcher activity, to handle most of the common plumbing for samples. + */ +public class SampleActivityBase extends FragmentActivity { + + public static final String TAG = "SampleActivityBase"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + initializeLogging(); + } + + /** Set up targets to receive log data */ + public void initializeLogging() { + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/dummydata/Cheeses.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/dummydata/Cheeses.java new file mode 100644 index 000000000..783735ce3 --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/dummydata/Cheeses.java @@ -0,0 +1,186 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.dummydata; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Random; + +/** + * Dummy data. + */ +public class Cheeses { + static final String[] CHEESES = { + "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi", + "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", + "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese", + "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell", + "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc", + "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss", + "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon", + "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase", + "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese", + "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy", + "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille", + "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore", + "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)", + "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves", + "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur", + "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon", + "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin", + "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)", + "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine", + "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza", + "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)", + "Buxton Blue", "Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta", + "Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie", + "Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat", + "Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano", + "Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain", + "Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou", + "Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar", + "Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno", + "Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack", + "Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper", + "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)", + "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese", + "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza", + "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley", + "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino", + "Cypress Grove Chevre", "Danablu (Danish Blue)", "Danbo", "Danish Fontina", + "Daralagjazsky", "Dauphin", "Delice des Fiouves", "Denhany Dorset Drum", "Derby", + "Dessertnyj Belyj", "Devon Blue", "Devon Garland", "Dolcelatte", "Doolin", + "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester", "Double Worcester", + "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra", "Dunlop", "Dunsyre Blue", + "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz", + "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne", "Esbareich", + "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo", "Exmoor Blue", + "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle", + "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis", "Flor de Guia", + "Flower Marie", "Folded", "Folded cheese with mint", "Fondant de Brebis", + "Fontainebleau", "Fontal", "Fontina Val d'Aosta", "Formaggio di capra", "Fougerus", + "Four Herb Gouda", "Fourme d' Ambert", "Fourme de Haute Loire", "Fourme de Montbrison", + "Fresh Jack", "Fresh Mozzarella", "Fresh Ricotta", "Fresh Truffles", "Fribourgeois", + "Friesekaas", "Friesian", "Friesla", "Frinault", "Fromage a Raclette", "Fromage Corse", + "Fromage de Montagne de Savoie", "Fromage Frais", "Fruit Cream Cheese", + "Frying Cheese", "Fynbo", "Gabriel", "Galette du Paludier", "Galette Lyonnaise", + "Galloway Goat's Milk Gems", "Gammelost", "Gaperon a l'Ail", "Garrotxa", "Gastanberra", + "Geitost", "Gippsland Blue", "Gjetost", "Gloucester", "Golden Cross", "Gorgonzola", + "Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost", + "Grafton Village Cheddar", "Grana", "Grana Padano", "Grand Vatel", + "Grataron d' Areches", "Gratte-Paille", "Graviera", "Greuilh", "Greve", + "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi", + "Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti", + "Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve", + "Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster", + "Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg", + "Jermi Tortes", "Jibneh Arabieh", "Jindi Brie", "Jubilee Blue", "Juustoleipa", + "Kadchgall", "Kaseri", "Kashta", "Kefalotyri", "Kenafa", "Kernhem", "Kervella Affine", + "Kikorangi", "King Island Cape Wickham Brie", "King River Gold", "Klosterkaese", + "Knockalara", "Kugelkase", "L'Aveyronnais", "L'Ecir de l'Aubrac", "La Taupiniere", + "La Vache Qui Rit", "Laguiole", "Lairobell", "Lajta", "Lanark Blue", "Lancashire", + "Langres", "Lappi", "Laruns", "Lavistown", "Le Brin", "Le Fium Orbo", "Le Lacandou", + "Le Roule", "Leafield", "Lebbene", "Leerdammer", "Leicester", "Leyden", "Limburger", + "Lincolnshire Poacher", "Lingot Saint Bousquet d'Orb", "Liptauer", "Little Rydings", + "Livarot", "Llanboidy", "Llanglofan Farmhouse", "Loch Arthur Farmhouse", + "Loddiswell Avondale", "Longhorn", "Lou Palou", "Lou Pevre", "Lyonnais", "Maasdam", + "Macconais", "Mahoe Aged Gouda", "Mahon", "Malvern", "Mamirolle", "Manchego", + "Manouri", "Manur", "Marble Cheddar", "Marbled Cheeses", "Maredsous", "Margotin", + "Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)", + "Mascarpone Torta", "Matocq", "Maytag Blue", "Meira", "Menallack Farmhouse", + "Menonita", "Meredith Blue", "Mesost", "Metton (Cancoillotte)", "Meyer Vintage Gouda", + "Mihalic Peynir", "Milleens", "Mimolette", "Mine-Gabhar", "Mini Baby Bells", "Mixte", + "Molbo", "Monastery Cheeses", "Mondseer", "Mont D'or Lyonnais", "Montasio", + "Monterey Jack", "Monterey Jack Dry", "Morbier", "Morbier Cru de Montagne", + "Mothais a la Feuille", "Mozzarella", "Mozzarella (Australian)", + "Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster", + "Murol", "Mycella", "Myzithra", "Naboulsi", "Nantais", "Neufchatel", + "Neufchatel (Australian)", "Niolo", "Nokkelost", "Northumberland", "Oaxaca", + "Olde York", "Olivet au Foin", "Olivet Bleu", "Olivet Cendre", + "Orkney Extra Mature Cheddar", "Orla", "Oschtjepka", "Ossau Fermier", "Ossau-Iraty", + "Oszczypek", "Oxford Blue", "P'tit Berrichon", "Palet de Babligny", "Paneer", "Panela", + "Pannerone", "Pant ys Gawn", "Parmesan (Parmigiano)", "Parmigiano Reggiano", + "Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage", + "Patefine Fort", "Pave d'Affinois", "Pave d'Auge", "Pave de Chirac", "Pave du Berry", + "Pecorino", "Pecorino in Walnut Leaves", "Pecorino Romano", "Peekskill Pyramid", + "Pelardon des Cevennes", "Pelardon des Corbieres", "Penamellera", "Penbryn", + "Pencarreg", "Perail de Brebis", "Petit Morin", "Petit Pardou", "Petit-Suisse", + "Picodon de Chevre", "Picos de Europa", "Piora", "Pithtviers au Foin", + "Plateau de Herve", "Plymouth Cheese", "Podhalanski", "Poivre d'Ane", "Polkolbin", + "Pont l'Eveque", "Port Nicholson", "Port-Salut", "Postel", "Pouligny-Saint-Pierre", + "Pourly", "Prastost", "Pressato", "Prince-Jean", "Processed Cheddar", "Provolone", + "Provolone (Australian)", "Pyengana Cheddar", "Pyramide", "Quark", + "Quark (Australian)", "Quartirolo Lombardo", "Quatre-Vents", "Quercy Petit", + "Queso Blanco", "Queso Blanco con Frutas --Pina y Mango", "Queso de Murcia", + "Queso del Montsec", "Queso del Tietar", "Queso Fresco", "Queso Fresco (Adobera)", + "Queso Iberico", "Queso Jalapeno", "Queso Majorero", "Queso Media Luna", + "Queso Para Frier", "Queso Quesadilla", "Rabacal", "Raclette", "Ragusano", "Raschera", + "Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou", + "Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder", + "Rigotte", "Rocamadour", "Rollot", "Romano", "Romans Part Dieu", "Roncal", "Roquefort", + "Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr", + "Saanenkaese", "Saga", "Sage Derby", "Sainte Maure", "Saint-Marcellin", + "Saint-Nectaire", "Saint-Paulin", "Salers", "Samso", "San Simon", "Sancerre", + "Sap Sago", "Sardo", "Sardo Egyptian", "Sbrinz", "Scamorza", "Schabzieger", "Schloss", + "Selles sur Cher", "Selva", "Serat", "Seriously Strong Cheddar", "Serra da Estrela", + "Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda", + "Somerset Brie", "Sonoma Jack", "Sottocenare al Tartufo", "Soumaintrain", + "Sourire Lozerien", "Spenwood", "Sraffordshire Organic", "St. Agur Blue Cheese", + "Stilton", "Stinking Bishop", "String", "Sussex Slipcote", "Sveciaost", "Swaledale", + "Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie", + "Tasmania Highland Chevre Log", "Taupiniere", "Teifi", "Telemea", "Testouri", + "Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar", + "Tilsit", "Timboon Brie", "Toma", "Tomme Brulee", "Tomme d'Abondance", + "Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes", + "Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet", + "Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe", + "Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa", + "Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois", + "Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue", + "Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington", + "Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou", + "Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue", + "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano" + }; + + public static ArrayList asList() { + ArrayList items = new ArrayList(); + for (int i = 0, z = CHEESES.length ; i < z ; i++) { + items.add(CHEESES[i]); + } + return items; + } + + /** + * Return a list of random cheeses. + * + * @param count the amount of cheeses to return. + */ + public static ArrayList randomList(int count) { + Random random = new Random(); + HashSet items = new HashSet(); + + // Make sure that don't infinity loop + count = Math.min(count, CHEESES.length); + + while (items.size() < count) { + items.add(CHEESES[random.nextInt(CHEESES.length)]); + } + + return new ArrayList(items); + } +} diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/Log.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/Log.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + *

When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.

+ */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +} \ No newline at end of file diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogNode.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.example.android.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogView.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogView.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.example.android.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/view/SlidingTabLayout.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/view/SlidingTabLayout.java new file mode 100644 index 000000000..20049e335 --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/view/SlidingTabLayout.java @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.view; + +import android.content.Context; +import android.graphics.Typeface; +import android.os.Build; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.HorizontalScrollView; +import android.widget.TextView; + +/** + * To be used with ViewPager to provide a tab indicator component which give constant feedback as to + * the user's scroll progress. + *

+ * To use the component, simply add it to your view hierarchy. Then in your + * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call + * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for. + *

+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors + * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The + * alternative is via the {@link TabColorizer} interface which provides you complete control over + * which color is used for any individual position. + *

+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)}, + * providing the layout ID of your custom layout. + */ +public class SlidingTabLayout extends HorizontalScrollView { + + /** + * Allows complete control over the colors drawn in the tab layout. Set with + * {@link #setCustomTabColorizer(TabColorizer)}. + */ + public interface TabColorizer { + + /** + * @return return the color of the indicator used when {@code position} is selected. + */ + int getIndicatorColor(int position); + + /** + * @return return the color of the divider drawn to the right of {@code position}. + */ + int getDividerColor(int position); + + } + + private static final int TITLE_OFFSET_DIPS = 24; + private static final int TAB_VIEW_PADDING_DIPS = 16; + private static final int TAB_VIEW_TEXT_SIZE_SP = 12; + + private int mTitleOffset; + + private int mTabViewLayoutId; + private int mTabViewTextViewId; + + private ViewPager mViewPager; + private ViewPager.OnPageChangeListener mViewPagerPageChangeListener; + + private final SlidingTabStrip mTabStrip; + + public SlidingTabLayout(Context context) { + this(context, null); + } + + public SlidingTabLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + // Disable the Scroll Bar + setHorizontalScrollBarEnabled(false); + // Make sure that the Tab Strips fills this View + setFillViewport(true); + + mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density); + + mTabStrip = new SlidingTabStrip(context); + addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + } + + /** + * Set the custom {@link TabColorizer} to be used. + * + * If you only require simple custmisation then you can use + * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve + * similar effects. + */ + public void setCustomTabColorizer(TabColorizer tabColorizer) { + mTabStrip.setCustomTabColorizer(tabColorizer); + } + + /** + * Sets the colors to be used for indicating the selected tab. These colors are treated as a + * circular array. Providing one color will mean that all tabs are indicated with the same color. + */ + public void setSelectedIndicatorColors(int... colors) { + mTabStrip.setSelectedIndicatorColors(colors); + } + + /** + * Sets the colors to be used for tab dividers. These colors are treated as a circular array. + * Providing one color will mean that all tabs are indicated with the same color. + */ + public void setDividerColors(int... colors) { + mTabStrip.setDividerColors(colors); + } + + /** + * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are + * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so + * that the layout can update it's scroll position correctly. + * + * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener) + */ + public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { + mViewPagerPageChangeListener = listener; + } + + /** + * Set the custom layout to be inflated for the tab views. + * + * @param layoutResId Layout id to be inflated + * @param textViewId id of the {@link TextView} in the inflated view + */ + public void setCustomTabView(int layoutResId, int textViewId) { + mTabViewLayoutId = layoutResId; + mTabViewTextViewId = textViewId; + } + + /** + * Sets the associated view pager. Note that the assumption here is that the pager content + * (number of tabs and tab titles) does not change after this call has been made. + */ + public void setViewPager(ViewPager viewPager) { + mTabStrip.removeAllViews(); + + mViewPager = viewPager; + if (viewPager != null) { + viewPager.setOnPageChangeListener(new InternalViewPagerListener()); + populateTabStrip(); + } + } + + /** + * Create a default view to be used for tabs. This is called if a custom tab view is not set via + * {@link #setCustomTabView(int, int)}. + */ + protected TextView createDefaultTabView(Context context) { + TextView textView = new TextView(context); + textView.setGravity(Gravity.CENTER); + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP); + textView.setTypeface(Typeface.DEFAULT_BOLD); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + // If we're running on Honeycomb or newer, then we can use the Theme's + // selectableItemBackground to ensure that the View has a pressed state + TypedValue outValue = new TypedValue(); + getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, + outValue, true); + textView.setBackgroundResource(outValue.resourceId); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style + textView.setAllCaps(true); + } + + int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density); + textView.setPadding(padding, padding, padding, padding); + + return textView; + } + + private void populateTabStrip() { + final PagerAdapter adapter = mViewPager.getAdapter(); + final View.OnClickListener tabClickListener = new TabClickListener(); + + for (int i = 0; i < adapter.getCount(); i++) { + View tabView = null; + TextView tabTitleView = null; + + if (mTabViewLayoutId != 0) { + // If there is a custom tab view layout id set, try and inflate it + tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip, + false); + tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId); + } + + if (tabView == null) { + tabView = createDefaultTabView(getContext()); + } + + if (tabTitleView == null && TextView.class.isInstance(tabView)) { + tabTitleView = (TextView) tabView; + } + + tabTitleView.setText(adapter.getPageTitle(i)); + tabView.setOnClickListener(tabClickListener); + + mTabStrip.addView(tabView); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mViewPager != null) { + scrollToTab(mViewPager.getCurrentItem(), 0); + } + } + + private void scrollToTab(int tabIndex, int positionOffset) { + final int tabStripChildCount = mTabStrip.getChildCount(); + if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) { + return; + } + + View selectedChild = mTabStrip.getChildAt(tabIndex); + if (selectedChild != null) { + int targetScrollX = selectedChild.getLeft() + positionOffset; + + if (tabIndex > 0 || positionOffset > 0) { + // If we're not at the first child and are mid-scroll, make sure we obey the offset + targetScrollX -= mTitleOffset; + } + + scrollTo(targetScrollX, 0); + } + } + + private class InternalViewPagerListener implements ViewPager.OnPageChangeListener { + private int mScrollState; + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + int tabStripChildCount = mTabStrip.getChildCount(); + if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) { + return; + } + + mTabStrip.onViewPagerPageChanged(position, positionOffset); + + View selectedTitle = mTabStrip.getChildAt(position); + int extraOffset = (selectedTitle != null) + ? (int) (positionOffset * selectedTitle.getWidth()) + : 0; + scrollToTab(position, extraOffset); + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrolled(position, positionOffset, + positionOffsetPixels); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + mScrollState = state; + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrollStateChanged(state); + } + } + + @Override + public void onPageSelected(int position) { + if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { + mTabStrip.onViewPagerPageChanged(position, 0f); + scrollToTab(position, 0); + } + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageSelected(position); + } + } + + } + + private class TabClickListener implements View.OnClickListener { + @Override + public void onClick(View v) { + for (int i = 0; i < mTabStrip.getChildCount(); i++) { + if (v == mTabStrip.getChildAt(i)) { + mViewPager.setCurrentItem(i); + return; + } + } + } + } + +} diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/view/SlidingTabStrip.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/view/SlidingTabStrip.java new file mode 100644 index 000000000..d5bbbae59 --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/view/SlidingTabStrip.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.view; + +import android.R; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.widget.LinearLayout; + +class SlidingTabStrip extends LinearLayout { + + private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2; + private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26; + private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8; + private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5; + + private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1; + private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20; + private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f; + + private final int mBottomBorderThickness; + private final Paint mBottomBorderPaint; + + private final int mSelectedIndicatorThickness; + private final Paint mSelectedIndicatorPaint; + + private final int mDefaultBottomBorderColor; + + private final Paint mDividerPaint; + private final float mDividerHeight; + + private int mSelectedPosition; + private float mSelectionOffset; + + private SlidingTabLayout.TabColorizer mCustomTabColorizer; + private final SimpleTabColorizer mDefaultTabColorizer; + + SlidingTabStrip(Context context) { + this(context, null); + } + + SlidingTabStrip(Context context, AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + + final float density = getResources().getDisplayMetrics().density; + + TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true); + final int themeForegroundColor = outValue.data; + + mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor, + DEFAULT_BOTTOM_BORDER_COLOR_ALPHA); + + mDefaultTabColorizer = new SimpleTabColorizer(); + mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR); + mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor, + DEFAULT_DIVIDER_COLOR_ALPHA)); + + mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density); + mBottomBorderPaint = new Paint(); + mBottomBorderPaint.setColor(mDefaultBottomBorderColor); + + mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density); + mSelectedIndicatorPaint = new Paint(); + + mDividerHeight = DEFAULT_DIVIDER_HEIGHT; + mDividerPaint = new Paint(); + mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density)); + } + + void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) { + mCustomTabColorizer = customTabColorizer; + invalidate(); + } + + void setSelectedIndicatorColors(int... colors) { + // Make sure that the custom colorizer is removed + mCustomTabColorizer = null; + mDefaultTabColorizer.setIndicatorColors(colors); + invalidate(); + } + + void setDividerColors(int... colors) { + // Make sure that the custom colorizer is removed + mCustomTabColorizer = null; + mDefaultTabColorizer.setDividerColors(colors); + invalidate(); + } + + void onViewPagerPageChanged(int position, float positionOffset) { + mSelectedPosition = position; + mSelectionOffset = positionOffset; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + final int height = getHeight(); + final int childCount = getChildCount(); + final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height); + final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null + ? mCustomTabColorizer + : mDefaultTabColorizer; + + // Thick colored underline below the current selection + if (childCount > 0) { + View selectedTitle = getChildAt(mSelectedPosition); + int left = selectedTitle.getLeft(); + int right = selectedTitle.getRight(); + int color = tabColorizer.getIndicatorColor(mSelectedPosition); + + if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) { + int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1); + if (color != nextColor) { + color = blendColors(nextColor, color, mSelectionOffset); + } + + // Draw the selection partway between the tabs + View nextTitle = getChildAt(mSelectedPosition + 1); + left = (int) (mSelectionOffset * nextTitle.getLeft() + + (1.0f - mSelectionOffset) * left); + right = (int) (mSelectionOffset * nextTitle.getRight() + + (1.0f - mSelectionOffset) * right); + } + + mSelectedIndicatorPaint.setColor(color); + + canvas.drawRect(left, height - mSelectedIndicatorThickness, right, + height, mSelectedIndicatorPaint); + } + + // Thin underline along the entire bottom edge + canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint); + + // Vertical separators between the titles + int separatorTop = (height - dividerHeightPx) / 2; + for (int i = 0; i < childCount - 1; i++) { + View child = getChildAt(i); + mDividerPaint.setColor(tabColorizer.getDividerColor(i)); + canvas.drawLine(child.getRight(), separatorTop, child.getRight(), + separatorTop + dividerHeightPx, mDividerPaint); + } + } + + /** + * Set the alpha value of the {@code color} to be the given {@code alpha} value. + */ + private static int setColorAlpha(int color, byte alpha) { + return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)); + } + + /** + * Blend {@code color1} and {@code color2} using the given ratio. + * + * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend, + * 0.0 will return {@code color2}. + */ + private static int blendColors(int color1, int color2, float ratio) { + final float inverseRation = 1f - ratio; + float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation); + float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation); + float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation); + return Color.rgb((int) r, (int) g, (int) b); + } + + private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer { + private int[] mIndicatorColors; + private int[] mDividerColors; + + @Override + public final int getIndicatorColor(int position) { + return mIndicatorColors[position % mIndicatorColors.length]; + } + + @Override + public final int getDividerColor(int position) { + return mDividerColors[position % mDividerColors.length]; + } + + void setIndicatorColors(int... colors) { + mIndicatorColors = colors; + } + + void setDividerColors(int... colors) { + mDividerColors = colors; + } + } +} \ No newline at end of file diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/MainActivity.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/MainActivity.java new file mode 100644 index 000000000..0191d87de --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/MainActivity.java @@ -0,0 +1,110 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + + + + +package com.example.android.swiperefreshmultipleviews; + +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.ViewAnimator; + +import com.example.android.common.activities.SampleActivityBase; +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogFragment; +import com.example.android.common.logger.LogWrapper; +import com.example.android.common.logger.MessageOnlyLogFilter; + +/** + * A simple launcher activity containing a summary sample description, sample log and a custom + * {@link android.support.v4.app.Fragment} which can display a view. + *

+ * For devices with displays with a width of 720dp or greater, the sample log is always visible, + * on other devices it's visibility is controlled by an item on the Action Bar. + */ +public class MainActivity extends SampleActivityBase { + + public static final String TAG = "MainActivity"; + + // Whether the Log Fragment is currently shown + private boolean mLogShown; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + SwipeRefreshMultipleViewsFragment fragment = new SwipeRefreshMultipleViewsFragment(); + transaction.replace(R.id.sample_content_fragment, fragment); + transaction.commit(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + MenuItem logToggle = menu.findItem(R.id.menu_toggle_log); + logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator); + logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log); + + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch(item.getItemId()) { + case R.id.menu_toggle_log: + mLogShown = !mLogShown; + ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output); + if (mLogShown) { + output.setDisplayedChild(1); + } else { + output.setDisplayedChild(0); + } + supportInvalidateOptionsMenu(); + return true; + } + return super.onOptionsItemSelected(item); + } + + /** Create a chain of targets that will receive log data */ + @Override + public void initializeLogging() { + // Wraps Android's native log framework. + LogWrapper logWrapper = new LogWrapper(); + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + Log.setLogNode(logWrapper); + + // Filter strips out everything except the message text. + MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); + logWrapper.setNext(msgFilter); + + // On screen logging via a fragment with a TextView. + LogFragment logFragment = (LogFragment) getSupportFragmentManager() + .findFragmentById(R.id.log_fragment); + msgFilter.setNext(logFragment.getLogView()); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/MultiSwipeRefreshLayout.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/MultiSwipeRefreshLayout.java new file mode 100644 index 000000000..4e5014554 --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/MultiSwipeRefreshLayout.java @@ -0,0 +1,107 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.swiperefreshmultipleviews; + +import android.content.Context; +import android.support.v4.view.ViewCompat; +import android.support.v4.widget.SwipeRefreshLayout; +import android.util.AttributeSet; +import android.view.View; +import android.widget.AbsListView; + +/** + * A descendant of {@link android.support.v4.widget.SwipeRefreshLayout} which supports multiple + * child views triggering a refresh gesture. You set the views which can trigger the gesture via + * {@link #setSwipeableChildren(int...)}, providing it the child ids. + */ +public class MultiSwipeRefreshLayout extends SwipeRefreshLayout { + + private View[] mSwipeableChildren; + + public MultiSwipeRefreshLayout(Context context) { + super(context); + } + + public MultiSwipeRefreshLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + /** + * Set the children which can trigger a refresh by swiping down when they are visible. These + * views need to be a descendant of this view. + */ + public void setSwipeableChildren(final int... ids) { + assert ids != null; + + // Iterate through the ids and find the Views + mSwipeableChildren = new View[ids.length]; + for (int i = 0; i < ids.length; i++) { + mSwipeableChildren[i] = findViewById(ids[i]); + } + } + + // BEGIN_INCLUDE(can_child_scroll_up) + /** + * This method controls when the swipe-to-refresh gesture is triggered. By returning false here + * we are signifying that the view is in a state where a refresh gesture can start. + * + *

As {@link android.support.v4.widget.SwipeRefreshLayout} only supports one direct child by + * default, we need to manually iterate through our swipeable children to see if any are in a + * state to trigger the gesture. If so we return false to start the gesture. + */ + @Override + public boolean canChildScrollUp() { + if (mSwipeableChildren != null && mSwipeableChildren.length > 0) { + // Iterate through the scrollable children and check if any of them can not scroll up + for (View view : mSwipeableChildren) { + if (view != null && view.isShown() && !canViewScrollUp(view)) { + // If the view is shown, and can not scroll upwards, return false and start the + // gesture. + return false; + } + } + } + return true; + } + // END_INCLUDE(can_child_scroll_up) + + // BEGIN_INCLUDE(can_view_scroll_up) + /** + * Utility method to check whether a {@link View} can scroll up from it's current position. + * Handles platform version differences, providing backwards compatible functionality where + * needed. + */ + private static boolean canViewScrollUp(View view) { + if (android.os.Build.VERSION.SDK_INT >= 14) { + // For ICS and above we can call canScrollVertically() to determine this + return ViewCompat.canScrollVertically(view, -1); + } else { + if (view instanceof AbsListView) { + // Pre-ICS we need to manually check the first visible item and the child view's top + // value + final AbsListView listView = (AbsListView) view; + return listView.getChildCount() > 0 && + (listView.getFirstVisiblePosition() > 0 + || listView.getChildAt(0).getTop() < listView.getPaddingTop()); + } else { + // For all other view types we just check the getScrollY() value + return view.getScrollY() > 0; + } + } + } + // END_INCLUDE(can_view_scroll_up) +} diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/SwipeRefreshMultipleViewsFragment.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/SwipeRefreshMultipleViewsFragment.java new file mode 100644 index 000000000..e2b83d355 --- /dev/null +++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/SwipeRefreshMultipleViewsFragment.java @@ -0,0 +1,256 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.swiperefreshmultipleviews; + +import com.example.android.common.dummydata.Cheeses; +import com.example.android.common.logger.Log; + +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.widget.SwipeRefreshLayout; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.GridView; + +import java.util.List; + +/** + * A sample which shows how to use {@link android.support.v4.widget.SwipeRefreshLayout} to add + * the 'swipe-to-refresh' gesture to a layout with multiple children. In this sample, + * SwipeRefreshLayout contains a scrollable {@link android.widget.GridView}, along with a + * {@link android.widget.TextView} empty view. + * + *

To provide an accessible way to trigger the refresh, this app also provides a refresh + * action item. + * + *

In this sample app, the refresh updates the GridView with a random set of new items. + */ +public class SwipeRefreshMultipleViewsFragment extends Fragment { + + private static final String LOG_TAG = SwipeRefreshMultipleViewsFragment.class.getSimpleName(); + + private static final int LIST_ITEM_COUNT = 40; + + /** + * The {@link MultiSwipeRefreshLayout} that detects swipe gestures and triggers callbacks in + * the app. + */ + private MultiSwipeRefreshLayout mSwipeRefreshLayout; + + /** + * The {@link android.widget.GridView} that displays the content that should be refreshed. + */ + private GridView mGridView; + + /** + * The {@link android.widget.ListAdapter} used to populate the {@link android.widget.GridView} + * defined in the previous statement. + */ + private ArrayAdapter mListAdapter; + + /** + * The {@link View} which is displayed when the GridView is empty. + */ + private View mEmptyView; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Notify the system to allow an options menu for this fragment. + setHasOptionsMenu(true); + } + + // BEGIN_INCLUDE (inflate_view) + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_sample, container, false); + + // Retrieve the SwipeRefreshLayout and GridView instances + mSwipeRefreshLayout = (MultiSwipeRefreshLayout) view.findViewById(R.id.swiperefresh); + + // BEGIN_INCLUDE (change_colors) + // Set the color scheme of the SwipeRefreshLayout by providing 4 color resource ids + mSwipeRefreshLayout.setColorScheme( + R.color.swipe_color_1, R.color.swipe_color_2, + R.color.swipe_color_3, R.color.swipe_color_4); + // END_INCLUDE (change_colors) + + // Retrieve the GridView + mGridView = (GridView) view.findViewById(android.R.id.list); + + // Retrieve the empty view + mEmptyView = view.findViewById(android.R.id.empty); + + return view; + } + // END_INCLUDE (inflate_view) + + // BEGIN_INCLUDE (setup_views) + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + /** + * Create an ArrayAdapter to contain the data for the GridView. Each item in the GridView + * uses the system-defined simple_list_item_1 layout that contains one TextView. Initially + */ + mListAdapter = new ArrayAdapter( + getActivity(), + android.R.layout.simple_list_item_1, + android.R.id.text1); + + // Set the adapter between the GridView and its backing data. + mGridView.setAdapter(mListAdapter); + + // Set the empty view so that it is displayed as needed + mGridView.setEmptyView(mEmptyView); + + // BEGIN_INCLUDE (setup_swipeable_children) + // Tell the MultiSwipeRefreshLayout which views are swipeable. In this case, the GridView + // and empty view. + mSwipeRefreshLayout.setSwipeableChildren(android.R.id.list, android.R.id.empty); + // END_INCLUDE (setup_swipeable_children) + + // BEGIN_INCLUDE (setup_refreshlistener) + /** + * Implement {@link SwipeRefreshLayout.OnRefreshListener}. When users do the "swipe to + * refresh" gesture, SwipeRefreshLayout invokes + * {@link SwipeRefreshLayout.OnRefreshListener#onRefresh onRefresh()}. In + * {@link SwipeRefreshLayout.OnRefreshListener#onRefresh onRefresh()}, call a method that + * refreshes the content. Call the same method in response to the Refresh action from the + * action bar. + */ + mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + Log.i(LOG_TAG, "onRefresh called from SwipeRefreshLayout"); + + initiateRefresh(); + } + }); + // END_INCLUDE (setup_refreshlistener) + } + // END_INCLUDE (setup_views) + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.main_menu, menu); + } + + // BEGIN_INCLUDE (setup_refresh_menu_listener) + /** + * Respond to the user's selection of the Refresh action item. Start the SwipeRefreshLayout + * progress bar, then initiate the background task that refreshes the content. + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_clear: + Log.i(LOG_TAG, "Clear menu item selected"); + mListAdapter.clear(); + return true; + + case R.id.menu_refresh: + Log.i(LOG_TAG, "Refresh menu item selected"); + + // We make sure that the SwipeRefreshLayout is displaying it's refreshing indicator + if (!mSwipeRefreshLayout.isRefreshing()) { + mSwipeRefreshLayout.setRefreshing(true); + } + + // Start our refresh background task + initiateRefresh(); + + return true; + } + + return super.onOptionsItemSelected(item); + } + // END_INCLUDE (setup_refresh_menu_listener) + + // BEGIN_INCLUDE (initiate_refresh) + /** + * By abstracting the refresh process to a single method, the app allows both the + * SwipeGestureLayout onRefresh() method and the Refresh action item to refresh the content. + */ + private void initiateRefresh() { + Log.i(LOG_TAG, "initiateRefresh"); + + /** + * Execute the background task, which uses {@link android.os.AsyncTask} to load the data. + */ + new DummyBackgroundTask().execute(); + } + // END_INCLUDE (initiate_refresh) + + // BEGIN_INCLUDE (refresh_complete) + /** + * When the AsyncTask finishes, it calls onRefreshComplete(), which updates the data in the + * ListAdapter and turns off the progress bar. + */ + private void onRefreshComplete(List result) { + Log.i(LOG_TAG, "onRefreshComplete"); + + // Remove all items from the ListAdapter, and then replace them with the new items + mListAdapter.clear(); + for (String cheese : result) { + mListAdapter.add(cheese); + } + + // Stop the refreshing indicator + mSwipeRefreshLayout.setRefreshing(false); + } + // END_INCLUDE (refresh_complete) + + /** + * Dummy {@link AsyncTask} which simulates a long running task to fetch new cheeses. + */ + private class DummyBackgroundTask extends AsyncTask> { + + static final int TASK_DURATION = 3 * 1000; // 3 seconds + + @Override + protected List doInBackground(Void... params) { + // Sleep for a small amount of time to simulate a background-task + try { + Thread.sleep(TASK_DURATION); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + // Return a new random list of cheeses + return Cheeses.randomList(LIST_ITEM_COUNT); + } + + @Override + protected void onPostExecute(List result) { + super.onPostExecute(result); + + // Tell the Fragment that the refresh has completed + onRefreshComplete(result); + } + + } +} \ No newline at end of file diff --git a/samples/browseable/TextLinkify/res/values-v11/template-styles.xml b/samples/browseable/TextLinkify/res/values-v11/template-styles.xml new file mode 100644 index 000000000..8c1ea66f2 --- /dev/null +++ b/samples/browseable/TextLinkify/res/values-v11/template-styles.xml @@ -0,0 +1,22 @@ + + + + + +