[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nindent_style = space\nindent_size = 4\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[{.travis.yml,.travis-script.sh,*.json,*.coffee,*.less}]\nindent_size = 2\n\n[*.md]\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": ".gitignore",
    "content": ".gradle\n*.iml\n*.keystore\n/local.properties\nprivate.properties\n/.idea/\n.DS_Store\n/build\nbin/\ngen/\nlibs/\naarDependencies/\n.project\n.classpath\nproject.properties\n*.swp\n"
  },
  {
    "path": ".travis-script.sh",
    "content": "#!/bin/bash\n\necho \"TEST_TARGET=${TEST_TARGET}\"\necho \"TRAVIS_PULL_REQUEST=${TRAVIS_PULL_REQUEST}\"\necho \"TRAVIS_BRANCH=${TRAVIS_BRANCH}\"\n\nif [ \"$TEST_TARGET\" = \"android\" ]; then\n  # Release build type is only for Google Play store currently,\n  # which resolve dependency from Maven Central.\n  # This causes build errors while developing a new feature, so disable release build.\n  ./gradlew --full-stacktrace assembleDevDebug :library:connectedCheck\nelif [ \"$TEST_TARGET\" = \"website\" ]; then\n  if [ \"$TRAVIS_PULL_REQUEST\" = \"false\" ] && [ \"$TRAVIS_BRANCH\" = \"master\" ] && [ ! -z \"$GH_TOKEN\" ]; then\n    echo \"Update website...\"\n    pushd website > /dev/null 2>&1\n    npm run deploy\n    popd > /dev/null 2>&1\n  fi\nfi\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: android\nsudo: false\nenv:\n  global:\n  - GIT_COMMITTER_NAME=ksoichiro\n  - GIT_COMMITTER_EMAIL=soichiro.kashima@gmail.com\n  - GIT_AUTHOR_NAME=ksoichiro\n  - GIT_AUTHOR_EMAIL=soichiro.kashima@gmail.com\n  - secure: Iw0mIseFZ6M/HGi/ERPBT4fabx0G1OVeHCbu6ANoVybO8yHBRHHuUc4pdZOOtEi+Ce/4a5TPZXbGPIVMZrN1ewS3uDAHsEFjmtehsqjk5iKXapvy04dwLsX9jCsQNl0n5679tRZ2eXUGqyVSddc5pIyWwJAGgBmnM/SHDaRy4YA=\n  matrix:\n  - TEST_TARGET=android\n  - TEST_TARGET=website\ncache:\n  directories:\n  - website/node_modules\n  - website/bower_components\ninstall:\n- true && ([ \"$TEST_TARGET\" != \"website\" ] || (cd website && npm install && cd ..))\n- true && ([ \"$TEST_TARGET\" != \"android\" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=tools)\n- true && ([ \"$TEST_TARGET\" != \"android\" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=build-tools-23.0.2)\n- true && ([ \"$TEST_TARGET\" != \"android\" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-19)\n- true && ([ \"$TEST_TARGET\" != \"android\" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-21)\n- true && ([ \"$TEST_TARGET\" != \"android\" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-22)\n- true && ([ \"$TEST_TARGET\" != \"android\" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-23)\n- true && ([ \"$TEST_TARGET\" != \"android\" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=sys-img-armeabi-v7a-android-19)\n- true && ([ \"$TEST_TARGET\" != \"android\" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=extra-android-support)\n- true && ([ \"$TEST_TARGET\" != \"android\" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=extra-android-m2repository)\nbefore_script:\n- true && ([ \"$TEST_TARGET\" != \"android\" ] || (echo no | android create avd --force -n test -t android-19 --abi default/armeabi-v7a))\n- true && ([ \"$TEST_TARGET\" != \"android\" ] || emulator -avd test -no-skin -no-audio -no-window &)\n- true && ([ \"$TEST_TARGET\" != \"android\" ] || android-wait-for-emulator)\nscript:\n- \"/bin/bash .travis-script.sh\"\nafter_success:\n- true && ([ \"$TEST_TARGET\" != \"android\" ] || ./gradlew :library:coveralls)\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Thank you for your contribution!\n\nAny contributions will be greatly appreciated.\n\nBefore submitting a new issue, please check the following guideline.\n\n## Describe your issue as much as possible\n\nThe library itself only provides the scroll information,\nand creating awesome scrolling effects depends deeply on how you use it: layout, offset calculation to animate views, etc.\n\nTherefore, if you find an issue, please describe not only the issue itself but also the following information:\n\n### If you find it in the sample app of this project\n\n* Required\n    * Activity name\n    * Operation to produce the issue\n    * Modified code\n        * If you modified some codes in the sample, it should be described with modified codes\n* Preferred\n    * Version (git commit hash) of the codes\n    * Android OS version\n    * device\n        * Nexus5, x86 emulator, etc.\n\n### If you find it in your app\n\n* Required\n    * Operation to produce the issue\n    * Related code\n        * Without the related codes, I can't say anything.  \n          Screenshot / movie is useful to understand the issue,\n          but not enough to discuss the cause of the issue.\n* Preferred\n    * Version of the library\n    * Android OS version\n    * device\n        * Nexus5, x86 emulator, etc.\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# Android-ObservableScrollView\n\n[![Build Status](http://img.shields.io/travis/ksoichiro/Android-ObservableScrollView.svg?style=flat)](https://travis-ci.org/ksoichiro/Android-ObservableScrollView)\n[![Coverage Status](https://img.shields.io/coveralls/ksoichiro/Android-ObservableScrollView/master.svg?style=flat)](https://coveralls.io/r/ksoichiro/Android-ObservableScrollView?branch=master)\n[![Maven Central](http://img.shields.io/maven-central/v/com.github.ksoichiro/android-observablescrollview.svg?style=flat)](https://github.com/ksoichiro/Android-ObservableScrollView/releases/latest)\n[![API](https://img.shields.io/badge/API-9%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=9)\n[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Android--ObservableScrollView-brightgreen.svg?style=flat)](https://android-arsenal.com/details/1/1136)\n\nAndroid library to observe scroll events on scrollable views.  \nIt's easy to interact with the Toolbar introduced in Android 5.0 Lollipop  and may be helpful to implement look and feel of Material Design apps.\n\n![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo12.gif)\n![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo10.gif)\n![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo11.gif)\n![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo13.gif)\n\n![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo1.gif)\n![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo2.gif)\n![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo3.gif)\n![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo4.gif)\n\n![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo5.gif)\n![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo6.gif)\n![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo7.gif)\n![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo8.gif)\n\n\n## Examples\n\n### Download from Google Play\n\n<a href=\"https://play.google.com/store/apps/details?id=com.github.ksoichiro.android.observablescrollview.samples2\"><img alt=\"Get it on Google Play\" src=\"https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png\" height=\"50px\"/></a>\n\nPlease note that the app on the Play store is not always the latest version.\n\n### Download from wercker\n\nIf you are a wercker user, you can download the latest build artifact.  \n[See here for details](docs/example/wercker.md).\n\n[![wercker status](https://app.wercker.com/status/8d1e27d9f4a662b25dbe70402733582b/m/master \"wercker status\")](https://app.wercker.com/project/bykey/8d1e27d9f4a662b25dbe70402733582b)\n\n### Install manually\n\nJust clone and execute `installDevDebug` task with Gradle.  \n[See here for details](docs/example/android-studio.md).\n\n## Usage\n\n1. Add `com.github.ksoichiro:android-observablescrollview` to your `dependencies` in `build.gradle`.\n1. Add `ObservableListView` or other views you'd like to use.\n1. Write some animation codes to the callbacks such as `onScrollChanged`, `onUpOrCancelMotionEvent`, etc.\n\nSee [the quick start guide for details](docs/quick-start/index.md),\nand [the documentation](docs/overview.md) for further more.\n\n## Reference\n\n* [Supported widgets](docs/reference/supported-widgets.md)\n* [Environment](docs/reference/environment.md)\n* [Release notes](docs/reference/release-notes.md)\n* [FAQ](docs/faq.md)\n\n## Apps that use this library\n[![Badge](http://www.libtastic.com/static/osbadges/4.png)](http://www.libtastic.com/technology/4/)\n\n* [Jair Player](https://play.google.com/store/apps/details?id=aj.jair.music) by Akshay Chordiya\n* [My Gradle](https://play.google.com/store/apps/details?id=se.project.generic.mygradle) by Erick Chavez Alcarraz\n* [ThemeDIY](https://play.google.com/store/apps/details?id=net.darkion.theme.maker) by Darkion Avey\n* [{Soft} Skills](https://play.google.com/store/apps/details?id=com.fanaticdevs.androider) by Fanatic Devs\n\nIf you're using this library in your app and you'd like to list it here,  \nplease let me know via [email](mailto:soichiro.kashima@gmail.com) or [pull requests](https://github.com/ksoichiro/Android-ObservableScrollView/pulls) or [issues](https://github.com/ksoichiro/Android-ObservableScrollView/issues).\n\n\n## Contributions\n\nAny contributions are welcome!  \nPlease check the [FAQ](docs/faq.md) and [contributing guideline](https://github.com/ksoichiro/Android-ObservableScrollView/tree/master/CONTRIBUTING.md) before submitting a new issue.\n\n\n## Developed By\n\n* Soichiro Kashima - [soichiro.kashima@gmail.com](mailto:soichiro.kashima@gmail.com)\n\n\n## Thanks\n\n* Inspired by `ObservableScrollView` in [romannurik-code](https://code.google.com/p/romannurik-code/).\n\n\n## License\n\n```license\nCopyright 2014 Soichiro Kashima\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n"
  },
  {
    "path": "build.gradle",
    "content": "buildscript {\n    repositories {\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.github.ksoichiro:gradle-eclipse-aar-plugin:0.1.1'\n        // Related issue: https://code.google.com/p/android/issues/detail?id=192875\n        classpath 'org.jacoco:org.jacoco.core:0.7.5.201505241946'\n    }\n}\n\nallprojects {\n    group = GROUP\n    version = VERSION_NAME\n    repositories {\n        mavenCentral()\n    }\n}\n\nsubprojects {\n    buildscript {\n        repositories {\n            mavenCentral()\n        }\n        dependencies {\n            classpath 'com.android.tools.build:gradle:1.5.0'\n            classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.1.0'\n        }\n    }\n}\n\napply plugin: 'com.github.ksoichiro.eclipse.aar'\n\neclipseAar {\n    projectNamePrefix = 'observablescrollview-'\n    cleanLibsDirectoryEnabled = true\n}\n"
  },
  {
    "path": "docs/_data.json",
    "content": "{\n  \"overview\": {\n    \"title\": \"Overview\"\n  },\n  \"faq\": {\n    \"title\": \"FAQ\"\n  }\n}\n"
  },
  {
    "path": "docs/_layout.ejs",
    "content": "<!DOCTYPE html>\n<html prefix=\"og: http://ogp.me/ns#\">\n<head>\n<%- partial(\"../_head\", { title: title, ogType: \"article\" }) %>\n</head>\n<body>\n\n<%- partial(\"../_nav\") %>\n\n<div class=\"container\">\n<p class=\"visible-xs\">\n    <button type=\"button\" class=\"btn-sidebar-toggle\" data-toggle=\"offcanvas\">Show menu</button>\n</p>\n<div id=\"grid-content\">\n<div id=\"sidebar\">\n    <ul>\n        <li><ul><li class=\"topic\"><a href=\"<%- baseUrl %>/docs/overview\">Overview</a></li></ul></li>\n        <% for (var sectionSlug in public._data) { %>\n            <% if (sectionSlug == \"index\") { continue; } %>\n            <li>\n                <h4 class=\"section\"><a href=\"<%- baseUrl %>/docs/<%- sectionSlug %>/\"><%- public._data[sectionSlug].title %></a></h4>\n                <ul>\n                    <% for (var topicSlug in public.docs[sectionSlug]._data) { %>\n                        <% if (topicSlug !== \"index\") { %>\n                            <li class=\"topic\"><a href=\"<%- baseUrl %>/docs/<%- sectionSlug %>/<%- topicSlug %>\"><%- public.docs[sectionSlug]._data[topicSlug].title %></a></li>\n                        <% } %>\n                    <% } %>\n                </ul>\n            </li>\n        <% } %>\n    </ul>\n</div>\n<div id=\"sidebar-main-content\">\n<%- yield %>\n</div>\n</div>\n</div>\n\n<%- partial(\"../_footer\") %>\n</body>\n</html>\n"
  },
  {
    "path": "docs/advanced/_data.json",
    "content": "{\n  \"index\": {\n    \"title\": \"Advanced techniques\"\n  },\n  \"sliding-up\": {\n    \"title\": \"Sliding up pattern\"\n  },\n  \"viewpager\": {\n    \"title\": \"ViewPager pattern\"\n  }\n}\n"
  },
  {
    "path": "docs/advanced/index.md",
    "content": "# Advanced techniques\n\nThis section describes advanced scrolling techniques.  \nWhen you've done this topic, you could be be an expert of handling scrolls!  \nI'd appreciate it if you could suggest new patterns or improvements of the library.\n\n1. [Sliding up pattern](../../docs/advanced/sliding-up.md)\n1. [ViewPager pattern](../../docs/advanced/viewpager.md)\n\n[Next: Sliding up pattern &raquo;](../../docs/advanced/sliding-up.md)\n"
  },
  {
    "path": "docs/advanced/sliding-up.md",
    "content": "# Sliding up pattern\n\nThis topic describes how to slide a panel from the bottom like Google's \"Map\" app,\nwhich are implemented in the following examples.\n\n* SlidingUpBaseActivity\n* SlidingUpGridViewActivity\n* SlidingUpListViewActivity\n* SlidingUpRecyclerViewActivity\n* SlidingUpScrollViewActivity\n* SlidingUpWebViewActivity\n\n---\n\nComing soon...\n\n[Next: ViewPager pattern &raquo;](../../docs/advanced/viewpager.md)\n"
  },
  {
    "path": "docs/advanced/viewpager.md",
    "content": "# ViewPager pattern\n\nThis topic describes how to integrate scrollable views with ViewPager,\nwhich are implemented in the following examples.\n\n* ViewPagerTab2Activity\n* ViewPagerTabActivity\n* ViewPagerTabFragmentActivity\n* ViewPagerTabListViewActivity\n* ViewPagerTabScrollViewActivity\n\n---\n\nComing soon...\n"
  },
  {
    "path": "docs/basic/_data.json",
    "content": "{\n  \"index\": {\n    \"title\": \"Basic techniques\"\n  },\n  \"show-hide-action-bar\": {\n    \"title\": \"Show and hide the ActionBar\"\n  },\n  \"translating-toolbar\": {\n    \"title\": \"Translating the Toolbar\"\n  },\n  \"parallax-image\": {\n    \"title\": \"Parallax image\"\n  },\n  \"sticky-header\": {\n    \"title\": \"Sticky header\"\n  },\n  \"flexible-space-toolbar\": {\n    \"title\": \"Flexible space on the Toolbar\"\n  },\n  \"flexible-space-with-image\": {\n    \"title\": \"Flexible space with image\"\n  },\n  \"filling-gap\": {\n    \"title\": \"Filling gap on top of the Toolbar\"\n  }\n}\n"
  },
  {
    "path": "docs/basic/filling-gap.md",
    "content": "# Filling gap on top of the Toolbar\n\nThis topic describes how to fill the gap on top of the Toolbar,\nwhich are implemented in the following examples.\n\n* FillGapBaseActivity\n* FillGapListViewActivity\n* FillGapRecyclerViewActivity\n* FillGapScrollViewActivity\n* FillGap2BaseActivity\n* FillGap2ListViewActivity\n* FillGap2RecyclerViewActivity\n* FillGap2ScrollViewActivity\n* FillGap3BaseActivity\n* FillGap3ListViewActivity\n* FillGap3RecyclerViewActivity\n* FillGap3ScrollViewActivity\n\nPlease note that these patterns only works for Android 4+.\n\n---\n\n## Overview\n\nThere are many examples for this pattern, but they can be classified to the following:\n\n* FillGap\n    * When swiping up, the header bar expands and fill the gap between the header and the top of the screen.\n* FillGap2\n    * Almost same as FillGap, but in this pattern,\n      after the gap is filled with primary color, the filled space is going to shrink,\n      and the header bar moves.\n* FillGap3\n    * Usually FillGap should work only when the Scrollable view can scroll.\n      But sometimes you may want to scroll them with few items, and you can achieve it with this pattern.\n    * This uses `TouchInterceptionFrameLayout` (one of the widgets in this library),\n      and this component does not handle \"velocity\" of scrolls,\n      so as soon as you touch up your fingers, translation will be stopped.\n\n## Pattern1 (FillGap)\n\n### ScrollView\n\n#### Basic structure\n\n```xml\n<FrameLayout android:clipChildren=\"false\">\n  <FrameLayout android:id=\"@+id/image_holder\">\n    <ImageView android:id=\"@+id/image\"/>\n  </FrameLayout>\n  <ObservableScrollView android:id=\"@+id/scroll\">\n    <TextView android:id=\"@+id/container\"/>\n  </ObservableScrollView>\n  <FrameLayout android:id=\"@+id/header\" android:clipChildren=\"false\">\n    <View android:id=\"@+id/header_background\"/>\n    <LinearLayout android:id=\"@+id/header_bar\">\n      <TextView android:id=\"@+id/title\"/>\n    </LinearLayout>\n  </FrameLayout>\n</FrameLayout>\n```\n\n`clipChildren` attribute is important.\nWithout it, part of the views are not drawn.\n\nIncorrect (without `android:clipChildren=\"false\"`):\n\n![](../../docs/images/basic_7.png)\n\nCorrect (with `android:clipChildren=\"false\"`):\n\n![](../../docs/images/basic_6.png)\n\nComing soon...\n\n## Pattern2 (FillGap2)\n\nComing soon...\n\n## Pattern3 (FillGap3)\n\nComing soon...\n\n[Next: Advanced techniques &raquo;](../../docs/advanced/index.md)\n"
  },
  {
    "path": "docs/basic/flexible-space-toolbar.md",
    "content": "# Flexible space on the Toolbar\n\nThis topic describes how to create flexible space on the Toolbar,\nwhich are implemented in the following examples.\n\n* FlexibleSpaceToolbarScrollViewActivity\n* FlexibleSpaceToolbarWebViewActivity\n\nI originally tried implementing this pattern (only the title animation):  \n[Flexible space with image](http://material-design.storage.googleapis.com/publish/material_v_3/material_ext_publish/0B969e8h0awhvQ3lJdU9WVTh1WWM/patterns_scrolling_flexspaceimage.webm)\n\n---\n\n## Using ScrollView\n\n### Layout with ScrollView\n\n#### Basic structure\n\n```xml\n<FrameLayout>\n  <ObservableScrollView android:id=\"@+id/scroll\">\n    <FrameLayout android:id=\"@+id/body\">\n      <TextView/>\n    </FrameLayout>\n  </ObservableScrollView>\n  <View android:id=\"@+id/flexible_space\"/>\n  <Toolbar android:id=\"@+id/toolbar\"/>\n  <RelativeLayout android:paddingLeft=\"@dimen/toolbar_margin_start\">\n    <TextView android:id=\"@+id/title\"/>\n    <LinearLayout android:orientation=\"vertical\">\n      <View android:layout_height=\"?attr/actionBarSize\"/>\n      <View android:layout_height=\"@dimen/flexible_space_height\"/>\n    </LinearLayout>\n  </RelativeLayout>\n</FrameLayout>\n```\n\nThe root `FrameLayout` is used for moving children separately.\n\nThe second `FrameLayout`(`@id/body`) inside the ScrollView is the main content,\nand you can put any views as you like.\nThis time, we'll add just a `TextView`.\n\n`View`(`@id/flexible_space`) is for a \"flexible space\" which has a opaque background.\nThis view will be translated vertically on scrolling.\n\n`Toolbar` is a normal Toolbar, but this Toolbar will not have \"title\".\n\nThe next `RelativeLayout` and its children are a little tricky.\nThe `TextView`(`@id/title`) is the real title view,\nand other views (`LinearLayout`, `View`) are padding.\nIn this \"flexible space\" pattern, `TextView`'s text should move and its font size should change,\nso it needs additional space.\nWe'll achieve these animations by animate `TextView` itself, so paddings should be outside the `TextView`.\n\nTo confirm other attributes,\nplease see `res/layout/activity_flexiblespacetoolbarscrollview.xml` in the example app.\n\n### Initialization\n\nAt first, set the Toolbar as the ActionBar and show \"homeAsUp\" button.\n\n```java\n@Override\nprotected void onCreate(Bundle savedInstanceState) {\n  super.onCreate(savedInstanceState);\n  setContentView(R.layout.activity_flexiblespacetoolbarscrollview);\n\n  setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n  getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n```\n\nAnd get the Activity's title and set it to the `TextView` which has the ID `@id/title`.\n\n```java\n  mTitleView = (TextView) findViewById(R.id.title);\n  mTitleView.setText(getTitle());\n  setTitle(null);\n```\n\nAnd initialize other views and fields.\n\n```java\nprivate View mFlexibleSpaceView;\nprivate View mToolbarView;\nprivate TextView mTitleView;\nprivate int mFlexibleSpaceHeight;\n\n@Override\nprotected void onCreate(Bundle savedInstanceState) {\n  // Codes that are already explained above are omitted\n  mFlexibleSpaceView = findViewById(R.id.flexible_space);\n  mToolbarView = findViewById(R.id.toolbar);\n\n  final ObservableScrollView scrollView = (ObservableScrollView) findViewById(R.id.scroll);\n  scrollView.setScrollViewCallbacks(this);\n\n  mFlexibleSpaceHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_height);\n  int flexibleSpaceAndToolbarHeight = mFlexibleSpaceHeight + getActionBarSize();\n\n  findViewById(R.id.body).setPadding(0, flexibleSpaceAndToolbarHeight, 0, 0);\n  mFlexibleSpaceView.getLayoutParams().height = flexibleSpaceAndToolbarHeight;\n}\n```\n\nYou should also add `implements ObservableScrollViewCallbacks` to the Activity\nand implement those methods as always.\n\n### Animation\n\nWe use `onScrollChanged()` to create animation.\nWe must write following codes:\n\n* Translate the flexible space view\n* Translate and scale the title view\n\n#### Translate the flexible space view\n\nThis is easy, just translate it using `scrollY`:\n\n```java\n@Override\npublic void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n  ViewHelper.setTranslationY(mFlexibleSpaceView, -scrollY);\n}\n```\n\n#### Scale the title view\n\nHow do you change the size of the font?  \nAt first I tried just changing the size of the font, but it didn't work. It should be scaled.\n\nThe scale should change from `1` to `1.x`. You can change `.x` to fit your app.\nIn this case, I used the height of the flexible space and the height of the Toolbar. This calculates the maximum `.x`:\n\n```java\nfloat maxScale = (float) (mFlexibleSpaceHeight - mToolbarView.getHeight()) / mToolbarView.getHeight();\n```\n\nThe scale (we call `.x` part as \"scale\" from here) should change between 0 to `maxScale`, so it can be written as follows.\n\n```java\n// scrollY should be limited.\nint adjustedScrollY = (int) ScrollUtils.getFloat(scrollY, 0, mFlexibleSpaceHeight);\n\n// When scrollY is 0, scale equals to maxScale.\n// When scrollY reaches to mFlexibleSpaceHeight, scale will be 0.\nfloat scale = maxScale * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight;\n```\n\nWhen scaling the view, we need to set the center point of scaling.\nYou can handle this by using `pivotX` and `pivotY`, and we should set them to `(0, 0)` like this image:\n\n![](../images/basic_4.png)\n\nWe will set `pivotX` and `pivotY` first, and then change the scale:\n\n```java\n// Pivot the title view to (0, 0)\nViewHelper.setPivotX(mTitleView, 0);\nViewHelper.setPivotY(mTitleView, 0);\n\n// Scale the title view\nViewHelper.setScaleX(mTitleView, 1 + scale);\nViewHelper.setScaleY(mTitleView, 1 + scale);\n```\n\n#### Translate the title view\n\nAnd about `translationY`, this is a little complicated.\nLet's see the following picture.\n\n![](../images/basic_5.png)\n\nThe minimum `translationY` is obviously 0, and we want to know\nthe maximum `translationY`.\nAs we can see in the picture, the maximum `translationY` can be calculated with `ht1 + hf - ht2`, so we can write like this:\n\n```java\nint maxTitleTranslationY = mToolbarView.getHeight() + mFlexibleSpaceHeight - (int) (mTitleView.getHeight() * (1 + scale));\n```\n\nAnd we should vary this value using `scrollY`.\n`scrollY` should be limited and it's already calculated as `adjustedY`:\n\n```java\n  int titleTranslationY = (int) (maxTitleTranslationY * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight);\n  ViewHelper.setTranslationY(mTitleView, titleTranslationY);\n```\n\nFinally, we've finished translation and scaling.\n\n```java\n@Override\npublic void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n  ViewHelper.setTranslationY(mFlexibleSpaceView, -scrollY);\n\n  // Calculate scale\n  int adjustedScrollY = (int) ScrollUtils.getFloat(scrollY, 0, mFlexibleSpaceHeight);\n  float maxScale = (float) (mFlexibleSpaceHeight - mToolbarView.getHeight()) / mToolbarView.getHeight();\n  float scale = maxScale * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight;\n\n  // Pivot the title view to (0, 0)\n  ViewHelper.setPivotX(mTitleView, 0);\n  ViewHelper.setPivotY(mTitleView, 0);\n\n  // Scale the title view\n  ViewHelper.setScaleX(mTitleView, 1 + scale);\n  ViewHelper.setScaleY(mTitleView, 1 + scale);\n\n  // Translate the title view\n  int maxTitleTranslationY = mToolbarView.getHeight() + mFlexibleSpaceHeight - (int) (mTitleView.getHeight() * (1 + scale));\n  int titleTranslationY = (int) (maxTitleTranslationY * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight);\n  ViewHelper.setTranslationY(mTitleView, titleTranslationY);\n}\n```\n\n#### Adjust the initial state of the title\n\nIt's almost finished, but maybe you will notice\nthat when the screen is launched,\nthe title is located at the top of the screen.\nIt should be located at the bottom of the header view area and have larger font.\n\nThis is because `onScrollChanged()` is not called.  \nYou can fix that by calling `onScrollChanged()` just after the views are laied out.\nAnd you can handle this \"laid out\" event by using `ViewTreeObserver#addOnGlobalLayoutListener()`.\n\n```java\n@Override\nprotected void onCreate(Bundle savedInstanceState) {\n  // Other initialization codes are omitted\n  ViewTreeObserver vto = mTitleView.getViewTreeObserver();\n  vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {\n    @Override\n    public void onGlobalLayout() {\n      if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n        view.getViewTreeObserver().removeGlobalOnLayoutListener(this);\n      } else {\n        view.getViewTreeObserver().removeOnGlobalLayoutListener(this);\n      }\n      updateFlexibleSpaceText(scrollView.getCurrentScrollY());\n    }\n  });\n}\n\n@Override\npublic void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n  updateFlexibleSpaceText(scrollY);\n}\n\nprivate void updateFlexibleSpaceText(scrollY) {\n  // Original animation codes are omitted\n}\n```\n\nYou can replace the following `ViewTreeObserver` codes\n\n```java\nViewTreeObserver vto = mTitleView.getViewTreeObserver();\nvto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {\n  @Override\n  public void onGlobalLayout() {\n    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n      view.getViewTreeObserver().removeGlobalOnLayoutListener(this);\n    } else {\n      view.getViewTreeObserver().removeOnGlobalLayoutListener(this);\n    }\n    updateFlexibleSpaceText(scrollView.getCurrentScrollY());\n  }\n});\n```\n\nto this:\n\n```java\nScrollUtils.addOnGlobalLayoutListener(mTitleView, new Runnable() {\n  @Override\n  public void run() {\n    updateFlexibleSpaceText(scrollView.getCurrentScrollY());\n  }\n});\n```\n\nThat's all!\n\n[Next: Flexible space with image &raquo;](../../docs/basic/flexible-space-with-image.md)\n"
  },
  {
    "path": "docs/basic/flexible-space-with-image.md",
    "content": "# Flexible space with image\n\nThis topic describes how to create flexible space with image,\nwhich are implemented in the following examples.\n\n* FlexibleSpaceWithImageListViewActivity\n* FlexibleSpaceWithImageRecyclerViewActivity\n* FlexibleSpaceWithImageScrollViewActivity\n\nFirst, please check the \"[Flexible space on the Toolbar](../../docs/basic/flexible-space-toolbar.md)\"\ntutorial, if you haven't.  \n\n---\n\n## Using ScrollView\n\n### Layout with ScrollView\n\n#### Basic structure\n\n```xml\n<FrameLayout>\n    <ImageView android:id=\"@+id/image\"/>\n    <View android:id=\"@+id/overlay\"/>\n    <ObservableScrollView android:id=\"@+id/scroll\">\n        <LinearLayout android:orientation=\"vertical\">\n            <View/>\n            <TextView/>\n        </LinearLayout>\n    </ObservableScrollView>\n    <LinearLayout android:orientation=\"vertical\">\n        <TextView android:id=\"@+id/title\"/>\n        <View/>\n    </LinearLayout>\n    <FloatingActionButton android:id=\"@+id/fab\"/>\n</FrameLayout>\n```\n\nThe root `FrameLayout` is used for moving children separately.\n\n`ImageView`(`@id/image`) is the image that will be translated with parallax effect.\n\n`View`(`@id/overlay`) is a overlay view as the name suggests.  \nIf you try this Activity in the demo app, you can see the image is fading in and out.\nThis view overlaps with the image and its opacity is changed by scroll position.\n\n`LinearLayout` and its chlidren are the real title views.  \nYou would have read the former tutorial, so I will not explain it so much.\n\n`FloatingActionButton` is a widget from the simple and awesome [FloatingActionButton](https://github.com/makovkastar/FloatingActionButton) library.  \nBut this is optional, so you can remove it if you are not going to place any buttons.\nI added it just because I think it's a very symbolic widget of the Material Design\nand some of you might be interested in it.\n\nTo confirm other attributes,\nplease see `res/layout/activity_flexiblespacewithimagescrollview.xml` in the example app.\n\n### Initialization\n\nMost of the codes are easy and not related to this pattern.  \nJust write the following initialization codes:\n\nCopy the title to the title view (`TextView`) and set null to the original title:\n\n```java\nmTitleView.setText(getTitle());\nsetTitle(null);\n```\n\nGet the dimension values and save them to fields (to simplify animation codes):\n\n```Java\nmFlexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\nmFlexibleSpaceShowFabOffset = getResources().getDimensionPixelSize(R.dimen.flexible_space_show_fab_offset);\nmFabMargin = getResources().getDimensionPixelSize(R.dimen.margin_standard);\nmActionBarSize = getActionBarSize();\n```\n\nGet the views which has ID to fields (to simplify animation codes), and initialize them if necessary:\n\n```java\nmImageView = findViewById(R.id.image);\nmOverlayView = findViewById(R.id.overlay);\nmScrollView = (ObservableScrollView) findViewById(R.id.scroll);\nmScrollView.setScrollViewCallbacks(this);\nmTitleView = (TextView) findViewById(R.id.title);\nmFab = findViewById(R.id.fab);\n```\n\nAlthough this is not so related to the scroll animation,\nyou should scale the floating action button (FAB) to 0 in `onCreate()`,\nbecause we'd like to hide it at the beginning and gradually show (scale) it\nby scrolling.\n\n```java\nViewHelper.setScaleX(mFab, 0);\nViewHelper.setScaleY(mFab, 0);\n```\n\nYou should also add `implements ObservableScrollViewCallbacks` to the Activity\nand implement those methods as always.\n\n### Animation\n\nWe use `onScrollChanged()` to create animation.  \nWe'll write the following codes:\n\n* Translate the overlay view and the image view\n* Change the alpha of the overlay view\n* Translate and scale the title view\n* Translate the FAB\n* Show/hide the FAB\n\nLet's see one by one.\n\n#### Translate the overlay view and the image view\n\nAs we implemented in the former tutorials,\nto move `ImageView` which is outside the ScrollView,\nwe must use `-scrollY` and divide it by 2 to create \"parallax\" effect.\n\n```java\n@Override\npublic void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n  float flexibleRange = mFlexibleSpaceImageHeight - mActionBarSize;\n  int minOverlayTransitionY = mActionBarSize - mOverlayView.getHeight();\n  ViewHelper.setTranslationY(mOverlayView, ScrollUtils.getFloat(-scrollY, minOverlayTransitionY, 0));\n  ViewHelper.setTranslationY(mImageView, ScrollUtils.getFloat(-scrollY / 2, minOverlayTransitionY, 0));\n```\n\nAlthough we want to move the overlay view with the image,\nwe don't have to make the scroll speed of the overlay to the same as the image view.\nSo translate `mOverlayView` to `-scrollY` (not `-scrollY / 2`).\n\n#### Change the alpha of the overlay view\n\nCalculating the alpha value is easy, just convert the `scrollY` to range between 0 and 1.\nTo do this, we divide `scrollY` by `flexibleRange` (which we assigned above),\nand limit the value range from 0 to 1 by using `ScrollUtils.getFloat()`.\n\n```java\n  ViewHelper.setAlpha(mOverlayView, ScrollUtils.getFloat((float) scrollY / flexibleRange, 0, 1));\n```\n\n#### Translate and scale the title view\n\nThis is almost the same as the \"Flexible space on the Toolbar\" pattern.\nThe differences are how to calculate the scale and the translationY.\n\n```java\n@Override\npublic void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n  // Codes that are already explained above are omitted\n\n  float scale = 1 + ScrollUtils.getFloat((flexibleRange - scrollY) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA);\n  ViewHelper.setPivotX(mTitleView, 0);\n  ViewHelper.setPivotY(mTitleView, 0);\n  ViewHelper.setScaleX(mTitleView, scale);\n  ViewHelper.setScaleY(mTitleView, scale);\n\n  int maxTitleTranslationY = (int) (mFlexibleSpaceImageHeight - mTitleView.getHeight() * scale);\n  int titleTranslationY = maxTitleTranslationY - scrollY;\n  ViewHelper.setTranslationY(mTitleView, titleTranslationY);\n```\n\n#### Translate the FAB\n\nTranslating the FAB is actually not related to this topic, but I'll explain for your reference.\n\nThe basic idea is to change the `translationY` property of the FAB,\nbut on pre-Honeycomb devices, this doesn't work when you use `setOnClickListener`.\nTo fix the issue, we'll set the margins of the FrameLayout and lay it out again by calling `requestLayout()`.\n\n```java\n@Override\npublic void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n  // Codes that are already explained above are omitted\n\n  int maxFabTranslationY = mFlexibleSpaceImageHeight - mFab.getHeight() / 2;\n  float fabTranslationY = ScrollUtils.getFloat(\n      -scrollY + mFlexibleSpaceImageHeight - mFab.getHeight() / 2,\n      mActionBarSize - mFab.getHeight() / 2,\n      maxFabTranslationY);\n  if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {\n    // On pre-honeycomb, ViewHelper.setTranslationX/Y does not set margin,\n    // which causes FAB's OnClickListener not working.\n    FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mFab.getLayoutParams();\n    lp.leftMargin = mOverlayView.getWidth() - mFabMargin - mFab.getWidth();\n    lp.topMargin = (int) fabTranslationY;\n    mFab.requestLayout();\n  } else {\n    ViewHelper.setTranslationX(mFab, mOverlayView.getWidth() - mFabMargin - mFab.getWidth());\n    ViewHelper.setTranslationY(mFab, fabTranslationY);\n  }\n```\n\nThe expression `- mFab.getHeight() / 2` in the calculation of `maxFabTranslationY` means\nthat the half of the FAB overlaps to the image.\n\nAnd about the `fabTranslationY` calculation,\nyou might think that the expression `mActionBarSize - mFab.getHeight() / 2` for the min value\nis meaningless, but this is required when you scroll the view fast.\nBecause if it scrolls faster than the FAB scaling to 0, it looks as if it just moved away.\n\n#### Show/hide the FAB\n\nShowing or hiding the FAB is easy.\nIf the translationY of the FAB exceeds the threshold, then hide it.\nOtherwise, show it.\n\n```java\n@Override\npublic void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n  // Codes that are already explained above are omitted\n\n  if (fabTranslationY < mFlexibleSpaceShowFabOffset) {\n    hideFab();\n  } else {\n    showFab();\n  }\n}\n```\n\n`hideFab()` and `showFab()` methods can be implemented like this:\n\n```java\n  private boolean mFabIsShown;\n\n  private void showFab() {\n    if (!mFabIsShown) {\n      ViewPropertyAnimator.animate(mFab).cancel();\n      ViewPropertyAnimator.animate(mFab).scaleX(1).scaleY(1).setDuration(200).start();\n      mFabIsShown = true;\n    }\n  }\n\n  private void hideFab() {\n    if (mFabIsShown) {\n      ViewPropertyAnimator.animate(mFab).cancel();\n      ViewPropertyAnimator.animate(mFab).scaleX(0).scaleY(0).setDuration(200).start();\n      mFabIsShown = false;\n    }\n  }\n```\n\nWe need a state variable to indicate whether the FAB is shown or not.\n\nThat's all.\n\n[Next: Filling gap on top of the Toolbar &raquo;](../../docs/basic/filling-gap.md)\n"
  },
  {
    "path": "docs/basic/index.md",
    "content": "# Basic techniques\n\nThis section explains the basic scrolling techniques.  \n\n1. [Show and hide the ActionBar](../../docs/basic/show-hide-action-bar.md)\n1. [Translating the Toolbar](../../docs/basic/translating-toolbar.md)\n1. [Parallax image](../../docs/basic/parallax-image.md)\n1. [Sticky header](../../docs/basic/sticky-header.md)\n1. [Flexible space on the Toolbar](../../docs/basic/flexible-space-toolbar.md)\n1. [Flexible space with image](../../docs/basic/flexible-space-with-image.md)\n1. [Filling gap on top of the Toolbar](../../docs/basic/filling-gap.md)\n\n[Next: Show and hide the ActionBar &raquo;](../../docs/basic/show-hide-action-bar.md)\n"
  },
  {
    "path": "docs/basic/parallax-image.md",
    "content": "# Parallax image\n\nThis topic describes how to create parallax effect,\nwhich are implemented in the following examples.\n\n* ParallaxToolbarScrollViewActivity\n* ParallaxToolbarListViewActivity\n\n---\n\n## Overview\n\nIn this topic, \"parallax\" means the following layout and behavior:\n\n* The layout has an image on the top of the layout.\n* The image will move with half the speed of that of the ScrollView.\n* ScrollView itself has a big padding, which is like a \"window\" to see the image.\n\nTo make the image \"parallax\", we need to do some tricks on the layout.  \n\n`ObservableScrollView` and `ObservableListView` are a little different\naround handling paddings.\nI'll explain from `ObservableScrollView`.\n\n---\n\n## ScrollView\n\n### Layout\n\n#### Basic structure\n\nAt first, let's see the following basic structure of the layout.\n\n```xml\n<FrameLayout>\n  <ObservableScrollView>\n    <RelativeLayout>\n      <ImageView/>\n      <View/>\n      <TextView/>\n    </RelativeLayout>\n  </ObservableScrollView>\n  <Toolbar/>\n</FrameLayout>\n```\n\nPlease note that in this XML, I intentionally omitted attributes(`android:XXX`)\nand package name (`com.github.XXX`) for readability.\n\n##### Why should we use FrameLayout?\n\nAs you can see on the example app, Toolbar is overlaid to the ObservableScrollView.\nTo do this, we need to use `FrameLayout` or `RelativeLayout`.\n\n##### What's inside of the ObservableScrollView?\n\n`ObservableScrollView` extends `ScrollView`, so it can have no more than 1 child.\nHowever we need more children, so placing a `ViewGroup` as the child of `ObservableScrollView` is required.\n\n`ImageView` is the `View` which is going to have \"parallax\" effect.\nYou can replace it to other type of `View` if you want.\n\n`TextView` is the main content of the screen, you can also replace it to other type of `View`.\n\n`View` is an \"anchor\", I'll explain it later.\n\nWe need to move the content and the image separately,\nso the parent of them &mdash; child of `ObservableScrollView` &mdash;\nshould be `RelativeLayout` or `FrameLayout`.\nThis time, we use `RelativeLayout` for that purpose.\n\n#### Don't move the content when its parent is scrolled\n\nHow do you place the main content (a `TextView` for this time) under the `ImageView`?\n\nSuppose you define the position with `android:layout_below` attribute:\n\n```xml\n<!-- some attributes are omitted -->\n<RelativeLayout>\n  <ImageView android:id=\"@+id/image\">\n  <TextView android:layout_below=\"@id/image\">\n</RelativeLayout>\n```\n\nWe need to move `ImageView` but if we do this, \nthe `TextView` moves with the same speed as `ImageView`\nbecause its layout is defined with `android:layout_below=\"@id/image\"`.\nSo we should define the `TextView`'s position with another \"anchor\" view:\n\n```xml\n<!-- some attributes are omitted -->\n<RelativeLayout>\n  <ImageView android:id=\"@+id/image\"\n             android:layout_height=\"@dimen/parallax_image_height\">\n  <View android:id=\"@+id/anchor\"\n        android:layout_height=\"@dimen/parallax_image_height\"\n        android:minHeight=\"@dimen/parallax_image_height\" />\n  <TextView android:layout_below=\"@id/anchor\">\n</RelativeLayout>\n```\n\nWith this anchor view, we can move only `ImageView`.\nThe anchor `View` and `TextView` will remain in their position.\n\n#### Set the content color explicitly\n\nWe need to set the background color of the main content explicitly,\nbecause the image is underlying.\n\n```xml\n<TextView android:layout_below=\"@id/anchor\"\n  android:background=\"@android:color/white\" />\n```\n\n#### Complete the layout\n\nNow set the rest of the attributes of the layout,\nsuch as `android:layout_width`, `android:padding`, etc.  \nPlease see the folloing codes for details.\n\n* `res/layout/activity_parallaxtoolbarscrollview.java`\n\n### Animation\n\n#### Basic structure of Activity\n\nWe use `AppCompatActivity` of the v7 appcompat library for the base `Activity` class,\nand implement `ObservableScrollViewCallbacks`.\n\n```java\npublic class ParallaxToolbarScrollViewActivity\n  extends AppCompatActivity implements ObservableScrollViewCallbacks {\n```\n\n#### Initialize views\n\nThen initialize the views like this.\n\n```java\n// Fields\nprivate View mImageView;\nprivate View mToolbarView;\nprivate ObservableScrollView mScrollView;\nprivate int mParallaxImageHeight;\n\n@Override\nprotected void onCreate(Bundle savedInstanceState) {\n  super.onCreate(savedInstanceState);\n  setContentView(R.layout.activity_parallaxtoolbarscrollview);\n\n  setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n  mImageView = findViewById(R.id.image);\n  mToolbarView = findViewById(R.id.toolbar);\n  mToolbarView.setBackgroundColor(\n    ScrollUtils.getColorWithAlpha(0, getResources().getColor(R.color.primary)));\n\n  mScrollView = (ObservableScrollView) findViewById(R.id.scroll);\n  mScrollView.setScrollViewCallbacks(this);\n\n  mParallaxImageHeight = getResources().getDimensionPixelSize(\n    R.dimen.parallax_image_height);\n}\n```\n\nThe Toolbar should be transparent at the beginning, so set the alpha of the background color to 0\nby using the `ScrollUtils` utility class.\nThis is optional and you can omit this if you don't use the Toolbar.\n\n#### Change the position on scrolling\n\nWe use `onScrollChanged()` method, one of `ObservableScrollViewCallbacks`, to animate the view.\nWhat we need to do in this method is:\n\n1. translate the `ImageView` in Y-axis using `scrollY` parameter\n1. change the alpha value of the background color of the `Toolbar` using `scrollY` parameter\n\nThe second one is optional. You can omit this if you don't use the Toolbar.\n\n##### Translate the ImageView\n\nJust set the `translateY` property to half of `scrollY`.  \nIf you want to change the \"depth\" of the parallax effect, adjust this value (`scrollY / 2`).\n\n```java\n@Override\npublic void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n  ViewHelper.setTranslationY(mImageView, scrollY / 2);\n}\n```\n\n##### Change the alpha of the Toolbar background color\n\nWe should change the alpha value of the background color of the Toolbar,\nso we can write like this.\n\n```java\n@Override\npublic void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n  int baseColor = getResources().getColor(R.color.primary);\n  float alpha = 0; // TODO Fix this value\n  mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(alpha, baseColor));\n```\n\nChanging alpha is a little complicated, so I wrote `float alpha = 0` temporarily.  \nLet's confirm the conditions of the colors and fix the `alpha` value.\n\n* If the `ObservableScrollView` is not scrolled, Toolbar is transparent.  \n  (If `scrollY` equals to 0, alpha of the Toolbar is 0.)\n* If the `ObservableScrollView` is scrolled, it becomes opaque gradually,\n  and when it's scrolled to a certain point, Toolbar is completely opaque.  \n  (If `scrollY` equals to `mParallaxImageHeight`, alpha of the Toolbar is 1.)\n\nWe need to express these conditions as a formula.\n\n`alpha` should changes from 0 to 1, but `scrollY` changes from 0 to thousands,\nso `scrollY` should be scaled.  \nWe should divide `scrollY` with `mParallaxImageHeight` because\nwhen `alpha` becomes 1, `scrollY` should be equal to `mParallaxImageHeight`.\n\n```java\nfloat alpha = (float) scrollY / mParallaxImageHeight;\n```\n\nPlease note that `scrollY` and `mParallaxImageHeight` are both type `int`,\nso you need to cast one of them to `float`.\n\nBut how is it when `scrollY` becomes more than `mParallaxImageHeight`?  \nLet's simulate the result values:\n\n| `scrollY` | `mParallaxImageHeight` | `alpha`   | Valid alpha?  |\n| ---------:| ----------------------:| ---------:| ------------- |\n| 0         | 300                    | 0         | Valid         |\n| 150       | 300                    | 0.5       | Valid         |\n| 300       | 300                    | 1.0       | Valid         |\n| 450       | 300                    | _**1.5**_ | _**Invalid**_ |\n\nAs we can see in the 4th row (`scrollY == 450`),\nwe need to control `alpha` so that it will not exceed 1.0.\nThis time we use `Math.min()` to limit the value from 0 to 1.\n\n```java\nfloat alpha = Math.min(1, (float) scrollY / mParallaxImageHeight);\n```\n\nNow it's done.  \n`onScrollChanged` will be like this:\n\n```java\n@Override\npublic void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n  int baseColor = getResources().getColor(R.color.primary);\n  float alpha = Math.min(1, (float) scrollY / mParallaxImageHeight);\n  mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(alpha, baseColor));\n  ViewHelper.setTranslationY(mImageView, scrollY / 2);\n}\n```\n\n#### Restore scroll state\n\nWe need to handle one more thing: restoring scroll state when the Activity is restored.  \n`ObservableScrollView` itself stores its scroll position,\nso you just need to update the view\nin the `onRestoreInstanceState()` method.\n\n```java\n@Override\nprotected void onRestoreInstanceState(Bundle savedInstanceState) {\n  super.onRestoreInstanceState(savedInstanceState);\n  onScrollChanged(mScrollView.getCurrentScrollY(), false, false);\n}\n```\n\n---\n\n## ListView\n\nLet's see the difference of the implementation between ListView version and ScrollView version.\n\n### Layout\n\n#### Basic structure\n\n```xml\n<FrameLayout>\n  <ImageView/>\n  <View/>\n  <ObservableListView/>\n  <Toolbar/>\n</FrameLayout>\n```\n\nWe use `FrameLayout` to the root view, just like ScrollView pattern.  \n`FrameLayout` can be used to move children views separately.\n\n`ImageView` is the view which should have \"parallax\" effect.\n\nThe next `View` is used for different purpose from that of ScrollView.\nI'll explain this later.\n\n\n#### Why do we use different layout?\n\nUnlike ScrollView, ListView cannot have children views,\nso `ImageView` should be outside of the scrollable view (ListView)\nand we should move the `ImageView` manually.\n\n#### How do we place ImageView and ListView?\n\n`ImageView` is going to be scrolled slower than ListView\n(because we're going to make \"parallax\" effect), \nso `ImageView` should be underneath the ListView.\nOtherwise, the bottom of the `ImageView` overlaps with the top of the ListView.\n\nAlso, ListView should have a big padding\nat the top of the ListView to make `ImageView` visible.  \nWe achieve this by adding a transparent header view to the ListView.\n\n#### Why do we need a View?\n\nAs I mentioned above, ListView should have a transparent header,\nso its background color should be also transparent.\nBut if we do this, not only the header view but also the items of the ListView\nbecome transparent.\n\n![](../images/basic_1.png)\n\n\nTo avoid this, we set a dummy background view under the ListView.\n\n### Animation\n\n#### Basic structure of Activity\n\nIt's same as `ParallaxToolbarScrollViewActivity` example.\n\n```java\npublic class ParallaxToolbarListViewActivity\n  extends BaseActivity implements ObservableScrollViewCallbacks {\n```\n\n#### Initialize views\n\nLike ScrollView, initialize the `ObservableListView`, `ImageView`, Toolbar, etc.  \nAnd as I explained, ListView should have a header view.\n\n```java\nprivate View mImageView;\nprivate View mToolbarView;\nprivate View mListBackgroundView;\nprivate ObservableListView mListView;\nprivate int mParallaxImageHeight;\n\n@Override\nprotected void onCreate(Bundle savedInstanceState) {\n  super.onCreate(savedInstanceState);\n  setContentView(R.layout.activity_parallaxtoolbarlistview) ;\n\n  setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n  mImageView = findViewById(R.id.image);\n  mToolbarView = findViewById(R.id.toolbar);\n  mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(0, getResources().getColor(R.color.primary)));\n\n  mParallaxImageHeight = getResources().getDimensionPixelSize(R.dimen.parallax_image_height);\n\n  mListView = (ObservableListView) findViewById(R.id.list);\n  mListView.setScrollViewCallbacks(this);\n  // Set padding view for ListView. This is the flexible space.\n  View paddingView = new View(this);\n  AbsListView.LayoutParams lp = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT,\n          mParallaxImageHeight);\n  paddingView.setLayoutParams(lp);\n  paddingView.setClickable(true);\n\n  mListView.addHeaderView(paddingView);\n  setDummyData(mListView);\n  mListBackgroundView = findViewById(R.id.list_background);\n```\n\nNote that following code is necessary to disable header view's list selector effect.\n\n```java\n  paddingView.setClickable(true);\n```\n\n`setDummyData()` should be replaced to appropriate data population codes.\n\n#### Change the position on scrolling\n\n##### Translate the ImageView\n\nWe use `onScrollChanged` method to translate views.\n\n```java\n@Override\npublic void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n}\n```\n\nBasically, we should just set the translateY property to half of `scrollY`.\nBut be careful, unlike ScrollView, when `scrollY` gets larger then `translateY` of `ImageView` should become smaller\nbecause `ImageView` is not a child of the ListView.\nSo we should use `-scrollY / 2` as `translationY` (and you can adjust \"`/ 2`\" if you want).\n\n```java\nViewHelper.setTranslationY(mImageView, -scrollY / 2);\n```\n\n##### Translate the background view\n\nThe background should move with ListView, but it should have an offset `mParallaxImageHeight`\nso we can write like this:\n\n```java\nViewHelper.setTranslationY(mListBackgroundView, mParallaxImageHeight - scrollY);\n```\n\nBut how is it when `scrollY` becomes more than `mParallaxImageHeight`?  \nLet's simulate the result values:\n\n| `mParallaxImageHeight` | `scrollY` | `mParallaxImageHeight - scrollY` | TranslationY of `mListViewBackgroundView` should be |\n| ----------------------:| ---------:|---------------------------------:|----------------------------------------------------:|\n| 300                    | 0         | 300                              | 300                                                 |\n| 300                    | 150       | 150                              | 150                                                 |\n| 300                    | 300       | 0                                | 0                                                   |\n| 300                    | 450       | -150                             | 0                                                   |\n\nThe 4th `mParallaxImageHeight - scrollY` becomes negative and it's invalid.  \nSo use `Math.max()` to avoid this.\n\n```java\nViewHelper.setTranslationY(mListBackgroundView, Math.max(0, -scrollY + mParallaxImageHeight));\n```\n\nThat's all.  \nThe rest of the codes are the same as `ObservableScrollView` example.\n\n[Next: Sticky header &raquo;](../../docs/basic/sticky-header.md)\n"
  },
  {
    "path": "docs/basic/show-hide-action-bar.md",
    "content": "# Show and hide the ActionBar\n\nThis topic describes how to show and hide the ActionBar,\nwhich are implemented in the following examples.\n\n* ActionBarControlGridViewActivity\n* ActionBarControlListViewActivity\n* ActionBarControlRecyclerViewActivity\n* ActionBarControlScrollViewActivity\n* ActionBarControlWebViewActivity\n\n---\n\n## Using the basic callbacks\n\nSuppose you've already checked the \"[Quick start](../../docs/quick-start/index.md)\" section,\nyou wouldn't know the meaning of the codes yet.  \nSo at first, let's see how those codes work.\n\n### ObservableScrollViewCallbacks\n\nIn the quick start guide, you wrote the implementation of `ObservableScrollViewCallbacks` (following methods).\n\n```java\n@Override\npublic void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n}\n@Override\npublic void onDownMotionEvent() {\n}\n@Override\npublic void onUpOrCancelMotionEvent(ScrollState scrollState) {\n}\n```\n\nThese are the methods of `ObservableScrollViewCallbacks` interface and all the `Observable*View`s can handle this callbacks.\n\n### onScrollChanged callback\n\nThis is called when the scroll change events occurred.  \nThis won't be called just after the view is laid out, so if you'd like to initialize the position of your views with this method, you should call this manually or invoke scroll as appropriate.\n\nYou would expect it to be called after `onCreate`,\nbut `ListView` (or other views) does not call back\nscroll change event, so `Observable*Views` cannot\ncall this.\nTherefore you should write like this:\n\n```java\nonScrollChanged(mListView.getCurrentScrollY(), false, false);\n```\n\nI know it's a bad pattern to call the \"callback\" methods from us, they should be called by the library when they should be. However, we cannot improve this because this behavior depends on the view classes in the Android SDK.\n\n### onDownMotionEvent callback\n\nThis is called when the down motion events occur.  \nThis can be useful if you'd like to know when the touch (or dragging) has begun.\n\n### onUpOrCancelMotionEvent callback\n\nThis is called when the dragging ended or canceled.\nThis is useful when you move some views when the scroll ends: showing/hiding a view, sliding a view to the anchor point, etc.\n\n## How it works: ActionBar animation\n\nAs I explained in the quick start section, the main animation code is in the `onUpOrCancelMotionEvent`.\n\nWhat we want to do is:\n\n1. to hide the ActionBar when we swipe up the view, because we want to see the contents.\n1. to show the ActionBar when we swipe down the view, because we want to tap a button on the ActionBar (it could be sharing the contents or going back to the former screen, for example).\n\nEither way, we should get the direction of scrolling when the dragging ends.\n`onUpOrCancelMotionEvent` callback has a `ScrollState` parameter. This parameter indicates the direction of the scroll, so we can write like this:\n\n```java\npublic void onUpOrCancelMotionEvent(ScrollState scrollState) {\n  if (scrollState == ScrollState.UP) {\n    // TODO show or hide the ActionBar\n  } else if (scrollState == ScrollState.DOWN) {\n    // TODO show or hide the ActionBar\n  }\n}\n```\n\nWhen you move your finger from the bottom of the screen to the top (swiping up), the state will be `ScrollState.UP`. So we can write `ActionBar#hide()` in this condition.\nAnd this event occurs every time you scroll the view, so if the `ActionBar` is already hidden, you don't have to hide it anymore.\n\nNow you know how to handle the other direction.\nShow the ActionBar when `ScrollState.DOWN` is passed to the callback.\n\n```java\nActionBar ab = getSupportActionBar();\nif (scrollState == ScrollState.UP) {\n  if (ab.isShowing()) {\n    ab.hide();\n  }\n} else if (scrollState == ScrollState.DOWN) {\n  if (!ab.isShowing()) {\n    ab.show();\n  }\n}\n```\n\n## Conclusion\n\nIf you'd like to animate the ActionBar, you should write animation codes in the `onUpOrCancelMotionEvent` callback.\nAlso, we used `ObservableListView` in the quick start, but in this pattern all types of `Observable*View`s have the same behavior, so you can write exactly the same.\n\n[Next: Translating the Toolbar &raquo;](../../docs/basic/translating-toolbar.md)\n"
  },
  {
    "path": "docs/basic/sticky-header.md",
    "content": "# Sticky header\n\nThis topic describes how to keep header on top of the screen,\nwhich are implemented in the following examples.\n\n* StickyHeaderListViewActivity\n* StickyHeaderRecyclerViewActivity\n* StickyHeaderScrollViewActivity\n* StickyHeaderWebViewActivity\n\n---\n\n## Overview\n\nThis is a complex version of [Toolbar translation pattern](../../docs/basic/translating-toolbar.md).\n\nWe add a features to keep the half of the header view to the top of the screen.\nAnd this time I'll explain using ScrollView.\nReplacing it to other type of scrollable views are not so difficult.\n\n## Using ScrollView\n\n### Layout with ScrollView\n\nLet's look at the layout file as always.\n\nHere is the basic structure of StickyHeader pattern with ScrollView.\nThis is a little difficult than Toolbar's one.\n\n```xml\n<FrameLayout>\n  <ObservableScrollView android:id=\"@+id/scroll\">\n    <LinearLayout android:orientation=\"vertical\">\n      <View android:minHeight=\"?attr/actionBarSize\"/>\n      <View android:minHeight=\"?attr/actionBarSize\"/>\n      <TextView/>\n    </LinearLayout>\n  </ObservableScrollView>\n  <LinearLayout\n    android:id=\"@+id/header\"\n    android:orientation=\"vertical\">\n    <Toolbar\n      android:id=\"@+id/toolbar\"\n      android:minHeight=\"?attr/actionBarSize\"/>\n    <TextView\n      android:id=\"@+id/sticky\"\n      android:layout_height=\"?attr/actionBarSize\"/>\n  </LinearLayout>\n</FrameLayout>\n```\n\nIn Toolbar translation pattern, we used only `ObservableScrollView` and `Toolbar` in `FrameLayout`.\nThis time, we need to make each views more complex.\n\n#### Create header space for ScrollView with twice the size of ActionBar\n\nAt the initial state of views, ScrollView needs to have a header view with twice the size of the ActionBar.\nThe half of this header view will be \"sticky\".  \nSo we simply add 2 `View`s with the height `?attr/actionBarSize` above the `TextView`.\n\nYou can also add just 1 `View` with a certain size with `dp`,\nbut it's better to use `?attr/actionBarSize` because it has multiple values for several size of screens,\nscreen rotation and OS versions, and using the standard size is good for users.\n\nAnother way to achieve this, is to set the height of the `View` programmatically.\nYou can resolve the value of `?attr/actionBarSize` in `Activity#onCreate()`, multiply it by 2 and set it to the `View`.\n\nAnd please note that `TextView` is the real content of the ScrollView, so you can replace it to other view if you want.\n\n#### Create sticky part for Toolbar\n\nToolbar is replaced to `LinearLayout`, and it contains a Toolbar and a `TextView`.\n`TextView` will be the \"sticky\" view.\nYou can replace it to some complex views.\n\n### Animate the views with ScrollView callbacks\n\nThis time, we use two callbacks: `onScrollChanged()` and `onUpOrCancelMotionEvent()` to animate views.\nWe are going to implement the following animation.\n\n1. Move the Toolbar and the sticky view (we call \"header views\") when the ScrollView is scrolled.\n1. When we scroll the ScrollView, the Toolbar will go out of the screen.\n   But when we scroll it more, sticky view must keep its position to the top of the screen.\n1. When the Toolbar is not completely hidden and we stop scrolling (touch up the ScrollView),\n    * the Toolbar will be shown completely, if we were swiping down.\n    * the Toolbar will be hidden completely, if we were swiping up.\n1. When we swipe down the ScrollView and touch up, the header view should\n   come out immediately. Sometimes it's called \"Quick Return\" pattern.\n\n#### Move the header view when ScrollView is scrolled\n\nOverride the `onScrollChanged()`, and implement some codes with the condition `if (dragging)`.\n\n```java\n@Override\npublic void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n  if (dragging) {\n    // TODO implement the rest of the codes\n  //} else { // ScrollView is scrolled by inertia\n  }\n}\n```\n\nThis is because we want to move views only when it is dragged.\nWithout this, we cannot achieve the 3rd condition above: showing or hiding the Toolbar automatically\nwhen the scroll ended.\n\nNext step, implement the header view translation.  \nAt first, create a field with name `mHeaderView`, and initialize it in `onCreate()`:\n\n```java\nmHeaderView = findViewById(R.id.header);\n```\n\nWhen the scrollY parameter gets increased, the translationY of `mHeaderView` should decrease.\nSo we can write like this:\n\n```java\n@Override\npublic void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n  if (dragging) {\n    ViewHelper.setTranslationY(mHeaderView, -scrollY);\n  }\n}\n```\n\n#### Sticky view must keep its position to the top of the screen\n\nThe header view will disappear completely, and this is not what we want.\n`mHeaderView` should stop after moving the height of Toolbar.\n\n```java\n@Override\npublic void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n  if (dragging) {\n    int toolbarHeight = mToolbarView.getHeight();\n    ViewHelper.setTranslationY(mHeaderView, Math.max(-toolbarHeight, -scrollY));\n  }\n}\n```\n\nYou can see the sticky view keeping its position to the top of the screen.\n\n#### When Toolbar is not completely hidden, show or hide it completely\n\nTo do this, we should implement `onUpOrCancelMotionEvent`.  \nIf we swipe down, Toolbar should be shown,\nand if we swipe up, Toolbar should be hidden.\n\n```java\n@Override\npublic void onUpOrCancelMotionEvent(ScrollState scrollState) {\n  if (scrollState == ScrollState.DOWN) {\n    showToolbar();\n  } else if (scrollState == ScrollState.UP) {\n    hideToolbar();\n  }\n}\n```\n\nBut when we swipe up and scrolled less than Toolbar's height,\nhiding the Toolbar makes white space around the top of the ScrollView.  \nSo we should show the Toolbar if `scrollY` is less than Toolbar's height.\n\n```java\n@Override\npublic void onUpOrCancelMotionEvent(ScrollState scrollState) {\n  if (scrollState == ScrollState.DOWN) {\n    showToolbar();\n  } else if (scrollState == ScrollState.UP) {\n    int toolbarHeight = mToolbarView.getHeight();\n    int scrollY = mScrollView.getCurrentScrollY();\n    if (toolbarHeight <= scrollY) {\n      hideToolbar();\n    } else {\n      showToolbar();\n    }\n  }\n}\n```\n\nAnd sometimes `scrollState` becomes `STOP` (or `null`).\nIf it becomes such values, the header view stops halfway.\nTo avoid this behavior, write `else` clause.\n\n```java\n@Override\npublic void onUpOrCancelMotionEvent(ScrollState scrollState) {\n  if (scrollState == ScrollState.DOWN) {\n    showToolbar();\n  } else if (scrollState == ScrollState.UP) {\n    int toolbarHeight = mToolbarView.getHeight();\n    int scrollY = mScrollView.getCurrentScrollY();\n    if (toolbarHeight <= scrollY) {\n      hideToolbar();\n    } else {\n      showToolbar();\n    }\n  } else {\n    // Even if onScrollChanged occurs without scrollY changing, toolbar should be adjusted\n    if (!toolbarIsShown() && !toolbarIsHidden()) {\n      // Toolbar is moving but doesn't know which to move:\n      // you can change this to hideToolbar()\n      showToolbar();\n    }\n  }\n}\n```\n\nThen write the unimplemented methods.  \nUnlike Toolbar translation pattern, we use `ViewPropertyAnimator.animate()`\nbecause it's simple and we don't have to change the height of views.\n\n```java\nprivate boolean toolbarIsShown() {\n  return ViewHelper.getTranslationY(mHeaderView) == 0;\n}\n\nprivate boolean toolbarIsHidden() {\n  return ViewHelper.getTranslationY(mHeaderView) == -mToolbarView.getHeight();\n}\n\nprivate void showToolbar() {\n  float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n  if (headerTranslationY != 0) {\n    ViewPropertyAnimator.animate(mHeaderView).cancel();\n    ViewPropertyAnimator.animate(mHeaderView).translationY(0).setDuration(200).start();\n  }\n}\n\nprivate void hideToolbar() {\n  float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n  int toolbarHeight = mToolbarView.getHeight();\n  if (headerTranslationY != -toolbarHeight) {\n    ViewPropertyAnimator.animate(mHeaderView).cancel();\n    ViewPropertyAnimator.animate(mHeaderView).translationY(-toolbarHeight).setDuration(200).start();\n  }\n}\n```\n\nOnce `ViewPropertyAnimator.animate()` is called, animation will be running in the next 200ms.\nAnd if the next animation(`showToolbar()` or `hideToolbar()`) is requested while the animation is running,\nthe current animation should be canceled.\nTherefore we call `ViewPropertyAnimator.animate(mHeaderView).cancel()`\nbefore calling `start()`.\n\n#### When swiping up, header view should scroll\n\nIt's almost completed, and if you think it's OK, you don't have to write the following codes.\n\nWhen we scroll so much and swip down little, the header view will be shown.\nAnd after that, when we drag ScrollView to upper side,\nI think that the header view should move with ScrollView, but it doesn't.\n\nSo we make the header view to scroll even when `scrollY` is larger than the Toolbar's height.\n\nTo do this, we just calculate the distance from the first touch point and the current point.\nAnd the distance from the first touch point become larger than Toolbar's height,\nthe header view should not scroll any longer.\n\n```java\n// Add a field to keep the first scrollY\nprivate int mBaseTranslationY;\n\n@Override\npublic void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n  if (dragging) {\n    int toolbarHeight = mToolbarView.getHeight();\n    if (firstScroll) { // Add this if clause\n      float currentHeaderTranslationY = ViewHelper.getTranslationY(mHeaderView);\n      if (-toolbarHeight < currentHeaderTranslationY) {\n        mBaseTranslationY = scrollY;\n      }\n    }\n    // Change -scrollY to -(scrollY - mBaseTranslationY)\n    float headerTranslationY = Math.max(-toolbarHeight, -(scrollY - mBaseTranslationY));\n    ViewPropertyAnimator.animate(mHeaderView).cancel();\n    ViewHelper.setTranslationY(mHeaderView, headerTranslationY);\n  }\n}\n\n@Override\npublic void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    // Should be cleared when scroll ends\n    mBaseTranslationY = 0;\n```\n\nIt's almost done, but sometimes we can see a weird behavior:\nthe header view leaves the top of the screen.\n\n![](../../docs/images/basic_2.png)\n\nThis is because `headerTranslationY` can become larger than 0,\nso it should be limited by using `Math.min()`.\n\n```java\nfloat headerTranslationY = Math.min(0, Math.max(-toolbarHeight, -(scrollY - mBaseTranslationY));\n```\n\nNow it's working, but don't you think it's a little complicated expression?  \nAndroid-ObservableScrollView provides a small utility class `ScrollUtils`,\nand we can replace `Math.min(max, Math.max(min, value))` to `ScrollUtils.getFloat()`.\n\n```java\nfloat headerTranslationY = ScrollUtils.getFloat(-(scrollY - mBaseTranslationY), -toolbarHeight, 0);\n```\n\nOf course, you can confirm the meaning of each parameters easily.\nPressing `F1` key on `getFloat()` will show the javadoc window:\n\n![](../../docs/images/basic_3.png)\n\n[Next: Flexible space on the Toolbar &raquo;](../../docs/basic/flexible-space-toolbar.md)\n"
  },
  {
    "path": "docs/basic/translating-toolbar.md",
    "content": "# Translating the Toolbar\n\nThis topic describes how to translate the Toolbar,\nwhich are implemented in the following examples.\n\n* ToolbarControlBaseActivity\n* ToolbarControlGridViewActivity\n* ToolbarControlListViewActivity\n* ToolbarControlRecyclerViewActivity\n* ToolbarControlScrollViewActivity\n* ToolbarControlWebViewActivity\n\n---\n\n## About the Toolbar\n\nIn this section we learn how to translate the Toolbar.  \nToolbar was introduced on Android 5.0, and you can also use it on pre-Lollipop devices\nby using [v7 appcompat library](http://developer.android.com/tools/support-library/features.html#v7-appcompat)\nof the Android Support Library package.\n\n## Design of the examples\n\nThe existing examples above, `ToolbarControlBaseActivity` has most of the codes to avoid writing duplicate codes.\nIf you use one of them, you don't have to use this structure: extending Activity is not required to achieve this effect.\n\n## Create layout file\n\nIn this topic, we use `ObservableListView` and `Toolbar`, and wrap them with `FrameLayout`.  \n`FrameLayout` and `RelativeLayout` are useful to translate views inside of it separately.\n\n```xml\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n  android:layout_width=\"match_parent\"\n  android:layout_height=\"match_parent\">\n\n  <android.support.v7.widget.Toolbar\n    android:id=\"@+id/toolbar\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"?attr/colorPrimary\"\n    android:minHeight=\"?attr/actionBarSize\"\n    app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n    app:theme=\"@style/Toolbar\" />\n\n  <com.github.ksoichiro.android.observablescrollview.ObservableListView\n    android:id=\"@+id/scrollable\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:layout_marginTop=\"?attr/actionBarSize\" />\n</FrameLayout>\n```\n\n## How to translate the Toolbar\n\nThe basic idea about showing/hiding the Toolbar is exactly the same as the ActionBar.\nHowever, the Toolbar class does not provide any convinient methods like `show()` and `hide()` which the ActionBar class has.\nTherefore we should implement such methods to translate the Toolbar.\nOur goal is to make the following codes work:\n\n```java\n@Override\npublic void onUpOrCancelMotionEvent(ScrollState scrollState) {\n  if (scrollState == ScrollState.UP) {\n    if (toolbarIsShown()) { // TODO Not implemented\n      hideToolbar(); // TODO Not implemented\n    }\n  } else if (scrollState == ScrollState.DOWN) {\n    if (toolbarIsHidden()) { // TODO Not implemented\n      showToolbar(); // TODO Not implemented\n    }\n  }\n}\n```\n\n## Using NineOldAndroids\n\nBefore we begin, you should confirm whether you're going to support pre-Honeycomb devices.\nTo translate the Toolbar, we would like to use the [Property Animation APIs](http://developer.android.com/guide/topics/graphics/prop-animation.html)\nwhich are introduced in API level 11, so if you are going to support pre-Honeycomb devices,\n[JakeWharton/NineOldAndroids](https://github.com/JakeWharton/NineOldAndroids/) might be useful (although it's marked as deprecated).\n\nIn this project, all the examples use NineOldAndroids.\nSo if you don't support pre-Honeycomb devices, please replace `ViewHelper.methodName(viewObject)` to `viewObject.methodName()`.\n\n```\nNineOldAndroids: ViewHelper.getTranslationY(mToolbar)\nPlatform API:    mToolbar.getTranslationY()\n```\n\nIf you use NineOldAndroids, add an entry to the `dependencies` closure in your `build.gradle`:\n\n```gradle\ndependencies {\n    compile 'com.nineoldandroids:library:2.4.0'\n}\n```\n\n## toolbarIsShown()/toolbarIsHidden()\n\nNow let's start from the easiest part.  \nTo avoid redundant translation, we need methods to check if the Toolbar is shown or hidden.  \nWith the property animation APIs (or NineOldAndroids), we just simply check the `translationY` property.\n\n```java\nprivate boolean toolbarIsShown() {\n  // Toolbar is 0 in Y-axis, so we can say it's shown.\n  return ViewHelper.getTranslationY(mToolbar) == 0;\n}\n\nprivate boolean toolbarIsHidden() {\n  // Toolbar is outside of the screen and absolute Y matches the height of it.\n  // So we can say it's hidden.\n  return ViewHelper.getTranslationY(mToolbar) == -mToolbar.getHeight();\n}\n```\n\n## Implement showToolbar()/hideToolbar()\n\nNext, let's implement methods to animate the Toolbar.  \nBefore thinking about details, write some pseudocodes to simplify the problem.  \nTo show or hide the Toolbar, we just need one method to move the Toolbar.\n\n```java\nprivate void showToolbar() {\n  moveToolbar(0);\n}\n\nprivate void hideToolbar() {\n  moveToolbar(-mToolbar.getHeight());\n}\n```\n\nThis should work, if we implement the `moveToolbar` method correctly :)\n\nMost of the animation codes are combination of property value calculations,\nand I think it's very hard to keep these information in my brain or imagine correctly.\nAnd this approach is useful to implement the complex animation.\n\n## Implement moveToolbar()\n\nAlthough we named the method `moveToolbar`, it's not everything we need to handle.\nIn ActionBar examples, not only the ActionBar is moved but also the height of the view (`Observable*View`) is changed.\nAnd we need to implement this behavior for the Toolbar.\n\nTo use the changing property values, we can use `ValueAnimator`.  \n`ValueAnimator` has a callback method `onAnimationUpdate`, and we can get the animation progress from it.  \n`ValueAnimator` itself does not animate anything, we need to animate something using a parameter of the callback.\n\n```java\nValueAnimator animator = ValueAnimator.ofFloat(0, 100).setDuration(200);\nanimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n  @Override\n  public void onAnimationUpdate(ValueAnimator animation) {\n    float value = (float) animation.getAnimatedValue();\n    // You can do whatever you want with the `value`.\n  }\n});\n```\n\nIn the example code above, the local variable `value` changes from `0f` to `100f` in 200ms.  \nIn this case, we should change the `translationY` property of the Toolbar,\nand change the height of the `Observable*View` like this:\n\n```java\nprivate void moveToolbar(float toTranslationY) {\n  ValueAnimator animator = ValueAnimator.ofFloat(ViewHelper.getTranslationY(mToolbar), toTranslationY).setDuration(200);\n  animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n    @Override\n    public void onAnimationUpdate(ValueAnimator animation) {\n      float translationY = (float) animation.getAnimatedValue();\n      ViewHelper.setTranslationY(mToolbar, translationY);\n      ViewHelper.setTranslationY((View) mScrollable, translationY);\n      FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) ((View) mScrollable).getLayoutParams();\n      lp.height = (int) -translationY + getScreenHeight() - lp.topMargin;\n      ((View) mScrollable).requestLayout();\n    }\n  });\n  animator.start();\n}\n```\n\nThe `translationY` local variable changes from `ViewHelper.getTranslationY(mToolbar)`( == current translationY)\nto `toTranslationY`.\n\nTo translate the Toolbar, we just call `ViewHelper.setTranslationY()`.  \nAnd to change the height of the wrapper view (`FrameLayout`), set the height value of `FrameLayout.LayoutParams`\nand update by calling `requestLayout()`.\n\n## Avoid redundant animation\n\nWe'd better check the current `translationY` value\nand if it's already equal to `toTranslationY`, stop the animation.\n\n```java\nprivate void moveToolbar(float toTranslationY) {\n  // Check the current translationY\n  if (ViewHelper.getTranslationY(mToolbar) == toTranslationY) {\n    return;\n  }\n  // Codes after that are omitted\n}\n```\n\nThat's all.\n\n[Next: Parallax image &raquo;](../../docs/basic/parallax-image.md)\n"
  },
  {
    "path": "docs/contributor/_data.json",
    "content": "{\n  \"index\": {\n    \"title\": \"For contributors\"\n  },\n  \"ci\": {\n    \"title\": \"CI\"\n  },\n  \"update-website\": {\n    \"title\": \"Update website\"\n  },\n  \"release\": {\n    \"title\": \"Release\"\n  }\n}\n"
  },
  {
    "path": "docs/contributor/ci.md",
    "content": "# CI\n\nComing soon...\n"
  },
  {
    "path": "docs/contributor/index.md",
    "content": "# For contributors\n\nThis section explains some operations for managing the project.\n\n1. [CI](../../docs/contributor/ci.md)\n1. [Update website](../../docs/contributor/update-website.md)\n1. [Release](../../docs/contributor/release.md)\n\n"
  },
  {
    "path": "docs/contributor/release.md",
    "content": "# Release\n\nThis is just a memo for me.\n\n## Bump up the version\n\nEdit `gradle.properties` and commit.\n\n```\nVERSION_NAME=x.x.x\n```\n\nIf you set the version suffix `-SNAPSHOT`, it will be handled as a snapshot.\n\n## Commit changes\n\nBefore the final confirmation and release, make sure that\nthere are no uncommitted changes in your repository.\n\n## Confirm test\n\nCheck if all tests pass at your machine.  \n\n```sh\n./gradlew clean :library:assemble :library:connectedCheck\n```\n\n## Upload archives\n\n### Set the credentials\n\nIf this is the first time for uploading archives,\nyou must write credentials to `~/.gradle/gradle.properties`.\n\n```\nNEXUS_USERNAME=xxxx\nNEXUS_PASSWORD=xxxx\n```\n\n### Upload\n\n```sh\n./gradlew :library:uploadArchives\n```\n\nOr you can clean, test and upload at once.\n\n```sh\n./gradlew clean :library:assemble :library:connectedCheck :library:uploadArchives\n```\n\n## Close the repo on Sonatype\n\nOpen [Sonatype Nexus Professional](https://oss.sonatype.org/) on your browser,\nfind your repo and close it.\nIf there are no problems, repository will be staged to the URL like this.\n\n```\nhttps://oss.sonatype.org/content/repositories/TEMPORARY_REPO_NAME/GROUP/ARTIFACT_ID/VERSION/ARTIFACT_ID-VERSION.aar\n```\n\nYou will receive an email with title \"Nexus: Staging Completed\",\nyou can know the appropriate URL from the email.\n\nSet the URL to the `repositories` in your `build.gradle`, and sync.\n\n```gradle\nrepositories {\n  maven {\n    url uri('https://oss.sonatype.org/content/repositories/TEMPORARY_REPO_NAME/')\n  }\n}\n```\n\n## Release\n\nAfter that, click \"Release\" to promote.\nIf it's processed successfully, you will receive an email with title \"Nexus: Promotion Completed\".\n\nIt takes 3 or 4 hours to be synced to the Maven Central Repository.\n\n## Create tag, update synced version and push\n\nIf it's successfully published to the Maven Central Repository,\n\n* create a tag like `v1.5.0`\n* update the `SYNCED_VERSION_NAME` in `gradle.properties`\n* push the master branch and the tag to GitHub\n"
  },
  {
    "path": "docs/contributor/update-website.md",
    "content": "# Update website\n\nComing soon...\n"
  },
  {
    "path": "docs/example/_data.json",
    "content": "{\n  \"index\": {\n    \"title\": \"Try the example app\"\n  },\n  \"google-play\": {\n    \"title\": \"Download from Google Play\"\n  },\n  \"wercker\": {\n    \"title\": \"Download from wercker\"\n  },\n  \"android-studio\": {\n    \"title\": \"Build on Android Studio\"\n  },\n  \"eclipse\": {\n    \"title\": \"Build on Eclipse\"\n  }\n}\n"
  },
  {
    "path": "docs/example/android-studio.md",
    "content": "# Build on Android Studio\n\nThis library and samples basically support Android Studio and Gradle.  \n(Actually, I'm using them to develop this library.)\n\nIf you're an Eclipse user, you can skip and go to the next topic.\n\n## Prerequisites\n\nPlease [check here](../../docs/reference/environment.md) to see if your enviroment satisfies the prerequisites for building the app.\n\n## Instructions\n\n### Get the source codes\n\nGet the source code of the library and example app, by cloning git repository or downloading archives.\n\nIf you use git, execute the following command in your workspace directory.\n\n```\n$ git clone https://github.com/ksoichiro/Android-ObservableScrollView.git\n```\n\nIf you are using Windows, try it on GitBash or Cygwin or something that supports git.\n\n### Import the project to Android Studio\n\n1. Select File &gt; New &gt; Import Project... from the menu.\n1. Select the directory that is cloned. If you can't see your cloned directory, click \"Refresh\" icon and find it.\n1. Android Studio will import the project and build it. This might take minutes to complete. Even when the project window is opened, wait until the Gradle tasks are finished and indexed.\n1. Click \"Run 'samples'\" button to build and launch the app. Don't forget to connect your devices to your machine.\n\n### Build and install using Gradle\n\nIf you just want to install the app to your device, you don't have to import project to Android Studio.\n\nAfter cloning the project, connect your device to your machine, and execute the following command on the terminal.\n\nMac / Linux / Git Bash, Cygwin on Windows:\n\n```sh\n$ cd /path/to/Android-ObservableScrollView\n$ ./gradlew installDevDebug\n```\n\nWindows (Command prompt):\n\n```sh\n> cd C:\\path\\to\\Android-ObservableScrollView\n> gradlew installDevDebug\n```\n\n\n[Next: Build on Eclipse &raquo;](../../docs/example/eclipse.md)\n"
  },
  {
    "path": "docs/example/eclipse.md",
    "content": "# Build on Eclipse\n\nThis library and samples basically support Android Studio and Gradle.  \nBecause they have strong power to handle dependencies and ability to configure flexibly,  \nand this library and sample app depend on them.\n\nHowever, some of you might still want to build or debug the project on Eclipse.\nIf you'd like to do that, please try the following instructions.\n\nPlease note that with these instructions you could bulid project on Eclipse, but test codes, build types ('debug' or 'release') and product flavors are still not supported.\n\n## Prerequisites\n\nPlease [check here](../../docs/reference/environment.md) to see if your enviroment satisfies the prerequisites for building the app.\n\n## Instructions\n\n### Get the source codes\n\nGet the source code of the library and example app, by cloning git repository or downloading archives.\n\nIf you use git, execute the following command in your workspace directory.\n\n```\n$ git clone https://github.com/ksoichiro/Android-ObservableScrollView.git\n```\n\nIf you are using Windows, try it on GitBash or Cygwin or something that supports git.\n\n### Define ANDROID_HOME environment variable\n\nIf you haven't define the environment variable `ANDROID_HOME` yet, define it to indicate Android SDK root directory.\n\n### Generate dependency codes for Eclipse\n\nBefore trying to import projects to Eclipse,\nexecute these command:\n\n```\n$ ./gradlew clean generateVersionInfoDebug generateEclipseDependencies\n```\n\nThis will generate dependency codes from AAR files using Gradle wrapper and some metadata files (`.classpath`, `.project`, `project.properties`).\n\n### Import projects to Eclipse and build app\n\n1. Launch Eclipse.\n1. Select `File` > `Import`.\n1. Select `General` > `Existing Projects into Workspace` and click `Next`.\n    * Warning: DO NOT `Android` > `Existing Android Code into Workspace`.\n1. Click `Browse` and select project root directory (`Android-ObservableScrollView`).\n1. Check `Search for nested projects`.\n1. Select all projects and click next.\n1. Some warning messages will be generated, but ignore them and wait until build finishes.\n\n### Run the app\n\n1. Confirm your device is connected.\n1. Right click `observablescrollview-samples` and select `Run As` > `Android Application`.\n\nThat's all!\n \n[Next: Basic techniques &raquo;](../../docs/basic/index.md)\n"
  },
  {
    "path": "docs/example/google-play.md",
    "content": "# Download from Google Play\n\nClick the following link to download the example app from Google Play.\n\n[![Get it on Google Play](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.github.ksoichiro.android.observablescrollview.samples2)\n\nPlease note that the app on the Play Store is not always the latest version.  \nIf you'd like to install the latest one,\n\n* install it manually.\n* or if you are a wercker user, you can download the latest build artifact from wercker.\n\n[Next: Download from wercker &raquo;](../../docs/example/wercker.md)\n"
  },
  {
    "path": "docs/example/index.md",
    "content": "# Try the examples app\n\nTo understand how it works, let's see the existing example app\nand check if there are some patterns you want to implement.\n\n1. [Download from Google Play](../../docs/example/google-play.md)\n1. [Download from wercker](../../docs/eaxmple/wercker.md)\n1. [Build on Android Studio](../../docs/example/android-studio.md)\n1. [Build on Eclipse](../../docs/example/eclipse.md)\n\n[Next: Download from Google Play &raquo;](../../docs/example/google-play.md)\n"
  },
  {
    "path": "docs/example/wercker.md",
    "content": "# Download from wercker\n\n[wercker](http://wercker.com/) is a CI service and this project uses wercker to provide the latest sample apk.\nIf you're not interested in this, go to the next topic.\n\n## Login to wercker\n\nAt first, you need to be a member of [wercker](http://wercker.com/) and should login before download the app.\n\n## Visit this repository\n\nClick the badge below to show this repository's builds.\n\n[![wercker status](https://app.wercker.com/status/8d1e27d9f4a662b25dbe70402733582b/m/master \"wercker status\")](https://app.wercker.com/project/bykey/8d1e27d9f4a662b25dbe70402733582b)\n\n## Select the build\n\nThen select the commit link that you want to download.  \nNote that green check mark in front of the link means successful builds and red ones are failure,  \nand you can only download the app from the green ones.\n\n![](../images/wercker_1.png)\n\n## Open the last section\n\nScroll the screen, and click anywhere in the \"inspect build result\" section to open it.\n\n![](../images/wercker_2.png)\n\n## Download the artifact\n\nFinally, you can download the apk file by clicking the `artifact.tar.gz` link.\n\n![](../images/wercker_3.png)\n\n[Next: Build on Android Studio &raquo;](../../docs/example/android-studio.md)\n"
  },
  {
    "path": "docs/faq.md",
    "content": "# FAQ\n\nThese are frequently asked questions from GitHub issues, emails I received from users, etc.\n\n## Q. When do you implement the new sample? I'm waiting for it so long.\n\n### A. Sorry and please help me if you could.\n\nFirst of all, I'm so grateful to all of you that you're interested in this project.  \nAnd of course I'd like to respond to all of your request!  \nBut unfortunately, I don't have enough time to do that...  \nIf you're interested in implementing new samples or fixing bugs, please help me. (Pull requests are welcome!)\n\n## Q. Does this library support Eclipse?\n\n### A. Yes, it does partially.\n\nPlease see [here](../docs/example/eclipse.md) for details.\n\n## Q. Doesn't work!\n\n### A. Please describe your issue as much as possible.\n\nAs I wrote in [the contribution guideline](https://github.com/ksoichiro/Android-ObservableScrollView/blob/master/CONTRIBUTING.md),\nthe library itself only provides the scroll information,\nand creating awesome scrolling effects depends deeply on how you use it: layout, offset calculation to animate views, etc.\n\nTherefore, if you find an issue, please describe not only the issue itself but also the related information like Activity name that has problems, operation to produce the issue, modified code (if any), etc.\n\n## Q. Paths are too long to build!\n\n### A. Move your projects upper to shorten the paths.\n\nOn Windows environment, if you locate the project directory like `C:\\Documents and Settings\\user\\workspace\\Android-ObservableScrollView\\`,\nAndroid Studio causes errors and fail to build the project because some of the paths in the output files are too long.\n\nIf you see this kind of problem, please move the project directory to upper directory to shorten the paths.\n\n## Q. Sample codes are too complex!\n\n### A. Sorry, please help me refactor them...\n\nYes, I know they're too complex as samples.\n\nI just aimed to show you that you can realize these kind of effects when you use this library.\nI'd very appreciate it if you help me refactor them.\n\n## Q. Updates of the library in master branch seems not be synced to Maven Central.\n\n### A. Sorry, please wait for the next release.\n\nI need to do following tasks to release the new version to the Maven Central, and it takes time, so please wait for the next release.  \nIf you're in a hurry, please send me an email. I'll release it as soon as possible.\n\n1. Test the library, at least the tests on Travis CI should pass.\n1. Check the compatibility for the past versions.\n1. Release SNAPSHOT version to the Sonatype snapshot repository.\n1. Release to the Sonatype repository. If it's successfully released, it will be synced to Maven Central in a couple of hours.\n1. Update README to prompt to use the latest version.\n\n## Q. Can I use this library with API level 8?\n\n### A. It's not supported, but you can.\n\nBy adding `tools:overrideLibrary` to `<uses-sdk>` tag,\nyou can build this library with `android:minSdkVersion=\"8\"`.\n\n```xml\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"com.example.app\">\n\n    <uses-sdk\n        android:minSdkVersion=\"8\"\n        android:targetSdkVersion=\"22\"\n        tools:overrideLibrary=\"com.github.ksoichiro.android.observablescrollview\" />\n```\n\nIf you have other libraries to override, separate them with comma.\n\n```xml\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"com.example.app\">\n\n    <uses-sdk\n        android:minSdkVersion=\"8\"\n        android:targetSdkVersion=\"22\"\n        tools:overrideLibrary=\"com.melnykov.fab,com.github.ksoichiro.android.observablescrollview\" />\n```\n\n"
  },
  {
    "path": "docs/overview.md",
    "content": "# Overview\n\nAndroid-ObservableScrollView is a library to handle scroll position for Android apps, and contains lots of examples to demonstrate how this library works.\n\nHowever, creating awesome scrolling effects depends deeply on how you use it: layout, offset calculation to animate views, etc.\n\nThis documentation describes how to install this library and apply to your application.\n\n## Get started\n\nSee [quick start](../docs/quick-start/index.md) section.\n\n## See the complete examples\n\nYou can try the example app, to see what this library can do.  \nSee [try the example app](../docs/example/index.md) section to know how to install the app.\n\n## Learn from basics\n\nNow you must have seen the examples and setup your environments, then let's learn how to use it in your app.  \nSee [basic techniques](../docs/basic/index.md) section.\n\n## Challenge complex and awesome techniques\n\nIf you'd like to create complex, awesome scrolling animation using ViewPager or something,\nplease check out [advanced techniques](../docs/advanced/index.md) section.\n\n## If you're interested in improving this library...\n\nPlease see the [contribution guideline](../CONTRIBUTING.md).  \nAlso, [for contributiors](../docs/contributor/index.md) section will be useful to understand / manage the entire project.\n"
  },
  {
    "path": "docs/quick-start/_data.json",
    "content": "{\n  \"index\": {\n    \"title\": \"Quick start\"\n  },\n  \"dependencies\": {\n    \"title\": \"Dependencies\"\n  },\n  \"layout\": {\n    \"title\": \"Layout\"\n  },\n  \"animation\": {\n    \"title\": \"Animation codes\"\n  }\n}\n"
  },
  {
    "path": "docs/quick-start/animation.md",
    "content": "# Animation codes\n\nThis time, we implement ActionBar animation using `AppCompatActivity` in the support library.\n\n## Apply layout to the activity\n\nAt first, let `Activity` extend the `AppCompatActivity` and set [the layout we wrote](../../docs/quick-start/layout.md) to it.\n\n```java\nimport android.support.v7.app.AppCompatActivity;\n\npublic class MainActivity extends AppCompatActivity {\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    setContentView(R.layout.activity_main);\n```\n\n## Initialize ObservableListView\n\nAdd some initialization codes to `onCreate()`:\n\n```java\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n    ObservableListView listView = (ObservableListView) findViewById(R.id.list);\n    listView.setScrollViewCallbacks(this);\n  }\n```\n\nYou will see an error around `setScrollViewCallbacks(this)` because the Activity does not implement the required interface yet.\nSo add `implements ObservableScrollViewCallbacks` to the Activity definition:\n\n```java\npublic class MainActivity extends AppCompatActivity\n  implements ObservableScrollViewCallbacks {\n```\n\nThen implement required methods:\n\n```java\n  @Override\n  public void onScrollChanged(int scrollY, boolean firstScroll,\n    boolean dragging) {\n  }\n\n  @Override\n  public void onDownMotionEvent() {\n  }\n\n  @Override\n  public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n  }\n}\n```\n\nNow we can handle the scroll events.\n\n## Populate list data\n\nBefore write codes to animate views, set data to ListView.\n\n```java\n    // Add these codes after ListView initialization\n    ArrayList<String> items = new ArrayList<String>();\n    for (int i = 1; i <= 100; i++) {\n      items.add(\"Item \" + i);\n    }\n    listView.setAdapter(new ArrayAdapter<String>(\n      this, android.R.layout.simple_list_item_1, items));\n```\n\n## Animate with scroll events\n\nFinally, we can write the main code now.\nAdd some code to show/hide the ActionBar in `onUpOrCancelMotionEvent` method for example.\n\n```java\n  @Override\n  public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    ActionBar ab = getSupportActionBar();\n    if (scrollState == ScrollState.UP) {\n      if (ab.isShowing()) {\n        ab.hide();\n      }\n    } else if (scrollState == ScrollState.DOWN) {\n      if (!ab.isShowing()) {\n        ab.show();\n      }\n    }\n  }\n```\n\n`ScrollState` parameter indicates the direction of swiping, and this event will occur when you touch up (or cancel) the ListView.\nThis is just an introduction so we don't use other events like `onScrollChanged`.\n\nNow let's build and launch the app.\n\nYou can see the ActionBar gets hidden or shown when you swipe the ListView.\n\nAs you can see, the most important codes are the animation codes in the callbacks.\nYou can learn how to write these code in this tutorial.\n\nIn the [next section](../../docs/example/index.md), we'll check the existing examples to see what we can do with this library.\n\n## Program list\n\nFollowing codes are the entire Activity, just for your reference.\n\n```java\nimport android.support.v7.app.AppCompatActivity;\n// other imports and package statement are omitted\n\npublic class MainActivity extends AppCompatActivity\n  implements ObservableScrollViewCallbacks {\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    setContentView(R.layout.activity_main);\n\n    ObservableListView listView = (ObservableListView) findViewById(R.id.list);\n    listView.setScrollViewCallbacks(this);\n\n    // TODO These are dummy. Populate your data here.\n    ArrayList<String> items = new ArrayList<String>();\n    for (int i = 1; i <= 100; i++) {\n      items.add(\"Item \" + i);\n    }\n    listView.setAdapter(new ArrayAdapter<String>(\n      this, android.R.layout.simple_list_item_1, items));\n  }\n\n  @Override\n  public void onScrollChanged(int scrollY, boolean firstScroll,\n    boolean dragging) {\n  }\n\n  @Override\n  public void onDownMotionEvent() {\n  }\n\n  @Override\n  public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    ActionBar ab = getSupportActionBar();\n    if (scrollState == ScrollState.UP) {\n      if (ab.isShowing()) {\n        ab.hide();\n      }\n    } else if (scrollState == ScrollState.DOWN) {\n      if (!ab.isShowing()) {\n        ab.show();\n      }\n    }\n  }\n}\n```\n\n[Next: Try the example app &raquo;](../../docs/example/index.md)\n"
  },
  {
    "path": "docs/quick-start/dependencies.md",
    "content": "# Dependencies\n\nThis library is published to the Maven Central repository, so you can use it through Gradle/Maven.  \nYou can use it in Eclipse, but Android Studio (or Gradle) is recommended.  \nIn Quick start guide, we assume you're using Android Studio.\n\n## build.gradle\n\nWrite the following dependency configuration to your `build.gradle`.\n\n```gradle\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    // Other dependencies are omitted\n    compile 'com.github.ksoichiro:android-observablescrollview:VERSION'\n}\n```\n\nYou should replace `VERSION` to the appropriate version number like `1.5.0`.\n\nThen, click \"sync\" button to get the library using the configuration above.\n\nTo confirm the available versions, search [the Maven Central Repository](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.ksoichiro%22%20AND%20a%3A%22android-observablescrollview%22).\n\n[Next: Layout &raquo;](../../docs/quick-start/layout.md)\n"
  },
  {
    "path": "docs/quick-start/index.md",
    "content": "# Quick start\n\nThank you for having interest in this library!  \nIn this section, I'll show some quick instructions for introducing this library into your app.\n\n1. [Dependencies](../../docs/quick-start/dependencies.md)\n1. [Layout](../../docs/quick-start/layout.md)\n1. [Animation codes](../../docs/quick-start/animation.md)\n\n[Next: Dependencies &raquo;](../../docs/quick-start/dependencies.md)\n"
  },
  {
    "path": "docs/quick-start/layout.md",
    "content": "# Layout\n\nAfter adding the dependency, let's write layout file such as `res/layout/activity_main.xml`.\n\nThis time, we'll use only one element `ObservableListView`.\n\n```xml\n<com.github.ksoichiro.android.observablescrollview.ObservableListView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/list\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n```\n\nAndroid-ObservableScrollView provides several types of views that are scroll-able such as `ObservableScrollView`, `ObservableGridView`, etc.  \nAnd they extend the standard widget (`ScrollView`, `GridView`, ...), and they provide some callbacks to get scroll events.\n\nAfter writing the above layout, you can write animation codes using these callbacks.\n\n[Next: Animation codes &raquo;](../../docs/quick-start/animation.md)\n"
  },
  {
    "path": "docs/reference/_data.json",
    "content": "{\n  \"index\": {\n    \"title\": \"Rerefence\"\n  },\n  \"supported-widgets\": {\n    \"title\": \"Supported widgets\"\n  },\n  \"environment\": {\n    \"title\": \"Environment\"\n  },\n  \"release-notes\": {\n    \"title\": \"Release notes\"\n  }\n}\n"
  },
  {
    "path": "docs/reference/environment.md",
    "content": "# Environment\n\n## Development\n\nThis project is built and tested under the following environment.\n\n* OS: Mac OS X 10.10\n* IDE: Android Studio 1.2\n* JDK: 1.7\n\n## Prerequisites for building on Android Studio\n\n* Android Studio (1.0.0+)\n* Oracle JDK 7\n* Android SDK Tools (Rev.24.1.2)\n* Android SDK Build-tools (Rev.22.0.1)\n* Android 5.1.1 SDK Platform (Rev.2)\n* Android Support Repository (Rev.14)\n* Android Support Library (Rev.21.1.1)\n\n## Prerequisites for building on Eclipse\n\n* [Eclipse IDE for Java Developers 4.4 (Luna) SR1](https://eclipse.org/downloads/packages/eclipse-ide-java-developers/lunasr1a)\n* [Eclipse ADT Plugin](http://developer.android.com/sdk/installing/installing-adt.html)\n* Oracle JDK 7\n* Android 5.0 SDK Platform (Rev.1+)\n* Android Support Repository (Rev.9+)\n* Android Support Library (Rev.21.0.2+)\n"
  },
  {
    "path": "docs/reference/index.md",
    "content": "# Reference\n\n1. [Supported widgets](../../docs/basic/supported-widgets.md)\n1. [Environment](../../docs/reference/environment.md)\n1. [Release notes](../../docs/reference/release-notes.md)\n"
  },
  {
    "path": "docs/reference/release-notes.md",
    "content": "# Release notes\n\n* v1.6.0\n    * Added header view feature to `ObservableGridView` (#148).\n    * Added footer view feature to `ObservableGridView` (#183).\n    * Updated `recyclerview-v7` library version to 22.2.0.\n    * Fixed ViewPager swiping bug in `ObservableListView` (#185).\n    * Fixed NPE in `ObservableRecyclerView` (#149).\n* v1.5.2\n    * Fix `ObservableGridView` to use first child of line in height calculation.\n* v1.5.1\n    * Fix `scrollY` of `onScrollChanged` in `ObservableGridView` jumps\n      when the first visible item changes.\n* v1.5.0\n    * Add a helper class `CacheFragmentStatePagerAdapter` to implement ViewPager pattern.\n    * Fix that swipe down (over-scroll) causes item click.\n* v1.4.0\n    * Add a custom view named `TouchInterceptionFrameLayout` and a new API `setTouchInterceptionViewGroup()` for `Scrollable`.  \n      With these class and API, you can move `Scrollable` itself using its scrolling events.\n    * Add a helper class `ScrollUtils` for implementing scrolling effects.\n* v1.3.2\n    * Fix that `ObservableRecyclerView` causes `BadParcelableException` on `onRestoreInstanceState`.\n* v1.3.1\n    * Fix that `onDownMotionEvent` not called and parameters of `onScrollChanged` are incorrect\n      when children views handle touch events.\n* v1.3.0\n    * Add new interface `Scrollable` to provide common API for scrollable widgets. \n* v1.2.1\n    * Fix that the scroll states and other internal information are lost after `onSaveInstanceState()`.\n    * Fix that the scrollY is incorrect if the ListView/RecyclerView don't scroll from the top.\n      (It's just approximating the scroll offset and not the complete solution but better than before.)\n* v1.2.0\n    * Add GridView support.\n    * Fix ObservableListView cannot detect onScrollChanged on Android 2.3.\n    * Fix ObservableScrollView cannot detect UP and DOWN state in onUpOrCancelMotionEvent before Android 4.4.\n* v1.1.0\n    * Add RecyclerView support.\n* v1.0.0\n    * Initial release.\n"
  },
  {
    "path": "docs/reference/supported-widgets.md",
    "content": "# Supported widgets\n\nWidgets are named with `Observable` prefix.  \n(e.g. `ListView` → `ObservableListView`)  \nYou can handle these widgets with `Scrollable` interface.\n\n| Widget | Since | Note |\n|:------:|:-----:| ---- |\n| ListView | v1.0.0 | - |\n| ScrollView | v1.0.0 | - |\n| WebView | v1.0.0 | - |\n| RecyclerView | v1.1.0 | It's supported but RecyclerView provides scroll states and position with [OnScrollListener](https://developer.android.com/reference/android/support/v7/widget/RecyclerView.OnScrollListener.html). You should use it if you don't have any reason. |\n| GridView | v1.2.0 | - |\n"
  },
  {
    "path": "gradle/gradle-mvn-push.gradle",
    "content": "/*\n * Copyright 2013 Chris Banes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\napply plugin: 'maven'\napply plugin: 'signing'\n\ndef isReleaseBuild() {\n    return VERSION_NAME.contains(\"SNAPSHOT\") == false\n}\n\ndef getReleaseRepositoryUrl() {\n    return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL\n            : \"https://oss.sonatype.org/service/local/staging/deploy/maven2/\"\n}\n\ndef getSnapshotRepositoryUrl() {\n    return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL\n            : \"https://oss.sonatype.org/content/repositories/snapshots/\"\n}\n\ndef getRepositoryUsername() {\n    return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : \"\"\n}\n\ndef getRepositoryPassword() {\n    return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : \"\"\n}\n\nafterEvaluate { project ->\n    uploadArchives {\n        repositories {\n            mavenDeployer {\n                beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }\n\n                pom.groupId = GROUP\n                pom.artifactId = POM_ARTIFACT_ID\n                pom.version = VERSION_NAME\n\n                repository(url: getReleaseRepositoryUrl()) {\n                    authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())\n                }\n                snapshotRepository(url: getSnapshotRepositoryUrl()) {\n                    authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())\n                }\n\n                pom.project {\n                    name POM_NAME\n                    packaging POM_PACKAGING\n                    description POM_DESCRIPTION\n                    url POM_URL\n\n                    scm {\n                        url POM_SCM_URL\n                        connection POM_SCM_CONNECTION\n                        developerConnection POM_SCM_DEV_CONNECTION\n                    }\n\n                    licenses {\n                        license {\n                            name POM_LICENCE_NAME\n                            url POM_LICENCE_URL\n                            distribution POM_LICENCE_DIST\n                        }\n                    }\n\n                    developers {\n                        developer {\n                            id POM_DEVELOPER_ID\n                            name POM_DEVELOPER_NAME\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    signing {\n        required { isReleaseBuild() && gradle.taskGraph.hasTask(\"uploadArchives\") }\n        sign configurations.archives\n    }\n\n    task androidJavadocs(type: Javadoc) {\n        source = android.sourceSets.main.java.srcDirs\n        classpath += project.files(android.getBootClasspath().join(File.pathSeparator))\n    }\n\n    task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {\n        classifier = 'javadoc'\n        from androidJavadocs.destinationDir\n    }\n\n    task androidSourcesJar(type: Jar) {\n        classifier = 'sources'\n        from android.sourceSets.main.java.sourceFiles\n    }\n\n    artifacts {\n        archives androidSourcesJar\n        archives androidJavadocsJar\n    }\n}\n"
  },
  {
    "path": "gradle/version.gradle",
    "content": "// Generate version information to show in the sample app.\n// This can be achieved more easily when we use BuildConfig in Gradle and Android Studio,\n// but we also support Eclipse so we don't use BuildConfig.\n\nproject.ext.versionInfo = new Expando()\nproject.ext.versionInfo.srcDir = 'version'\n\n// Get git commit hash for naming the APK file if 'git' is available\ntry {\n    project.ext.versionInfo.build = \"git rev-parse --short HEAD\".execute().text.trim()\n} catch (ignored) {\n    project.ext.versionInfo.build = \"unknown\"\n}\nproject.ext.versionInfo.releaseVersionName = project.ext.versionInfo.build\n\nandroid.sourceSets.findAll { it in android.buildTypes }.each {\n    it.java.srcDirs += \"src/${project.ext.versionInfo.srcDir}/${it.name}/java\"\n}\n\nandroid.buildTypes.each { buildType ->\n    task \"generateVersionInfo${buildType.name.capitalize()}\" << {\n        def packageName = android.defaultConfig.applicationId\n        def dir = project.file(\"src/${project.ext.versionInfo.srcDir}/${buildType.name}/java/${packageName.tr('.', '/')}\")\n        if (dir.exists()) {\n            dir.listFiles().each {\n                project.delete(it)\n            }\n        } else {\n            project.mkdir(dir)\n        }\n        def libraryVersion = buildType.name == 'release' ? project.ext.versionInfo.releaseVersionName : project.ext.versionInfo.build\n        def className = 'VersionInfo'\n        new File(dir, \"${className}.java\").text = \"\"\"\\\npackage ${packageName};\n\n// DO NOT EDIT: This file is automatically generated.\npublic class ${className} {\n    public static final String LIBRARY_VERSION = \"${libraryVersion}\";\n    public static final String BUILD = \"${project.ext.versionInfo.build}\";\n}\n\"\"\"\n    }\n}\n\ntask cleanVersionInfo << {\n    def dir = project.file(\"src/${project.ext.versionInfo.srcDir}\")\n    if (dir.exists()) {\n        dir.listFiles().findAll { it.isDirectory() }.each {\n            project.delete(it)\n        }\n    }\n}\nclean.dependsOn(tasks['cleanVersionInfo'])\n\nafterEvaluate {\n    android.applicationVariants.all {\n        tasks[\"generate${it.name.capitalize()}Sources\"].dependsOn(\"generateVersionInfo${it.buildType.name.capitalize()}\")\n    }\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Wed Apr 10 15:27:10 PDT 2013\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.4-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "VERSION_NAME=1.7.0-SNAPSHOT\nSYNCED_VERSION_NAME=1.5.2\nGROUP=com.github.ksoichiro\n\nPOM_DESCRIPTION=Android library to observe scroll events on scrollable views.\nPOM_URL=https://github.com/ksoichiro/Android-ObservableScrollView\nPOM_SCM_URL=https://github.com/ksoichiro/Android-ObservableScrollView\nPOM_SCM_CONNECTION=scm:git:git://github.com/ksoichiro/Android-ObservableScrollView.git\nPOM_SCM_DEV_CONNECTION=scm:git:git@github.com:ksoichiro/Android-ObservableScrollView.git\nPOM_LICENCE_NAME=Apache License 2.0\nPOM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0\nPOM_LICENCE_DIST=repo\nPOM_DEVELOPER_ID=ksoichiro\nPOM_DEVELOPER_NAME=Soichiro Kashima\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched.\nif $cygwin ; then\n    [ -n \"$JAVA_HOME\" ] && JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\nfi\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >&-\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >&-\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "library/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "library/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<manifest package=\"com.github.ksoichiro.android.observablescrollview\">\n</manifest>\n"
  },
  {
    "path": "library/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\ndependencies {\n    compile 'com.android.support:recyclerview-v7:23.1.1'\n    androidTestCompile ('com.android.support:appcompat-v7:23.1.1') {\n        exclude module: 'support-v4'\n    }\n    androidTestCompile ('com.nineoldandroids:library:2.4.0') {\n        exclude module: 'support-v4'\n    }\n}\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.2\"\n\n    defaultConfig {\n        minSdkVersion 9\n    }\n\n    buildTypes {\n        debug {\n            testCoverageEnabled = true\n        }\n    }\n\n    sourceSets {\n        main {\n            manifest.srcFile 'AndroidManifest.xml'\n            res.srcDirs = ['res']\n        }\n    }\n\n    lintOptions {\n        abortOnError false\n    }\n}\n\n// When testing on Travis CI,\n// connectedCheck task doesn't output logs for more than 10 minutes often,\n// which causes build failure.\n// To avoid this, we change the log level for test tasks.\n// Test tasks for buildTypes will be defined on evaluation phase,\n// so do it on afterEvaluate.\nafterEvaluate { project ->\n    tasks.withType(VerificationTask) {\n        logging.level = LogLevel.INFO\n    }\n}\n\napply plugin: 'com.github.kt3k.coveralls'\n\ncoveralls.jacocoReportPath = 'build/reports/coverage/debug/report.xml'\n\n// This is from 'https://github.com/chrisbanes/gradle-mvn-push'\napply from: \"${rootDir}/gradle/gradle-mvn-push.gradle\"\n"
  },
  {
    "path": "library/gradle.properties",
    "content": "POM_NAME=Android-ObservableScrollView\nPOM_ARTIFACT_ID=android-observablescrollview\nPOM_PACKAGING=aar\n"
  },
  {
    "path": "library/res/.gitkeep",
    "content": ""
  },
  {
    "path": "library/src/androidTest/AndroidManifest.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.github.ksoichiro.android.observablescrollview.test\">\n\n    <application>\n        <uses-library android:name=\"android.test.runner\" />\n        <activity android:name=\".GridViewActivity\" />\n        <activity android:name=\".HeaderGridViewActivity\" />\n        <activity android:name=\".ListViewActivity\" />\n        <activity android:name=\".ListViewScrollFromBottomActivity\" />\n        <activity android:name=\".RecyclerViewActivity\" />\n        <activity android:name=\".RecyclerViewScrollFromBottomActivity\" />\n        <activity android:name=\".ScrollViewActivity\" />\n        <activity android:name=\".TouchInterceptionGridViewActivity\" android:theme=\"@style/AppTheme.Toolbar\" />\n        <activity android:name=\".TouchInterceptionListViewActivity\" android:theme=\"@style/AppTheme.Toolbar\" />\n        <activity android:name=\".TouchInterceptionRecyclerViewActivity\" android:theme=\"@style/AppTheme.Toolbar\" />\n        <activity android:name=\".TouchInterceptionScrollViewActivity\" android:theme=\"@style/AppTheme.Toolbar\" />\n        <activity android:name=\".TouchInterceptionWebViewActivity\" android:theme=\"@style/AppTheme.Toolbar\" />\n        <activity android:name=\".ViewPagerTabActivity\" android:theme=\"@style/AppTheme.Toolbar\" />\n        <activity android:name=\".ViewPagerTab2Activity\" android:theme=\"@style/AppTheme.Toolbar\" />\n        <activity android:name=\".WebViewActivity\" />\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "library/src/androidTest/assets/lipsum.html",
    "content": "<html>\n<head>\n<style type=\"text/css\">\n#container {\npadding: 1em;\n}\n</style>\n</head>\n<body>\n<div id=\"container\">\n<p>Lorem ipsum dolor sit amet, ut duis lorem provident sed felis blandit, condimentum donec lectus ipsum et mauris, morbi porttitor interdum feugiat nulla donec sodales, vestibulum nisl primis a molestie vestibulum quam, sapien mauris metus risus suspendisse magnis. Augue viverra nulla faucibus egestas eu, a etiam id congue rutrum ante, arcu tincidunt donec quam felis at ornare, iaculis ligula sodales venenatis commodo volutpat neque, suspendisse elit praesent tellus felis mi amet. Inceptos amet tempor lectus lorem est non, ac donec ac libero neque mauris, tellus ante metus eget leo consequat. Scelerisque dolor curabitur pretium blandit ut feugiat, amet lacus pulvinar justo convallis ut, sed natoque ipsum urna posuere nibh eu. Sed at sed vulputate sit orci, facilisis a aliquam tellus quam aliquam, eu aliquam donec at molestie ante, pellentesque mauris lorem ultrices libero faucibus porta, imperdiet adipiscing sit hac diam ut nulla. Lacus enim elit pulvinar donec vehicula dapibus, accumsan purus officia cursus dolor sapien, eu amet dis mauris mi nulla ut. Non accusamus etiam pede non urna tempus, vestibulum aliquam tortor eget pharetra sodales, in vestibulum ut justo orci nulla, lobortis purus sem semper consectetuer magni purus. Dolor a leo vestibulum amet ut sit, arcu ut eaque urna fusce aliquet turpis, sed fermentum sed vestibulum nisl pede, tristique enim lorem posuere in laborum ut. Vestibulum id id justo leo nulla, magna lobortis ullamcorper et dignissim pellentesque, duis suspendisse quis id lorem ante. Vivamus a nullam ante adipiscing amet, mi vel consectetuer nunc aenean pede quisque, eget rhoncus dis porttitor habitant nunc vivamus, duis cubilia blandit non donec justo dictumst, praesent vitae nulla nam pulvinar urna. Adipiscing adipiscing justo urna pulvinar imperdiet nullam, vitae fusce rhoncus proin nonummy suscipit, ullamcorper amet et non potenti platea ultrices, mauris nullam sapien nunc justo vel, eu semper pellentesque arcu fusce augue. Malesuada mauris nibh sit a a scelerisque, velit sem lectus tellus convallis consectetuer, ultricies auctor a ante eros amet sed.</p>\n<p>Risus lacus duis leo platea wisi, felis maecenas rutrum in id in donec, non id a potenti libero eget, posuere elit ea sed pellentesque quis. Sunt lacus urna lorem elit duis, nibh donec purus quisque consectetuer dolor, neque vestibulum proin ornare eros nonummy phasellus. Iaculis cras eu at egestas dolor montes, viverra quisque malesuada consectetuer semper maecenas, a sed vitae donec tempor aliqua metus, ornare mollis suscipit et erat fusce, sit orci aut auctor elementum fames aliquam. Platea dui integer magnis non metus, minus dignissimos ante massa nostra et, rutrum sapien egestas quis sapien donec donec. Erat sit a eros aenean natoque, quam libero id lorem enim proin, lorem ipsum fermentum mattis metus et. Aliquam aliquet suscipit purus conubia at neque, platea vivamus vestibulum nulla quibusdam senectus, et morbi lectus malesuada gravida donec, elementum sit convallis pellentesque velit amet. Et eveniet viverra vehicula consectetuer justo, provident sed commodo non lacinia velit, tempor phasellus vel leo nisl cras, vivamus et arcu interdum dui eu amet. Volutpat wisi rhoncus vel turpis diam quibusdam, dapibus elit est quisque cubilia mauris, nulla elit magna tempor accumsan bibendum, lorem varius sed interdum eget mattis, scelerisque egestas feugiat donec dui molestie. Leo facilisis nisl sit montes ligula sed, enim commodo consectetuer nunc est et, ut sed vehicula dolor luctus elit. Fermentum cras donec eget nibh est vel, sed justo risus et pharetra diam, eu vivamus egestas ligula risus diam, sed justo eget hac ut mauris. Vestibulum diam nec vitae mi eget suspendisse, aenean arcu purus facilisis purus class in, id aliquam sit id scelerisque sapien etiam. Ut nullam sit sed at mauris lobortis, consequat dolor autem ipsum euismod nulla, elit quis proin eget conubia varius, erat arcu massa mus in mauris, scelerisque ut eu sollicitudin libero leo urna.</p>\n<p>Consectetuer luctus tempor elit ut dolor ligula, quis dui per dui hendrerit ante sagittis, in quisque pretium in eleifend enim. Condimentum iaculis vitae feugiat dis tellus vel, lectus dolor nec dui nulla nascetur, et pellentesque curabitur lorem leo velit eget. Id nascetur arcu lobortis suspendisse imperdiet urna, natoque nascetur ante in porta a, interdum hendrerit mi bibendum platea tellus, urna in enim ornare vestibulum faucibus enim. Leo fusce egestas ante nec volutpat, in tempor vel facilisis potenti ut, pede at non lorem a commodo, nulla dolor orci interdum vestibulum nulla. Dui nulla vestibulum quisque a pharetra porta, integer nec ipsum nec sed dui pharetra, magna et dignissim ipsum sed dictum, litora eros vivamus scelerisque libero ipsum. Sed ac ac lorem molestie adipiscing morbi, pellentesque imperdiet nunc quis morbi amet ante, libero dui ligula nec risus neque et, velit nonummy phasellus et facilisi amet, ligula in elementum non sapien pulvinar faucibus. Eu leo ut posuere sed aliquet, tincidunt vel urna volutpat tempus sem, sit felis aliquet vestibulum condimentum sit, amet nibh vel tellus purus ullamcorper libero, nulla vestibulum pede ut vestibulum pretium. Eu nulla vestibulum a neque in metus, quisquam nam sed cursus eget luctus, pede ultrices nec sed dignissim pellentesque, sit class cursus metus nulla placerat mauris, consequat mollis neque vivamus amet pede. Mauris dolor nulla diam eros bibendum, quam ante vestibulum morbi non ligula vel, molestie curabitur rhoncus nulla euismod interdum non. Nulla fringilla lorem mollis ad massa, sit molestie nibh lorem arcu volutpat, accumsan commodo lectus eu et donec, sit tempor tempus rutrum in curabitur amet. Nec urna euismod a tincidunt commodo, eu pede turpis libero vitae viverra, ante vestibulum nam non habitasse potenti, mauris imperdiet in in nunc convallis. Et nostra wisi in est accumsan vehicula, quisque vitae felis mauris sed vulputate nec, ante imperdiet sollicitudin massa iaculis massa sit.</p>\n<p>Quam libero nulla netus eu porta curae, ut nulla bibendum facilisis et urna sed, quis congue vestibulum aliquam interdum etiam. Nulla vel lobortis ullamcorper vitae excepturi, neque urna feugiat lectus vel lacinia, massa pretium orci eu metus neque vulputate. Imperdiet ac velit rhoncus nulla malesuada nullam, nec pulvinar justo gravida lorem rutrum magna, habitasse repudiandae mi eros vestibulum ante, nec euismod dui iaculis in turpis pretium, ac id metus egestas proin lacus lectus. Laoreet lorem nec vitae risus erat arcu, vitae quam ut in ante tristique, porta dolor pede quam et odio nam, arcu lacus sem congue ante cursus massa. Et mattis sagittis erat accumsan fusce quam, vehicula ligula beatae natoque fusce sodales conubia, habitasse metus cum magnis viverra nam cursus, egestas urna wisi primis blandit eu magna, eget libero elit lacus lorem dis aliquam. Ut mauris ante natoque lacus massa, justo a lectus sodales enim adipiscing id, accumsan ut ipsum vestibulum sed enim auctor, vitae congue tincidunt id phasellus lacinia scelerisque, tincidunt sapien nulla euismod volutpat iaculis. Platea sociis nec aliquet nec molestie, in mi et augue sapien in vivamus, integer fames proin vitae in ullamcorper et. Fringilla etiam sapiente rhoncus suspendisse nec id, lobortis cras eget egestas dui ac nec, justo lacus ut lorem bibendum quia eros, eget a gravida id donec nunc suscipit, porta sed in sodales non rutrum. Lectus vel dui elementum pellentesque magna aliquam, vitae non sit pede et fusce nibh, id id deserunt ornare dui sit condimentum, in adipiscing imperdiet turpis nam aliquet, facilisis metus magna lacus wisi facilisis tortor. Vulputate elit accumsan quam amet ligula, suspendisse lacus mi nonummy integer urna, libero nulla nunc varius in odio, laoreet nulla amet placerat amet nec. Consectetuer vel massa hendrerit vitae iaculis id, sed ut ut laudantium odio in, elit vestibulum duis ante maecenas interdum in, neque vehicula ultrices varius in quam, pede tellus pellentesque sed nullam quis.</p>\n</div>\n</body>\n</html>"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/SavedStateTest.java",
    "content": "package com.github.ksoichiro.android.observablescrollview;\n\nimport android.os.Parcel;\nimport android.test.InstrumentationTestCase;\nimport android.util.SparseIntArray;\nimport android.view.AbsSavedState;\n\npublic class SavedStateTest extends InstrumentationTestCase {\n\n    public void testGridViewSavedState() throws Throwable {\n        Parcel parcel = Parcel.obtain();\n        ObservableGridView.SavedState state1 = new ObservableGridView.SavedState(AbsSavedState.EMPTY_STATE);\n        state1.prevFirstVisiblePosition = 1;\n        state1.prevFirstVisibleChildHeight = 2;\n        state1.prevScrolledChildrenHeight = 3;\n        state1.prevScrollY = 4;\n        state1.scrollY = 5;\n        state1.childrenHeights = new SparseIntArray();\n        state1.childrenHeights.put(0, 10);\n        state1.childrenHeights.put(1, 20);\n        state1.childrenHeights.put(2, 30);\n        state1.writeToParcel(parcel, 0);\n\n        parcel.setDataPosition(0);\n\n        ObservableGridView.SavedState state2 = ObservableGridView.SavedState.CREATOR.createFromParcel(parcel);\n        assertNotNull(state2);\n        assertEquals(state1.prevFirstVisiblePosition, state2.prevFirstVisiblePosition);\n        assertEquals(state1.prevFirstVisibleChildHeight, state2.prevFirstVisibleChildHeight);\n        assertEquals(state1.prevScrolledChildrenHeight, state2.prevScrolledChildrenHeight);\n        assertEquals(state1.prevScrollY, state2.prevScrollY);\n        assertEquals(state1.scrollY, state2.scrollY);\n        assertNotNull(state1.childrenHeights);\n        assertEquals(3, state1.childrenHeights.size());\n        assertEquals(10, state1.childrenHeights.get(0));\n        assertEquals(20, state1.childrenHeights.get(1));\n        assertEquals(30, state1.childrenHeights.get(2));\n    }\n\n    public void testListViewSavedState() throws Throwable {\n        Parcel parcel = Parcel.obtain();\n        ObservableListView.SavedState state1 = new ObservableListView.SavedState(AbsSavedState.EMPTY_STATE);\n        state1.prevFirstVisiblePosition = 1;\n        state1.prevFirstVisibleChildHeight = 2;\n        state1.prevScrolledChildrenHeight = 3;\n        state1.prevScrollY = 4;\n        state1.scrollY = 5;\n        state1.childrenHeights = new SparseIntArray();\n        state1.childrenHeights.put(0, 10);\n        state1.childrenHeights.put(1, 20);\n        state1.childrenHeights.put(2, 30);\n        state1.writeToParcel(parcel, 0);\n\n        parcel.setDataPosition(0);\n\n        ObservableListView.SavedState state2 = ObservableListView.SavedState.CREATOR.createFromParcel(parcel);\n        assertNotNull(state2);\n        assertEquals(state1.prevFirstVisiblePosition, state2.prevFirstVisiblePosition);\n        assertEquals(state1.prevFirstVisibleChildHeight, state2.prevFirstVisibleChildHeight);\n        assertEquals(state1.prevScrolledChildrenHeight, state2.prevScrolledChildrenHeight);\n        assertEquals(state1.prevScrollY, state2.prevScrollY);\n        assertEquals(state1.scrollY, state2.scrollY);\n        assertNotNull(state1.childrenHeights);\n        assertEquals(3, state1.childrenHeights.size());\n        assertEquals(10, state1.childrenHeights.get(0));\n        assertEquals(20, state1.childrenHeights.get(1));\n        assertEquals(30, state1.childrenHeights.get(2));\n    }\n\n    public void testRecyclerViewSavedState() throws Throwable {\n        Parcel parcel = Parcel.obtain();\n        ObservableRecyclerView.SavedState state1 = new ObservableRecyclerView.SavedState(AbsSavedState.EMPTY_STATE);\n        state1.prevFirstVisiblePosition = 1;\n        state1.prevFirstVisibleChildHeight = 2;\n        state1.prevScrolledChildrenHeight = 3;\n        state1.prevScrollY = 4;\n        state1.scrollY = 5;\n        state1.childrenHeights = new SparseIntArray();\n        state1.childrenHeights.put(0, 10);\n        state1.childrenHeights.put(1, 20);\n        state1.childrenHeights.put(2, 30);\n        state1.writeToParcel(parcel, 0);\n\n        parcel.setDataPosition(0);\n\n        ObservableRecyclerView.SavedState state2 = ObservableRecyclerView.SavedState.CREATOR.createFromParcel(parcel);\n        assertNotNull(state2);\n        assertEquals(state1.prevFirstVisiblePosition, state2.prevFirstVisiblePosition);\n        assertEquals(state1.prevFirstVisibleChildHeight, state2.prevFirstVisibleChildHeight);\n        assertEquals(state1.prevScrolledChildrenHeight, state2.prevScrolledChildrenHeight);\n        assertEquals(state1.prevScrollY, state2.prevScrollY);\n        assertEquals(state1.scrollY, state2.scrollY);\n        assertNotNull(state1.childrenHeights);\n        assertEquals(3, state1.childrenHeights.size());\n        assertEquals(10, state1.childrenHeights.get(0));\n        assertEquals(20, state1.childrenHeights.get(1));\n        assertEquals(30, state1.childrenHeights.get(2));\n    }\n\n    public void testScrollViewSavedState() throws Throwable {\n        Parcel parcel = Parcel.obtain();\n        ObservableScrollView.SavedState state1 = new ObservableScrollView.SavedState(AbsSavedState.EMPTY_STATE);\n        state1.prevScrollY = 1;\n        state1.scrollY = 2;\n        state1.writeToParcel(parcel, 0);\n\n        parcel.setDataPosition(0);\n\n        ObservableScrollView.SavedState state2 = ObservableScrollView.SavedState.CREATOR.createFromParcel(parcel);\n        assertNotNull(state2);\n        assertEquals(state1.prevScrollY, state2.prevScrollY);\n        assertEquals(state1.scrollY, state2.scrollY);\n    }\n\n    public void testWebViewSavedState() throws Throwable {\n        Parcel parcel = Parcel.obtain();\n        ObservableWebView.SavedState state1 = new ObservableWebView.SavedState(AbsSavedState.EMPTY_STATE);\n        state1.prevScrollY = 1;\n        state1.scrollY = 2;\n        state1.writeToParcel(parcel, 0);\n\n        parcel.setDataPosition(0);\n\n        ObservableWebView.SavedState state2 = ObservableWebView.SavedState.CREATOR.createFromParcel(parcel);\n        assertNotNull(state2);\n        assertEquals(state1.prevScrollY, state2.prevScrollY);\n        assertEquals(state1.scrollY, state2.scrollY);\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivity.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.widget.AbsListView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\npublic class GridViewActivity extends Activity implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_gridview);\n        ObservableGridView scrollable = (ObservableGridView) findViewById(R.id.scrollable);\n        scrollable.setScrollViewCallbacks(this);\n        UiTestUtils.setDummyData(this, scrollable);\n        scrollable.setOnScrollListener(new AbsListView.OnScrollListener() {\n            @Override\n            public void onScrollStateChanged(AbsListView view, int scrollState) {\n            }\n\n            @Override\n            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {\n            }\n        });\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.test.ActivityInstrumentationTestCase2;\nimport android.util.DisplayMetrics;\nimport android.util.TypedValue;\nimport android.view.View;\nimport android.widget.FrameLayout;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\npublic class GridViewActivityTest extends ActivityInstrumentationTestCase2<GridViewActivity> {\n\n    private Activity activity;\n    private ObservableGridView scrollable;\n    private int[] callbackCounter;\n\n    public GridViewActivityTest() {\n        super(GridViewActivity.class);\n    }\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        setActivityInitialTouchMode(true);\n        activity = getActivity();\n        scrollable = (ObservableGridView) activity.findViewById(R.id.scrollable);\n        callbackCounter = new int[2];\n    }\n\n    public void testInitialize() throws Throwable {\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                new ObservableGridView(activity);\n                new ObservableGridView(activity, null, 0);\n            }\n        });\n    }\n\n    public void testScroll() throws Throwable {\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testSaveAndRestoreInstanceState() throws Throwable {\n        UiTestUtils.saveAndRestoreInstanceState(this, activity);\n        testScroll();\n    }\n\n    public void testScrollVerticallyTo() throws Throwable {\n        final DisplayMetrics metrics = activity.getResources().getDisplayMetrics();\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.scrollVerticallyTo((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, metrics));\n            }\n        });\n        getInstrumentation().waitForIdleSync();\n\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.scrollVerticallyTo(0);\n            }\n        });\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testNoCallbacks() throws Throwable {\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable = (ObservableGridView) activity.findViewById(R.id.scrollable);\n                scrollable.setScrollViewCallbacks(null);\n            }\n        });\n        testScroll();\n    }\n\n    public void testCallbacks() throws Throwable {\n        final ObservableScrollViewCallbacks[] callbacks = new ObservableScrollViewCallbacks[2];\n        callbackCounter[0] = 0;\n        callbackCounter[1] = 0;\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable = (ObservableGridView) activity.findViewById(R.id.scrollable);\n                callbacks[0] = new ObservableScrollViewCallbacks() {\n                    @Override\n                    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n                        callbackCounter[0]++;\n                    }\n\n                    @Override\n                    public void onDownMotionEvent() {\n                    }\n\n                    @Override\n                    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n                    }\n                };\n                scrollable.addScrollViewCallbacks(callbacks[0]);\n                callbacks[1] = new ObservableScrollViewCallbacks() {\n                    @Override\n                    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n                        callbackCounter[1]++;\n                    }\n\n                    @Override\n                    public void onDownMotionEvent() {\n                    }\n\n                    @Override\n                    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n                    }\n                };\n                scrollable.addScrollViewCallbacks(callbacks[1]);\n            }\n        });\n        testScroll();\n        // Assert that all the callbacks are enabled and get called.\n        assertTrue(0 < callbackCounter[0]);\n        assertTrue(0 < callbackCounter[1]);\n\n        // Remove one of the callbacks and scroll again to assert it's really removed.\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.removeScrollViewCallbacks(callbacks[0]);\n            }\n        });\n        callbackCounter[0] = 0;\n        callbackCounter[1] = 0;\n        testScroll();\n        assertTrue(0 == callbackCounter[0]);\n        assertTrue(0 < callbackCounter[1]);\n\n        // Clear all callbacks and assert they're really removed.\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.clearScrollViewCallbacks();\n            }\n        });\n        callbackCounter[0] = 0;\n        callbackCounter[1] = 0;\n        testScroll();\n        assertTrue(0 == callbackCounter[0]);\n        assertTrue(0 == callbackCounter[1]);\n    }\n\n    public void testCannotAddHeaderOrFooterWhenAdapterIsAlreadySet() throws Throwable {\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    View view = new View(activity);\n                    final int flexibleSpaceImageHeight = activity.getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n                    FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, flexibleSpaceImageHeight);\n                    view.setLayoutParams(lp);\n                    view.setClickable(true);\n                    scrollable.addHeaderView(view);\n                    fail();\n                } catch (IllegalStateException ignore) {\n                }\n\n                try {\n                    View view = new View(activity);\n                    final int flexibleSpaceImageHeight = activity.getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n                    FrameLayout.LayoutParams lpf = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, flexibleSpaceImageHeight);\n                    view.setLayoutParams(lpf);\n                    scrollable.addFooterView(view);\n                    fail();\n                } catch (IllegalStateException ignore) {\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivity.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.view.View;\nimport android.widget.AbsListView;\nimport android.widget.FrameLayout;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\npublic class HeaderGridViewActivity extends Activity implements ObservableScrollViewCallbacks {\n\n    public View headerView;\n    public View footerView;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_gridview);\n        ObservableGridView scrollable = (ObservableGridView) findViewById(R.id.scrollable);\n        // Set padding view for GridView. This is the flexible space.\n        headerView = new View(this);\n        final int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,\n                flexibleSpaceImageHeight);\n        headerView.setLayoutParams(lp);\n\n        // This is required to disable header's list selector effect\n        headerView.setClickable(true);\n\n        scrollable.addHeaderView(headerView);\n\n        // Footer is also available.\n        footerView = new View(this);\n        FrameLayout.LayoutParams lpf = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,\n            flexibleSpaceImageHeight);\n        footerView.setLayoutParams(lpf);\n        scrollable.addFooterView(footerView);\n\n        scrollable.setScrollViewCallbacks(this);\n        UiTestUtils.setDummyData(this, scrollable);\n        scrollable.setOnScrollListener(new AbsListView.OnScrollListener() {\n            @Override\n            public void onScrollStateChanged(AbsListView view, int scrollState) {\n            }\n\n            @Override\n            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {\n            }\n        });\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.test.ActivityInstrumentationTestCase2;\nimport android.util.DisplayMetrics;\nimport android.util.TypedValue;\nimport android.view.View;\nimport android.widget.FrameLayout;\nimport android.widget.ListAdapter;\nimport android.widget.SimpleAdapter;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic class HeaderGridViewActivityTest extends ActivityInstrumentationTestCase2<HeaderGridViewActivity> {\n\n    private HeaderGridViewActivity activity;\n    private ObservableGridView scrollable;\n\n    public HeaderGridViewActivityTest() {\n        super(HeaderGridViewActivity.class);\n    }\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        setActivityInitialTouchMode(true);\n        activity = getActivity();\n        scrollable = (ObservableGridView) activity.findViewById(R.id.scrollable);\n    }\n\n    public void testScroll() throws Throwable {\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testSaveAndRestoreInstanceState() throws Throwable {\n        UiTestUtils.saveAndRestoreInstanceState(this, activity);\n        testScroll();\n    }\n\n    public void testScrollVerticallyTo() throws Throwable {\n        final DisplayMetrics metrics = activity.getResources().getDisplayMetrics();\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.scrollVerticallyTo((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, metrics));\n            }\n        });\n        getInstrumentation().waitForIdleSync();\n\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.scrollVerticallyTo(0);\n            }\n        });\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testHeaderViewFeatures() throws Throwable {\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                assertEquals(1, scrollable.getHeaderViewCount());\n                assertEquals(1, scrollable.getFooterViewCount());\n                ListAdapter adapter = scrollable.getAdapter();\n                assertTrue(adapter instanceof ObservableGridView.HeaderViewGridAdapter);\n                ObservableGridView.HeaderViewGridAdapter hvgAdapter = (ObservableGridView.HeaderViewGridAdapter) adapter;\n                assertEquals(1, hvgAdapter.getHeadersCount());\n                assertEquals(1, hvgAdapter.getFootersCount());\n                assertNotNull(hvgAdapter.getWrappedAdapter());\n                assertTrue(hvgAdapter.areAllItemsEnabled());\n                assertFalse(hvgAdapter.isEmpty());\n                Object data = hvgAdapter.getItem(0);\n                assertNull(data);\n                assertNotNull(hvgAdapter.getView(0, null, scrollable));\n                assertNotNull(hvgAdapter.getView(1, null, scrollable));\n                assertNotNull(hvgAdapter.getFilter());\n                assertTrue(scrollable.removeHeaderView(activity.headerView));\n                assertEquals(0, scrollable.getHeaderViewCount());\n                assertEquals(0, hvgAdapter.getHeadersCount());\n                assertFalse(scrollable.removeHeaderView(activity.headerView));\n\n                activity.headerView = new View(activity);\n                final int flexibleSpaceImageHeight = activity.getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n                FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,\n                    flexibleSpaceImageHeight);\n                activity.headerView.setLayoutParams(lp);\n\n                // This is required to disable header's list selector effect\n                activity.headerView.setClickable(true);\n\n                scrollable.addHeaderView(activity.headerView);\n\n                assertEquals(100/* items */ + 2/* header */ + 2/* footer */, hvgAdapter.getCount());\n                assertEquals(1, hvgAdapter.getHeadersCount());\n                assertEquals(2, hvgAdapter.getNumColumns());\n                // If the header is added by addHeader(View),\n                // HeaderViewGridAdapter doesn't contain any associated data.\n                // headerData does NOT mean the view.\n                // If we want to get the view, we should use getView().\n                assertNull(hvgAdapter.getItem(0));\n                assertNull(hvgAdapter.getItem(1));\n\n                assertEquals(1, hvgAdapter.getFootersCount());\n                assertNull(hvgAdapter.getItem(100/* items */ + 2/* header */ + 2/* footer */ - 1 - 1));\n                assertNull(hvgAdapter.getItem(100/* items */ + 2/* header */ + 2/* footer */ - 1));\n            }\n        });\n        // Scroll to bottom and try removing re-adding the footer view.\n        for (int i = 0; i < 10; i++) {\n            UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP);\n        }\n        getInstrumentation().waitForIdleSync();\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                ListAdapter adapter = scrollable.getAdapter();\n                ObservableGridView.HeaderViewGridAdapter hvgAdapter = (ObservableGridView.HeaderViewGridAdapter) adapter;\n\n                assertTrue(scrollable.removeFooterView(activity.footerView));\n                assertEquals(0, scrollable.getFooterViewCount());\n                assertEquals(0, hvgAdapter.getFootersCount());\n                assertFalse(scrollable.removeFooterView(activity.footerView));\n\n                activity.footerView = new View(activity);\n                final int flexibleSpaceImageHeight = activity.getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n                FrameLayout.LayoutParams lpf = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,\n                    flexibleSpaceImageHeight);\n                activity.footerView.setLayoutParams(lpf);\n                scrollable.addFooterView(activity.footerView);\n            }\n        });\n    }\n\n    public void testHeaderViewGridExceptions() throws Throwable {\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    new ObservableGridView.HeaderViewGridAdapter(null, null, null);\n                } catch (IllegalArgumentException e) {\n                    fail();\n                }\n                ListAdapter adapter = scrollable.getAdapter();\n                ObservableGridView.HeaderViewGridAdapter hvgAdapter = (ObservableGridView.HeaderViewGridAdapter) adapter;\n                try {\n                    hvgAdapter.setNumColumns(0);\n                } catch (IllegalArgumentException e) {\n                    fail();\n                }\n                ArrayList<ObservableGridView.FixedViewInfo> headerViewInfos = new ArrayList<>();\n                ObservableGridView.HeaderViewGridAdapter adapter1 = new ObservableGridView.HeaderViewGridAdapter(headerViewInfos, null, null);\n                assertTrue(adapter1.isEmpty());\n                try {\n                    adapter1.isEnabled(-1);\n                    fail();\n                } catch (ArrayIndexOutOfBoundsException ignore) {\n                }\n                try {\n                    adapter1.getItem(-1);\n                    fail();\n                } catch (ArrayIndexOutOfBoundsException ignore) {\n                }\n                try {\n                    adapter1.getView(0, null, null);\n                    fail();\n                } catch (ArrayIndexOutOfBoundsException ignore) {\n                }\n                try {\n                    adapter1.getView(-1, null, scrollable);\n                    fail();\n                } catch (ArrayIndexOutOfBoundsException ignore) {\n                }\n            }\n        });\n    }\n\n    public void testHeaderViewGridAdapter() throws Throwable {\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    new ObservableGridView.HeaderViewGridAdapter(null, null, null);\n                } catch (IllegalArgumentException ignore) {\n                    fail();\n                }\n            }\n        });\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                ArrayList<ObservableGridView.FixedViewInfo> list = new ArrayList<>();\n                Map<String, String> map = new LinkedHashMap<>();\n                map.put(\"text\", \"A\");\n                List<Map<String, ?>> data = new ArrayList<>();\n                data.add(map);\n                ObservableGridView.HeaderViewGridAdapter adapter =\n                    new ObservableGridView.HeaderViewGridAdapter(\n                        list,\n                        null,\n                        new SimpleAdapter(\n                            activity,\n                            data,\n                            android.R.layout.simple_list_item_1,\n                            new String[]{\"text\"},\n                            new int[]{android.R.id.text1}));\n                assertFalse(adapter.removeHeader(null));\n                assertEquals(1, adapter.getCount());\n            }\n        });\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                ArrayList<ObservableGridView.FixedViewInfo> list = new ArrayList<>();\n                ObservableGridView.HeaderViewGridAdapter adapter =\n                    new ObservableGridView.HeaderViewGridAdapter(\n                        list,\n                        null,\n                        null);\n                assertEquals(0, adapter.getCount());\n                try {\n                    adapter.isEnabled(1);\n                    fail();\n                } catch (IndexOutOfBoundsException ignore) {\n                }\n                try {\n                    adapter.getItem(1);\n                    fail();\n                } catch (IndexOutOfBoundsException ignore) {\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivity.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.widget.AbsListView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\npublic class ListViewActivity extends Activity implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_listview);\n        ObservableListView scrollable = (ObservableListView) findViewById(R.id.scrollable);\n        scrollable.setScrollViewCallbacks(this);\n        UiTestUtils.setDummyData(this, scrollable);\n        scrollable.setOnScrollListener(new AbsListView.OnScrollListener() {\n            @Override\n            public void onScrollStateChanged(AbsListView view, int scrollState) {\n            }\n\n            @Override\n            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {\n            }\n        });\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.test.ActivityInstrumentationTestCase2;\nimport android.util.DisplayMetrics;\nimport android.util.TypedValue;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\npublic class ListViewActivityTest extends ActivityInstrumentationTestCase2<ListViewActivity> {\n\n    private Activity activity;\n    private ObservableListView scrollable;\n    private int[] callbackCounter;\n\n    public ListViewActivityTest() {\n        super(ListViewActivity.class);\n    }\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        setActivityInitialTouchMode(true);\n        activity = getActivity();\n        scrollable = (ObservableListView) activity.findViewById(R.id.scrollable);\n        callbackCounter = new int[2];\n    }\n\n    public void testInitialize() throws Throwable {\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                new ObservableListView(activity);\n                new ObservableListView(activity, null, 0);\n            }\n        });\n    }\n\n    public void testScroll() throws Throwable {\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testSaveAndRestoreInstanceState() throws Throwable {\n        UiTestUtils.saveAndRestoreInstanceState(this, activity);\n        testScroll();\n    }\n\n    public void testScrollVerticallyTo() throws Throwable {\n        final DisplayMetrics metrics = activity.getResources().getDisplayMetrics();\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.scrollVerticallyTo((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, metrics));\n            }\n        });\n        getInstrumentation().waitForIdleSync();\n\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.scrollVerticallyTo(0);\n            }\n        });\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testNoCallbacks() throws Throwable {\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable = (ObservableListView) activity.findViewById(R.id.scrollable);\n                scrollable.setScrollViewCallbacks(null);\n            }\n        });\n        testScroll();\n    }\n\n    public void testCallbacks() throws Throwable {\n        final ObservableScrollViewCallbacks[] callbacks = new ObservableScrollViewCallbacks[2];\n        callbackCounter[0] = 0;\n        callbackCounter[1] = 0;\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable = (ObservableListView) activity.findViewById(R.id.scrollable);\n                callbacks[0] = new ObservableScrollViewCallbacks() {\n                    @Override\n                    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n                        callbackCounter[0]++;\n                    }\n\n                    @Override\n                    public void onDownMotionEvent() {\n                    }\n\n                    @Override\n                    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n                    }\n                };\n                scrollable.addScrollViewCallbacks(callbacks[0]);\n                callbacks[1] = new ObservableScrollViewCallbacks() {\n                    @Override\n                    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n                        callbackCounter[1]++;\n                    }\n\n                    @Override\n                    public void onDownMotionEvent() {\n                    }\n\n                    @Override\n                    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n                    }\n                };\n                scrollable.addScrollViewCallbacks(callbacks[1]);\n            }\n        });\n        testScroll();\n        // Assert that all the callbacks are enabled and get called.\n        assertTrue(0 < callbackCounter[0]);\n        assertTrue(0 < callbackCounter[1]);\n\n        // Remove one of the callbacks and scroll again to assert it's really removed.\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.removeScrollViewCallbacks(callbacks[0]);\n            }\n        });\n        callbackCounter[0] = 0;\n        callbackCounter[1] = 0;\n        testScroll();\n        assertTrue(0 == callbackCounter[0]);\n        assertTrue(0 < callbackCounter[1]);\n\n        // Clear all callbacks and assert they're really removed.\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.clearScrollViewCallbacks();\n            }\n        });\n        callbackCounter[0] = 0;\n        callbackCounter[1] = 0;\n        testScroll();\n        assertTrue(0 == callbackCounter[0]);\n        assertTrue(0 == callbackCounter[1]);\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivity.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.os.Bundle;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\n\npublic class ListViewScrollFromBottomActivity extends ListViewActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        final ObservableListView scrollable = (ObservableListView) findViewById(R.id.scrollable);\n        ScrollUtils.addOnGlobalLayoutListener(scrollable, new Runnable() {\n            @Override\n            public void run() {\n                int count = scrollable.getAdapter().getCount() - 1;\n                int position = count == 0 ? 1 : count > 0 ? count : 0;\n                scrollable.smoothScrollToPosition(position);\n                scrollable.setSelection(position);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivityTest.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.test.ActivityInstrumentationTestCase2;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\n\npublic class ListViewScrollFromBottomActivityTest extends ActivityInstrumentationTestCase2<ListViewScrollFromBottomActivity> {\n\n    private Activity activity;\n    private ObservableListView scrollable;\n\n    public ListViewScrollFromBottomActivityTest() {\n        super(ListViewScrollFromBottomActivity.class);\n    }\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        setActivityInitialTouchMode(true);\n        activity = getActivity();\n        scrollable = (ObservableListView) activity.findViewById(R.id.scrollable);\n    }\n\n    public void testScroll() throws Throwable {\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP);\n        getInstrumentation().waitForIdleSync();\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivity.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.support.v7.widget.LinearLayoutManager;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\npublic class RecyclerViewActivity extends Activity implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_recyclerview);\n\n        ObservableRecyclerView recyclerView = (ObservableRecyclerView) findViewById(R.id.scrollable);\n        recyclerView.setLayoutManager(new LinearLayoutManager(this));\n        recyclerView.setHasFixedSize(true);\n        recyclerView.setScrollViewCallbacks(this);\n        UiTestUtils.setDummyData(this, recyclerView);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.test.ActivityInstrumentationTestCase2;\nimport android.util.DisplayMetrics;\nimport android.util.TypedValue;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\npublic class RecyclerViewActivityTest extends ActivityInstrumentationTestCase2<RecyclerViewActivity> {\n\n    private Activity activity;\n    private ObservableRecyclerView scrollable;\n    private int[] callbackCounter;\n\n    public RecyclerViewActivityTest() {\n        super(RecyclerViewActivity.class);\n    }\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        setActivityInitialTouchMode(true);\n        activity = getActivity();\n        scrollable = (ObservableRecyclerView) activity.findViewById(R.id.scrollable);\n        callbackCounter = new int[2];\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testInitialize() throws Throwable {\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                new ObservableRecyclerView(activity);\n                new ObservableRecyclerView(activity, null, 0);\n            }\n        });\n    }\n\n    public void testScroll() throws Throwable {\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testSaveAndRestoreInstanceState() throws Throwable {\n        UiTestUtils.saveAndRestoreInstanceState(this, activity);\n        testScroll();\n    }\n\n    public void testScrollVerticallyTo() throws Throwable {\n        final DisplayMetrics metrics = activity.getResources().getDisplayMetrics();\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.scrollVerticallyTo((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, metrics));\n            }\n        });\n        getInstrumentation().waitForIdleSync();\n\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.scrollVerticallyTo(0);\n            }\n        });\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testNoCallbacks() throws Throwable {\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable = (ObservableRecyclerView) activity.findViewById(R.id.scrollable);\n                scrollable.setScrollViewCallbacks(null);\n            }\n        });\n        testScroll();\n    }\n\n    public void testCallbacks() throws Throwable {\n        final ObservableScrollViewCallbacks[] callbacks = new ObservableScrollViewCallbacks[2];\n        callbackCounter[0] = 0;\n        callbackCounter[1] = 0;\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable = (ObservableRecyclerView) activity.findViewById(R.id.scrollable);\n                callbacks[0] = new ObservableScrollViewCallbacks() {\n                    @Override\n                    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n                        callbackCounter[0]++;\n                    }\n\n                    @Override\n                    public void onDownMotionEvent() {\n                    }\n\n                    @Override\n                    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n                    }\n                };\n                scrollable.addScrollViewCallbacks(callbacks[0]);\n                callbacks[1] = new ObservableScrollViewCallbacks() {\n                    @Override\n                    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n                        callbackCounter[1]++;\n                    }\n\n                    @Override\n                    public void onDownMotionEvent() {\n                    }\n\n                    @Override\n                    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n                    }\n                };\n                scrollable.addScrollViewCallbacks(callbacks[1]);\n            }\n        });\n        testScroll();\n        // Assert that all the callbacks are enabled and get called.\n        assertTrue(0 < callbackCounter[0]);\n        assertTrue(0 < callbackCounter[1]);\n\n        // Remove one of the callbacks and scroll again to assert it's really removed.\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.removeScrollViewCallbacks(callbacks[0]);\n            }\n        });\n        callbackCounter[0] = 0;\n        callbackCounter[1] = 0;\n        testScroll();\n        assertTrue(0 == callbackCounter[0]);\n        assertTrue(0 < callbackCounter[1]);\n\n        // Clear all callbacks and assert they're really removed.\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.clearScrollViewCallbacks();\n            }\n        });\n        callbackCounter[0] = 0;\n        callbackCounter[1] = 0;\n        testScroll();\n        assertTrue(0 == callbackCounter[0]);\n        assertTrue(0 == callbackCounter[1]);\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivity.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.os.Bundle;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\n\npublic class RecyclerViewScrollFromBottomActivity extends RecyclerViewActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        final ObservableRecyclerView scrollable = (ObservableRecyclerView) findViewById(R.id.scrollable);\n        ScrollUtils.addOnGlobalLayoutListener(scrollable, new Runnable() {\n            @Override\n            public void run() {\n                int count = scrollable.getAdapter().getItemCount() - 1;\n                int position = count == 0 ? 1 : count > 0 ? count : 0;\n                scrollable.scrollToPosition(position);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivityTest.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.test.ActivityInstrumentationTestCase2;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\n\npublic class RecyclerViewScrollFromBottomActivityTest extends ActivityInstrumentationTestCase2<RecyclerViewScrollFromBottomActivity> {\n\n    private Activity activity;\n    private ObservableRecyclerView scrollable;\n\n    public RecyclerViewScrollFromBottomActivityTest() {\n        super(RecyclerViewScrollFromBottomActivity.class);\n    }\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        setActivityInitialTouchMode(true);\n        activity = getActivity();\n        scrollable = (ObservableRecyclerView) activity.findViewById(R.id.scrollable);\n    }\n\n    public void testScroll() throws Throwable {\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP);\n        getInstrumentation().waitForIdleSync();\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollUtilsTest.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.graphics.Color;\nimport android.test.InstrumentationTestCase;\n\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\n\nimport junit.framework.Assert;\n\npublic class ScrollUtilsTest extends InstrumentationTestCase {\n\n    public void testGetFloat() {\n        Assert.assertEquals(1.0f, ScrollUtils.getFloat(1, 0, 2));\n        assertEquals(0.0f, ScrollUtils.getFloat(-1, 0, 2));\n        assertEquals(2.0f, ScrollUtils.getFloat(3, 0, 2));\n    }\n\n    public void testGetColorWithAlpha() {\n        assertEquals(Color.parseColor(\"#00123456\"), ScrollUtils.getColorWithAlpha(0, Color.parseColor(\"#FF123456\")));\n        assertEquals(Color.parseColor(\"#FF123456\"), ScrollUtils.getColorWithAlpha(1, Color.parseColor(\"#FF123456\")));\n    }\n\n    public void testMixColors() {\n        assertEquals(Color.parseColor(\"#000000\"), ScrollUtils.mixColors(Color.parseColor(\"#000000\"), Color.parseColor(\"#FFFFFF\"), 0));\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivity.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\n\npublic class ScrollViewActivity extends Activity implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_scrollview);\n        ((Scrollable) findViewById(R.id.scrollable)).setScrollViewCallbacks(this);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.test.ActivityInstrumentationTestCase2;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\npublic class ScrollViewActivityTest extends ActivityInstrumentationTestCase2<ScrollViewActivity> {\n\n    private Activity activity;\n    private ObservableScrollView scrollable;\n    private int[] callbackCounter;\n\n    public ScrollViewActivityTest() {\n        super(ScrollViewActivity.class);\n    }\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        setActivityInitialTouchMode(true);\n        activity = getActivity();\n        scrollable = (ObservableScrollView) activity.findViewById(R.id.scrollable);\n        callbackCounter = new int[2];\n    }\n\n    public void testInitialize() throws Throwable {\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                new ObservableScrollView(activity);\n                new ObservableScrollView(activity, null, 0);\n            }\n        });\n    }\n\n    public void testScroll() throws Throwable {\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testSaveAndRestoreInstanceState() throws Throwable {\n        UiTestUtils.saveAndRestoreInstanceState(this, activity);\n        testScroll();\n    }\n\n    public void testNoCallbacks() throws Throwable {\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable = (ObservableScrollView) activity.findViewById(R.id.scrollable);\n                scrollable.setScrollViewCallbacks(null);\n            }\n        });\n        testScroll();\n    }\n\n    public void testCallbacks() throws Throwable {\n        final ObservableScrollViewCallbacks[] callbacks = new ObservableScrollViewCallbacks[2];\n        callbackCounter[0] = 0;\n        callbackCounter[1] = 0;\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable = (ObservableScrollView) activity.findViewById(R.id.scrollable);\n                callbacks[0] = new ObservableScrollViewCallbacks() {\n                    @Override\n                    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n                        callbackCounter[0]++;\n                    }\n\n                    @Override\n                    public void onDownMotionEvent() {\n                    }\n\n                    @Override\n                    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n                    }\n                };\n                scrollable.addScrollViewCallbacks(callbacks[0]);\n                callbacks[1] = new ObservableScrollViewCallbacks() {\n                    @Override\n                    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n                        callbackCounter[1]++;\n                    }\n\n                    @Override\n                    public void onDownMotionEvent() {\n                    }\n\n                    @Override\n                    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n                    }\n                };\n                scrollable.addScrollViewCallbacks(callbacks[1]);\n            }\n        });\n        testScroll();\n        // Assert that all the callbacks are enabled and get called.\n        assertTrue(0 < callbackCounter[0]);\n        assertTrue(0 < callbackCounter[1]);\n\n        // Remove one of the callbacks and scroll again to assert it's really removed.\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.removeScrollViewCallbacks(callbacks[0]);\n            }\n        });\n        callbackCounter[0] = 0;\n        callbackCounter[1] = 0;\n        testScroll();\n        assertTrue(0 == callbackCounter[0]);\n        assertTrue(0 < callbackCounter[1]);\n\n        // Clear all callbacks and assert they're really removed.\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.clearScrollViewCallbacks();\n            }\n        });\n        callbackCounter[0] = 0;\n        callbackCounter[1] = 0;\n        testScroll();\n        assertTrue(0 == callbackCounter[0]);\n        assertTrue(0 == callbackCounter[1]);\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleHeaderRecyclerAdapter.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.content.Context;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\nimport java.util.ArrayList;\n\npublic class SimpleHeaderRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {\n    private static final int VIEW_TYPE_HEADER = 0;\n    private static final int VIEW_TYPE_ITEM = 1;\n\n    private LayoutInflater mInflater;\n    private ArrayList<String> mItems;\n    private View mHeaderView;\n\n    public SimpleHeaderRecyclerAdapter(Context context, ArrayList<String> items, View headerView) {\n        mInflater = LayoutInflater.from(context);\n        mItems = items;\n        mHeaderView = headerView;\n    }\n\n    @Override\n    public int getItemCount() {\n        if (mHeaderView == null) {\n            return mItems.size();\n        } else {\n            return mItems.size() + 1;\n        }\n    }\n\n    @Override\n    public int getItemViewType(int position) {\n        return (position == 0) ? VIEW_TYPE_HEADER : VIEW_TYPE_ITEM;\n    }\n\n    @Override\n    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n        if (viewType == VIEW_TYPE_HEADER) {\n            return new HeaderViewHolder(mHeaderView);\n        } else {\n            return new ItemViewHolder(mInflater.inflate(android.R.layout.simple_list_item_1, parent, false));\n        }\n    }\n\n    @Override\n    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {\n        if (viewHolder instanceof ItemViewHolder) {\n            ((ItemViewHolder) viewHolder).textView.setText(mItems.get(position - 1));\n        }\n    }\n\n    static class HeaderViewHolder extends RecyclerView.ViewHolder {\n        public HeaderViewHolder(View view) {\n            super(view);\n        }\n    }\n\n    static class ItemViewHolder extends RecyclerView.ViewHolder {\n        TextView textView;\n\n        public ItemViewHolder(View view) {\n            super(view);\n            textView = (TextView) view.findViewById(android.R.id.text1);\n        }\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleRecyclerAdapter.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.content.Context;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\nimport java.util.ArrayList;\n\npublic class SimpleRecyclerAdapter extends RecyclerView.Adapter<SimpleRecyclerAdapter.ViewHolder> {\n    private LayoutInflater mInflater;\n    private ArrayList<String> mItems;\n\n    public SimpleRecyclerAdapter(Context context, ArrayList<String> items) {\n        mInflater = LayoutInflater.from(context);\n        mItems = items;\n    }\n\n    @Override\n    public int getItemCount() {\n        return mItems.size();\n    }\n\n    @Override\n    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n        return new ViewHolder(mInflater.inflate(android.R.layout.simple_list_item_1, parent, false));\n    }\n\n    @Override\n    public void onBindViewHolder(ViewHolder viewHolder, int position) {\n        viewHolder.textView.setText(mItems.get(position));\n    }\n\n    static class ViewHolder extends RecyclerView.ViewHolder {\n        TextView textView;\n\n        public ViewHolder(View view) {\n            super(view);\n            textView = (TextView) view.findViewById(android.R.id.text1);\n        }\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivity.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.view.MotionEvent;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\nimport com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout;\nimport com.nineoldandroids.view.ViewHelper;\n\npublic class TouchInterceptionGridViewActivity extends Activity implements ObservableScrollViewCallbacks {\n\n    private TouchInterceptionFrameLayout mInterceptionLayout;\n    private Scrollable mScrollable;\n\n    private int mIntersectionHeight;\n    private int mHeaderBarHeight;\n\n    private float mScrollYOnDownMotion;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_touchinterception_gridview);\n        ((TextView) findViewById(R.id.title)).setText(getClass().getSimpleName());\n        mScrollable = (Scrollable) findViewById(R.id.scrollable);\n        mScrollable.setScrollViewCallbacks(this);\n        UiTestUtils.setDummyData(this, (ObservableGridView) mScrollable);\n\n        mIntersectionHeight = getResources().getDimensionPixelSize(R.dimen.intersection_height);\n        mHeaderBarHeight = getResources().getDimensionPixelSize(R.dimen.header_bar_height);\n\n        mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.scroll_wrapper);\n        mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n\n    private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() {\n        @Override\n        public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) {\n            final int minInterceptionLayoutY = -mIntersectionHeight;\n            return minInterceptionLayoutY < (int) ViewHelper.getY(mInterceptionLayout)\n                    || (moving && mScrollable.getCurrentScrollY() - diffY < 0);\n        }\n\n        @Override\n        public void onDownMotionEvent(MotionEvent ev) {\n            mScrollYOnDownMotion = mScrollable.getCurrentScrollY();\n        }\n\n        @Override\n        public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) {\n            float translationY = ViewHelper.getTranslationY(mInterceptionLayout) - mScrollYOnDownMotion + diffY;\n            if (translationY < -mIntersectionHeight) {\n                translationY = -mIntersectionHeight;\n            } else if (getScreenHeight() - mHeaderBarHeight < translationY) {\n                translationY = getScreenHeight() - mHeaderBarHeight;\n            }\n\n            slideTo(translationY, true);\n        }\n\n        @Override\n        public void onUpOrCancelMotionEvent(MotionEvent ev) {\n        }\n    };\n\n    private void slideTo(float translationY, final boolean animated) {\n        ViewHelper.setTranslationY(mInterceptionLayout, translationY);\n\n        if (translationY < 0) {\n            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams();\n            lp.height = (int) -translationY + getScreenHeight();\n            mInterceptionLayout.requestLayout();\n        }\n    }\n\n    private int getScreenHeight() {\n        return findViewById(android.R.id.content).getHeight();\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivityTest.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.test.ActivityInstrumentationTestCase2;\nimport android.test.TouchUtils;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\n\npublic class TouchInterceptionGridViewActivityTest extends ActivityInstrumentationTestCase2<TouchInterceptionGridViewActivity> {\n\n    private Activity activity;\n    private ObservableGridView scrollable;\n\n    public TouchInterceptionGridViewActivityTest() {\n        super(TouchInterceptionGridViewActivity.class);\n    }\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        setActivityInitialTouchMode(true);\n        activity = getActivity();\n        scrollable = (ObservableGridView) activity.findViewById(R.id.scrollable);\n    }\n\n    public void testScroll() throws Throwable {\n        TouchUtils.touchAndCancelView(this, scrollable);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testSaveAndRestoreInstanceState() throws Throwable {\n        UiTestUtils.saveAndRestoreInstanceState(this, activity);\n        testScroll();\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivity.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.view.MotionEvent;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\nimport com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout;\nimport com.nineoldandroids.view.ViewHelper;\n\npublic class TouchInterceptionListViewActivity extends Activity implements ObservableScrollViewCallbacks {\n\n    private TouchInterceptionFrameLayout mInterceptionLayout;\n    private Scrollable mScrollable;\n\n    private int mIntersectionHeight;\n    private int mHeaderBarHeight;\n\n    private float mScrollYOnDownMotion;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_touchinterception_listview);\n        ((TextView) findViewById(R.id.title)).setText(getClass().getSimpleName());\n        mScrollable = (Scrollable) findViewById(R.id.scrollable);\n        mScrollable.setScrollViewCallbacks(this);\n        UiTestUtils.setDummyData(this, (ObservableListView) mScrollable);\n\n        mIntersectionHeight = getResources().getDimensionPixelSize(R.dimen.intersection_height);\n        mHeaderBarHeight = getResources().getDimensionPixelSize(R.dimen.header_bar_height);\n\n        mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.scroll_wrapper);\n        mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n\n    private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() {\n        @Override\n        public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) {\n            final int minInterceptionLayoutY = -mIntersectionHeight;\n            return minInterceptionLayoutY < (int) ViewHelper.getY(mInterceptionLayout)\n                    || (moving && mScrollable.getCurrentScrollY() - diffY < 0);\n        }\n\n        @Override\n        public void onDownMotionEvent(MotionEvent ev) {\n            mScrollYOnDownMotion = mScrollable.getCurrentScrollY();\n        }\n\n        @Override\n        public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) {\n            float translationY = ViewHelper.getTranslationY(mInterceptionLayout) - mScrollYOnDownMotion + diffY;\n            if (translationY < -mIntersectionHeight) {\n                translationY = -mIntersectionHeight;\n            } else if (getScreenHeight() - mHeaderBarHeight < translationY) {\n                translationY = getScreenHeight() - mHeaderBarHeight;\n            }\n\n            slideTo(translationY, true);\n        }\n\n        @Override\n        public void onUpOrCancelMotionEvent(MotionEvent ev) {\n        }\n    };\n\n    private void slideTo(float translationY, final boolean animated) {\n        ViewHelper.setTranslationY(mInterceptionLayout, translationY);\n\n        if (translationY < 0) {\n            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams();\n            lp.height = (int) -translationY + getScreenHeight();\n            mInterceptionLayout.requestLayout();\n        }\n    }\n\n    private int getScreenHeight() {\n        return findViewById(android.R.id.content).getHeight();\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivityTest.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.test.ActivityInstrumentationTestCase2;\nimport android.test.TouchUtils;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\n\npublic class TouchInterceptionListViewActivityTest extends ActivityInstrumentationTestCase2<TouchInterceptionListViewActivity> {\n\n    private Activity activity;\n    private ObservableListView scrollable;\n\n    public TouchInterceptionListViewActivityTest() {\n        super(TouchInterceptionListViewActivity.class);\n    }\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        setActivityInitialTouchMode(true);\n        activity = getActivity();\n        scrollable = (ObservableListView) activity.findViewById(R.id.scrollable);\n    }\n\n    public void testScroll() throws Throwable {\n        TouchUtils.touchAndCancelView(this, scrollable);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testSaveAndRestoreInstanceState() throws Throwable {\n        UiTestUtils.saveAndRestoreInstanceState(this, activity);\n        testScroll();\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivity.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.view.MotionEvent;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\nimport com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout;\nimport com.nineoldandroids.view.ViewHelper;\n\npublic class TouchInterceptionRecyclerViewActivity extends Activity implements ObservableScrollViewCallbacks {\n\n    private TouchInterceptionFrameLayout mInterceptionLayout;\n    private Scrollable mScrollable;\n\n    private int mIntersectionHeight;\n    private int mHeaderBarHeight;\n\n    private float mScrollYOnDownMotion;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_touchinterception_recyclerview);\n        ((TextView) findViewById(R.id.title)).setText(getClass().getSimpleName());\n        mScrollable = (Scrollable) findViewById(R.id.scrollable);\n        mScrollable.setScrollViewCallbacks(this);\n        ObservableRecyclerView recyclerView = (ObservableRecyclerView) mScrollable;\n        recyclerView.setLayoutManager(new LinearLayoutManager(this));\n        recyclerView.setHasFixedSize(true);\n        recyclerView.setScrollViewCallbacks(this);\n        UiTestUtils.setDummyData(this, recyclerView);\n\n        mIntersectionHeight = getResources().getDimensionPixelSize(R.dimen.intersection_height);\n        mHeaderBarHeight = getResources().getDimensionPixelSize(R.dimen.header_bar_height);\n\n        mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.scroll_wrapper);\n        mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n\n    private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() {\n        @Override\n        public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) {\n            final int minInterceptionLayoutY = -mIntersectionHeight;\n            return minInterceptionLayoutY < (int) ViewHelper.getY(mInterceptionLayout)\n                    || (moving && mScrollable.getCurrentScrollY() - diffY < 0);\n        }\n\n        @Override\n        public void onDownMotionEvent(MotionEvent ev) {\n            mScrollYOnDownMotion = mScrollable.getCurrentScrollY();\n        }\n\n        @Override\n        public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) {\n            float translationY = ViewHelper.getTranslationY(mInterceptionLayout) - mScrollYOnDownMotion + diffY;\n            if (translationY < -mIntersectionHeight) {\n                translationY = -mIntersectionHeight;\n            } else if (getScreenHeight() - mHeaderBarHeight < translationY) {\n                translationY = getScreenHeight() - mHeaderBarHeight;\n            }\n\n            slideTo(translationY, true);\n        }\n\n        @Override\n        public void onUpOrCancelMotionEvent(MotionEvent ev) {\n        }\n    };\n\n    private void slideTo(float translationY, final boolean animated) {\n        ViewHelper.setTranslationY(mInterceptionLayout, translationY);\n\n        if (translationY < 0) {\n            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams();\n            lp.height = (int) -translationY + getScreenHeight();\n            mInterceptionLayout.requestLayout();\n        }\n    }\n\n    private int getScreenHeight() {\n        return findViewById(android.R.id.content).getHeight();\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivityTest.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.test.ActivityInstrumentationTestCase2;\nimport android.test.TouchUtils;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\n\npublic class TouchInterceptionRecyclerViewActivityTest extends ActivityInstrumentationTestCase2<TouchInterceptionRecyclerViewActivity> {\n\n    private Activity activity;\n    private ObservableRecyclerView scrollable;\n\n    public TouchInterceptionRecyclerViewActivityTest() {\n        super(TouchInterceptionRecyclerViewActivity.class);\n    }\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        setActivityInitialTouchMode(true);\n        activity = getActivity();\n        scrollable = (ObservableRecyclerView) activity.findViewById(R.id.scrollable);\n    }\n\n    public void testScroll() throws Throwable {\n        TouchUtils.touchAndCancelView(this, scrollable);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testSaveAndRestoreInstanceState() throws Throwable {\n        UiTestUtils.saveAndRestoreInstanceState(this, activity);\n        testScroll();\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivity.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.view.MotionEvent;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\nimport com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout;\nimport com.nineoldandroids.view.ViewHelper;\n\npublic class TouchInterceptionScrollViewActivity extends Activity implements ObservableScrollViewCallbacks {\n\n    private TouchInterceptionFrameLayout mInterceptionLayout;\n    private Scrollable mScrollable;\n\n    private int mIntersectionHeight;\n    private int mHeaderBarHeight;\n\n    private float mScrollYOnDownMotion;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_touchinterception_scrollview);\n        ((TextView) findViewById(R.id.title)).setText(getClass().getSimpleName());\n        mScrollable = (Scrollable) findViewById(R.id.scrollable);\n        mScrollable.setScrollViewCallbacks(this);\n\n        mIntersectionHeight = getResources().getDimensionPixelSize(R.dimen.intersection_height);\n        mHeaderBarHeight = getResources().getDimensionPixelSize(R.dimen.header_bar_height);\n\n        mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.scroll_wrapper);\n        mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n\n    private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() {\n        @Override\n        public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) {\n            final int minInterceptionLayoutY = -mIntersectionHeight;\n            return minInterceptionLayoutY < (int) ViewHelper.getY(mInterceptionLayout)\n                    || (moving && mScrollable.getCurrentScrollY() - diffY < 0);\n        }\n\n        @Override\n        public void onDownMotionEvent(MotionEvent ev) {\n            mScrollYOnDownMotion = mScrollable.getCurrentScrollY();\n        }\n\n        @Override\n        public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) {\n            float translationY = ViewHelper.getTranslationY(mInterceptionLayout) - mScrollYOnDownMotion + diffY;\n            if (translationY < -mIntersectionHeight) {\n                translationY = -mIntersectionHeight;\n            } else if (getScreenHeight() - mHeaderBarHeight < translationY) {\n                translationY = getScreenHeight() - mHeaderBarHeight;\n            }\n\n            slideTo(translationY, true);\n        }\n\n        @Override\n        public void onUpOrCancelMotionEvent(MotionEvent ev) {\n        }\n    };\n\n    private void slideTo(float translationY, final boolean animated) {\n        ViewHelper.setTranslationY(mInterceptionLayout, translationY);\n\n        if (translationY < 0) {\n            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams();\n            lp.height = (int) -translationY + getScreenHeight();\n            mInterceptionLayout.requestLayout();\n        }\n    }\n\n    private int getScreenHeight() {\n        return findViewById(android.R.id.content).getHeight();\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivityTest.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Build;\nimport android.test.ActivityInstrumentationTestCase2;\nimport android.test.TouchUtils;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout;\n\npublic class TouchInterceptionScrollViewActivityTest extends ActivityInstrumentationTestCase2<TouchInterceptionScrollViewActivity> {\n\n    private Activity activity;\n    private ObservableScrollView scrollable;\n\n    public TouchInterceptionScrollViewActivityTest() {\n        super(TouchInterceptionScrollViewActivity.class);\n    }\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        setActivityInitialTouchMode(true);\n        activity = getActivity();\n        scrollable = (ObservableScrollView) activity.findViewById(R.id.scrollable);\n    }\n\n    public void testInitialize() throws Throwable {\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                new TouchInterceptionFrameLayout(activity);\n                new TouchInterceptionFrameLayout(activity, null, 0);\n                if (Build.VERSION_CODES.LOLLIPOP <= Build.VERSION.SDK_INT) {\n                    new TouchInterceptionFrameLayout(activity, null, 0, 0);\n                }\n            }\n        });\n    }\n\n    public void testScroll() throws Throwable {\n        TouchUtils.touchAndCancelView(this, scrollable);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testSaveAndRestoreInstanceState() throws Throwable {\n        UiTestUtils.saveAndRestoreInstanceState(this, activity);\n        testScroll();\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivity.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.view.MotionEvent;\nimport android.webkit.WebView;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\nimport com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout;\nimport com.nineoldandroids.view.ViewHelper;\n\npublic class TouchInterceptionWebViewActivity extends Activity implements ObservableScrollViewCallbacks {\n\n    private TouchInterceptionFrameLayout mInterceptionLayout;\n    private Scrollable mScrollable;\n\n    private int mIntersectionHeight;\n    private int mHeaderBarHeight;\n\n    private float mScrollYOnDownMotion;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_touchinterception_webview);\n        ((TextView) findViewById(R.id.title)).setText(getClass().getSimpleName());\n        mScrollable = (Scrollable) findViewById(R.id.scrollable);\n        mScrollable.setScrollViewCallbacks(this);\n        ((WebView) mScrollable).loadUrl(\"file:///android_asset/lipsum.html\");\n\n        mIntersectionHeight = getResources().getDimensionPixelSize(R.dimen.intersection_height);\n        mHeaderBarHeight = getResources().getDimensionPixelSize(R.dimen.header_bar_height);\n\n        mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.scroll_wrapper);\n        mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n\n    private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() {\n        @Override\n        public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) {\n            final int minInterceptionLayoutY = -mIntersectionHeight;\n            return minInterceptionLayoutY < (int) ViewHelper.getY(mInterceptionLayout)\n                    || (moving && mScrollable.getCurrentScrollY() - diffY < 0);\n        }\n\n        @Override\n        public void onDownMotionEvent(MotionEvent ev) {\n            mScrollYOnDownMotion = mScrollable.getCurrentScrollY();\n        }\n\n        @Override\n        public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) {\n            float translationY = ViewHelper.getTranslationY(mInterceptionLayout) - mScrollYOnDownMotion + diffY;\n            if (translationY < -mIntersectionHeight) {\n                translationY = -mIntersectionHeight;\n            } else if (getScreenHeight() - mHeaderBarHeight < translationY) {\n                translationY = getScreenHeight() - mHeaderBarHeight;\n            }\n\n            slideTo(translationY, true);\n        }\n\n        @Override\n        public void onUpOrCancelMotionEvent(MotionEvent ev) {\n        }\n    };\n\n    private void slideTo(float translationY, final boolean animated) {\n        ViewHelper.setTranslationY(mInterceptionLayout, translationY);\n\n        if (translationY < 0) {\n            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams();\n            lp.height = (int) -translationY + getScreenHeight();\n            mInterceptionLayout.requestLayout();\n        }\n    }\n\n    private int getScreenHeight() {\n        return findViewById(android.R.id.content).getHeight();\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivityTest.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.test.ActivityInstrumentationTestCase2;\nimport android.test.TouchUtils;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableWebView;\n\npublic class TouchInterceptionWebViewActivityTest extends ActivityInstrumentationTestCase2<TouchInterceptionWebViewActivity> {\n\n    private Activity activity;\n    private ObservableWebView scrollable;\n\n    public TouchInterceptionWebViewActivityTest() {\n        super(TouchInterceptionWebViewActivity.class);\n    }\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        setActivityInitialTouchMode(true);\n        activity = getActivity();\n        scrollable = (ObservableWebView) activity.findViewById(R.id.scrollable);\n    }\n\n    public void testScroll() throws Throwable {\n        TouchUtils.touchAndCancelView(this, scrollable);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testSaveAndRestoreInstanceState() throws Throwable {\n        UiTestUtils.saveAndRestoreInstanceState(this, activity);\n        testScroll();\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.support.v7.widget.RecyclerView;\nimport android.test.InstrumentationTestCase;\nimport android.test.TouchUtils;\nimport android.util.TypedValue;\nimport android.view.View;\nimport android.widget.ArrayAdapter;\nimport android.widget.GridView;\nimport android.widget.ListView;\n\nimport java.util.ArrayList;\n\npublic class UiTestUtils {\n\n    private static final int NUM_OF_ITEMS = 100;\n    private static final int NUM_OF_ITEMS_FEW = 3;\n    private static final int DRAG_STEP_COUNT = 50;\n\n    public enum Direction {\n        LEFT, RIGHT, UP, DOWN\n    }\n\n    private UiTestUtils() {\n    }\n\n    public static void saveAndRestoreInstanceState(final InstrumentationTestCase test, final Activity activity) throws Throwable {\n        test.runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                Bundle outState = new Bundle();\n                test.getInstrumentation().callActivityOnSaveInstanceState(activity, outState);\n                test.getInstrumentation().callActivityOnPause(activity);\n                test.getInstrumentation().callActivityOnResume(activity);\n                test.getInstrumentation().callActivityOnRestoreInstanceState(activity, outState);\n            }\n        });\n        test.getInstrumentation().waitForIdleSync();\n    }\n\n    public static void swipeHorizontally(InstrumentationTestCase test, View v, Direction direction) {\n        int[] xy = new int[2];\n        v.getLocationOnScreen(xy);\n\n        final int viewWidth = v.getWidth();\n        final int viewHeight = v.getHeight();\n\n        float distanceFromEdge = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8,\n                v.getResources().getDisplayMetrics());\n        float xStart = xy[0] + ((direction == Direction.LEFT) ? (viewWidth - distanceFromEdge) : distanceFromEdge);\n        float xEnd = xy[0] + ((direction == Direction.LEFT) ? distanceFromEdge : (viewWidth - distanceFromEdge));\n        float y = xy[1] + (viewHeight / 2.0f);\n\n        TouchUtils.drag(test, xStart, xEnd, y, y, DRAG_STEP_COUNT);\n    }\n\n    public static void swipeVertically(InstrumentationTestCase test, View v, Direction direction) {\n        int[] xy = new int[2];\n        v.getLocationOnScreen(xy);\n\n        final int viewWidth = v.getWidth();\n        final int viewHeight = v.getHeight();\n\n        float distanceFromEdge = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8,\n                v.getResources().getDisplayMetrics());\n        float x = xy[0] + (viewWidth / 2.0f);\n        float yStart = xy[1] + ((direction == Direction.UP) ? (viewHeight - distanceFromEdge) : distanceFromEdge);\n        float yEnd = xy[1] + ((direction == Direction.UP) ? distanceFromEdge : (viewHeight - distanceFromEdge));\n\n        TouchUtils.drag(test, x, x, yStart, yEnd, DRAG_STEP_COUNT);\n    }\n\n    public static ArrayList<String> getDummyData() {\n        return getDummyData(NUM_OF_ITEMS);\n    }\n\n    public static ArrayList<String> getDummyData(int num) {\n        ArrayList<String> items = new ArrayList<String>();\n        for (int i = 1; i <= num; i++) {\n            items.add(\"Item \" + i);\n        }\n        return items;\n    }\n\n    public static void setDummyData(Context context, GridView gridView) {\n        gridView.setAdapter(new ArrayAdapter<String>(context, android.R.layout.simple_list_item_1, getDummyData()));\n    }\n\n    public static void setDummyData(Context context, ListView listView) {\n        setDummyData(context, listView, NUM_OF_ITEMS);\n    }\n\n    public static void setDummyDataFew(Context context, ListView listView) {\n        setDummyData(context, listView, NUM_OF_ITEMS_FEW);\n    }\n\n    public static void setDummyData(Context context, ListView listView, int num) {\n        listView.setAdapter(new ArrayAdapter<String>(context, android.R.layout.simple_list_item_1, getDummyData(num)));\n    }\n\n    public static void setDummyDataWithHeader(Context context, ListView listView, View headerView) {\n        listView.addHeaderView(headerView);\n        setDummyData(context, listView);\n    }\n\n    public static void setDummyData(Context context, RecyclerView recyclerView) {\n        setDummyData(context, recyclerView, NUM_OF_ITEMS);\n    }\n\n    public static void setDummyDataFew(Context context, RecyclerView recyclerView) {\n        setDummyData(context, recyclerView, NUM_OF_ITEMS_FEW);\n    }\n\n    public static void setDummyData(Context context, RecyclerView recyclerView, int num) {\n        recyclerView.setAdapter(new SimpleRecyclerAdapter(context, getDummyData(num)));\n    }\n\n    public static void setDummyDataWithHeader(Context context, RecyclerView recyclerView, View headerView) {\n        recyclerView.setAdapter(new SimpleHeaderRecyclerAdapter(context, getDummyData(), headerView));\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2Activity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.content.res.TypedArray;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.app.ActionBarActivity;\nimport android.support.v7.widget.Toolbar;\nimport android.util.TypedValue;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.widget.FrameLayout;\n\nimport com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\nimport com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout;\nimport com.google.samples.apps.iosched.ui.widget.SlidingTabLayout;\nimport com.nineoldandroids.animation.ValueAnimator;\nimport com.nineoldandroids.view.ViewHelper;\n\npublic class ViewPagerTab2Activity extends ActionBarActivity implements ObservableScrollViewCallbacks {\n\n    private View mToolbarView;\n    private TouchInterceptionFrameLayout mInterceptionLayout;\n    private ViewPager mPager;\n    private NavigationAdapter mPagerAdapter;\n    private int mSlop;\n    private boolean mScrolled;\n    private ScrollState mLastScrollState;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_viewpagertab2);\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        mToolbarView = findViewById(R.id.toolbar);\n        mPagerAdapter = new NavigationAdapter(getSupportFragmentManager());\n        mPager = (ViewPager) findViewById(R.id.pager);\n        mPager.setAdapter(mPagerAdapter);\n        // Padding for ViewPager must be set outside the ViewPager itself\n        // because with padding, EdgeEffect of ViewPager become strange.\n        final int tabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height);\n        findViewById(R.id.pager_wrapper).setPadding(0, getActionBarSize() + tabHeight, 0, 0);\n\n        SlidingTabLayout slidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);\n        slidingTabLayout.setCustomTabView(R.layout.tab_indicator, android.R.id.text1);\n        slidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.accent));\n        slidingTabLayout.setDistributeEvenly(true);\n        slidingTabLayout.setViewPager(mPager);\n\n        ViewConfiguration vc = ViewConfiguration.get(this);\n        mSlop = vc.getScaledTouchSlop();\n        mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.container);\n        mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        if (!mScrolled) {\n            // This event can be used only when TouchInterceptionFrameLayout\n            // doesn't handle the consecutive events.\n            adjustToolbar(scrollState);\n        }\n    }\n\n    private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() {\n        @Override\n        public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) {\n            if (!mScrolled && mSlop < Math.abs(diffX) && Math.abs(diffY) < Math.abs(diffX)) {\n                // Horizontal scroll is maybe handled by ViewPager\n                return false;\n            }\n\n            Scrollable scrollable = getCurrentScrollable();\n            if (scrollable == null) {\n                mScrolled = false;\n                return false;\n            }\n\n            // If interceptionLayout can move, it should intercept.\n            // And once it begins to move, horizontal scroll shouldn't work any longer.\n            int toolbarHeight = mToolbarView.getHeight();\n            int translationY = (int) ViewHelper.getTranslationY(mInterceptionLayout);\n            boolean scrollingUp = 0 < diffY;\n            boolean scrollingDown = diffY < 0;\n            if (scrollingUp) {\n                if (translationY < 0) {\n                    mScrolled = true;\n                    mLastScrollState = ScrollState.UP;\n                    return true;\n                }\n            } else if (scrollingDown) {\n                if (-toolbarHeight < translationY) {\n                    mScrolled = true;\n                    mLastScrollState = ScrollState.DOWN;\n                    return true;\n                }\n            }\n            mScrolled = false;\n            return false;\n        }\n\n        @Override\n        public void onDownMotionEvent(MotionEvent ev) {\n        }\n\n        @Override\n        public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) {\n            float translationY = ScrollUtils.getFloat(ViewHelper.getTranslationY(mInterceptionLayout) + diffY, -mToolbarView.getHeight(), 0);\n            ViewHelper.setTranslationY(mInterceptionLayout, translationY);\n            if (translationY < 0) {\n                FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams();\n                lp.height = (int) (-translationY + getScreenHeight());\n                mInterceptionLayout.requestLayout();\n            }\n        }\n\n        @Override\n        public void onUpOrCancelMotionEvent(MotionEvent ev) {\n            mScrolled = false;\n            adjustToolbar(mLastScrollState);\n        }\n    };\n\n    public Scrollable getCurrentScrollable() {\n        Fragment fragment = getCurrentFragment();\n        if (fragment == null) {\n            return null;\n        }\n        View view = fragment.getView();\n        if (view == null) {\n            return null;\n        }\n        return (Scrollable) view.findViewById(R.id.scroll);\n    }\n\n    private void adjustToolbar(ScrollState scrollState) {\n        int toolbarHeight = mToolbarView.getHeight();\n        final Scrollable scrollable = getCurrentScrollable();\n        if (scrollable == null) {\n            return;\n        }\n        int scrollY = scrollable.getCurrentScrollY();\n        if (scrollState == ScrollState.DOWN) {\n            showToolbar();\n        } else if (scrollState == ScrollState.UP) {\n            if (toolbarHeight <= scrollY) {\n                hideToolbar();\n            } else {\n                showToolbar();\n            }\n        } else if (!toolbarIsShown() && !toolbarIsHidden()) {\n            // Toolbar is moving but doesn't know which to move:\n            // you can change this to hideToolbar()\n            showToolbar();\n        }\n    }\n\n    private Fragment getCurrentFragment() {\n        return mPagerAdapter.getItemAt(mPager.getCurrentItem());\n    }\n\n    private boolean toolbarIsShown() {\n        return ViewHelper.getTranslationY(mInterceptionLayout) == 0;\n    }\n\n    private boolean toolbarIsHidden() {\n        return ViewHelper.getTranslationY(mInterceptionLayout) == -mToolbarView.getHeight();\n    }\n\n    private void showToolbar() {\n        animateToolbar(0);\n    }\n\n    private void hideToolbar() {\n        animateToolbar(-mToolbarView.getHeight());\n    }\n\n    private void animateToolbar(final float toY) {\n        float layoutTranslationY = ViewHelper.getTranslationY(mInterceptionLayout);\n        if (layoutTranslationY != toY) {\n            ValueAnimator animator = ValueAnimator.ofFloat(ViewHelper.getTranslationY(mInterceptionLayout), toY).setDuration(200);\n            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n                @Override\n                public void onAnimationUpdate(ValueAnimator animation) {\n                    float translationY = (float) animation.getAnimatedValue();\n                    ViewHelper.setTranslationY(mInterceptionLayout, translationY);\n                    if (translationY < 0) {\n                        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams();\n                        lp.height = (int) (-translationY + getScreenHeight());\n                        mInterceptionLayout.requestLayout();\n                    }\n                }\n            });\n            animator.start();\n        }\n    }\n\n    private int getActionBarSize() {\n        TypedValue typedValue = new TypedValue();\n        int[] textSizeAttr = new int[]{R.attr.actionBarSize};\n        int indexOfAttrTextSize = 0;\n        TypedArray a = obtainStyledAttributes(typedValue.data, textSizeAttr);\n        int actionBarSize = a.getDimensionPixelSize(indexOfAttrTextSize, -1);\n        a.recycle();\n        return actionBarSize;\n    }\n\n    private int getScreenHeight() {\n        return findViewById(android.R.id.content).getHeight();\n    }\n\n    /**\n     * This adapter provides two types of fragments as an example.\n     * {@linkplain #createItem(int)} should be modified if you use this example for your app.\n     */\n    private static class NavigationAdapter extends CacheFragmentStatePagerAdapter {\n\n        private static final String[] TITLES = new String[]{\"Applepie\", \"Butter Cookie\", \"Cupcake\", \"Donut\", \"Eclair\", \"Froyo\", \"Gingerbread\", \"Honeycomb\", \"Ice Cream Sandwich\", \"Jelly Bean\", \"KitKat\", \"Lollipop\"};\n\n        public NavigationAdapter(FragmentManager fm) {\n            super(fm);\n        }\n\n        @Override\n        protected Fragment createItem(int position) {\n            Fragment f;\n            final int pattern = position % 5;\n            switch (pattern) {\n                case 0:\n                    f = new ViewPagerTab2ScrollViewFragment();\n                    break;\n                case 1:\n                    f = new ViewPagerTab2ListViewFragment();\n                    break;\n                case 2:\n                    f = new ViewPagerTab2RecyclerViewFragment();\n                    break;\n                case 3:\n                    f = new ViewPagerTab2GridViewFragment();\n                    break;\n                case 4:\n                default:\n                    f = new ViewPagerTab2WebViewFragment();\n                    break;\n            }\n            return f;\n        }\n\n        @Override\n        public int getCount() {\n            return TITLES.length;\n        }\n\n        @Override\n        public CharSequence getPageTitle(int position) {\n            return TITLES[position];\n        }\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ActivityTest.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.test.ActivityInstrumentationTestCase2;\nimport android.view.View;\n\npublic class ViewPagerTab2ActivityTest extends ActivityInstrumentationTestCase2<ViewPagerTab2Activity> {\n\n    private ViewPagerTab2Activity activity;\n\n    public ViewPagerTab2ActivityTest() {\n        super(ViewPagerTab2Activity.class);\n    }\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        setActivityInitialTouchMode(true);\n        activity = getActivity();\n    }\n\n    public void testScroll() throws Throwable {\n        for (int i = 0; i < 5; i++) {\n            UiTestUtils.swipeHorizontally(this, activity.findViewById(R.id.pager), UiTestUtils.Direction.LEFT);\n            getInstrumentation().waitForIdleSync();\n            scroll();\n        }\n        for (int i = 0; i < 5; i++) {\n            UiTestUtils.swipeHorizontally(this, activity.findViewById(R.id.pager), UiTestUtils.Direction.RIGHT);\n            getInstrumentation().waitForIdleSync();\n            scroll();\n        }\n    }\n\n    public void scroll() throws Throwable {\n        View scrollable = ((View) activity.getCurrentScrollable()).findViewById(R.id.scroll);\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testSaveAndRestoreInstanceState() throws Throwable {\n        for (int i = 0; i < 5; i++) {\n            UiTestUtils.saveAndRestoreInstanceState(this, activity);\n            scroll();\n\n            UiTestUtils.swipeHorizontally(this, activity.findViewById(R.id.pager), UiTestUtils.Direction.LEFT);\n            getInstrumentation().waitForIdleSync();\n        }\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2GridViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\npublic class ViewPagerTab2GridViewFragment extends Fragment {\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_gridview, container, false);\n\n        Activity parentActivity = getActivity();\n        final ObservableGridView gridView = (ObservableGridView) view.findViewById(R.id.scroll);\n        UiTestUtils.setDummyData(getActivity(), gridView);\n        gridView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.container));\n\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            gridView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ListViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\npublic class ViewPagerTab2ListViewFragment extends Fragment {\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_listview, container, false);\n\n        Activity parentActivity = getActivity();\n        final ObservableListView listView = (ObservableListView) view.findViewById(R.id.scroll);\n        UiTestUtils.setDummyData(getActivity(), listView);\n        listView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.container));\n\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            listView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2RecyclerViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\npublic class ViewPagerTab2RecyclerViewFragment extends Fragment {\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_recyclerview, container, false);\n\n        Activity parentActivity = getActivity();\n        final ObservableRecyclerView recyclerView = (ObservableRecyclerView) view.findViewById(R.id.scroll);\n        recyclerView.setLayoutManager(new LinearLayoutManager(parentActivity));\n        recyclerView.setHasFixedSize(false);\n        UiTestUtils.setDummyData(getActivity(), recyclerView);\n        recyclerView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.container));\n\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            recyclerView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ScrollViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\npublic class ViewPagerTab2ScrollViewFragment extends Fragment {\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_scrollview_noheader, container, false);\n\n        final ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll);\n        Activity parentActivity = getActivity();\n        scrollView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.container));\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            scrollView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2WebViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ObservableWebView;\n\npublic class ViewPagerTab2WebViewFragment extends Fragment {\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_webview, container, false);\n\n        final ObservableWebView webView = (ObservableWebView) view.findViewById(R.id.scroll);\n        webView.loadUrl(\"file:///android_asset/lipsum.html\");\n        Activity parentActivity = getActivity();\n        webView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.container));\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            webView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivity.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.app.ActionBarActivity;\nimport android.support.v7.widget.Toolbar;\nimport android.view.View;\n\nimport com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\nimport com.google.samples.apps.iosched.ui.widget.SlidingTabLayout;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\npublic class ViewPagerTabActivity extends ActionBarActivity implements ObservableScrollViewCallbacks {\n\n    private View mHeaderView;\n    private View mToolbarView;\n    private int mBaseTranslationY;\n    private ViewPager mPager;\n    private NavigationAdapter mPagerAdapter;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_viewpagertab);\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        mHeaderView = findViewById(R.id.header);\n        mToolbarView = findViewById(R.id.toolbar);\n        mPagerAdapter = new NavigationAdapter(getSupportFragmentManager());\n        mPager = (ViewPager) findViewById(R.id.pager);\n        mPager.setAdapter(mPagerAdapter);\n\n        SlidingTabLayout slidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);\n        slidingTabLayout.setCustomTabView(R.layout.tab_indicator, android.R.id.text1);\n        slidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.accent));\n        slidingTabLayout.setDistributeEvenly(true);\n        slidingTabLayout.setViewPager(mPager);\n\n        // When the page is selected, other fragments' scrollY should be adjusted\n        // according to the toolbar status(shown/hidden)\n        slidingTabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {\n            @Override\n            public void onPageScrolled(int i, float v, int i2) {\n            }\n\n            @Override\n            public void onPageSelected(int i) {\n                propagateToolbarState(toolbarIsShown());\n            }\n\n            @Override\n            public void onPageScrollStateChanged(int i) {\n            }\n        });\n\n        propagateToolbarState(toolbarIsShown());\n    }\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        if (dragging) {\n            int toolbarHeight = mToolbarView.getHeight();\n            float currentHeaderTranslationY = ViewHelper.getTranslationY(mHeaderView);\n            if (firstScroll) {\n                if (-toolbarHeight < currentHeaderTranslationY) {\n                    mBaseTranslationY = scrollY;\n                }\n            }\n            float headerTranslationY = ScrollUtils.getFloat(-(scrollY - mBaseTranslationY), -toolbarHeight, 0);\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewHelper.setTranslationY(mHeaderView, headerTranslationY);\n        }\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        mBaseTranslationY = 0;\n\n        Fragment fragment = getCurrentFragment();\n        if (fragment == null) {\n            return;\n        }\n        View view = fragment.getView();\n        if (view == null) {\n            return;\n        }\n\n        // ObservableXxxViews have same API\n        // but currently they don't have any common interfaces.\n        adjustToolbar(scrollState, view);\n    }\n\n    public Scrollable getCurrentScrollable() {\n        Fragment fragment = getCurrentFragment();\n        if (fragment == null) {\n            return null;\n        }\n        View view = fragment.getView();\n        if (view == null) {\n            return null;\n        }\n        return (Scrollable) view.findViewById(R.id.scroll);\n    }\n\n    private void adjustToolbar(ScrollState scrollState, View view) {\n        int toolbarHeight = mToolbarView.getHeight();\n        final Scrollable scrollView = (Scrollable) view.findViewById(R.id.scroll);\n        if (scrollView == null) {\n            return;\n        }\n        int scrollY = scrollView.getCurrentScrollY();\n        if (scrollState == ScrollState.DOWN) {\n            showToolbar();\n        } else if (scrollState == ScrollState.UP) {\n            if (toolbarHeight <= scrollY) {\n                hideToolbar();\n            } else {\n                showToolbar();\n            }\n        } else {\n            // Even if onScrollChanged occurs without scrollY changing, toolbar should be adjusted\n            if (toolbarIsShown() || toolbarIsHidden()) {\n                // Toolbar is completely moved, so just keep its state\n                // and propagate it to other pages\n                propagateToolbarState(toolbarIsShown());\n            } else {\n                // Toolbar is moving but doesn't know which to move:\n                // you can change this to hideToolbar()\n                showToolbar();\n            }\n        }\n    }\n\n    public Fragment getCurrentFragment() {\n        return mPagerAdapter.getItemAt(mPager.getCurrentItem());\n    }\n\n    private void propagateToolbarState(boolean isShown) {\n        int toolbarHeight = mToolbarView.getHeight();\n\n        // Set scrollY for the fragments that are not created yet\n        mPagerAdapter.setScrollY(isShown ? 0 : toolbarHeight);\n\n        // Set scrollY for the active fragments\n        for (int i = 0; i < mPagerAdapter.getCount(); i++) {\n            // Skip current item\n            if (i == mPager.getCurrentItem()) {\n                continue;\n            }\n\n            // Skip destroyed or not created item\n            Fragment f = mPagerAdapter.getItemAt(i);\n            if (f == null) {\n                continue;\n            }\n\n            View view = f.getView();\n            if (view == null) {\n                continue;\n            }\n            propagateToolbarState(isShown, view, toolbarHeight);\n        }\n    }\n\n    private void propagateToolbarState(boolean isShown, View view, int toolbarHeight) {\n        Scrollable scrollView = (Scrollable) view.findViewById(R.id.scroll);\n        if (scrollView == null) {\n            return;\n        }\n        if (isShown) {\n            // Scroll up\n            if (0 < scrollView.getCurrentScrollY()) {\n                scrollView.scrollVerticallyTo(0);\n            }\n        } else {\n            // Scroll down (to hide padding)\n            if (scrollView.getCurrentScrollY() < toolbarHeight) {\n                scrollView.scrollVerticallyTo(toolbarHeight);\n            }\n        }\n    }\n\n    private boolean toolbarIsShown() {\n        return ViewHelper.getTranslationY(mHeaderView) == 0;\n    }\n\n    private boolean toolbarIsHidden() {\n        return ViewHelper.getTranslationY(mHeaderView) == -mToolbarView.getHeight();\n    }\n\n    private void showToolbar() {\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        if (headerTranslationY != 0) {\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewPropertyAnimator.animate(mHeaderView).translationY(0).setDuration(200).start();\n        }\n        propagateToolbarState(true);\n    }\n\n    private void hideToolbar() {\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        int toolbarHeight = mToolbarView.getHeight();\n        if (headerTranslationY != -toolbarHeight) {\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewPropertyAnimator.animate(mHeaderView).translationY(-toolbarHeight).setDuration(200).start();\n        }\n        propagateToolbarState(false);\n    }\n\n    /**\n     * This adapter provides two types of fragments as an example.\n     * {@linkplain #createItem(int)} should be modified if you use this example for your app.\n     */\n    private static class NavigationAdapter extends CacheFragmentStatePagerAdapter {\n\n        private static final String[] TITLES = new String[]{\"Applepie\", \"Butter Cookie\", \"Cupcake\", \"Donut\", \"Eclair\", \"Froyo\", \"Gingerbread\", \"Honeycomb\", \"Ice Cream Sandwich\", \"Jelly Bean\", \"KitKat\", \"Lollipop\"};\n\n        private int mScrollY;\n\n        public NavigationAdapter(FragmentManager fm) {\n            super(fm);\n        }\n\n        public void setScrollY(int scrollY) {\n            mScrollY = scrollY;\n        }\n\n        @Override\n        protected Fragment createItem(int position) {\n            // Initialize fragments.\n            // Please be sure to pass scroll position to each fragments using setArguments.\n            Fragment f;\n            final int pattern = position % 3;\n            switch (pattern) {\n                case 0: {\n                    f = new ViewPagerTabScrollViewFragment();\n                    if (0 <= mScrollY) {\n                        Bundle args = new Bundle();\n                        args.putInt(ViewPagerTabScrollViewFragment.ARG_SCROLL_Y, mScrollY);\n                        f.setArguments(args);\n                    }\n                    break;\n                }\n                case 1: {\n                    f = new ViewPagerTabListViewFragment();\n                    if (0 < mScrollY) {\n                        Bundle args = new Bundle();\n                        args.putInt(ViewPagerTabListViewFragment.ARG_INITIAL_POSITION, 1);\n                        f.setArguments(args);\n                    }\n                    break;\n                }\n                case 2:\n                default: {\n                    f = new ViewPagerTabRecyclerViewFragment();\n                    if (0 < mScrollY) {\n                        Bundle args = new Bundle();\n                        args.putInt(ViewPagerTabRecyclerViewFragment.ARG_INITIAL_POSITION, 1);\n                        f.setArguments(args);\n                    }\n                    break;\n                }\n            }\n            return f;\n        }\n\n        @Override\n        public int getCount() {\n            return TITLES.length;\n        }\n\n        @Override\n        public CharSequence getPageTitle(int position) {\n            return TITLES[position];\n        }\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivityTest.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.test.ActivityInstrumentationTestCase2;\nimport android.view.View;\n\npublic class ViewPagerTabActivityTest extends ActivityInstrumentationTestCase2<ViewPagerTabActivity> {\n\n    private ViewPagerTabActivity activity;\n\n    public ViewPagerTabActivityTest() {\n        super(ViewPagerTabActivity.class);\n    }\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        setActivityInitialTouchMode(true);\n        activity = getActivity();\n    }\n\n    public void testScroll() throws Throwable {\n        for (int i = 0; i < 3; i++) {\n            UiTestUtils.swipeHorizontally(this, activity.findViewById(R.id.pager), UiTestUtils.Direction.LEFT);\n            getInstrumentation().waitForIdleSync();\n            scroll();\n        }\n        for (int i = 0; i < 3; i++) {\n            UiTestUtils.swipeHorizontally(this, activity.findViewById(R.id.pager), UiTestUtils.Direction.RIGHT);\n            getInstrumentation().waitForIdleSync();\n            scroll();\n        }\n    }\n\n    public void scroll() throws Throwable {\n        View scrollable = ((View) activity.getCurrentScrollable()).findViewById(R.id.scroll);\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testSaveAndRestoreInstanceState() throws Throwable {\n        for (int i = 0; i < 3; i++) {\n            UiTestUtils.saveAndRestoreInstanceState(this, activity);\n            scroll();\n\n            UiTestUtils.swipeHorizontally(this, activity.findViewById(R.id.pager), UiTestUtils.Direction.LEFT);\n            getInstrumentation().waitForIdleSync();\n        }\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabListViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\n\npublic class ViewPagerTabListViewFragment extends Fragment {\n\n    public static final String ARG_INITIAL_POSITION = \"ARG_INITIAL_POSITION\";\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_listview, container, false);\n\n        Activity parentActivity = getActivity();\n        final ObservableListView listView = (ObservableListView) view.findViewById(R.id.scroll);\n        UiTestUtils.setDummyDataWithHeader(getActivity(), listView, inflater.inflate(R.layout.padding, null));\n\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            // Scroll to the specified position after layout\n            Bundle args = getArguments();\n            if (args != null && args.containsKey(ARG_INITIAL_POSITION)) {\n                final int initialPosition = args.getInt(ARG_INITIAL_POSITION, 0);\n                ScrollUtils.addOnGlobalLayoutListener(listView, new Runnable() {\n                    @Override\n                    public void run() {\n                        // scrollTo() doesn't work, should use setSelection()\n                        listView.setSelection(initialPosition);\n                    }\n                });\n            }\n            listView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabRecyclerViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\n\npublic class ViewPagerTabRecyclerViewFragment extends Fragment {\n\n    public static final String ARG_INITIAL_POSITION = \"ARG_INITIAL_POSITION\";\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_recyclerview, container, false);\n\n        Activity parentActivity = getActivity();\n        final ObservableRecyclerView recyclerView = (ObservableRecyclerView) view.findViewById(R.id.scroll);\n        recyclerView.setLayoutManager(new LinearLayoutManager(parentActivity));\n        recyclerView.setHasFixedSize(false);\n        View headerView = LayoutInflater.from(parentActivity).inflate(R.layout.padding, null);\n        UiTestUtils.setDummyDataWithHeader(getActivity(), recyclerView, headerView);\n\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            // Scroll to the specified offset after layout\n            Bundle args = getArguments();\n            if (args != null && args.containsKey(ARG_INITIAL_POSITION)) {\n                final int initialPosition = args.getInt(ARG_INITIAL_POSITION, 0);\n                ScrollUtils.addOnGlobalLayoutListener(recyclerView, new Runnable() {\n                    @Override\n                    public void run() {\n                        recyclerView.scrollVerticallyToPosition(initialPosition);\n                    }\n                });\n            }\n            recyclerView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabScrollViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\n\npublic class ViewPagerTabScrollViewFragment extends Fragment {\n\n    public static final String ARG_SCROLL_Y = \"ARG_SCROLL_Y\";\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_scrollview, container, false);\n\n        final ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll);\n        Activity parentActivity = getActivity();\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            // Scroll to the specified offset after layout\n            Bundle args = getArguments();\n            if (args != null && args.containsKey(ARG_SCROLL_Y)) {\n                final int scrollY = args.getInt(ARG_SCROLL_Y, 0);\n                ScrollUtils.addOnGlobalLayoutListener(scrollView, new Runnable() {\n                    @Override\n                    public void run() {\n                        scrollView.scrollTo(0, scrollY);\n                    }\n                });\n            }\n            scrollView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivity.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.os.Bundle;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ObservableWebView;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\n\npublic class WebViewActivity extends Activity implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_webview);\n        ObservableWebView scrollable = (ObservableWebView) findViewById(R.id.scrollable);\n        scrollable.setScrollViewCallbacks(this);\n        scrollable.loadUrl(\"file:///android_asset/lipsum.html\");\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.test;\n\nimport android.app.Activity;\nimport android.test.ActivityInstrumentationTestCase2;\nimport android.util.DisplayMetrics;\nimport android.util.TypedValue;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ObservableWebView;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\npublic class WebViewActivityTest extends ActivityInstrumentationTestCase2<WebViewActivity> {\n\n    private Activity activity;\n    private ObservableWebView scrollable;\n    private int[] callbackCounter;\n\n    public WebViewActivityTest() {\n        super(WebViewActivity.class);\n    }\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        setActivityInitialTouchMode(true);\n        activity = getActivity();\n        scrollable = (ObservableWebView) activity.findViewById(R.id.scrollable);\n        callbackCounter = new int[2];\n    }\n\n    public void testInitialize() throws Throwable {\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                new ObservableWebView(activity);\n                new ObservableWebView(activity, null, 0);\n            }\n        });\n    }\n\n    public void testScroll() throws Throwable {\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP);\n        getInstrumentation().waitForIdleSync();\n\n        UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN);\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testSaveAndRestoreInstanceState() throws Throwable {\n        UiTestUtils.saveAndRestoreInstanceState(this, activity);\n        testScroll();\n    }\n\n    public void testScrollVerticallyTo() throws Throwable {\n        final DisplayMetrics metrics = activity.getResources().getDisplayMetrics();\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.scrollVerticallyTo((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, metrics));\n            }\n        });\n        getInstrumentation().waitForIdleSync();\n\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.scrollVerticallyTo(0);\n            }\n        });\n        getInstrumentation().waitForIdleSync();\n    }\n\n    public void testNoCallbacks() throws Throwable {\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable = (ObservableWebView) activity.findViewById(R.id.scrollable);\n                scrollable.setScrollViewCallbacks(null);\n            }\n        });\n        testScroll();\n    }\n\n    public void testCallbacks() throws Throwable {\n        final ObservableScrollViewCallbacks[] callbacks = new ObservableScrollViewCallbacks[2];\n        callbackCounter[0] = 0;\n        callbackCounter[1] = 0;\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable = (ObservableWebView) activity.findViewById(R.id.scrollable);\n                callbacks[0] = new ObservableScrollViewCallbacks() {\n                    @Override\n                    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n                        callbackCounter[0]++;\n                    }\n\n                    @Override\n                    public void onDownMotionEvent() {\n                    }\n\n                    @Override\n                    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n                    }\n                };\n                scrollable.addScrollViewCallbacks(callbacks[0]);\n                callbacks[1] = new ObservableScrollViewCallbacks() {\n                    @Override\n                    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n                        callbackCounter[1]++;\n                    }\n\n                    @Override\n                    public void onDownMotionEvent() {\n                    }\n\n                    @Override\n                    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n                    }\n                };\n                scrollable.addScrollViewCallbacks(callbacks[1]);\n            }\n        });\n        testScroll();\n        // Assert that all the callbacks are enabled and get called.\n        assertTrue(0 < callbackCounter[0]);\n        assertTrue(0 < callbackCounter[1]);\n\n        // Remove one of the callbacks and scroll again to assert it's really removed.\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.removeScrollViewCallbacks(callbacks[0]);\n            }\n        });\n        callbackCounter[0] = 0;\n        callbackCounter[1] = 0;\n        testScroll();\n        assertTrue(0 == callbackCounter[0]);\n        assertTrue(0 < callbackCounter[1]);\n\n        // Clear all callbacks and assert they're really removed.\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                scrollable.clearScrollViewCallbacks();\n            }\n        });\n        callbackCounter[0] = 0;\n        callbackCounter[1] = 0;\n        testScroll();\n        assertTrue(0 == callbackCounter[0]);\n        assertTrue(0 == callbackCounter[1]);\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabLayout.java",
    "content": "/*\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.samples.apps.iosched.ui.widget;\n\nimport android.content.Context;\nimport android.graphics.Typeface;\nimport android.support.v4.view.PagerAdapter;\nimport android.support.v4.view.ViewPager;\nimport android.util.AttributeSet;\nimport android.util.SparseArray;\nimport android.util.TypedValue;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.HorizontalScrollView;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\n/**\n * To be used with ViewPager to provide a tab indicator component which give constant feedback as to\n * the user's scroll progress.\n * <p>\n * To use the component, simply add it to your view hierarchy. Then in your\n * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call\n * {@link #setViewPager(android.support.v4.view.ViewPager)} providing it the ViewPager this layout is being used for.\n * <p>\n * The colors can be customized in two ways. The first and simplest is to provide an array of colors\n * via {@link #setSelectedIndicatorColors(int...)}. The\n * alternative is via the {@link com.google.samples.apps.iosched.ui.widget.SlidingTabLayout.TabColorizer} interface which provides you complete control over\n * which color is used for any individual position.\n * <p>\n * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},\n * providing the layout ID of your custom layout.\n */\npublic class SlidingTabLayout extends HorizontalScrollView {\n    /**\n     * Allows complete control over the colors drawn in the tab layout. Set with\n     * {@link #setCustomTabColorizer(com.google.samples.apps.iosched.ui.widget.SlidingTabLayout.TabColorizer)}.\n     */\n    public interface TabColorizer {\n\n        /**\n         * @return return the color of the indicator used when {@code position} is selected.\n         */\n        int getIndicatorColor(int position);\n\n    }\n\n    private static final int TITLE_OFFSET_DIPS = 24;\n    private static final int TAB_VIEW_PADDING_DIPS = 16;\n    private static final int TAB_VIEW_TEXT_SIZE_SP = 12;\n\n    private int mTitleOffset;\n\n    private int mTabViewLayoutId;\n    private int mTabViewTextViewId;\n    private boolean mDistributeEvenly;\n\n    private ViewPager mViewPager;\n    private SparseArray<String> mContentDescriptions = new SparseArray<String>();\n    private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;\n\n    private final SlidingTabStrip mTabStrip;\n\n    public SlidingTabLayout(Context context) {\n        this(context, null);\n    }\n\n    public SlidingTabLayout(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n\n        // Disable the Scroll Bar\n        setHorizontalScrollBarEnabled(false);\n        // Make sure that the Tab Strips fills this View\n        setFillViewport(true);\n\n        mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);\n\n        mTabStrip = new SlidingTabStrip(context);\n        addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);\n    }\n\n    /**\n     * Set the custom {@link com.google.samples.apps.iosched.ui.widget.SlidingTabLayout.TabColorizer} to be used.\n     *\n     * If you only require simple custmisation then you can use\n     * {@link #setSelectedIndicatorColors(int...)} to achieve\n     * similar effects.\n     */\n    public void setCustomTabColorizer(TabColorizer tabColorizer) {\n        mTabStrip.setCustomTabColorizer(tabColorizer);\n    }\n\n    public void setDistributeEvenly(boolean distributeEvenly) {\n        mDistributeEvenly = distributeEvenly;\n    }\n\n    /**\n     * Sets the colors to be used for indicating the selected tab. These colors are treated as a\n     * circular array. Providing one color will mean that all tabs are indicated with the same color.\n     */\n    public void setSelectedIndicatorColors(int... colors) {\n        mTabStrip.setSelectedIndicatorColors(colors);\n    }\n\n    /**\n     * Set the {@link android.support.v4.view.ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are\n     * required to set any {@link android.support.v4.view.ViewPager.OnPageChangeListener} through this method. This is so\n     * that the layout can update it's scroll position correctly.\n     *\n     * @see android.support.v4.view.ViewPager#setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener)\n     */\n    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {\n        mViewPagerPageChangeListener = listener;\n    }\n\n    /**\n     * Set the custom layout to be inflated for the tab views.\n     *\n     * @param layoutResId Layout id to be inflated\n     * @param textViewId id of the {@link android.widget.TextView} in the inflated view\n     */\n    public void setCustomTabView(int layoutResId, int textViewId) {\n        mTabViewLayoutId = layoutResId;\n        mTabViewTextViewId = textViewId;\n    }\n\n    /**\n     * Sets the associated view pager. Note that the assumption here is that the pager content\n     * (number of tabs and tab titles) does not change after this call has been made.\n     */\n    public void setViewPager(ViewPager viewPager) {\n        mTabStrip.removeAllViews();\n\n        mViewPager = viewPager;\n        if (viewPager != null) {\n            viewPager.setOnPageChangeListener(new InternalViewPagerListener());\n            populateTabStrip();\n        }\n    }\n\n    /**\n     * Create a default view to be used for tabs. This is called if a custom tab view is not set via\n     * {@link #setCustomTabView(int, int)}.\n     */\n    protected TextView createDefaultTabView(Context context) {\n        TextView textView = new TextView(context);\n        textView.setGravity(Gravity.CENTER);\n        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);\n        textView.setTypeface(Typeface.DEFAULT_BOLD);\n        textView.setLayoutParams(new LinearLayout.LayoutParams(\n                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));\n\n        TypedValue outValue = new TypedValue();\n        getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,\n                outValue, true);\n        textView.setBackgroundResource(outValue.resourceId);\n        textView.setAllCaps(true);\n\n        int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);\n        textView.setPadding(padding, padding, padding, padding);\n\n        return textView;\n    }\n\n    private void populateTabStrip() {\n        final PagerAdapter adapter = mViewPager.getAdapter();\n        final OnClickListener tabClickListener = new TabClickListener();\n\n        for (int i = 0; i < adapter.getCount(); i++) {\n            View tabView = null;\n            TextView tabTitleView = null;\n\n            if (mTabViewLayoutId != 0) {\n                // If there is a custom tab view layout id set, try and inflate it\n                tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,\n                        false);\n                tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);\n            }\n\n            if (tabView == null) {\n                tabView = createDefaultTabView(getContext());\n            }\n\n            if (tabTitleView == null && TextView.class.isInstance(tabView)) {\n                tabTitleView = (TextView) tabView;\n            }\n\n            if (mDistributeEvenly) {\n                LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) tabView.getLayoutParams();\n                lp.width = 0;\n                lp.weight = 1;\n            }\n\n            tabTitleView.setText(adapter.getPageTitle(i));\n            tabView.setOnClickListener(tabClickListener);\n            String desc = mContentDescriptions.get(i, null);\n            if (desc != null) {\n                tabView.setContentDescription(desc);\n            }\n\n            mTabStrip.addView(tabView);\n            if (i == mViewPager.getCurrentItem()) {\n                tabView.setSelected(true);\n            }\n        }\n    }\n\n    public void setContentDescription(int i, String desc) {\n        mContentDescriptions.put(i, desc);\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n\n        if (mViewPager != null) {\n            scrollToTab(mViewPager.getCurrentItem(), 0);\n        }\n    }\n\n    private void scrollToTab(int tabIndex, int positionOffset) {\n        final int tabStripChildCount = mTabStrip.getChildCount();\n        if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {\n            return;\n        }\n\n        View selectedChild = mTabStrip.getChildAt(tabIndex);\n        if (selectedChild != null) {\n            int targetScrollX = selectedChild.getLeft() + positionOffset;\n\n            if (tabIndex > 0 || positionOffset > 0) {\n                // If we're not at the first child and are mid-scroll, make sure we obey the offset\n                targetScrollX -= mTitleOffset;\n            }\n\n            scrollTo(targetScrollX, 0);\n        }\n    }\n\n    private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {\n        private int mScrollState;\n\n        @Override\n        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {\n            int tabStripChildCount = mTabStrip.getChildCount();\n            if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {\n                return;\n            }\n\n            mTabStrip.onViewPagerPageChanged(position, positionOffset);\n\n            View selectedTitle = mTabStrip.getChildAt(position);\n            int extraOffset = (selectedTitle != null)\n                    ? (int) (positionOffset * selectedTitle.getWidth())\n                    : 0;\n            scrollToTab(position, extraOffset);\n\n            if (mViewPagerPageChangeListener != null) {\n                mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,\n                        positionOffsetPixels);\n            }\n        }\n\n        @Override\n        public void onPageScrollStateChanged(int state) {\n            mScrollState = state;\n\n            if (mViewPagerPageChangeListener != null) {\n                mViewPagerPageChangeListener.onPageScrollStateChanged(state);\n            }\n        }\n\n        @Override\n        public void onPageSelected(int position) {\n            if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {\n                mTabStrip.onViewPagerPageChanged(position, 0f);\n                scrollToTab(position, 0);\n            }\n            for (int i = 0; i < mTabStrip.getChildCount(); i++) {\n                mTabStrip.getChildAt(i).setSelected(position == i);\n            }\n            if (mViewPagerPageChangeListener != null) {\n                mViewPagerPageChangeListener.onPageSelected(position);\n            }\n        }\n\n    }\n\n    private class TabClickListener implements OnClickListener {\n        @Override\n        public void onClick(View v) {\n            for (int i = 0; i < mTabStrip.getChildCount(); i++) {\n                if (v == mTabStrip.getChildAt(i)) {\n                    mViewPager.setCurrentItem(i);\n                    return;\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "library/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabStrip.java",
    "content": "/*\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.samples.apps.iosched.ui.widget;\n\nimport android.R;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.util.AttributeSet;\nimport android.util.TypedValue;\nimport android.view.View;\nimport android.widget.LinearLayout;\n\nclass SlidingTabStrip extends LinearLayout {\n\n    private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 0;\n    private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;\n    private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 3;\n    private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;\n\n    private final int mBottomBorderThickness;\n    private final Paint mBottomBorderPaint;\n\n    private final int mSelectedIndicatorThickness;\n    private final Paint mSelectedIndicatorPaint;\n\n    private final int mDefaultBottomBorderColor;\n\n    private int mSelectedPosition;\n    private float mSelectionOffset;\n\n    private SlidingTabLayout.TabColorizer mCustomTabColorizer;\n    private final SimpleTabColorizer mDefaultTabColorizer;\n\n    SlidingTabStrip(Context context) {\n        this(context, null);\n    }\n\n    SlidingTabStrip(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        setWillNotDraw(false);\n\n        final float density = getResources().getDisplayMetrics().density;\n\n        TypedValue outValue = new TypedValue();\n        context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);\n        final int themeForegroundColor =  outValue.data;\n\n        mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,\n                DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);\n\n        mDefaultTabColorizer = new SimpleTabColorizer();\n        mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);\n\n        mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);\n        mBottomBorderPaint = new Paint();\n        mBottomBorderPaint.setColor(mDefaultBottomBorderColor);\n\n        mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);\n        mSelectedIndicatorPaint = new Paint();\n    }\n\n    void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {\n        mCustomTabColorizer = customTabColorizer;\n        invalidate();\n    }\n\n    void setSelectedIndicatorColors(int... colors) {\n        // Make sure that the custom colorizer is removed\n        mCustomTabColorizer = null;\n        mDefaultTabColorizer.setIndicatorColors(colors);\n        invalidate();\n    }\n\n    void onViewPagerPageChanged(int position, float positionOffset) {\n        mSelectedPosition = position;\n        mSelectionOffset = positionOffset;\n        invalidate();\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        final int height = getHeight();\n        final int childCount = getChildCount();\n        final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null\n                ? mCustomTabColorizer\n                : mDefaultTabColorizer;\n\n        // Thick colored underline below the current selection\n        if (childCount > 0) {\n            View selectedTitle = getChildAt(mSelectedPosition);\n            int left = selectedTitle.getLeft();\n            int right = selectedTitle.getRight();\n            int color = tabColorizer.getIndicatorColor(mSelectedPosition);\n\n            if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {\n                int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);\n                if (color != nextColor) {\n                    color = blendColors(nextColor, color, mSelectionOffset);\n                }\n\n                // Draw the selection partway between the tabs\n                View nextTitle = getChildAt(mSelectedPosition + 1);\n                left = (int) (mSelectionOffset * nextTitle.getLeft() +\n                        (1.0f - mSelectionOffset) * left);\n                right = (int) (mSelectionOffset * nextTitle.getRight() +\n                        (1.0f - mSelectionOffset) * right);\n            }\n\n            mSelectedIndicatorPaint.setColor(color);\n\n            canvas.drawRect(left, height - mSelectedIndicatorThickness, right,\n                    height, mSelectedIndicatorPaint);\n        }\n\n        // Thin underline along the entire bottom edge\n        canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);\n    }\n\n    /**\n     * Set the alpha value of the {@code color} to be the given {@code alpha} value.\n     */\n    private static int setColorAlpha(int color, byte alpha) {\n        return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));\n    }\n\n    /**\n     * Blend {@code color1} and {@code color2} using the given ratio.\n     *\n     * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,\n     *              0.0 will return {@code color2}.\n     */\n    private static int blendColors(int color1, int color2, float ratio) {\n        final float inverseRation = 1f - ratio;\n        float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);\n        float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);\n        float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);\n        return Color.rgb((int) r, (int) g, (int) b);\n    }\n\n    private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {\n        private int[] mIndicatorColors;\n\n        @Override\n        public final int getIndicatorColor(int position) {\n            return mIndicatorColors[position % mIndicatorColors.length];\n        }\n\n        void setIndicatorColors(int... colors) {\n            mIndicatorColors = colors;\n        }\n    }\n}\n"
  },
  {
    "path": "library/src/androidTest/res/color/tab_text_color.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:color=\"#fff\" android:state_selected=\"true\" />\n    <item android:color=\"#9fff\" />\n\n</selector>"
  },
  {
    "path": "library/src/androidTest/res/layout/activity_gridview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableGridView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scrollable\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:numColumns=\"2\" />\n"
  },
  {
    "path": "library/src/androidTest/res/layout/activity_listview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableListView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scrollable\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n"
  },
  {
    "path": "library/src/androidTest/res/layout/activity_recyclerview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scrollable\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n"
  },
  {
    "path": "library/src/androidTest/res/layout/activity_scrollview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scrollable\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fillViewport=\"true\">\n\n    <TextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/lipsum\" />\n\n</com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n"
  },
  {
    "path": "library/src/androidTest/res/layout/activity_touchinterception_gridview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipChildren=\"false\">\n\n    <com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout\n        android:id=\"@+id/scroll_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipChildren=\"false\">\n\n        <com.github.ksoichiro.android.observablescrollview.ObservableGridView\n            android:id=\"@+id/scrollable\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"@dimen/header_bar_height\"\n            android:numColumns=\"2\" />\n\n        <FrameLayout\n            android:id=\"@+id/header\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:clipChildren=\"false\">\n\n            <View\n                android:id=\"@+id/header_background\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:background=\"@color/primary\"\n                android:minHeight=\"@dimen/header_bar_height\" />\n\n            <LinearLayout\n                android:id=\"@+id/header_bar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:minHeight=\"@dimen/header_bar_height\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    android:id=\"@+id/title\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/action_bar_size\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center_vertical\"\n                    android:maxLines=\"1\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"20sp\" />\n            </LinearLayout>\n        </FrameLayout>\n    </com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>\n</FrameLayout>\n"
  },
  {
    "path": "library/src/androidTest/res/layout/activity_touchinterception_listview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipChildren=\"false\">\n\n    <com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout\n        android:id=\"@+id/scroll_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipChildren=\"false\">\n\n        <com.github.ksoichiro.android.observablescrollview.ObservableListView\n            android:id=\"@+id/scrollable\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"@dimen/header_bar_height\"\n            android:overScrollMode=\"never\" />\n\n        <FrameLayout\n            android:id=\"@+id/header\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:clipChildren=\"false\">\n\n            <View\n                android:id=\"@+id/header_background\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:background=\"@color/primary\"\n                android:minHeight=\"@dimen/header_bar_height\" />\n\n            <LinearLayout\n                android:id=\"@+id/header_bar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:minHeight=\"@dimen/header_bar_height\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    android:id=\"@+id/title\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/action_bar_size\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center_vertical\"\n                    android:maxLines=\"1\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"20sp\" />\n            </LinearLayout>\n        </FrameLayout>\n    </com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>\n</FrameLayout>\n"
  },
  {
    "path": "library/src/androidTest/res/layout/activity_touchinterception_recyclerview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipChildren=\"false\">\n\n    <com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout\n        android:id=\"@+id/scroll_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipChildren=\"false\">\n\n        <com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView\n            android:id=\"@+id/scrollable\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"@dimen/header_bar_height\" />\n\n        <FrameLayout\n            android:id=\"@+id/header\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:clipChildren=\"false\">\n\n            <View\n                android:id=\"@+id/header_background\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:background=\"@color/primary\"\n                android:minHeight=\"@dimen/header_bar_height\" />\n\n            <LinearLayout\n                android:id=\"@+id/header_bar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:minHeight=\"@dimen/header_bar_height\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    android:id=\"@+id/title\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/action_bar_size\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center_vertical\"\n                    android:maxLines=\"1\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"20sp\" />\n            </LinearLayout>\n        </FrameLayout>\n    </com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>\n</FrameLayout>\n"
  },
  {
    "path": "library/src/androidTest/res/layout/activity_touchinterception_scrollview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipChildren=\"false\">\n\n    <com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout\n        android:id=\"@+id/scroll_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipChildren=\"false\">\n\n        <com.github.ksoichiro.android.observablescrollview.ObservableScrollView\n            android:id=\"@+id/scrollable\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"@dimen/header_bar_height\"\n            android:background=\"@android:color/white\"\n            android:fillViewport=\"true\">\n\n            <TextView\n                android:id=\"@+id/container\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/lipsum\" />\n        </com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n\n        <FrameLayout\n            android:id=\"@+id/header\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:clipChildren=\"false\">\n\n            <View\n                android:id=\"@+id/header_background\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:background=\"@color/primary\"\n                android:minHeight=\"@dimen/header_bar_height\" />\n\n            <LinearLayout\n                android:id=\"@+id/header_bar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:minHeight=\"@dimen/header_bar_height\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    android:id=\"@+id/title\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/action_bar_size\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center_vertical\"\n                    android:maxLines=\"1\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"20sp\" />\n            </LinearLayout>\n        </FrameLayout>\n    </com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>\n</FrameLayout>\n"
  },
  {
    "path": "library/src/androidTest/res/layout/activity_touchinterception_webview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipChildren=\"false\">\n\n    <com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout\n        android:id=\"@+id/scroll_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipChildren=\"false\">\n\n        <com.github.ksoichiro.android.observablescrollview.ObservableWebView\n            android:id=\"@+id/scrollable\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"@dimen/header_bar_height\" />\n\n        <FrameLayout\n            android:id=\"@+id/header\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:clipChildren=\"false\">\n\n            <View\n                android:id=\"@+id/header_background\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:background=\"@color/primary\"\n                android:minHeight=\"@dimen/header_bar_height\" />\n\n            <LinearLayout\n                android:id=\"@+id/header_bar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:minHeight=\"@dimen/header_bar_height\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    android:id=\"@+id/title\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/action_bar_size\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center_vertical\"\n                    android:maxLines=\"1\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"20sp\" />\n            </LinearLayout>\n        </FrameLayout>\n    </com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>\n</FrameLayout>\n"
  },
  {
    "path": "library/src/androidTest/res/layout/activity_viewpagertab.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <!--\n    Padding for ViewPager must be set outside the ViewPager itself\n    because with padding, EdgeEffect of ViewPager become strange.\n    -->\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingTop=\"@dimen/tab_height\">\n\n        <android.support.v4.view.ViewPager\n            android:id=\"@+id/pager\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\" />\n    </FrameLayout>\n\n    <LinearLayout\n        android:id=\"@+id/header\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/colorPrimary\"\n        android:orientation=\"vertical\">\n\n        <android.support.v7.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"?attr/colorPrimary\"\n            android:minHeight=\"?attr/actionBarSize\"\n            app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n            app:theme=\"@style/Toolbar\" />\n\n        <com.google.samples.apps.iosched.ui.widget.SlidingTabLayout\n            android:id=\"@+id/sliding_tabs\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/tab_height\"\n            android:background=\"@color/primary\" />\n    </LinearLayout>\n\n</FrameLayout>\n"
  },
  {
    "path": "library/src/androidTest/res/layout/activity_viewpagertab2.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <FrameLayout\n        android:id=\"@+id/pager_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <android.support.v4.view.ViewPager\n            android:id=\"@+id/pager\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\" />\n    </FrameLayout>\n\n    <LinearLayout\n        android:id=\"@+id/header\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/colorPrimary\"\n        android:orientation=\"vertical\">\n\n        <android.support.v7.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"?attr/colorPrimary\"\n            android:minHeight=\"?attr/actionBarSize\"\n            app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n            app:theme=\"@style/Toolbar\" />\n\n        <com.google.samples.apps.iosched.ui.widget.SlidingTabLayout\n            android:id=\"@+id/sliding_tabs\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/tab_height\"\n            android:background=\"@color/primary\" />\n    </LinearLayout>\n\n</com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>"
  },
  {
    "path": "library/src/androidTest/res/layout/activity_webview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableWebView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scrollable\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n"
  },
  {
    "path": "library/src/androidTest/res/layout/fragment_gridview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableGridView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:numColumns=\"2\" />\n"
  },
  {
    "path": "library/src/androidTest/res/layout/fragment_listview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableListView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n"
  },
  {
    "path": "library/src/androidTest/res/layout/fragment_recyclerview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:scrollbars=\"vertical\" />\n"
  },
  {
    "path": "library/src/androidTest/res/layout/fragment_scrollview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/white\"\n    android:fillViewport=\"true\"\n    android:overScrollMode=\"never\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\">\n\n        <include layout=\"@layout/padding\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@android:color/white\"\n            android:text=\"@string/lipsum\" />\n    </LinearLayout>\n</com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n"
  },
  {
    "path": "library/src/androidTest/res/layout/fragment_scrollview_noheader.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/white\"\n    android:fillViewport=\"true\"\n    android:overScrollMode=\"never\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\">\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@android:color/white\"\n            android:text=\"@string/lipsum\" />\n    </LinearLayout>\n</com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n"
  },
  {
    "path": "library/src/androidTest/res/layout/fragment_webview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableWebView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n"
  },
  {
    "path": "library/src/androidTest/res/layout/padding.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<View xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"?attr/actionBarSize\"\n    android:minHeight=\"?attr/actionBarSize\" />\n"
  },
  {
    "path": "library/src/androidTest/res/layout/tab_indicator.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<TextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@android:id/text1\"\n    style=\"@style/ThemeOverlay.AppCompat.Light\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"48dp\"\n    android:gravity=\"center\"\n    android:paddingLeft=\"16dp\"\n    android:paddingRight=\"16dp\"\n    android:textColor=\"@color/tab_text_color\"\n    android:textSize=\"14sp\" />"
  },
  {
    "path": "library/src/androidTest/res/values/colors.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<resources>\n\n    <color name=\"primary\">#009688</color>\n    <color name=\"primaryDark\">#00796b</color>\n    <color name=\"accent\">#eeff41</color>\n\n</resources>\n"
  },
  {
    "path": "library/src/androidTest/res/values/dimens.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<resources>\n    <dimen name=\"tab_height\">48dp</dimen>\n    <dimen name=\"header_bar_height\">72dp</dimen>\n    <dimen name=\"action_bar_size\">56dp</dimen>\n    <dimen name=\"intersection_height\">16dp</dimen>\n    <dimen name=\"flexible_space_image_height\">240dp</dimen>\n</resources>\n"
  },
  {
    "path": "library/src/androidTest/res/values/strings.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<resources>\n\n    <string name=\"lipsum\">Lorem ipsum dolor sit amet, ut duis lorem provident sed felis blandit, condimentum donec lectus ipsum et mauris, morbi porttitor interdum feugiat nulla donec sodales, vestibulum nisl primis a molestie vestibulum quam, sapien mauris metus risus suspendisse magnis. Augue viverra nulla faucibus egestas eu, a etiam id congue rutrum ante, arcu tincidunt donec quam felis at ornare, iaculis ligula sodales venenatis commodo volutpat neque, suspendisse elit praesent tellus felis mi amet. Inceptos amet tempor lectus lorem est non, ac donec ac libero neque mauris, tellus ante metus eget leo consequat. Scelerisque dolor curabitur pretium blandit ut feugiat, amet lacus pulvinar justo convallis ut, sed natoque ipsum urna posuere nibh eu. Sed at sed vulputate sit orci, facilisis a aliquam tellus quam aliquam, eu aliquam donec at molestie ante, pellentesque mauris lorem ultrices libero faucibus porta, imperdiet adipiscing sit hac diam ut nulla. Lacus enim elit pulvinar donec vehicula dapibus, accumsan purus officia cursus dolor sapien, eu amet dis mauris mi nulla ut. Non accusamus etiam pede non urna tempus, vestibulum aliquam tortor eget pharetra sodales, in vestibulum ut justo orci nulla, lobortis purus sem semper consectetuer magni purus. Dolor a leo vestibulum amet ut sit, arcu ut eaque urna fusce aliquet turpis, sed fermentum sed vestibulum nisl pede, tristique enim lorem posuere in laborum ut. Vestibulum id id justo leo nulla, magna lobortis ullamcorper et dignissim pellentesque, duis suspendisse quis id lorem ante. Vivamus a nullam ante adipiscing amet, mi vel consectetuer nunc aenean pede quisque, eget rhoncus dis porttitor habitant nunc vivamus, duis cubilia blandit non donec justo dictumst, praesent vitae nulla nam pulvinar urna. Adipiscing adipiscing justo urna pulvinar imperdiet nullam, vitae fusce rhoncus proin nonummy suscipit, ullamcorper amet et non potenti platea ultrices, mauris nullam sapien nunc justo vel, eu semper pellentesque arcu fusce augue. Malesuada mauris nibh sit a a scelerisque, velit sem lectus tellus convallis consectetuer, ultricies auctor a ante eros amet sed.\\n\\n\nRisus lacus duis leo platea wisi, felis maecenas rutrum in id in donec, non id a potenti libero eget, posuere elit ea sed pellentesque quis. Sunt lacus urna lorem elit duis, nibh donec purus quisque consectetuer dolor, neque vestibulum proin ornare eros nonummy phasellus. Iaculis cras eu at egestas dolor montes, viverra quisque malesuada consectetuer semper maecenas, a sed vitae donec tempor aliqua metus, ornare mollis suscipit et erat fusce, sit orci aut auctor elementum fames aliquam. Platea dui integer magnis non metus, minus dignissimos ante massa nostra et, rutrum sapien egestas quis sapien donec donec. Erat sit a eros aenean natoque, quam libero id lorem enim proin, lorem ipsum fermentum mattis metus et. Aliquam aliquet suscipit purus conubia at neque, platea vivamus vestibulum nulla quibusdam senectus, et morbi lectus malesuada gravida donec, elementum sit convallis pellentesque velit amet. Et eveniet viverra vehicula consectetuer justo, provident sed commodo non lacinia velit, tempor phasellus vel leo nisl cras, vivamus et arcu interdum dui eu amet. Volutpat wisi rhoncus vel turpis diam quibusdam, dapibus elit est quisque cubilia mauris, nulla elit magna tempor accumsan bibendum, lorem varius sed interdum eget mattis, scelerisque egestas feugiat donec dui molestie. Leo facilisis nisl sit montes ligula sed, enim commodo consectetuer nunc est et, ut sed vehicula dolor luctus elit. Fermentum cras donec eget nibh est vel, sed justo risus et pharetra diam, eu vivamus egestas ligula risus diam, sed justo eget hac ut mauris. Vestibulum diam nec vitae mi eget suspendisse, aenean arcu purus facilisis purus class in, id aliquam sit id scelerisque sapien etiam. Ut nullam sit sed at mauris lobortis, consequat dolor autem ipsum euismod nulla, elit quis proin eget conubia varius, erat arcu massa mus in mauris, scelerisque ut eu sollicitudin libero leo urna.\\n\\n\nConsectetuer luctus tempor elit ut dolor ligula, quis dui per dui hendrerit ante sagittis, in quisque pretium in eleifend enim. Condimentum iaculis vitae feugiat dis tellus vel, lectus dolor nec dui nulla nascetur, et pellentesque curabitur lorem leo velit eget. Id nascetur arcu lobortis suspendisse imperdiet urna, natoque nascetur ante in porta a, interdum hendrerit mi bibendum platea tellus, urna in enim ornare vestibulum faucibus enim. Leo fusce egestas ante nec volutpat, in tempor vel facilisis potenti ut, pede at non lorem a commodo, nulla dolor orci interdum vestibulum nulla. Dui nulla vestibulum quisque a pharetra porta, integer nec ipsum nec sed dui pharetra, magna et dignissim ipsum sed dictum, litora eros vivamus scelerisque libero ipsum. Sed ac ac lorem molestie adipiscing morbi, pellentesque imperdiet nunc quis morbi amet ante, libero dui ligula nec risus neque et, velit nonummy phasellus et facilisi amet, ligula in elementum non sapien pulvinar faucibus. Eu leo ut posuere sed aliquet, tincidunt vel urna volutpat tempus sem, sit felis aliquet vestibulum condimentum sit, amet nibh vel tellus purus ullamcorper libero, nulla vestibulum pede ut vestibulum pretium. Eu nulla vestibulum a neque in metus, quisquam nam sed cursus eget luctus, pede ultrices nec sed dignissim pellentesque, sit class cursus metus nulla placerat mauris, consequat mollis neque vivamus amet pede. Mauris dolor nulla diam eros bibendum, quam ante vestibulum morbi non ligula vel, molestie curabitur rhoncus nulla euismod interdum non. Nulla fringilla lorem mollis ad massa, sit molestie nibh lorem arcu volutpat, accumsan commodo lectus eu et donec, sit tempor tempus rutrum in curabitur amet. Nec urna euismod a tincidunt commodo, eu pede turpis libero vitae viverra, ante vestibulum nam non habitasse potenti, mauris imperdiet in in nunc convallis. Et nostra wisi in est accumsan vehicula, quisque vitae felis mauris sed vulputate nec, ante imperdiet sollicitudin massa iaculis massa sit.\\n\\n\nQuam libero nulla netus eu porta curae, ut nulla bibendum facilisis et urna sed, quis congue vestibulum aliquam interdum etiam. Nulla vel lobortis ullamcorper vitae excepturi, neque urna feugiat lectus vel lacinia, massa pretium orci eu metus neque vulputate. Imperdiet ac velit rhoncus nulla malesuada nullam, nec pulvinar justo gravida lorem rutrum magna, habitasse repudiandae mi eros vestibulum ante, nec euismod dui iaculis in turpis pretium, ac id metus egestas proin lacus lectus. Laoreet lorem nec vitae risus erat arcu, vitae quam ut in ante tristique, porta dolor pede quam et odio nam, arcu lacus sem congue ante cursus massa. Et mattis sagittis erat accumsan fusce quam, vehicula ligula beatae natoque fusce sodales conubia, habitasse metus cum magnis viverra nam cursus, egestas urna wisi primis blandit eu magna, eget libero elit lacus lorem dis aliquam. Ut mauris ante natoque lacus massa, justo a lectus sodales enim adipiscing id, accumsan ut ipsum vestibulum sed enim auctor, vitae congue tincidunt id phasellus lacinia scelerisque, tincidunt sapien nulla euismod volutpat iaculis. Platea sociis nec aliquet nec molestie, in mi et augue sapien in vivamus, integer fames proin vitae in ullamcorper et. Fringilla etiam sapiente rhoncus suspendisse nec id, lobortis cras eget egestas dui ac nec, justo lacus ut lorem bibendum quia eros, eget a gravida id donec nunc suscipit, porta sed in sodales non rutrum. Lectus vel dui elementum pellentesque magna aliquam, vitae non sit pede et fusce nibh, id id deserunt ornare dui sit condimentum, in adipiscing imperdiet turpis nam aliquet, facilisis metus magna lacus wisi facilisis tortor. Vulputate elit accumsan quam amet ligula, suspendisse lacus mi nonummy integer urna, libero nulla nunc varius in odio, laoreet nulla amet placerat amet nec. Consectetuer vel massa hendrerit vitae iaculis id, sed ut ut laudantium odio in, elit vestibulum duis ante maecenas interdum in, neque vehicula ultrices varius in quam, pede tellus pellentesque sed nullam quis.</string>\n\n</resources>\n"
  },
  {
    "path": "library/src/androidTest/res/values/styles.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<resources>\n\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <item name=\"colorPrimary\">@color/primary</item>\n        <item name=\"colorPrimaryDark\">@color/primaryDark</item>\n        <item name=\"colorAccent\">@color/accent</item>\n    </style>\n\n    <style name=\"AppTheme.Toolbar\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n        <item name=\"colorPrimary\">@color/primary</item>\n        <item name=\"colorPrimaryDark\">@color/primaryDark</item>\n        <item name=\"colorAccent\">@color/accent</item>\n    </style>\n\n    <style name=\"Toolbar\" parent=\"Theme.AppCompat\">\n        <item name=\"colorPrimary\">@color/primary</item>\n        <item name=\"colorPrimaryDark\">@color/primaryDark</item>\n        <item name=\"colorAccent\">@color/accent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "library/src/main/java/com/github/ksoichiro/android/observablescrollview/CacheFragmentStatePagerAdapter.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview;\n\nimport android.os.Bundle;\nimport android.os.Parcelable;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.app.FragmentStatePagerAdapter;\nimport android.util.SparseArray;\nimport android.view.ViewGroup;\n\n/**\n * FragmentStatePagerAdapter that caches each pages.\n * <p>FragmentStatePagerAdapter is also originally caches pages,\n * but its keys are not public nor documented, so depending\n * on how it create cache key is dangerous.</p>\n * <p>This adapter caches pages by itself and provide getter method to the cache.</p>\n */\npublic abstract class CacheFragmentStatePagerAdapter extends FragmentStatePagerAdapter {\n\n    private static final String STATE_SUPER_STATE = \"superState\";\n    private static final String STATE_PAGES = \"pages\";\n    private static final String STATE_PAGE_INDEX_PREFIX = \"pageIndex:\";\n    private static final String STATE_PAGE_KEY_PREFIX = \"page:\";\n\n    private FragmentManager mFm;\n    private SparseArray<Fragment> mPages;\n\n    public CacheFragmentStatePagerAdapter(FragmentManager fm) {\n        super(fm);\n        mPages = new SparseArray<>();\n        mFm = fm;\n    }\n\n    @Override\n    public Parcelable saveState() {\n        Parcelable p = super.saveState();\n        Bundle bundle = new Bundle();\n        bundle.putParcelable(STATE_SUPER_STATE, p);\n\n        bundle.putInt(STATE_PAGES, mPages.size());\n        if (0 < mPages.size()) {\n            for (int i = 0; i < mPages.size(); i++) {\n                int position = mPages.keyAt(i);\n                bundle.putInt(createCacheIndex(i), position);\n                Fragment f = mPages.get(position);\n                mFm.putFragment(bundle, createCacheKey(position), f);\n            }\n        }\n        return bundle;\n    }\n\n    @Override\n    public void restoreState(Parcelable state, ClassLoader loader) {\n        Bundle bundle = (Bundle) state;\n        int pages = bundle.getInt(STATE_PAGES);\n        if (0 < pages) {\n            for (int i = 0; i < pages; i++) {\n                int position = bundle.getInt(createCacheIndex(i));\n                Fragment f = mFm.getFragment(bundle, createCacheKey(position));\n                mPages.put(position, f);\n            }\n        }\n\n        Parcelable p = bundle.getParcelable(STATE_SUPER_STATE);\n        super.restoreState(p, loader);\n    }\n\n    /**\n     * Get a new Fragment instance.\n     * <p>Each fragments are automatically cached in this method,\n     * so you don't have to do it by yourself.\n     * If you want to implement instantiation of Fragments,\n     * you should override {@link #createItem(int)} instead.</p>\n     * <p/>\n     * {@inheritDoc}\n     *\n     * @param position Position of the item in the adapter.\n     * @return Fragment instance.\n     */\n    @Override\n    public Fragment getItem(int position) {\n        Fragment f = createItem(position);\n        // We should cache fragments manually to access to them later\n        mPages.put(position, f);\n        return f;\n    }\n\n    @Override\n    public void destroyItem(ViewGroup container, int position, Object object) {\n        if (0 <= mPages.indexOfKey(position)) {\n            mPages.remove(position);\n        }\n        super.destroyItem(container, position, object);\n    }\n\n    /**\n     * Get the item at the specified position in the adapter.\n     *\n     * @param position Position of the item in the adapter.\n     * @return Fragment instance.\n     */\n    public Fragment getItemAt(int position) {\n        return mPages.get(position);\n    }\n\n    /**\n     * Create a new Fragment instance.\n     * This is called inside {@link #getItem(int)}.\n     *\n     * @param position Position of the item in the adapter.\n     * @return Fragment instance.\n     */\n    protected abstract Fragment createItem(int position);\n\n    /**\n     * Create an index string for caching Fragment pages.\n     *\n     * @param index Index of the item in the adapter.\n     * @return Key string for caching Fragment pages.\n     */\n    protected String createCacheIndex(int index) {\n        return STATE_PAGE_INDEX_PREFIX + index;\n    }\n\n    /**\n     * Create a key string for caching Fragment pages.\n     *\n     * @param position Position of the item in the adapter.\n     * @return Key string for caching Fragment pages.\n     */\n    protected String createCacheKey(int position) {\n        return STATE_PAGE_KEY_PREFIX + position;\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n * Copyright (C) 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview;\n\nimport android.content.Context;\nimport android.database.DataSetObservable;\nimport android.database.DataSetObserver;\nimport android.os.Build;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.util.AttributeSet;\nimport android.util.SparseIntArray;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AbsListView;\nimport android.widget.AdapterView;\nimport android.widget.Filter;\nimport android.widget.Filterable;\nimport android.widget.FrameLayout;\nimport android.widget.GridView;\nimport android.widget.ListAdapter;\nimport android.widget.WrapperListAdapter;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * GridView that its scroll position can be observed.\n */\npublic class ObservableGridView extends GridView implements Scrollable {\n\n    // Fields that should be saved onSaveInstanceState\n    private int mPrevFirstVisiblePosition;\n    private int mPrevFirstVisibleChildHeight = -1;\n    private int mPrevScrolledChildrenHeight;\n    private int mPrevScrollY;\n    private int mScrollY;\n    private SparseIntArray mChildrenHeights;\n\n    // Fields that don't need to be saved onSaveInstanceState\n    private ObservableScrollViewCallbacks mCallbacks;\n    private List<ObservableScrollViewCallbacks> mCallbackCollection;\n    private ScrollState mScrollState;\n    private boolean mFirstScroll;\n    private boolean mDragging;\n    private boolean mIntercepted;\n    private MotionEvent mPrevMoveEvent;\n    private ViewGroup mTouchInterceptionViewGroup;\n    private ArrayList<FixedViewInfo> mHeaderViewInfos;\n    private ArrayList<FixedViewInfo> mFooterViewInfos;\n\n    private OnScrollListener mOriginalScrollListener;\n    private OnScrollListener mScrollListener = new OnScrollListener() {\n        @Override\n        public void onScrollStateChanged(AbsListView view, int scrollState) {\n            if (mOriginalScrollListener != null) {\n                mOriginalScrollListener.onScrollStateChanged(view, scrollState);\n            }\n        }\n\n        @Override\n        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {\n            if (mOriginalScrollListener != null) {\n                mOriginalScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);\n            }\n            // AbsListView#invokeOnItemScrollListener calls onScrollChanged(0, 0, 0, 0)\n            // on Android 4.0+, but Android 2.3 is not. (Android 3.0 is unknown)\n            // So call it with onScrollListener.\n            onScrollChanged();\n        }\n    };\n\n    public ObservableGridView(Context context) {\n        super(context);\n        init();\n    }\n\n    public ObservableGridView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public ObservableGridView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        init();\n    }\n\n    @Override\n    public void onRestoreInstanceState(Parcelable state) {\n        SavedState ss = (SavedState) state;\n        mPrevFirstVisiblePosition = ss.prevFirstVisiblePosition;\n        mPrevFirstVisibleChildHeight = ss.prevFirstVisibleChildHeight;\n        mPrevScrolledChildrenHeight = ss.prevScrolledChildrenHeight;\n        mPrevScrollY = ss.prevScrollY;\n        mScrollY = ss.scrollY;\n        mChildrenHeights = ss.childrenHeights;\n        super.onRestoreInstanceState(ss.getSuperState());\n    }\n\n    @Override\n    public Parcelable onSaveInstanceState() {\n        Parcelable superState = super.onSaveInstanceState();\n        SavedState ss = new SavedState(superState);\n        ss.prevFirstVisiblePosition = mPrevFirstVisiblePosition;\n        ss.prevFirstVisibleChildHeight = mPrevFirstVisibleChildHeight;\n        ss.prevScrolledChildrenHeight = mPrevScrolledChildrenHeight;\n        ss.prevScrollY = mPrevScrollY;\n        ss.scrollY = mScrollY;\n        ss.childrenHeights = mChildrenHeights;\n        return ss;\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        if (hasNoCallbacks()) {\n            return super.onInterceptTouchEvent(ev);\n        }\n        switch (ev.getActionMasked()) {\n            case MotionEvent.ACTION_DOWN:\n                // Whether or not motion events are consumed by children,\n                // flag initializations which are related to ACTION_DOWN events should be executed.\n                // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are\n                // passed to parent (this view), the flags will be invalid.\n                // Also, applications might implement initialization codes to onDownMotionEvent,\n                // so call it here.\n                mFirstScroll = mDragging = true;\n                dispatchOnDownMotionEvent();\n                break;\n        }\n        return super.onInterceptTouchEvent(ev);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        if (hasNoCallbacks()) {\n            return super.onTouchEvent(ev);\n        }\n        switch (ev.getActionMasked()) {\n            case MotionEvent.ACTION_UP:\n            case MotionEvent.ACTION_CANCEL:\n                mIntercepted = false;\n                mDragging = false;\n                dispatchOnUpOrCancelMotionEvent(mScrollState);\n                break;\n            case MotionEvent.ACTION_MOVE:\n                if (mPrevMoveEvent == null) {\n                    mPrevMoveEvent = ev;\n                }\n                float diffY = ev.getY() - mPrevMoveEvent.getY();\n                mPrevMoveEvent = MotionEvent.obtainNoHistory(ev);\n                if (getCurrentScrollY() - diffY <= 0) {\n                    // Can't scroll anymore.\n\n                    if (mIntercepted) {\n                        // Already dispatched ACTION_DOWN event to parents, so stop here.\n                        return false;\n                    }\n\n                    // Apps can set the interception target other than the direct parent.\n                    final ViewGroup parent;\n                    if (mTouchInterceptionViewGroup == null) {\n                        parent = (ViewGroup) getParent();\n                    } else {\n                        parent = mTouchInterceptionViewGroup;\n                    }\n\n                    // Get offset to parents. If the parent is not the direct parent,\n                    // we should aggregate offsets from all of the parents.\n                    float offsetX = 0;\n                    float offsetY = 0;\n                    for (View v = this; v != null && v != parent; v = (View) v.getParent()) {\n                        offsetX += v.getLeft() - v.getScrollX();\n                        offsetY += v.getTop() - v.getScrollY();\n                    }\n                    final MotionEvent event = MotionEvent.obtainNoHistory(ev);\n                    event.offsetLocation(offsetX, offsetY);\n\n                    if (parent.onInterceptTouchEvent(event)) {\n                        mIntercepted = true;\n\n                        // If the parent wants to intercept ACTION_MOVE events,\n                        // we pass ACTION_DOWN event to the parent\n                        // as if these touch events just have began now.\n                        event.setAction(MotionEvent.ACTION_DOWN);\n\n                        // Return this onTouchEvent() first and set ACTION_DOWN event for parent\n                        // to the queue, to keep events sequence.\n                        post(new Runnable() {\n                            @Override\n                            public void run() {\n                                parent.dispatchTouchEvent(event);\n                            }\n                        });\n                        return false;\n                    }\n                    // Even when this can't be scrolled anymore,\n                    // simply returning false here may cause subView's click,\n                    // so delegate it to super.\n                    return super.onTouchEvent(ev);\n                }\n                break;\n        }\n        return super.onTouchEvent(ev);\n    }\n\n    public void addFooterView(View v) {\n        addFooterView(v, null, true);\n    }\n\n    public void addFooterView(View v, Object data, boolean isSelectable) {\n        ListAdapter mAdapter = getAdapter();\n        if (mAdapter != null && !(mAdapter instanceof HeaderViewGridAdapter)) {\n            throw new IllegalStateException(\n                \"Cannot add header view to grid -- setAdapter has already been called.\");\n        }\n\n        ViewGroup.LayoutParams lyp = v.getLayoutParams();\n\n        FixedViewInfo info = new FixedViewInfo();\n        FrameLayout fl = new FullWidthFixedViewLayout(getContext());\n\n        if (lyp != null) {\n            v.setLayoutParams(new FrameLayout.LayoutParams(lyp.width, lyp.height));\n            fl.setLayoutParams(new AbsListView.LayoutParams(lyp.width, lyp.height));\n        }\n        fl.addView(v);\n        info.view = v;\n        info.viewContainer = fl;\n        info.data = data;\n        info.isSelectable = isSelectable;\n        mFooterViewInfos.add(info);\n\n        if (mAdapter != null) {\n            ((HeaderViewGridAdapter) mAdapter).notifyDataSetChanged();\n        }\n    }\n\n    public int getFooterViewCount() {\n        return mFooterViewInfos.size();\n    }\n\n    public boolean removeFooterView(View v) {\n        if (mFooterViewInfos.size() > 0) {\n            boolean result = false;\n            ListAdapter adapter = getAdapter();\n            if (adapter != null && ((HeaderViewGridAdapter) adapter).removeFooter(v)) {\n                result = true;\n            }\n            removeFixedViewInfo(v, mFooterViewInfos);\n            return result;\n        }\n        return false;\n    }\n\n    @Override\n    public void setOnScrollListener(OnScrollListener l) {\n        // Don't set l to super.setOnScrollListener().\n        // l receives all events through mScrollListener.\n        mOriginalScrollListener = l;\n    }\n\n    @Override\n    public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        mCallbacks = listener;\n    }\n\n    @Override\n    public void addScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        if (mCallbackCollection == null) {\n            mCallbackCollection = new ArrayList<>();\n        }\n        mCallbackCollection.add(listener);\n    }\n\n    @Override\n    public void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        if (mCallbackCollection != null) {\n            mCallbackCollection.remove(listener);\n        }\n    }\n\n    @Override\n    public void clearScrollViewCallbacks() {\n        if (mCallbackCollection != null) {\n            mCallbackCollection.clear();\n        }\n    }\n\n    @Override\n    public void setTouchInterceptionViewGroup(ViewGroup viewGroup) {\n        mTouchInterceptionViewGroup = viewGroup;\n    }\n\n    @Override\n    public void scrollVerticallyTo(int y) {\n        scrollTo(0, y);\n    }\n\n    @Override\n    public int getCurrentScrollY() {\n        return mScrollY;\n    }\n\n    @Override\n    public void setClipChildren(boolean clipChildren) {\n        // Ignore, since the header rows depend on not being clipped\n    }\n\n    @Override\n    public void setAdapter(ListAdapter adapter) {\n        if (0 < mHeaderViewInfos.size()) {\n            HeaderViewGridAdapter headerViewGridAdapter = new HeaderViewGridAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);\n            int numColumns = getNumColumnsCompat();\n            if (1 < numColumns) {\n                headerViewGridAdapter.setNumColumns(numColumns);\n            }\n            super.setAdapter(headerViewGridAdapter);\n        } else {\n            super.setAdapter(adapter);\n        }\n    }\n\n    public void addHeaderView(View v, Object data, boolean isSelectable) {\n        ListAdapter adapter = getAdapter();\n        if (adapter != null && !(adapter instanceof HeaderViewGridAdapter)) {\n            throw new IllegalStateException(\"Cannot add header view to grid -- setAdapter has already been called.\");\n        }\n        FixedViewInfo info = new FixedViewInfo();\n        FrameLayout fl = new FullWidthFixedViewLayout(getContext());\n        fl.addView(v);\n        info.view = v;\n        info.viewContainer = fl;\n        info.data = data;\n        info.isSelectable = isSelectable;\n        mHeaderViewInfos.add(info);\n        // in the case of re-adding a header view, or adding one later on,\n        // we need to notify the observer\n        if (adapter != null) {\n            ((HeaderViewGridAdapter) adapter).notifyDataSetChanged();\n        }\n    }\n\n    public void addHeaderView(View v) {\n        addHeaderView(v, null, true);\n    }\n\n    public int getHeaderViewCount() {\n        return mHeaderViewInfos.size();\n    }\n\n    public boolean removeHeaderView(View v) {\n        if (mHeaderViewInfos.size() > 0) {\n            boolean result = false;\n            ListAdapter adapter = getAdapter();\n            if (adapter != null && adapter instanceof HeaderViewGridAdapter && ((HeaderViewGridAdapter) adapter).removeHeader(v)) {\n                result = true;\n            }\n            removeFixedViewInfo(v, mHeaderViewInfos);\n            return result;\n        }\n        return false;\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        ListAdapter adapter = getAdapter();\n        if (adapter != null && adapter instanceof HeaderViewGridAdapter) {\n            ((HeaderViewGridAdapter) adapter).setNumColumns(getNumColumnsCompat());\n        }\n    }\n\n    private void init() {\n        mChildrenHeights = new SparseIntArray();\n        mHeaderViewInfos = new ArrayList<>();\n        mFooterViewInfos = new ArrayList<>();\n        super.setClipChildren(false);\n        super.setOnScrollListener(mScrollListener);\n    }\n\n    private int getNumColumnsCompat() {\n        if (Build.VERSION.SDK_INT >= 11) {\n            return getNumColumns();\n        } else {\n            int columns = 0;\n            if (getChildCount() > 0) {\n                int width = getChildAt(0).getMeasuredWidth();\n                if (width > 0) {\n                    columns = getWidth() / width;\n                }\n            }\n            return columns > 0 ? columns : AUTO_FIT;\n        }\n    }\n\n    private void onScrollChanged() {\n        if (hasNoCallbacks()) {\n            return;\n        }\n        if (getChildCount() > 0) {\n            int firstVisiblePosition = getFirstVisiblePosition();\n            for (int i = getFirstVisiblePosition(), j = 0; i <= getLastVisiblePosition(); i++, j++) {\n                if (mChildrenHeights.indexOfKey(i) < 0 || getChildAt(j).getHeight() != mChildrenHeights.get(i)) {\n                    if (i % getNumColumnsCompat() == 0) {\n                        mChildrenHeights.put(i, getChildAt(j).getHeight());\n                    }\n                }\n            }\n\n            View firstVisibleChild = getChildAt(0);\n            if (firstVisibleChild != null) {\n                if (mPrevFirstVisiblePosition < firstVisiblePosition) {\n                    // scroll down\n                    int skippedChildrenHeight = 0;\n                    if (firstVisiblePosition - mPrevFirstVisiblePosition != 1) {\n                        for (int i = firstVisiblePosition - 1; i > mPrevFirstVisiblePosition; i--) {\n                            if (0 < mChildrenHeights.indexOfKey(i)) {\n                                skippedChildrenHeight += mChildrenHeights.get(i);\n                            }\n                        }\n                    }\n                    mPrevScrolledChildrenHeight += mPrevFirstVisibleChildHeight + skippedChildrenHeight;\n                    mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight();\n                } else if (firstVisiblePosition < mPrevFirstVisiblePosition) {\n                    // scroll up\n                    int skippedChildrenHeight = 0;\n                    if (mPrevFirstVisiblePosition - firstVisiblePosition != 1) {\n                        for (int i = mPrevFirstVisiblePosition - 1; i > firstVisiblePosition; i--) {\n                            if (0 < mChildrenHeights.indexOfKey(i)) {\n                                skippedChildrenHeight += mChildrenHeights.get(i);\n                            }\n                        }\n                    }\n                    mPrevScrolledChildrenHeight -= firstVisibleChild.getHeight() + skippedChildrenHeight;\n                    mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight();\n                } else if (firstVisiblePosition == 0) {\n                    mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight();\n                    mPrevScrolledChildrenHeight = 0;\n                }\n                if (mPrevFirstVisibleChildHeight < 0) {\n                    mPrevFirstVisibleChildHeight = 0;\n                }\n                mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop() + getPaddingTop();\n                mPrevFirstVisiblePosition = firstVisiblePosition;\n\n                dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging);\n                if (mFirstScroll) {\n                    mFirstScroll = false;\n                }\n\n                if (mPrevScrollY < mScrollY) {\n                    mScrollState = ScrollState.UP;\n                } else if (mScrollY < mPrevScrollY) {\n                    mScrollState = ScrollState.DOWN;\n                } else {\n                    mScrollState = ScrollState.STOP;\n                }\n                mPrevScrollY = mScrollY;\n            }\n        }\n    }\n\n    private void removeFixedViewInfo(View v, ArrayList<FixedViewInfo> where) {\n        int len = where.size();\n        for (int i = 0; i < len; ++i) {\n            FixedViewInfo info = where.get(i);\n            if (info.view == v) {\n                where.remove(i);\n                break;\n            }\n        }\n    }\n\n    private boolean hasNoCallbacks() {\n        return mCallbacks == null && mCallbackCollection == null;\n    }\n\n    private class FullWidthFixedViewLayout extends FrameLayout {\n        public FullWidthFixedViewLayout(Context context) {\n            super(context);\n        }\n\n        @Override\n        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n            int targetWidth = ObservableGridView.this.getMeasuredWidth()\n                - ObservableGridView.this.getPaddingLeft()\n                - ObservableGridView.this.getPaddingRight();\n            widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth,\n                MeasureSpec.getMode(widthMeasureSpec));\n            super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        }\n    }\n\n    static class SavedState extends BaseSavedState {\n        int prevFirstVisiblePosition;\n        int prevFirstVisibleChildHeight = -1;\n        int prevScrolledChildrenHeight;\n        int prevScrollY;\n        int scrollY;\n        SparseIntArray childrenHeights;\n\n        /**\n         * Called by onSaveInstanceState.\n         */\n        SavedState(Parcelable superState) {\n            super(superState);\n        }\n\n        /**\n         * Called by CREATOR.\n         */\n        private SavedState(Parcel in) {\n            super(in);\n            prevFirstVisiblePosition = in.readInt();\n            prevFirstVisibleChildHeight = in.readInt();\n            prevScrolledChildrenHeight = in.readInt();\n            prevScrollY = in.readInt();\n            scrollY = in.readInt();\n            childrenHeights = new SparseIntArray();\n            final int numOfChildren = in.readInt();\n            if (0 < numOfChildren) {\n                for (int i = 0; i < numOfChildren; i++) {\n                    final int key = in.readInt();\n                    final int value = in.readInt();\n                    childrenHeights.put(key, value);\n                }\n            }\n        }\n\n        @Override\n        public void writeToParcel(Parcel out, int flags) {\n            super.writeToParcel(out, flags);\n            out.writeInt(prevFirstVisiblePosition);\n            out.writeInt(prevFirstVisibleChildHeight);\n            out.writeInt(prevScrolledChildrenHeight);\n            out.writeInt(prevScrollY);\n            out.writeInt(scrollY);\n            final int numOfChildren = childrenHeights == null ? 0 : childrenHeights.size();\n            out.writeInt(numOfChildren);\n            if (0 < numOfChildren) {\n                for (int i = 0; i < numOfChildren; i++) {\n                    out.writeInt(childrenHeights.keyAt(i));\n                    out.writeInt(childrenHeights.valueAt(i));\n                }\n            }\n        }\n\n        public static final Parcelable.Creator<SavedState> CREATOR\n            = new Parcelable.Creator<SavedState>() {\n            @Override\n            public SavedState createFromParcel(Parcel in) {\n                return new SavedState(in);\n            }\n\n            @Override\n            public SavedState[] newArray(int size) {\n                return new SavedState[size];\n            }\n        };\n    }\n\n    private void dispatchOnDownMotionEvent() {\n        if (mCallbacks != null) {\n            mCallbacks.onDownMotionEvent();\n        }\n        if (mCallbackCollection != null) {\n            for (int i = 0; i < mCallbackCollection.size(); i++) {\n                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);\n                callbacks.onDownMotionEvent();\n            }\n        }\n    }\n\n    private void dispatchOnScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        if (mCallbacks != null) {\n            mCallbacks.onScrollChanged(scrollY, firstScroll, dragging);\n        }\n        if (mCallbackCollection != null) {\n            for (int i = 0; i < mCallbackCollection.size(); i++) {\n                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);\n                callbacks.onScrollChanged(scrollY, firstScroll, dragging);\n            }\n        }\n    }\n\n    private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) {\n        if (mCallbacks != null) {\n            mCallbacks.onUpOrCancelMotionEvent(scrollState);\n        }\n        if (mCallbackCollection != null) {\n            for (int i = 0; i < mCallbackCollection.size(); i++) {\n                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);\n                callbacks.onUpOrCancelMotionEvent(scrollState);\n            }\n        }\n    }\n\n    public static class FixedViewInfo {\n        public View view;\n        public ViewGroup viewContainer;\n        public Object data;\n        public boolean isSelectable;\n    }\n\n    public static class HeaderViewGridAdapter implements WrapperListAdapter, Filterable {\n        private final DataSetObservable mDataSetObservable = new DataSetObservable();\n        private final ListAdapter mAdapter;\n        static final ArrayList<FixedViewInfo> EMPTY_INFO_LIST = new ArrayList<>();\n\n        // This ArrayList is assumed to NOT be null.\n        ArrayList<FixedViewInfo> mHeaderViewInfos;\n        ArrayList<FixedViewInfo> mFooterViewInfos;\n        private int mNumColumns = 1;\n        private int mRowHeight = -1;\n        boolean mAreAllFixedViewsSelectable;\n        private final boolean mIsFilterable;\n        private boolean mCachePlaceHoldView = true;\n        // From Recycle Bin or calling getView, this a question...\n        private boolean mCacheFirstHeaderView = false;\n\n        public HeaderViewGridAdapter(ArrayList<FixedViewInfo> headerViewInfos, ArrayList<FixedViewInfo> footViewInfos, ListAdapter adapter) {\n            mAdapter = adapter;\n            mIsFilterable = adapter instanceof Filterable;\n            if (headerViewInfos == null) {\n                mHeaderViewInfos = EMPTY_INFO_LIST;\n            } else {\n                mHeaderViewInfos = headerViewInfos;\n            }\n\n            if (footViewInfos == null) {\n                mFooterViewInfos = EMPTY_INFO_LIST;\n            } else {\n                mFooterViewInfos = footViewInfos;\n            }\n            mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos)\n                && areAllListInfosSelectable(mFooterViewInfos);\n        }\n\n        public int getNumColumns() {\n            return mNumColumns;\n        }\n\n        public void setNumColumns(int numColumns) {\n            if (numColumns < 1) {\n                return;\n            }\n            if (mNumColumns != numColumns) {\n                mNumColumns = numColumns;\n                notifyDataSetChanged();\n            }\n        }\n\n        public void setRowHeight(int height) {\n            mRowHeight = height;\n        }\n\n        public int getHeadersCount() {\n            return mHeaderViewInfos.size();\n        }\n\n        public int getFootersCount() {\n            return mFooterViewInfos.size();\n        }\n\n        /**\n         * @return True if this adapter doesn't contain any data.  This is used to determine\n         * whether the empty view should be displayed.  A typical implementation will return\n         * getCount() == 0 but since getCount() includes the headers and footers, specialized\n         * adapters might want a different behavior.\n         */\n        @Override\n        public boolean isEmpty() {\n            return (mAdapter == null || mAdapter.isEmpty());\n        }\n\n        private boolean areAllListInfosSelectable(ArrayList<FixedViewInfo> infos) {\n            if (infos != null) {\n                for (FixedViewInfo info : infos) {\n                    if (!info.isSelectable) {\n                        return false;\n                    }\n                }\n            }\n            return true;\n        }\n\n        public boolean removeHeader(View v) {\n            for (int i = 0; i < mHeaderViewInfos.size(); i++) {\n                FixedViewInfo info = mHeaderViewInfos.get(i);\n                if (info.view == v) {\n                    mHeaderViewInfos.remove(i);\n                    mAreAllFixedViewsSelectable =\n                        areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos);\n                    mDataSetObservable.notifyChanged();\n                    return true;\n                }\n            }\n            return false;\n        }\n\n        public boolean removeFooter(View v) {\n            for (int i = 0; i < mFooterViewInfos.size(); i++) {\n                FixedViewInfo info = mFooterViewInfos.get(i);\n                if (info.view == v) {\n                    mFooterViewInfos.remove(i);\n                    mAreAllFixedViewsSelectable =\n                        areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos);\n                    mDataSetObservable.notifyChanged();\n                    return true;\n                }\n            }\n            return false;\n        }\n\n        @Override\n        public int getCount() {\n            if (mAdapter != null) {\n                return (getFootersCount() + getHeadersCount()) * mNumColumns + getAdapterAndPlaceHolderCount();\n            } else {\n                return (getFootersCount() + getHeadersCount()) * mNumColumns;\n            }\n        }\n\n        @Override\n        public boolean areAllItemsEnabled() {\n            return mAdapter == null || mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();\n        }\n\n        private int getAdapterAndPlaceHolderCount() {\n            return (int) (Math.ceil(1f * mAdapter.getCount() / mNumColumns) * mNumColumns);\n        }\n\n        @Override\n        public boolean isEnabled(int position) {\n            // Header (negative positions will throw an IndexOutOfBoundsException)\n            int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;\n            if (position < numHeadersAndPlaceholders) {\n                return position % mNumColumns == 0\n                    && mHeaderViewInfos.get(position / mNumColumns).isSelectable;\n            }\n\n            // Adapter\n            final int adjPosition = position - numHeadersAndPlaceholders;\n            int adapterCount = 0;\n            if (mAdapter != null) {\n                adapterCount = getAdapterAndPlaceHolderCount();\n                if (adjPosition < adapterCount) {\n                    return adjPosition < mAdapter.getCount() && mAdapter.isEnabled(adjPosition);\n                }\n            }\n\n            // Footer (off-limits positions will throw an IndexOutOfBoundsException)\n            final int footerPosition = adjPosition - adapterCount;\n            return footerPosition % mNumColumns == 0\n                && mFooterViewInfos.get(footerPosition / mNumColumns).isSelectable;\n        }\n\n        @Override\n        public Object getItem(int position) {\n            // Header (negative positions will throw an ArrayIndexOutOfBoundsException)\n            int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;\n            if (position < numHeadersAndPlaceholders) {\n                if (position % mNumColumns == 0) {\n                    return mHeaderViewInfos.get(position / mNumColumns).data;\n                }\n                return null;\n            }\n\n            // Adapter\n            final int adjPosition = position - numHeadersAndPlaceholders;\n            int adapterCount = 0;\n            if (mAdapter != null) {\n                adapterCount = getAdapterAndPlaceHolderCount();\n                if (adjPosition < adapterCount) {\n                    if (adjPosition < mAdapter.getCount()) {\n                        return mAdapter.getItem(adjPosition);\n                    } else {\n                        return null;\n                    }\n                }\n            }\n\n            // Footer (off-limits positions will throw an IndexOutOfBoundsException)\n            final int footerPosition = adjPosition - adapterCount;\n            if (footerPosition % mNumColumns == 0) {\n                return mFooterViewInfos.get(footerPosition).data;\n            } else {\n                return null;\n            }\n        }\n\n        @Override\n        public long getItemId(int position) {\n            int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;\n            if (mAdapter != null && position >= numHeadersAndPlaceholders) {\n                int adjPosition = position - numHeadersAndPlaceholders;\n                int adapterCount = mAdapter.getCount();\n                if (adjPosition < adapterCount) {\n                    return mAdapter.getItemId(adjPosition);\n                }\n            }\n            return -1;\n        }\n\n        @Override\n        public boolean hasStableIds() {\n            return mAdapter != null && mAdapter.hasStableIds();\n        }\n\n        @Override\n        public View getView(int position, View convertView, ViewGroup parent) {\n\n            // Header (negative positions will throw an ArrayIndexOutOfBoundsException)\n            int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;\n            if (position < numHeadersAndPlaceholders) {\n                View headerViewContainer = mHeaderViewInfos\n                    .get(position / mNumColumns).viewContainer;\n                if (position % mNumColumns == 0) {\n                    return headerViewContainer;\n                } else {\n                    if (convertView == null) {\n                        convertView = new View(parent.getContext());\n                    }\n                    // We need to do this because GridView uses the height of the last item\n                    // in a row to determine the height for the entire row.\n                    convertView.setVisibility(View.INVISIBLE);\n                    convertView.setMinimumHeight(headerViewContainer.getHeight());\n                    return convertView;\n                }\n            }\n            // Adapter\n            final int adjPosition = position - numHeadersAndPlaceholders;\n            int adapterCount = 0;\n            if (mAdapter != null) {\n                adapterCount = getAdapterAndPlaceHolderCount();\n                if (adjPosition < adapterCount) {\n                    if (adjPosition < mAdapter.getCount()) {\n                        return mAdapter.getView(adjPosition, convertView, parent);\n                    } else {\n                        if (convertView == null) {\n                            convertView = new View(parent.getContext());\n                        }\n                        convertView.setVisibility(View.INVISIBLE);\n                        convertView.setMinimumHeight(mRowHeight);\n                        return convertView;\n                    }\n                }\n            }\n            // Footer\n            final int footerPosition = adjPosition - adapterCount;\n            if (footerPosition < getCount()) {\n                View footViewContainer = mFooterViewInfos\n                    .get(footerPosition / mNumColumns).viewContainer;\n                if (position % mNumColumns == 0) {\n                    return footViewContainer;\n                } else {\n                    if (convertView == null) {\n                        convertView = new View(parent.getContext());\n                    }\n                    // We need to do this because GridView uses the height of the last item\n                    // in a row to determine the height for the entire row.\n                    convertView.setVisibility(View.INVISIBLE);\n                    convertView.setMinimumHeight(footViewContainer.getHeight());\n                    return convertView;\n                }\n            }\n            throw new ArrayIndexOutOfBoundsException(position);\n        }\n\n        @Override\n        public int getItemViewType(int position) {\n\n            final int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;\n            final int adapterViewTypeStart = mAdapter == null ? 0 : mAdapter.getViewTypeCount() - 1;\n            int type = AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;\n            if (mCachePlaceHoldView) {\n                // Header\n                if (position < numHeadersAndPlaceholders) {\n                    if (position == 0) {\n                        if (mCacheFirstHeaderView) {\n                            type = adapterViewTypeStart + mHeaderViewInfos.size() + mFooterViewInfos.size() + 1 + 1;\n                        }\n                    }\n                    if (position % mNumColumns != 0) {\n                        type = adapterViewTypeStart + (position / mNumColumns + 1);\n                    }\n                }\n            }\n\n            // Adapter\n            final int adjPosition = position - numHeadersAndPlaceholders;\n            int adapterCount = 0;\n            if (mAdapter != null) {\n                adapterCount = getAdapterAndPlaceHolderCount();\n                if (adjPosition >= 0 && adjPosition < adapterCount) {\n                    if (adjPosition < mAdapter.getCount()) {\n                        type = mAdapter.getItemViewType(adjPosition);\n                    } else {\n                        if (mCachePlaceHoldView) {\n                            type = adapterViewTypeStart + mHeaderViewInfos.size() + 1;\n                        }\n                    }\n                }\n            }\n\n            if (mCachePlaceHoldView) {\n                // Footer\n                final int footerPosition = adjPosition - adapterCount;\n                if (footerPosition >= 0 && footerPosition < getCount() && (footerPosition % mNumColumns) != 0) {\n                    type = adapterViewTypeStart + mHeaderViewInfos.size() + 1 + (footerPosition / mNumColumns + 1);\n                }\n            }\n\n            return type;\n        }\n\n        /**\n         * Content view, content view holder, header[0], header and footer placeholder(s).\n         */\n        @Override\n        public int getViewTypeCount() {\n            int count = mAdapter == null ? 1 : mAdapter.getViewTypeCount();\n            if (mCachePlaceHoldView) {\n                int offset = mHeaderViewInfos.size() + 1 + mFooterViewInfos.size();\n                if (mCacheFirstHeaderView) {\n                    offset += 1;\n                }\n                count += offset;\n            }\n\n            return count;\n        }\n\n        @Override\n        public void registerDataSetObserver(DataSetObserver observer) {\n            mDataSetObservable.registerObserver(observer);\n            if (mAdapter != null) {\n                mAdapter.registerDataSetObserver(observer);\n            }\n        }\n\n        @Override\n        public void unregisterDataSetObserver(DataSetObserver observer) {\n            mDataSetObservable.unregisterObserver(observer);\n            if (mAdapter != null) {\n                mAdapter.unregisterDataSetObserver(observer);\n            }\n        }\n\n        @Override\n        public Filter getFilter() {\n            if (mIsFilterable) {\n                return ((Filterable) mAdapter).getFilter();\n            }\n            return null;\n        }\n\n        @Override\n        public ListAdapter getWrappedAdapter() {\n            return mAdapter;\n        }\n\n        public void notifyDataSetChanged() {\n            mDataSetObservable.notifyChanged();\n        }\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview;\n\nimport android.content.Context;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.util.AttributeSet;\nimport android.util.SparseIntArray;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AbsListView;\nimport android.widget.ListView;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * ListView that its scroll position can be observed.\n */\npublic class ObservableListView extends ListView implements Scrollable {\n\n    // Fields that should be saved onSaveInstanceState\n    private int mPrevFirstVisiblePosition;\n    private int mPrevFirstVisibleChildHeight = -1;\n    private int mPrevScrolledChildrenHeight;\n    private int mPrevScrollY;\n    private int mScrollY;\n    private SparseIntArray mChildrenHeights;\n\n    // Fields that don't need to be saved onSaveInstanceState\n    private ObservableScrollViewCallbacks mCallbacks;\n    private List<ObservableScrollViewCallbacks> mCallbackCollection;\n    private ScrollState mScrollState;\n    private boolean mFirstScroll;\n    private boolean mDragging;\n    private boolean mIntercepted;\n    private MotionEvent mPrevMoveEvent;\n    private ViewGroup mTouchInterceptionViewGroup;\n\n    private OnScrollListener mOriginalScrollListener;\n    private OnScrollListener mScrollListener = new OnScrollListener() {\n        @Override\n        public void onScrollStateChanged(AbsListView view, int scrollState) {\n            if (mOriginalScrollListener != null) {\n                mOriginalScrollListener.onScrollStateChanged(view, scrollState);\n            }\n        }\n\n        @Override\n        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {\n            if (mOriginalScrollListener != null) {\n                mOriginalScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);\n            }\n            // AbsListView#invokeOnItemScrollListener calls onScrollChanged(0, 0, 0, 0)\n            // on Android 4.0+, but Android 2.3 is not. (Android 3.0 is unknown)\n            // So call it with onScrollListener.\n            onScrollChanged();\n        }\n    };\n\n    public ObservableListView(Context context) {\n        super(context);\n        init();\n    }\n\n    public ObservableListView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public ObservableListView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        init();\n    }\n\n    @Override\n    public void onRestoreInstanceState(Parcelable state) {\n        SavedState ss = (SavedState) state;\n        mPrevFirstVisiblePosition = ss.prevFirstVisiblePosition;\n        mPrevFirstVisibleChildHeight = ss.prevFirstVisibleChildHeight;\n        mPrevScrolledChildrenHeight = ss.prevScrolledChildrenHeight;\n        mPrevScrollY = ss.prevScrollY;\n        mScrollY = ss.scrollY;\n        mChildrenHeights = ss.childrenHeights;\n        super.onRestoreInstanceState(ss.getSuperState());\n    }\n\n    @Override\n    public Parcelable onSaveInstanceState() {\n        Parcelable superState = super.onSaveInstanceState();\n        SavedState ss = new SavedState(superState);\n        ss.prevFirstVisiblePosition = mPrevFirstVisiblePosition;\n        ss.prevFirstVisibleChildHeight = mPrevFirstVisibleChildHeight;\n        ss.prevScrolledChildrenHeight = mPrevScrolledChildrenHeight;\n        ss.prevScrollY = mPrevScrollY;\n        ss.scrollY = mScrollY;\n        ss.childrenHeights = mChildrenHeights;\n        return ss;\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        if (hasNoCallbacks()) {\n            return super.onInterceptTouchEvent(ev);\n        }\n        switch (ev.getActionMasked()) {\n            case MotionEvent.ACTION_DOWN:\n                // Whether or not motion events are consumed by children,\n                // flag initializations which are related to ACTION_DOWN events should be executed.\n                // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are\n                // passed to parent (this view), the flags will be invalid.\n                // Also, applications might implement initialization codes to onDownMotionEvent,\n                // so call it here.\n                mFirstScroll = mDragging = true;\n                dispatchOnDownMotionEvent();\n                break;\n        }\n        return super.onInterceptTouchEvent(ev);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        if (hasNoCallbacks()) {\n            return super.onTouchEvent(ev);\n        }\n        switch (ev.getActionMasked()) {\n            case MotionEvent.ACTION_UP:\n            case MotionEvent.ACTION_CANCEL:\n                mIntercepted = false;\n                mDragging = false;\n                dispatchOnUpOrCancelMotionEvent(mScrollState);\n                break;\n            case MotionEvent.ACTION_MOVE:\n                if (mPrevMoveEvent == null) {\n                    mPrevMoveEvent = ev;\n                }\n                float diffY = ev.getY() - mPrevMoveEvent.getY();\n                mPrevMoveEvent = MotionEvent.obtainNoHistory(ev);\n                if (getCurrentScrollY() - diffY <= 0) {\n                    // Can't scroll anymore.\n\n                    if (mIntercepted) {\n                        // Already dispatched ACTION_DOWN event to parents, so stop here.\n                        return false;\n                    }\n\n                    // Apps can set the interception target other than the direct parent.\n                    final ViewGroup parent;\n                    if (mTouchInterceptionViewGroup == null) {\n                        parent = (ViewGroup) getParent();\n                    } else {\n                        parent = mTouchInterceptionViewGroup;\n                    }\n\n                    // Get offset to parents. If the parent is not the direct parent,\n                    // we should aggregate offsets from all of the parents.\n                    float offsetX = 0;\n                    float offsetY = 0;\n                    for (View v = this; v != null && v != parent; ) {\n                        offsetX += v.getLeft() - v.getScrollX();\n                        offsetY += v.getTop() - v.getScrollY();\n                        try {\n                            v = (View) v.getParent();\n                        } catch (ClassCastException ex) {\n                            break;\n                        }\n                    }\n                    final MotionEvent event = MotionEvent.obtainNoHistory(ev);\n                    event.offsetLocation(offsetX, offsetY);\n\n                    if (parent.onInterceptTouchEvent(event)) {\n                        mIntercepted = true;\n\n                        // If the parent wants to intercept ACTION_MOVE events,\n                        // we pass ACTION_DOWN event to the parent\n                        // as if these touch events just have began now.\n                        event.setAction(MotionEvent.ACTION_DOWN);\n\n                        // Return this onTouchEvent() first and set ACTION_DOWN event for parent\n                        // to the queue, to keep events sequence.\n                        post(new Runnable() {\n                            @Override\n                            public void run() {\n                                parent.dispatchTouchEvent(event);\n                            }\n                        });\n                        return false;\n                    }\n                    // Even when this can't be scrolled anymore,\n                    // simply returning false here may cause subView's click,\n                    // so delegate it to super.\n                    return super.onTouchEvent(ev);\n                }\n                break;\n        }\n        return super.onTouchEvent(ev);\n    }\n\n    @Override\n    public void setOnScrollListener(OnScrollListener l) {\n        // Don't set l to super.setOnScrollListener().\n        // l receives all events through mScrollListener.\n        mOriginalScrollListener = l;\n    }\n\n    @Override\n    public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        mCallbacks = listener;\n    }\n\n    @Override\n    public void addScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        if (mCallbackCollection == null) {\n            mCallbackCollection = new ArrayList<>();\n        }\n        mCallbackCollection.add(listener);\n    }\n\n    @Override\n    public void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        if (mCallbackCollection != null) {\n            mCallbackCollection.remove(listener);\n        }\n    }\n\n    @Override\n    public void clearScrollViewCallbacks() {\n        if (mCallbackCollection != null) {\n            mCallbackCollection.clear();\n        }\n    }\n\n    @Override\n    public void setTouchInterceptionViewGroup(ViewGroup viewGroup) {\n        mTouchInterceptionViewGroup = viewGroup;\n    }\n\n    @Override\n    public void scrollVerticallyTo(int y) {\n        View firstVisibleChild = getChildAt(0);\n        if (firstVisibleChild != null) {\n            int baseHeight = firstVisibleChild.getHeight();\n            int position = y / baseHeight;\n            setSelection(position);\n        }\n    }\n\n    @Override\n    public int getCurrentScrollY() {\n        return mScrollY;\n    }\n\n    private void init() {\n        mChildrenHeights = new SparseIntArray();\n        super.setOnScrollListener(mScrollListener);\n    }\n\n    private void onScrollChanged() {\n        if (hasNoCallbacks()) {\n            return;\n        }\n        if (getChildCount() > 0) {\n            int firstVisiblePosition = getFirstVisiblePosition();\n            for (int i = getFirstVisiblePosition(), j = 0; i <= getLastVisiblePosition(); i++, j++) {\n                if (mChildrenHeights.indexOfKey(i) < 0 || getChildAt(j).getHeight() != mChildrenHeights.get(i)) {\n                    mChildrenHeights.put(i, getChildAt(j).getHeight());\n                }\n            }\n\n            View firstVisibleChild = getChildAt(0);\n            if (firstVisibleChild != null) {\n                if (mPrevFirstVisiblePosition < firstVisiblePosition) {\n                    // scroll down\n                    int skippedChildrenHeight = 0;\n                    if (firstVisiblePosition - mPrevFirstVisiblePosition != 1) {\n                        for (int i = firstVisiblePosition - 1; i > mPrevFirstVisiblePosition; i--) {\n                            if (0 < mChildrenHeights.indexOfKey(i)) {\n                                skippedChildrenHeight += mChildrenHeights.get(i);\n                            } else {\n                                // Approximate each item's height to the first visible child.\n                                // It may be incorrect, but without this, scrollY will be broken\n                                // when scrolling from the bottom.\n                                skippedChildrenHeight += firstVisibleChild.getHeight();\n                            }\n                        }\n                    }\n                    mPrevScrolledChildrenHeight += mPrevFirstVisibleChildHeight + skippedChildrenHeight;\n                    mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight();\n                } else if (firstVisiblePosition < mPrevFirstVisiblePosition) {\n                    // scroll up\n                    int skippedChildrenHeight = 0;\n                    if (mPrevFirstVisiblePosition - firstVisiblePosition != 1) {\n                        for (int i = mPrevFirstVisiblePosition - 1; i > firstVisiblePosition; i--) {\n                            if (0 < mChildrenHeights.indexOfKey(i)) {\n                                skippedChildrenHeight += mChildrenHeights.get(i);\n                            } else {\n                                // Approximate each item's height to the first visible child.\n                                // It may be incorrect, but without this, scrollY will be broken\n                                // when scrolling from the bottom.\n                                skippedChildrenHeight += firstVisibleChild.getHeight();\n                            }\n                        }\n                    }\n                    mPrevScrolledChildrenHeight -= firstVisibleChild.getHeight() + skippedChildrenHeight;\n                    mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight();\n                } else if (firstVisiblePosition == 0) {\n                    mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight();\n                    mPrevScrolledChildrenHeight = 0;\n                }\n                if (mPrevFirstVisibleChildHeight < 0) {\n                    mPrevFirstVisibleChildHeight = 0;\n                }\n                mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop() +\n                            firstVisiblePosition * getDividerHeight() + getPaddingTop();\n                mPrevFirstVisiblePosition = firstVisiblePosition;\n\n                dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging);\n                if (mFirstScroll) {\n                    mFirstScroll = false;\n                }\n\n                if (mPrevScrollY < mScrollY) {\n                    mScrollState = ScrollState.UP;\n                } else if (mScrollY < mPrevScrollY) {\n                    mScrollState = ScrollState.DOWN;\n                } else {\n                    mScrollState = ScrollState.STOP;\n                }\n                mPrevScrollY = mScrollY;\n            }\n        }\n    }\n\n    private void dispatchOnDownMotionEvent() {\n        if (mCallbacks != null) {\n            mCallbacks.onDownMotionEvent();\n        }\n        if (mCallbackCollection != null) {\n            for (int i = 0; i < mCallbackCollection.size(); i++) {\n                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);\n                callbacks.onDownMotionEvent();\n            }\n        }\n    }\n\n    private void dispatchOnScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        if (mCallbacks != null) {\n            mCallbacks.onScrollChanged(scrollY, firstScroll, dragging);\n        }\n        if (mCallbackCollection != null) {\n            for (int i = 0; i < mCallbackCollection.size(); i++) {\n                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);\n                callbacks.onScrollChanged(scrollY, firstScroll, dragging);\n            }\n        }\n    }\n\n    private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) {\n        if (mCallbacks != null) {\n            mCallbacks.onUpOrCancelMotionEvent(scrollState);\n        }\n        if (mCallbackCollection != null) {\n            for (int i = 0; i < mCallbackCollection.size(); i++) {\n                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);\n                callbacks.onUpOrCancelMotionEvent(scrollState);\n            }\n        }\n    }\n\n    private boolean hasNoCallbacks() {\n        return mCallbacks == null && mCallbackCollection == null;\n    }\n\n    static class SavedState extends BaseSavedState {\n        int prevFirstVisiblePosition;\n        int prevFirstVisibleChildHeight = -1;\n        int prevScrolledChildrenHeight;\n        int prevScrollY;\n        int scrollY;\n        SparseIntArray childrenHeights;\n\n        /**\n         * Called by onSaveInstanceState.\n         */\n        SavedState(Parcelable superState) {\n            super(superState);\n        }\n\n        /**\n         * Called by CREATOR.\n         */\n        private SavedState(Parcel in) {\n            super(in);\n            prevFirstVisiblePosition = in.readInt();\n            prevFirstVisibleChildHeight = in.readInt();\n            prevScrolledChildrenHeight = in.readInt();\n            prevScrollY = in.readInt();\n            scrollY = in.readInt();\n            childrenHeights = new SparseIntArray();\n            final int numOfChildren = in.readInt();\n            if (0 < numOfChildren) {\n                for (int i = 0; i < numOfChildren; i++) {\n                    final int key = in.readInt();\n                    final int value = in.readInt();\n                    childrenHeights.put(key, value);\n                }\n            }\n        }\n\n        @Override\n        public void writeToParcel(Parcel out, int flags) {\n            super.writeToParcel(out, flags);\n            out.writeInt(prevFirstVisiblePosition);\n            out.writeInt(prevFirstVisibleChildHeight);\n            out.writeInt(prevScrolledChildrenHeight);\n            out.writeInt(prevScrollY);\n            out.writeInt(scrollY);\n            final int numOfChildren = childrenHeights == null ? 0 : childrenHeights.size();\n            out.writeInt(numOfChildren);\n            if (0 < numOfChildren) {\n                for (int i = 0; i < numOfChildren; i++) {\n                    out.writeInt(childrenHeights.keyAt(i));\n                    out.writeInt(childrenHeights.valueAt(i));\n                }\n            }\n        }\n\n        public static final Parcelable.Creator<SavedState> CREATOR\n            = new Parcelable.Creator<SavedState>() {\n            @Override\n            public SavedState createFromParcel(Parcel in) {\n                return new SavedState(in);\n            }\n\n            @Override\n            public SavedState[] newArray(int size) {\n                return new SavedState[size];\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview;\n\nimport android.content.Context;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.AttributeSet;\nimport android.util.SparseIntArray;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * RecyclerView that its scroll position can be observed.\n * Before using this, please consider to use the RecyclerView.OnScrollListener\n * provided by the support library officially.\n */\npublic class ObservableRecyclerView extends RecyclerView implements Scrollable {\n\n    private static int recyclerViewLibraryVersion = 22;\n\n    // Fields that should be saved onSaveInstanceState\n    private int mPrevFirstVisiblePosition;\n    private int mPrevFirstVisibleChildHeight = -1;\n    private int mPrevScrolledChildrenHeight;\n    private int mPrevScrollY;\n    private int mScrollY;\n    private SparseIntArray mChildrenHeights;\n\n    // Fields that don't need to be saved onSaveInstanceState\n    private ObservableScrollViewCallbacks mCallbacks;\n    private List<ObservableScrollViewCallbacks> mCallbackCollection;\n    private ScrollState mScrollState;\n    private boolean mFirstScroll;\n    private boolean mDragging;\n    private boolean mIntercepted;\n    private MotionEvent mPrevMoveEvent;\n    private ViewGroup mTouchInterceptionViewGroup;\n\n    public ObservableRecyclerView(Context context) {\n        super(context);\n        init();\n    }\n\n    public ObservableRecyclerView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public ObservableRecyclerView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        init();\n    }\n\n    @Override\n    public void onRestoreInstanceState(Parcelable state) {\n        SavedState ss = (SavedState) state;\n        mPrevFirstVisiblePosition = ss.prevFirstVisiblePosition;\n        mPrevFirstVisibleChildHeight = ss.prevFirstVisibleChildHeight;\n        mPrevScrolledChildrenHeight = ss.prevScrolledChildrenHeight;\n        mPrevScrollY = ss.prevScrollY;\n        mScrollY = ss.scrollY;\n        mChildrenHeights = ss.childrenHeights;\n        super.onRestoreInstanceState(ss.getSuperState());\n    }\n\n    @Override\n    public Parcelable onSaveInstanceState() {\n        Parcelable superState = super.onSaveInstanceState();\n        SavedState ss = new SavedState(superState);\n        ss.prevFirstVisiblePosition = mPrevFirstVisiblePosition;\n        ss.prevFirstVisibleChildHeight = mPrevFirstVisibleChildHeight;\n        ss.prevScrolledChildrenHeight = mPrevScrolledChildrenHeight;\n        ss.prevScrollY = mPrevScrollY;\n        ss.scrollY = mScrollY;\n        ss.childrenHeights = mChildrenHeights;\n        return ss;\n    }\n\n    @Override\n    protected void onScrollChanged(int l, int t, int oldl, int oldt) {\n        super.onScrollChanged(l, t, oldl, oldt);\n        if (hasNoCallbacks()) {\n            return;\n        }\n        if (getChildCount() > 0) {\n            int firstVisiblePosition = getChildAdapterPosition(getChildAt(0));\n            int lastVisiblePosition = getChildAdapterPosition(getChildAt(getChildCount() - 1));\n            for (int i = firstVisiblePosition, j = 0; i <= lastVisiblePosition; i++, j++) {\n                int childHeight = 0;\n                View child = getChildAt(j);\n                if (child != null) {\n                    if (mChildrenHeights.indexOfKey(i) < 0 || (child.getHeight() != mChildrenHeights.get(i))) {\n                        childHeight = child.getHeight();\n                    }\n                }\n                mChildrenHeights.put(i, childHeight);\n            }\n\n            View firstVisibleChild = getChildAt(0);\n            if (firstVisibleChild != null) {\n                if (mPrevFirstVisiblePosition < firstVisiblePosition) {\n                    // scroll down\n                    int skippedChildrenHeight = 0;\n                    if (firstVisiblePosition - mPrevFirstVisiblePosition != 1) {\n                        for (int i = firstVisiblePosition - 1; i > mPrevFirstVisiblePosition; i--) {\n                            if (0 < mChildrenHeights.indexOfKey(i)) {\n                                skippedChildrenHeight += mChildrenHeights.get(i);\n                            } else {\n                                // Approximate each item's height to the first visible child.\n                                // It may be incorrect, but without this, scrollY will be broken\n                                // when scrolling from the bottom.\n                                skippedChildrenHeight += firstVisibleChild.getHeight();\n                            }\n                        }\n                    }\n                    mPrevScrolledChildrenHeight += mPrevFirstVisibleChildHeight + skippedChildrenHeight;\n                    mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight();\n                } else if (firstVisiblePosition < mPrevFirstVisiblePosition) {\n                    // scroll up\n                    int skippedChildrenHeight = 0;\n                    if (mPrevFirstVisiblePosition - firstVisiblePosition != 1) {\n                        for (int i = mPrevFirstVisiblePosition - 1; i > firstVisiblePosition; i--) {\n                            if (0 < mChildrenHeights.indexOfKey(i)) {\n                                skippedChildrenHeight += mChildrenHeights.get(i);\n                            } else {\n                                // Approximate each item's height to the first visible child.\n                                // It may be incorrect, but without this, scrollY will be broken\n                                // when scrolling from the bottom.\n                                skippedChildrenHeight += firstVisibleChild.getHeight();\n                            }\n                        }\n                    }\n                    mPrevScrolledChildrenHeight -= firstVisibleChild.getHeight() + skippedChildrenHeight;\n                    mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight();\n                } else if (firstVisiblePosition == 0) {\n                    mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight();\n                    mPrevScrolledChildrenHeight = 0;\n                }\n                if (mPrevFirstVisibleChildHeight < 0) {\n                    mPrevFirstVisibleChildHeight = 0;\n                }\n                mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop() + getPaddingTop();\n                mPrevFirstVisiblePosition = firstVisiblePosition;\n\n                dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging);\n                if (mFirstScroll) {\n                    mFirstScroll = false;\n                }\n\n                if (mPrevScrollY < mScrollY) {\n                    //down\n                    mScrollState = ScrollState.UP;\n                } else if (mScrollY < mPrevScrollY) {\n                    //up\n                    mScrollState = ScrollState.DOWN;\n                } else {\n                    mScrollState = ScrollState.STOP;\n                }\n                mPrevScrollY = mScrollY;\n            }\n        }\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        if (hasNoCallbacks()) {\n            return super.onInterceptTouchEvent(ev);\n        }\n        switch (ev.getActionMasked()) {\n            case MotionEvent.ACTION_DOWN:\n                // Whether or not motion events are consumed by children,\n                // flag initializations which are related to ACTION_DOWN events should be executed.\n                // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are\n                // passed to parent (this view), the flags will be invalid.\n                // Also, applications might implement initialization codes to onDownMotionEvent,\n                // so call it here.\n                mFirstScroll = mDragging = true;\n                dispatchOnDownMotionEvent();\n                break;\n        }\n        return super.onInterceptTouchEvent(ev);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        if (hasNoCallbacks()) {\n            return super.onTouchEvent(ev);\n        }\n        switch (ev.getActionMasked()) {\n            case MotionEvent.ACTION_UP:\n            case MotionEvent.ACTION_CANCEL:\n                mIntercepted = false;\n                mDragging = false;\n                dispatchOnUpOrCancelMotionEvent(mScrollState);\n                break;\n            case MotionEvent.ACTION_MOVE:\n                if (mPrevMoveEvent == null) {\n                    mPrevMoveEvent = ev;\n                }\n                float diffY = ev.getY() - mPrevMoveEvent.getY();\n                mPrevMoveEvent = MotionEvent.obtainNoHistory(ev);\n                if (getCurrentScrollY() - diffY <= 0) {\n                    // Can't scroll anymore.\n\n                    if (mIntercepted) {\n                        // Already dispatched ACTION_DOWN event to parents, so stop here.\n                        return false;\n                    }\n\n                    // Apps can set the interception target other than the direct parent.\n                    final ViewGroup parent;\n                    if (mTouchInterceptionViewGroup == null) {\n                        parent = (ViewGroup) getParent();\n                    } else {\n                        parent = mTouchInterceptionViewGroup;\n                    }\n\n                    // Get offset to parents. If the parent is not the direct parent,\n                    // we should aggregate offsets from all of the parents.\n                    float offsetX = 0;\n                    float offsetY = 0;\n                    for (View v = this; v != null && v != parent; v = (View) v.getParent()) {\n                        offsetX += v.getLeft() - v.getScrollX();\n                        offsetY += v.getTop() - v.getScrollY();\n                    }\n                    final MotionEvent event = MotionEvent.obtainNoHistory(ev);\n                    event.offsetLocation(offsetX, offsetY);\n\n                    if (parent.onInterceptTouchEvent(event)) {\n                        mIntercepted = true;\n\n                        // If the parent wants to intercept ACTION_MOVE events,\n                        // we pass ACTION_DOWN event to the parent\n                        // as if these touch events just have began now.\n                        event.setAction(MotionEvent.ACTION_DOWN);\n\n                        // Return this onTouchEvent() first and set ACTION_DOWN event for parent\n                        // to the queue, to keep events sequence.\n                        post(new Runnable() {\n                            @Override\n                            public void run() {\n                                parent.dispatchTouchEvent(event);\n                            }\n                        });\n                        return false;\n                    }\n                    // Even when this can't be scrolled anymore,\n                    // simply returning false here may cause subView's click,\n                    // so delegate it to super.\n                    return super.onTouchEvent(ev);\n                }\n                break;\n        }\n        return super.onTouchEvent(ev);\n    }\n\n    @Override\n    public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        mCallbacks = listener;\n    }\n\n    @Override\n    public void addScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        if (mCallbackCollection == null) {\n            mCallbackCollection = new ArrayList<>();\n        }\n        mCallbackCollection.add(listener);\n    }\n\n    @Override\n    public void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        if (mCallbackCollection != null) {\n            mCallbackCollection.remove(listener);\n        }\n    }\n\n    @Override\n    public void clearScrollViewCallbacks() {\n        if (mCallbackCollection != null) {\n            mCallbackCollection.clear();\n        }\n    }\n\n    @Override\n    public void setTouchInterceptionViewGroup(ViewGroup viewGroup) {\n        mTouchInterceptionViewGroup = viewGroup;\n    }\n\n    @Override\n    public void scrollVerticallyTo(int y) {\n        View firstVisibleChild = getChildAt(0);\n        if (firstVisibleChild != null) {\n            int baseHeight = firstVisibleChild.getHeight();\n            int position = y / baseHeight;\n            scrollVerticallyToPosition(position);\n        }\n    }\n\n    /**\n     * <p>Same as {@linkplain #scrollToPosition(int)} but it scrolls to the position not only make\n     * the position visible.</p>\n     * <p>It depends on {@code LayoutManager} how {@linkplain #scrollToPosition(int)} works,\n     * and currently we know that {@linkplain LinearLayoutManager#scrollToPosition(int)} just\n     * make the position visible.</p>\n     * <p>In LinearLayoutManager, scrollToPositionWithOffset() is provided for scrolling to the position.\n     * This method checks which LayoutManager is set,\n     * and handles which method should be called for scrolling.</p>\n     * <p>Other know classes (StaggeredGridLayoutManager and GridLayoutManager) are not tested.</p>\n     *\n     * @param position Position to scroll.\n     */\n    public void scrollVerticallyToPosition(int position) {\n        LayoutManager lm = getLayoutManager();\n\n        if (lm != null && lm instanceof LinearLayoutManager) {\n            ((LinearLayoutManager) lm).scrollToPositionWithOffset(position, 0);\n        } else {\n            scrollToPosition(position);\n        }\n    }\n\n    @Override\n    public int getCurrentScrollY() {\n        return mScrollY;\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public int getChildAdapterPosition(View child) {\n        if (22 <= recyclerViewLibraryVersion) {\n            return super.getChildAdapterPosition(child);\n        }\n        return getChildPosition(child);\n    }\n\n    private void init() {\n        mChildrenHeights = new SparseIntArray();\n        checkLibraryVersion();\n    }\n\n    private void checkLibraryVersion() {\n        try {\n            super.getChildAdapterPosition(null);\n        } catch (NoSuchMethodError e) {\n            recyclerViewLibraryVersion = 21;\n        }\n    }\n\n    private void dispatchOnDownMotionEvent() {\n        if (mCallbacks != null) {\n            mCallbacks.onDownMotionEvent();\n        }\n        if (mCallbackCollection != null) {\n            for (int i = 0; i < mCallbackCollection.size(); i++) {\n                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);\n                callbacks.onDownMotionEvent();\n            }\n        }\n    }\n\n    private void dispatchOnScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        if (mCallbacks != null) {\n            mCallbacks.onScrollChanged(scrollY, firstScroll, dragging);\n        }\n        if (mCallbackCollection != null) {\n            for (int i = 0; i < mCallbackCollection.size(); i++) {\n                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);\n                callbacks.onScrollChanged(scrollY, firstScroll, dragging);\n            }\n        }\n    }\n\n    private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) {\n        if (mCallbacks != null) {\n            mCallbacks.onUpOrCancelMotionEvent(scrollState);\n        }\n        if (mCallbackCollection != null) {\n            for (int i = 0; i < mCallbackCollection.size(); i++) {\n                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);\n                callbacks.onUpOrCancelMotionEvent(scrollState);\n            }\n        }\n    }\n\n    private boolean hasNoCallbacks() {\n        return mCallbacks == null && mCallbackCollection == null;\n    }\n\n    /**\n     * This saved state class is a Parcelable and should not extend\n     * {@link android.view.View.BaseSavedState} nor {@link android.view.AbsSavedState}\n     * because its super class AbsSavedState's constructor\n     * {@link android.view.AbsSavedState#AbsSavedState(Parcel)} currently passes null\n     * as a class loader to read its superstate from Parcelable.\n     * This causes {@link android.os.BadParcelableException} when restoring saved states.\n     * <p/>\n     * The super class \"RecyclerView\" is a part of the support library,\n     * and restoring its saved state requires the class loader that loaded the RecyclerView.\n     * It seems that the class loader is not required when restoring from RecyclerView itself,\n     * but it is required when restoring from RecyclerView's subclasses.\n     */\n    static class SavedState implements Parcelable {\n        public static final SavedState EMPTY_STATE = new SavedState() {\n        };\n\n        int prevFirstVisiblePosition;\n        int prevFirstVisibleChildHeight = -1;\n        int prevScrolledChildrenHeight;\n        int prevScrollY;\n        int scrollY;\n        SparseIntArray childrenHeights;\n\n        // This keeps the parent(RecyclerView)'s state\n        Parcelable superState;\n\n        /**\n         * Called by EMPTY_STATE instantiation.\n         */\n        private SavedState() {\n            superState = null;\n        }\n\n        /**\n         * Called by onSaveInstanceState.\n         */\n        SavedState(Parcelable superState) {\n            this.superState = superState != EMPTY_STATE ? superState : null;\n        }\n\n        /**\n         * Called by CREATOR.\n         */\n        private SavedState(Parcel in) {\n            // Parcel 'in' has its parent(RecyclerView)'s saved state.\n            // To restore it, class loader that loaded RecyclerView is required.\n            Parcelable superState = in.readParcelable(RecyclerView.class.getClassLoader());\n            this.superState = superState != null ? superState : EMPTY_STATE;\n\n            prevFirstVisiblePosition = in.readInt();\n            prevFirstVisibleChildHeight = in.readInt();\n            prevScrolledChildrenHeight = in.readInt();\n            prevScrollY = in.readInt();\n            scrollY = in.readInt();\n            childrenHeights = new SparseIntArray();\n            final int numOfChildren = in.readInt();\n            if (0 < numOfChildren) {\n                for (int i = 0; i < numOfChildren; i++) {\n                    final int key = in.readInt();\n                    final int value = in.readInt();\n                    childrenHeights.put(key, value);\n                }\n            }\n        }\n\n        @Override\n        public int describeContents() {\n            return 0;\n        }\n\n        @Override\n        public void writeToParcel(Parcel out, int flags) {\n            out.writeParcelable(superState, flags);\n\n            out.writeInt(prevFirstVisiblePosition);\n            out.writeInt(prevFirstVisibleChildHeight);\n            out.writeInt(prevScrolledChildrenHeight);\n            out.writeInt(prevScrollY);\n            out.writeInt(scrollY);\n            final int numOfChildren = childrenHeights == null ? 0 : childrenHeights.size();\n            out.writeInt(numOfChildren);\n            if (0 < numOfChildren) {\n                for (int i = 0; i < numOfChildren; i++) {\n                    out.writeInt(childrenHeights.keyAt(i));\n                    out.writeInt(childrenHeights.valueAt(i));\n                }\n            }\n        }\n\n        public Parcelable getSuperState() {\n            return superState;\n        }\n\n        public static final Parcelable.Creator<SavedState> CREATOR\n            = new Parcelable.Creator<SavedState>() {\n            @Override\n            public SavedState createFromParcel(Parcel in) {\n                return new SavedState(in);\n            }\n\n            @Override\n            public SavedState[] newArray(int size) {\n                return new SavedState[size];\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview;\n\nimport android.content.Context;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ScrollView;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * ScrollView that its scroll position can be observed.\n */\npublic class ObservableScrollView extends ScrollView implements Scrollable {\n\n    // Fields that should be saved onSaveInstanceState\n    private int mPrevScrollY;\n    private int mScrollY;\n\n    // Fields that don't need to be saved onSaveInstanceState\n    private ObservableScrollViewCallbacks mCallbacks;\n    private List<ObservableScrollViewCallbacks> mCallbackCollection;\n    private ScrollState mScrollState;\n    private boolean mFirstScroll;\n    private boolean mDragging;\n    private boolean mIntercepted;\n    private MotionEvent mPrevMoveEvent;\n    private ViewGroup mTouchInterceptionViewGroup;\n\n    public ObservableScrollView(Context context) {\n        super(context);\n    }\n\n    public ObservableScrollView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public ObservableScrollView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n    }\n\n    @Override\n    public void onRestoreInstanceState(Parcelable state) {\n        SavedState ss = (SavedState) state;\n        mPrevScrollY = ss.prevScrollY;\n        mScrollY = ss.scrollY;\n        super.onRestoreInstanceState(ss.getSuperState());\n    }\n\n    @Override\n    public Parcelable onSaveInstanceState() {\n        Parcelable superState = super.onSaveInstanceState();\n        SavedState ss = new SavedState(superState);\n        ss.prevScrollY = mPrevScrollY;\n        ss.scrollY = mScrollY;\n        return ss;\n    }\n\n    @Override\n    protected void onScrollChanged(int l, int t, int oldl, int oldt) {\n        super.onScrollChanged(l, t, oldl, oldt);\n        if (hasNoCallbacks()) {\n            return;\n        }\n        mScrollY = t;\n\n        dispatchOnScrollChanged(t, mFirstScroll, mDragging);\n        if (mFirstScroll) {\n            mFirstScroll = false;\n        }\n\n        if (mPrevScrollY < t) {\n            mScrollState = ScrollState.UP;\n        } else if (t < mPrevScrollY) {\n            mScrollState = ScrollState.DOWN;\n            //} else {\n            // Keep previous state while dragging.\n            // Never makes it STOP even if scrollY not changed.\n            // Before Android 4.4, onTouchEvent calls onScrollChanged directly for ACTION_MOVE,\n            // which makes mScrollState always STOP when onUpOrCancelMotionEvent is called.\n            // STOP state is now meaningless for ScrollView.\n        }\n        mPrevScrollY = t;\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        if (hasNoCallbacks()) {\n            return super.onInterceptTouchEvent(ev);\n        }\n        switch (ev.getActionMasked()) {\n            case MotionEvent.ACTION_DOWN:\n                // Whether or not motion events are consumed by children,\n                // flag initializations which are related to ACTION_DOWN events should be executed.\n                // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are\n                // passed to parent (this view), the flags will be invalid.\n                // Also, applications might implement initialization codes to onDownMotionEvent,\n                // so call it here.\n                mFirstScroll = mDragging = true;\n                dispatchOnDownMotionEvent();\n                break;\n        }\n        return super.onInterceptTouchEvent(ev);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        if (hasNoCallbacks()) {\n            return super.onTouchEvent(ev);\n        }\n        switch (ev.getActionMasked()) {\n            case MotionEvent.ACTION_UP:\n            case MotionEvent.ACTION_CANCEL:\n                mIntercepted = false;\n                mDragging = false;\n                dispatchOnUpOrCancelMotionEvent(mScrollState);\n                break;\n            case MotionEvent.ACTION_MOVE:\n                if (mPrevMoveEvent == null) {\n                    mPrevMoveEvent = ev;\n                }\n                float diffY = ev.getY() - mPrevMoveEvent.getY();\n                mPrevMoveEvent = MotionEvent.obtainNoHistory(ev);\n                if (getCurrentScrollY() - diffY <= 0) {\n                    // Can't scroll anymore.\n\n                    if (mIntercepted) {\n                        // Already dispatched ACTION_DOWN event to parents, so stop here.\n                        return false;\n                    }\n\n                    // Apps can set the interception target other than the direct parent.\n                    final ViewGroup parent;\n                    if (mTouchInterceptionViewGroup == null) {\n                        parent = (ViewGroup) getParent();\n                    } else {\n                        parent = mTouchInterceptionViewGroup;\n                    }\n\n                    // Get offset to parents. If the parent is not the direct parent,\n                    // we should aggregate offsets from all of the parents.\n                    float offsetX = 0;\n                    float offsetY = 0;\n                    for (View v = this; v != null && v != parent; v = (View) v.getParent()) {\n                        offsetX += v.getLeft() - v.getScrollX();\n                        offsetY += v.getTop() - v.getScrollY();\n                    }\n                    final MotionEvent event = MotionEvent.obtainNoHistory(ev);\n                    event.offsetLocation(offsetX, offsetY);\n\n                    if (parent.onInterceptTouchEvent(event)) {\n                        mIntercepted = true;\n\n                        // If the parent wants to intercept ACTION_MOVE events,\n                        // we pass ACTION_DOWN event to the parent\n                        // as if these touch events just have began now.\n                        event.setAction(MotionEvent.ACTION_DOWN);\n\n                        // Return this onTouchEvent() first and set ACTION_DOWN event for parent\n                        // to the queue, to keep events sequence.\n                        post(new Runnable() {\n                            @Override\n                            public void run() {\n                                parent.dispatchTouchEvent(event);\n                            }\n                        });\n                        return false;\n                    }\n                    // Even when this can't be scrolled anymore,\n                    // simply returning false here may cause subView's click,\n                    // so delegate it to super.\n                    return super.onTouchEvent(ev);\n                }\n                break;\n        }\n        return super.onTouchEvent(ev);\n    }\n\n    @Override\n    public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        mCallbacks = listener;\n    }\n\n    @Override\n    public void addScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        if (mCallbackCollection == null) {\n            mCallbackCollection = new ArrayList<>();\n        }\n        mCallbackCollection.add(listener);\n    }\n\n    @Override\n    public void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        if (mCallbackCollection != null) {\n            mCallbackCollection.remove(listener);\n        }\n    }\n\n    @Override\n    public void clearScrollViewCallbacks() {\n        if (mCallbackCollection != null) {\n            mCallbackCollection.clear();\n        }\n    }\n\n    @Override\n    public void setTouchInterceptionViewGroup(ViewGroup viewGroup) {\n        mTouchInterceptionViewGroup = viewGroup;\n    }\n\n    @Override\n    public void scrollVerticallyTo(int y) {\n        scrollTo(0, y);\n    }\n\n    @Override\n    public int getCurrentScrollY() {\n        return mScrollY;\n    }\n\n    private void dispatchOnDownMotionEvent() {\n        if (mCallbacks != null) {\n            mCallbacks.onDownMotionEvent();\n        }\n        if (mCallbackCollection != null) {\n            for (int i = 0; i < mCallbackCollection.size(); i++) {\n                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);\n                callbacks.onDownMotionEvent();\n            }\n        }\n    }\n\n    private void dispatchOnScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        if (mCallbacks != null) {\n            mCallbacks.onScrollChanged(scrollY, firstScroll, dragging);\n        }\n        if (mCallbackCollection != null) {\n            for (int i = 0; i < mCallbackCollection.size(); i++) {\n                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);\n                callbacks.onScrollChanged(scrollY, firstScroll, dragging);\n            }\n        }\n    }\n\n    private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) {\n        if (mCallbacks != null) {\n            mCallbacks.onUpOrCancelMotionEvent(scrollState);\n        }\n        if (mCallbackCollection != null) {\n            for (int i = 0; i < mCallbackCollection.size(); i++) {\n                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);\n                callbacks.onUpOrCancelMotionEvent(scrollState);\n            }\n        }\n    }\n\n    private boolean hasNoCallbacks() {\n        return mCallbacks == null && mCallbackCollection == null;\n    }\n\n    static class SavedState extends BaseSavedState {\n        int prevScrollY;\n        int scrollY;\n\n        /**\n         * Called by onSaveInstanceState.\n         */\n        SavedState(Parcelable superState) {\n            super(superState);\n        }\n\n        /**\n         * Called by CREATOR.\n         */\n        private SavedState(Parcel in) {\n            super(in);\n            prevScrollY = in.readInt();\n            scrollY = in.readInt();\n        }\n\n        @Override\n        public void writeToParcel(Parcel out, int flags) {\n            super.writeToParcel(out, flags);\n            out.writeInt(prevScrollY);\n            out.writeInt(scrollY);\n        }\n\n        public static final Parcelable.Creator<SavedState> CREATOR\n            = new Parcelable.Creator<SavedState>() {\n            @Override\n            public SavedState createFromParcel(Parcel in) {\n                return new SavedState(in);\n            }\n\n            @Override\n            public SavedState[] newArray(int size) {\n                return new SavedState[size];\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollViewCallbacks.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview;\n\n/**\n * Callbacks for Scrollable widgets.\n */\npublic interface ObservableScrollViewCallbacks {\n    /**\n     * Called when the scroll change events occurred.\n     * <p>This won't be called just after the view is laid out, so if you'd like to\n     * initialize the position of your views with this method, you should call this manually\n     * or invoke scroll as appropriate.</p>\n     *\n     * @param scrollY     Scroll position in Y axis.\n     * @param firstScroll True when this is called for the first time in the consecutive motion events.\n     * @param dragging    True when the view is dragged and false when the view is scrolled in the inertia.\n     */\n    void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging);\n\n    /**\n     * Called when the down motion event occurred.\n     */\n    void onDownMotionEvent();\n\n    /**\n     * Called when the dragging ended or canceled.\n     *\n     * @param scrollState State to indicate the scroll direction.\n     */\n    void onUpOrCancelMotionEvent(ScrollState scrollState);\n}\n"
  },
  {
    "path": "library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview;\n\nimport android.content.Context;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.webkit.WebView;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * WebView that its scroll position can be observed.\n */\npublic class ObservableWebView extends WebView implements Scrollable {\n\n    // Fields that should be saved onSaveInstanceState\n    private int mPrevScrollY;\n    private int mScrollY;\n\n    // Fields that don't need to be saved onSaveInstanceState\n    private ObservableScrollViewCallbacks mCallbacks;\n    private List<ObservableScrollViewCallbacks> mCallbackCollection;\n    private ScrollState mScrollState;\n    private boolean mFirstScroll;\n    private boolean mDragging;\n    private boolean mIntercepted;\n    private MotionEvent mPrevMoveEvent;\n    private ViewGroup mTouchInterceptionViewGroup;\n\n    public ObservableWebView(Context context) {\n        super(context);\n    }\n\n    public ObservableWebView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public ObservableWebView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n    }\n\n    @Override\n    public void onRestoreInstanceState(Parcelable state) {\n        SavedState ss = (SavedState) state;\n        mPrevScrollY = ss.prevScrollY;\n        mScrollY = ss.scrollY;\n        super.onRestoreInstanceState(ss.getSuperState());\n    }\n\n    @Override\n    public Parcelable onSaveInstanceState() {\n        Parcelable superState = super.onSaveInstanceState();\n        SavedState ss = new SavedState(superState);\n        ss.prevScrollY = mPrevScrollY;\n        ss.scrollY = mScrollY;\n        return ss;\n    }\n\n    @Override\n    protected void onScrollChanged(int l, int t, int oldl, int oldt) {\n        super.onScrollChanged(l, t, oldl, oldt);\n        if (hasNoCallbacks()) {\n            return;\n        }\n        mScrollY = t;\n\n        dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging);\n        if (mFirstScroll) {\n            mFirstScroll = false;\n        }\n\n        if (mPrevScrollY < t) {\n            mScrollState = ScrollState.UP;\n        } else if (t < mPrevScrollY) {\n            mScrollState = ScrollState.DOWN;\n        } else {\n            mScrollState = ScrollState.STOP;\n        }\n        mPrevScrollY = t;\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        if (hasNoCallbacks()) {\n            return super.onInterceptTouchEvent(ev);\n        }\n        switch (ev.getActionMasked()) {\n            case MotionEvent.ACTION_DOWN:\n                // Whether or not motion events are consumed by children,\n                // flag initializations which are related to ACTION_DOWN events should be executed.\n                // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are\n                // passed to parent (this view), the flags will be invalid.\n                // Also, applications might implement initialization codes to onDownMotionEvent,\n                // so call it here.\n                mFirstScroll = mDragging = true;\n                dispatchOnDownMotionEvent();\n                break;\n        }\n        return super.onInterceptTouchEvent(ev);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        if (hasNoCallbacks()) {\n            return super.onTouchEvent(ev);\n        }\n        switch (ev.getActionMasked()) {\n            case MotionEvent.ACTION_DOWN:\n                break;\n            case MotionEvent.ACTION_UP:\n            case MotionEvent.ACTION_CANCEL:\n                mIntercepted = false;\n                mDragging = false;\n                dispatchOnUpOrCancelMotionEvent(mScrollState);\n                break;\n            case MotionEvent.ACTION_MOVE:\n                if (mPrevMoveEvent == null) {\n                    mPrevMoveEvent = ev;\n                }\n                float diffY = ev.getY() - mPrevMoveEvent.getY();\n                mPrevMoveEvent = MotionEvent.obtainNoHistory(ev);\n                if (getCurrentScrollY() - diffY <= 0) {\n                    // Can't scroll anymore.\n\n                    if (mIntercepted) {\n                        // Already dispatched ACTION_DOWN event to parents, so stop here.\n                        return false;\n                    }\n\n                    // Apps can set the interception target other than the direct parent.\n                    final ViewGroup parent;\n                    if (mTouchInterceptionViewGroup == null) {\n                        parent = (ViewGroup) getParent();\n                    } else {\n                        parent = mTouchInterceptionViewGroup;\n                    }\n\n                    // Get offset to parents. If the parent is not the direct parent,\n                    // we should aggregate offsets from all of the parents.\n                    float offsetX = 0;\n                    float offsetY = 0;\n                    for (View v = this; v != null && v != parent; v = (View) v.getParent()) {\n                        offsetX += v.getLeft() - v.getScrollX();\n                        offsetY += v.getTop() - v.getScrollY();\n                    }\n                    final MotionEvent event = MotionEvent.obtainNoHistory(ev);\n                    event.offsetLocation(offsetX, offsetY);\n\n                    if (parent.onInterceptTouchEvent(event)) {\n                        mIntercepted = true;\n\n                        // If the parent wants to intercept ACTION_MOVE events,\n                        // we pass ACTION_DOWN event to the parent\n                        // as if these touch events just have began now.\n                        event.setAction(MotionEvent.ACTION_DOWN);\n\n                        // Return this onTouchEvent() first and set ACTION_DOWN event for parent\n                        // to the queue, to keep events sequence.\n                        post(new Runnable() {\n                            @Override\n                            public void run() {\n                                parent.dispatchTouchEvent(event);\n                            }\n                        });\n                        return false;\n                    }\n                    // Even when this can't be scrolled anymore,\n                    // simply returning false here may cause subView's click,\n                    // so delegate it to super.\n                    return super.onTouchEvent(ev);\n                }\n                break;\n        }\n        return super.onTouchEvent(ev);\n    }\n\n    @Override\n    public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        mCallbacks = listener;\n    }\n\n    @Override\n    public void addScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        if (mCallbackCollection == null) {\n            mCallbackCollection = new ArrayList<>();\n        }\n        mCallbackCollection.add(listener);\n    }\n\n    @Override\n    public void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        if (mCallbackCollection != null) {\n            mCallbackCollection.remove(listener);\n        }\n    }\n\n    @Override\n    public void clearScrollViewCallbacks() {\n        if (mCallbackCollection != null) {\n            mCallbackCollection.clear();\n        }\n    }\n\n    @Override\n    public void setTouchInterceptionViewGroup(ViewGroup viewGroup) {\n        mTouchInterceptionViewGroup = viewGroup;\n    }\n\n    @Override\n    public void scrollVerticallyTo(int y) {\n        scrollTo(0, y);\n    }\n\n    @Override\n    public int getCurrentScrollY() {\n        return mScrollY;\n    }\n\n    private void dispatchOnDownMotionEvent() {\n        if (mCallbacks != null) {\n            mCallbacks.onDownMotionEvent();\n        }\n        if (mCallbackCollection != null) {\n            for (int i = 0; i < mCallbackCollection.size(); i++) {\n                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);\n                callbacks.onDownMotionEvent();\n            }\n        }\n    }\n\n    private void dispatchOnScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        if (mCallbacks != null) {\n            mCallbacks.onScrollChanged(scrollY, firstScroll, dragging);\n        }\n        if (mCallbackCollection != null) {\n            for (int i = 0; i < mCallbackCollection.size(); i++) {\n                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);\n                callbacks.onScrollChanged(scrollY, firstScroll, dragging);\n            }\n        }\n    }\n\n    private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) {\n        if (mCallbacks != null) {\n            mCallbacks.onUpOrCancelMotionEvent(scrollState);\n        }\n        if (mCallbackCollection != null) {\n            for (int i = 0; i < mCallbackCollection.size(); i++) {\n                ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i);\n                callbacks.onUpOrCancelMotionEvent(scrollState);\n            }\n        }\n    }\n\n    private boolean hasNoCallbacks() {\n        return mCallbacks == null && mCallbackCollection == null;\n    }\n\n    static class SavedState extends BaseSavedState {\n        int prevScrollY;\n        int scrollY;\n\n        /**\n         * Called by onSaveInstanceState.\n         */\n        SavedState(Parcelable superState) {\n            super(superState);\n        }\n\n        /**\n         * Called by CREATOR.\n         */\n        private SavedState(Parcel in) {\n            super(in);\n            prevScrollY = in.readInt();\n            scrollY = in.readInt();\n        }\n\n        @Override\n        public void writeToParcel(Parcel out, int flags) {\n            super.writeToParcel(out, flags);\n            out.writeInt(prevScrollY);\n            out.writeInt(scrollY);\n        }\n\n        public static final Parcelable.Creator<SavedState> CREATOR\n            = new Parcelable.Creator<SavedState>() {\n            @Override\n            public SavedState createFromParcel(Parcel in) {\n                return new SavedState(in);\n            }\n\n            @Override\n            public SavedState[] newArray(int size) {\n                return new SavedState[size];\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollState.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview;\n\n/**\n * Constants that indicates the scroll state of the Scrollable widgets.\n */\npublic enum ScrollState {\n    /**\n     * Widget is stopped.\n     * This state does not always mean that this widget have never been scrolled.\n     */\n    STOP,\n\n    /**\n     * Widget is scrolled up by swiping it down.\n     */\n    UP,\n\n    /**\n     * Widget is scrolled down by swiping it up.\n     */\n    DOWN,\n}\n"
  },
  {
    "path": "library/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollUtils.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview;\n\nimport android.os.Build;\nimport android.view.View;\nimport android.view.ViewTreeObserver;\n\n/**\n * Utilities for creating scrolling effects.\n */\npublic final class ScrollUtils {\n\n    private ScrollUtils() {\n    }\n\n    /**\n     * Return a float value within the range.\n     * <p>This is just a wrapper for Math.min() and Math.max().\n     * This may be useful if you feel it confusing (\"Which is min and which is max?\").</p>\n     *\n     * @param value    The target value.\n     * @param minValue Minimum value. If value is less than this, minValue will be returned.\n     * @param maxValue Maximum value. If value is greater than this, maxValue will be returned.\n     * @return Float value limited to the range.\n     */\n    public static float getFloat(final float value, final float minValue, final float maxValue) {\n        return Math.min(maxValue, Math.max(minValue, value));\n    }\n\n    /**\n     * Create a color integer value with specified alpha.\n     * <p>This may be useful to change alpha value of background color.</p>\n     *\n     * @param alpha     Alpha value from 0.0f to 1.0f.\n     * @param baseColor Base color. alpha value will be ignored.\n     * @return A color with alpha made from base color.\n     */\n    public static int getColorWithAlpha(float alpha, int baseColor) {\n        int a = Math.min(255, Math.max(0, (int) (alpha * 255))) << 24;\n        int rgb = 0x00ffffff & baseColor;\n        return a + rgb;\n    }\n\n    /**\n     * Add an OnGlobalLayoutListener for the view.\n     * <p>This is just a convenience method for using {@code ViewTreeObserver.OnGlobalLayoutListener()}.\n     * This also handles removing listener when onGlobalLayout is called.</p>\n     *\n     * @param view     The target view to add global layout listener.\n     * @param runnable Runnable to be executed after the view is laid out.\n     */\n    public static void addOnGlobalLayoutListener(final View view, final Runnable runnable) {\n        ViewTreeObserver vto = view.getViewTreeObserver();\n        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {\n            @SuppressWarnings(\"deprecation\")\n            @Override\n            public void onGlobalLayout() {\n                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n                    view.getViewTreeObserver().removeGlobalOnLayoutListener(this);\n                } else {\n                    view.getViewTreeObserver().removeOnGlobalLayoutListener(this);\n                }\n                runnable.run();\n            }\n        });\n    }\n\n    /**\n     * Mix two colors.\n     * <p>{@code toColor} will be {@code toAlpha/1} percent,\n     * and {@code fromColor} will be {@code (1-toAlpha)/1} percent.</p>\n     *\n     * @param fromColor First color to be mixed.\n     * @param toColor   Second color to be mixed.\n     * @param toAlpha   Alpha value of toColor, 0.0f to 1.0f.\n     * @return Mixed color value in ARGB. Alpha is fixed value (255).\n     */\n    public static int mixColors(int fromColor, int toColor, float toAlpha) {\n        float[] fromCmyk = ScrollUtils.cmykFromRgb(fromColor);\n        float[] toCmyk = ScrollUtils.cmykFromRgb(toColor);\n        float[] result = new float[4];\n        for (int i = 0; i < 4; i++) {\n            result[i] = Math.min(1, fromCmyk[i] * (1 - toAlpha) + toCmyk[i] * toAlpha);\n        }\n        return 0xff000000 + (0x00ffffff & ScrollUtils.rgbFromCmyk(result));\n    }\n\n    /**\n     * Convert RGB color to CMYK color.\n     *\n     * @param rgbColor Target color.\n     * @return CMYK array.\n     */\n    public static float[] cmykFromRgb(int rgbColor) {\n        int red = (0xff0000 & rgbColor) >> 16;\n        int green = (0xff00 & rgbColor) >> 8;\n        int blue = (0xff & rgbColor);\n        float black = Math.min(1.0f - red / 255.0f, Math.min(1.0f - green / 255.0f, 1.0f - blue / 255.0f));\n        float cyan = 1.0f;\n        float magenta = 1.0f;\n        float yellow = 1.0f;\n        if (black != 1.0f) {\n            // black 1.0 causes zero divide\n            cyan = (1.0f - (red / 255.0f) - black) / (1.0f - black);\n            magenta = (1.0f - (green / 255.0f) - black) / (1.0f - black);\n            yellow = (1.0f - (blue / 255.0f) - black) / (1.0f - black);\n        }\n        return new float[]{cyan, magenta, yellow, black};\n    }\n\n    /**\n     * Convert CYMK color to RGB color.\n     * This method doesn't check if cmyk is not null or have 4 elements in array.\n     *\n     * @param cmyk Target CYMK color. Each value should be between 0.0f to 1.0f,\n     *             and should be set in this order: cyan, magenta, yellow, black.\n     * @return ARGB color. Alpha is fixed value (255).\n     */\n    public static int rgbFromCmyk(float[] cmyk) {\n        float cyan = cmyk[0];\n        float magenta = cmyk[1];\n        float yellow = cmyk[2];\n        float black = cmyk[3];\n        int red = (int) ((1.0f - Math.min(1.0f, cyan * (1.0f - black) + black)) * 255);\n        int green = (int) ((1.0f - Math.min(1.0f, magenta * (1.0f - black) + black)) * 255);\n        int blue = (int) ((1.0f - Math.min(1.0f, yellow * (1.0f - black) + black)) * 255);\n        return ((0xff & red) << 16) + ((0xff & green) << 8) + (0xff & blue);\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/github/ksoichiro/android/observablescrollview/Scrollable.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview;\n\nimport android.view.ViewGroup;\n\n/**\n * Interface for providing common API for observable and scrollable widgets.\n */\npublic interface Scrollable {\n    /**\n     * Set a callback listener.<br>\n     * Developers should use {@link #addScrollViewCallbacks(ObservableScrollViewCallbacks)}\n     * and {@link #removeScrollViewCallbacks(ObservableScrollViewCallbacks)}.\n     *\n     * @param listener Listener to set.\n     */\n    @Deprecated\n    void setScrollViewCallbacks(ObservableScrollViewCallbacks listener);\n\n    /**\n     * Add a callback listener.\n     *\n     * @param listener Listener to add.\n     * @since 1.7.0\n     */\n    void addScrollViewCallbacks(ObservableScrollViewCallbacks listener);\n\n    /**\n     * Remove a callback listener.\n     *\n     * @param listener Listener to remove.\n     * @since 1.7.0\n     */\n    void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener);\n\n    /**\n     * Clear callback listeners.\n     *\n     * @since 1.7.0\n     */\n    void clearScrollViewCallbacks();\n\n    /**\n     * Scroll vertically to the absolute Y.<br>\n     * Implemented classes are expected to scroll to the exact Y pixels from the top,\n     * but it depends on the type of the widget.\n     *\n     * @param y Vertical position to scroll to.\n     */\n    void scrollVerticallyTo(int y);\n\n    /**\n     * Return the current Y of the scrollable view.\n     *\n     * @return Current Y pixel.\n     */\n    int getCurrentScrollY();\n\n    /**\n     * Set a touch motion event delegation ViewGroup.<br>\n     * This is used to pass motion events back to parent view.\n     * It's up to the implementation classes whether or not it works.\n     *\n     * @param viewGroup ViewGroup object to dispatch motion events.\n     */\n    void setTouchInterceptionViewGroup(ViewGroup viewGroup);\n}\n"
  },
  {
    "path": "library/src/main/java/com/github/ksoichiro/android/observablescrollview/TouchInterceptionFrameLayout.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.graphics.PointF;\nimport android.graphics.Rect;\nimport android.os.Build;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.widget.FrameLayout;\n\n/**\n * A layout that delegates interception of touch motion events.\n * This layout is provided to move the container of Scrollable views using scroll position.\n * Please note that this class overrides or uses touch events API such as onTouchEvent,\n * onInterceptTouchEvent and dispatchTouchEvent,\n * so be careful when you handle touches with this layout.\n */\npublic class TouchInterceptionFrameLayout extends FrameLayout {\n\n    /**\n     * Callbacks for TouchInterceptionFrameLayout.\n     */\n    public interface TouchInterceptionListener {\n        /**\n         * Determine whether the layout should intercept this event.\n         *\n         * @param ev     Motion event.\n         * @param moving True if this event is ACTION_MOVE type.\n         * @param diffX  Difference between previous X and current X, if moving is true.\n         * @param diffY  Difference between previous Y and current Y, if moving is true.\n         * @return True if the layout should intercept.\n         */\n        boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY);\n\n        /**\n         * Called if the down motion event is intercepted by this layout.\n         *\n         * @param ev Motion event.\n         */\n        void onDownMotionEvent(MotionEvent ev);\n\n        /**\n         * Called if the move motion event is intercepted by this layout.\n         *\n         * @param ev    Motion event.\n         * @param diffX Difference between previous X and current X.\n         * @param diffY Difference between previous Y and current Y.\n         */\n        void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY);\n\n        /**\n         * Called if the up (or cancel) motion event is intercepted by this layout.\n         *\n         * @param ev Motion event.\n         */\n        void onUpOrCancelMotionEvent(MotionEvent ev);\n    }\n\n    private boolean mIntercepting;\n    private boolean mDownMotionEventPended;\n    private boolean mBeganFromDownMotionEvent;\n    private boolean mChildrenEventsCanceled;\n    private PointF mInitialPoint;\n    private MotionEvent mPendingDownMotionEvent;\n    private TouchInterceptionListener mTouchInterceptionListener;\n\n    public TouchInterceptionFrameLayout(Context context) {\n        super(context);\n    }\n\n    public TouchInterceptionFrameLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public TouchInterceptionFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @TargetApi(Build.VERSION_CODES.LOLLIPOP)\n    public TouchInterceptionFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {\n        super(context, attrs, defStyleAttr, defStyleRes);\n    }\n\n    public void setScrollInterceptionListener(TouchInterceptionListener listener) {\n        mTouchInterceptionListener = listener;\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        if (mTouchInterceptionListener == null) {\n            return false;\n        }\n\n        // In here, we must initialize touch state variables\n        // and ask if we should intercept this event.\n        // Whether we should intercept or not is kept for the later event handling.\n        switch (ev.getActionMasked()) {\n            case MotionEvent.ACTION_DOWN:\n                mInitialPoint = new PointF(ev.getX(), ev.getY());\n                mPendingDownMotionEvent = MotionEvent.obtainNoHistory(ev);\n                mDownMotionEventPended = true;\n                mIntercepting = mTouchInterceptionListener.shouldInterceptTouchEvent(ev, false, 0, 0);\n                mBeganFromDownMotionEvent = mIntercepting;\n                mChildrenEventsCanceled = false;\n                return mIntercepting;\n            case MotionEvent.ACTION_MOVE:\n                // ACTION_MOVE will be passed suddenly, so initialize to avoid exception.\n                if (mInitialPoint == null) {\n                    mInitialPoint = new PointF(ev.getX(), ev.getY());\n                }\n\n                // diffX and diffY are the origin of the motion, and should be difference\n                // from the position of the ACTION_DOWN event occurred.\n                float diffX = ev.getX() - mInitialPoint.x;\n                float diffY = ev.getY() - mInitialPoint.y;\n                mIntercepting = mTouchInterceptionListener.shouldInterceptTouchEvent(ev, true, diffX, diffY);\n                return mIntercepting;\n        }\n        return false;\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        if (mTouchInterceptionListener != null) {\n            switch (ev.getActionMasked()) {\n                case MotionEvent.ACTION_DOWN:\n                    if (mIntercepting) {\n                        mTouchInterceptionListener.onDownMotionEvent(ev);\n                        duplicateTouchEventForChildren(ev);\n                        return true;\n                    }\n                    break;\n                case MotionEvent.ACTION_MOVE:\n                    // ACTION_MOVE will be passed suddenly, so initialize to avoid exception.\n                    if (mInitialPoint == null) {\n                        mInitialPoint = new PointF(ev.getX(), ev.getY());\n                    }\n\n                    // diffX and diffY are the origin of the motion, and should be difference\n                    // from the position of the ACTION_DOWN event occurred.\n                    float diffX = ev.getX() - mInitialPoint.x;\n                    float diffY = ev.getY() - mInitialPoint.y;\n                    mIntercepting = mTouchInterceptionListener.shouldInterceptTouchEvent(ev, true, diffX, diffY);\n                    if (mIntercepting) {\n                        // If this layout didn't receive ACTION_DOWN motion event,\n                        // we should generate ACTION_DOWN event with current position.\n                        if (!mBeganFromDownMotionEvent) {\n                            mBeganFromDownMotionEvent = true;\n\n                            MotionEvent event = MotionEvent.obtainNoHistory(mPendingDownMotionEvent);\n                            event.setLocation(ev.getX(), ev.getY());\n                            mTouchInterceptionListener.onDownMotionEvent(event);\n\n                            mInitialPoint = new PointF(ev.getX(), ev.getY());\n                            diffX = diffY = 0;\n                        }\n\n                        // Children's touches should be canceled\n                        if (!mChildrenEventsCanceled) {\n                            mChildrenEventsCanceled = true;\n                            duplicateTouchEventForChildren(obtainMotionEvent(ev, MotionEvent.ACTION_CANCEL));\n                        }\n\n                        mTouchInterceptionListener.onMoveMotionEvent(ev, diffX, diffY);\n\n                        // If next mIntercepting become false,\n                        // then we should generate fake ACTION_DOWN event.\n                        // Therefore we set pending flag to true as if this is a down motion event.\n                        mDownMotionEventPended = true;\n\n                        // Whether or not this event is consumed by the listener,\n                        // assume it consumed because we declared to intercept the event.\n                        return true;\n                    } else {\n                        if (mDownMotionEventPended) {\n                            mDownMotionEventPended = false;\n                            MotionEvent event = MotionEvent.obtainNoHistory(mPendingDownMotionEvent);\n                            event.setLocation(ev.getX(), ev.getY());\n                            duplicateTouchEventForChildren(ev, event);\n                        } else {\n                            duplicateTouchEventForChildren(ev);\n                        }\n\n                        // If next mIntercepting become true,\n                        // then we should generate fake ACTION_DOWN event.\n                        // Therefore we set beganFromDownMotionEvent flag to false\n                        // as if we haven't received a down motion event.\n                        mBeganFromDownMotionEvent = false;\n\n                        // Reserve children's click cancellation here if they've already canceled\n                        mChildrenEventsCanceled = false;\n                    }\n                    break;\n                case MotionEvent.ACTION_UP:\n                case MotionEvent.ACTION_CANCEL:\n                    mBeganFromDownMotionEvent = false;\n                    if (mIntercepting) {\n                        mTouchInterceptionListener.onUpOrCancelMotionEvent(ev);\n                    }\n\n                    // Children's touches should be canceled regardless of\n                    // whether or not this layout intercepted the consecutive motion events.\n                    if (!mChildrenEventsCanceled) {\n                        mChildrenEventsCanceled = true;\n                        if (mDownMotionEventPended) {\n                            mDownMotionEventPended = false;\n                            MotionEvent event = MotionEvent.obtainNoHistory(mPendingDownMotionEvent);\n                            event.setLocation(ev.getX(), ev.getY());\n                            duplicateTouchEventForChildren(ev, event);\n                        } else {\n                            duplicateTouchEventForChildren(ev);\n                        }\n                    }\n                    return true;\n            }\n        }\n        return super.onTouchEvent(ev);\n    }\n\n    private MotionEvent obtainMotionEvent(MotionEvent base, int action) {\n        MotionEvent ev = MotionEvent.obtainNoHistory(base);\n        ev.setAction(action);\n        return ev;\n    }\n\n    /**\n     * Duplicate touch events to child views.\n     * We want to dispatch a down motion event and the move events to\n     * child views, but calling dispatchTouchEvent() causes StackOverflowError.\n     * Therefore we do it manually.\n     *\n     * @param ev            Motion event to be passed to children.\n     * @param pendingEvents Pending events like ACTION_DOWN. This will be passed to the children before ev.\n     */\n    private void duplicateTouchEventForChildren(MotionEvent ev, MotionEvent... pendingEvents) {\n        if (ev == null) {\n            return;\n        }\n        for (int i = getChildCount() - 1; 0 <= i; i--) {\n            View childView = getChildAt(i);\n            if (childView != null) {\n                Rect childRect = new Rect();\n                childView.getHitRect(childRect);\n                MotionEvent event = MotionEvent.obtainNoHistory(ev);\n                if (!childRect.contains((int) event.getX(), (int) event.getY())) {\n                    continue;\n                }\n                float offsetX = -childView.getLeft();\n                float offsetY = -childView.getTop();\n                boolean consumed = false;\n                if (pendingEvents != null) {\n                    for (MotionEvent pe : pendingEvents) {\n                        if (pe != null) {\n                            MotionEvent peAdjusted = MotionEvent.obtainNoHistory(pe);\n                            peAdjusted.offsetLocation(offsetX, offsetY);\n                            consumed |= childView.dispatchTouchEvent(peAdjusted);\n                        }\n                    }\n                }\n                event.offsetLocation(offsetX, offsetY);\n                consumed |= childView.dispatchTouchEvent(event);\n                if (consumed) {\n                    break;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "samples/.gitignore",
    "content": "/build\n/src/version/\n"
  },
  {
    "path": "samples/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"com.github.ksoichiro.android.observablescrollview.samples\"\n    android:versionCode=\"5\"\n    android:versionName=\"1.3.0\">\n\n    <uses-sdk\n        android:minSdkVersion=\"9\"\n        android:targetSdkVersion=\"22\"\n        tools:overrideLibrary=\"com.melnykov.fab\" />\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@drawable/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\">\n        <activity\n            android:name=\".MainActivity\"\n            android:label=\"@string/title_activity_main\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".AboutActivity\"\n            android:label=\"@string/title_activity_about\" />\n        <activity\n            android:name=\".ActionBarControlGridViewActivity\"\n            android:label=\"@string/title_activity_actionbarcontrolgridview\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ActionBarControlListViewActivity\"\n            android:label=\"@string/title_activity_actionbarcontrollistview\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ActionBarControlRecyclerViewActivity\"\n            android:label=\"@string/title_activity_actionbarcontrolrecyclerview\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ActionBarControlScrollViewActivity\"\n            android:label=\"@string/title_activity_actionbarcontrolscrollview\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ActionBarControlWebViewActivity\"\n            android:label=\"@string/title_activity_actionbarcontrolwebview\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FillGap2ListViewActivity\"\n            android:label=\"@string/title_activity_fillgaplistview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FillGap2RecyclerViewActivity\"\n            android:label=\"@string/title_activity_fillgaprecyclerview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FillGap2ScrollViewActivity\"\n            android:label=\"@string/title_activity_fillgapscrollview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FillGap3ListViewActivity\"\n            android:label=\"@string/title_activity_fillgap3listview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FillGap3RecyclerViewActivity\"\n            android:label=\"@string/title_activity_fillgap3recyclerview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FillGap3ScrollViewActivity\"\n            android:label=\"@string/title_activity_fillgap3scrollview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FillGapListViewActivity\"\n            android:label=\"@string/title_activity_fillgaplistview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FillGapRecyclerViewActivity\"\n            android:label=\"@string/title_activity_fillgaprecyclerview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FillGapScrollViewActivity\"\n            android:label=\"@string/title_activity_fillgapscrollview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FlexibleSpaceToolbarScrollViewActivity\"\n            android:label=\"@string/title_activity_flexiblespacetoolbarscrollview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FlexibleSpaceWithImageGridViewActivity\"\n            android:label=\"@string/title_activity_flexiblespacewithimagegridview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FlexibleSpaceWithImageScrollViewActivity\"\n            android:label=\"@string/title_activity_flexiblespacewithimagescrollview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FlexibleSpaceWithImageRecyclerViewActivity\"\n            android:label=\"@string/title_activity_flexiblespacewithimageRecyclerview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FlexibleSpaceWithImageWithViewPagerTabActivity\"\n            android:label=\"@string/title_activity_flexiblespacewithimagewithviewpagertab\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FlexibleSpaceWithImageWithViewPagerTab2Activity\"\n            android:label=\"@string/title_activity_flexiblespacewithimagewithviewpagertab2\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FlexibleSpaceWithImageListViewActivity\"\n            android:label=\"@string/title_activity_flexiblespacewithimagelistview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FlexibleSpaceToolbarWebViewActivity\"\n            android:label=\"@string/title_activity_flexiblespacewithwebview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FragmentActionBarControlListViewActivity\"\n            android:label=\"@string/title_activity_fragmentactionbarcontrollistview\"\n            android:theme=\"@style/AppTheme\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".FragmentTransitionActivity\"\n            android:label=\"@string/title_activity_fragmenttransition\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".HandleTouchGridViewActivity\"\n            android:label=\"@string/title_activity_handletouchgridview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".HandleTouchListViewActivity\"\n            android:label=\"@string/title_activity_handletouchlistview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".HandleTouchRecyclerViewActivity\"\n            android:label=\"@string/title_activity_handletouchrecyclerview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".HandleTouchScrollViewActivity\"\n            android:label=\"@string/title_activity_handletouchscrollview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".HandleTouchWebViewActivity\"\n            android:label=\"@string/title_activity_handletouchwebview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ParallaxToolbarGridViewActivity\"\n            android:label=\"@string/title_activity_parallaxtoolbargridview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ParallaxToolbarListViewActivity\"\n            android:label=\"@string/title_activity_parallaxtoolbarlistview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ParallaxToolbarScrollViewActivity\"\n            android:label=\"@string/title_activity_parallaxtoolbarscrollview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ScrollFromBottomListViewActivity\"\n            android:label=\"@string/title_activity_scrollfrombottomlistview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ScrollFromBottomRecyclerViewActivity\"\n            android:label=\"@string/title_activity_scrollfrombottomrecyclerview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".SlidingUpGridViewActivity\"\n            android:label=\"@string/title_activity_slidingupgridview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".SlidingUpListViewActivity\"\n            android:label=\"@string/title_activity_slidinguplistview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".SlidingUpRecyclerViewActivity\"\n            android:label=\"@string/title_activity_slidinguprecyclerview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".SlidingUpScrollViewActivity\"\n            android:label=\"@string/title_activity_slidingupscrollview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".SlidingUpWebViewActivity\"\n            android:label=\"@string/title_activity_slidingupwebview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".StickyHeaderListViewActivity\"\n            android:label=\"@string/title_activity_stickyheaderlistview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".StickyHeaderRecyclerViewActivity\"\n            android:label=\"@string/title_activity_stickyheaderrecyclerview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".StickyHeaderScrollViewActivity\"\n            android:label=\"@string/title_activity_stickyheaderscrollview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".StickyHeaderWebViewActivity\"\n            android:label=\"@string/title_activity_stickyheaderwebview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ToolbarControlGridViewActivity\"\n            android:label=\"@string/title_activity_toolbarcontrolgridview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ToolbarControlListViewActivity\"\n            android:label=\"@string/title_activity_toolbarcontrollistview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ToolbarControlRecyclerViewActivity\"\n            android:label=\"@string/title_activity_toolbarcontrolrecyclerview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ToolbarControlScrollViewActivity\"\n            android:label=\"@string/title_activity_toolbarcontrolscrollview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ToolbarControlWebViewActivity\"\n            android:label=\"@string/title_activity_toolbarcontrolwebview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ViewPagerTab2Activity\"\n            android:label=\"@string/title_activity_viewpagertab2\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ViewPagerTabActivity\"\n            android:label=\"@string/title_activity_viewpagertab\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ViewPagerTabFragmentActivity\"\n            android:label=\"@string/title_activity_viewpagertabfragment\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ViewPagerTabListViewActivity\"\n            android:label=\"@string/title_activity_viewpagertablistview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ViewPagerTabScrollViewActivity\"\n            android:label=\"@string/title_activity_viewpagertabscrollview\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".ViewPagerTabScrollViewWithFabActivity\"\n            android:label=\"@string/title_activity_viewpagertabscrollviewwithfab\"\n            android:theme=\"@style/AppTheme.Toolbar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"com.github.ksoichiro.android.observablescrollview.samples\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "samples/README.md",
    "content": "# Samples\n\nThis sample project demonstrates how the Android-ObservableScrollView works.\n\nThis document's goal is to lead you to run the sample app and help understanding how to use this library.\n\nPlease note that this document is still work in progress.\nAlthough I've built the app on Android Studio, Eclipse, Gradle on Mac and Gradle on Linux of Travis CI, there might be some implicit dependencies which I haven't noticed and you couldn't build it correctly.\nTherefore I'd greatly appreciate it if you report it to me.\n\n## How to build\n\n### on Android Studio\n\nTODO\n\n### on Eclipse\n\nTODO\n\n### on Gradle\n\nWindows:\n\n```sh\n> gradlew installDevDebug\n```\n\nLinux/Mac:\n\n```sh\n$ ./gradlew installDevDebug\n```"
  },
  {
    "path": "samples/assets/handletouch.html",
    "content": "<html>\n<head>\n<style type=\"text/css\">\n#container {\npadding: 1em;\n}\n.btn {\nmargin-top: 4px;\nmargin-bottom: 4px;\n}\n</style>\n</head>\n<body>\n<div id=\"container\">\n<p>Lorem ipsum dolor sit amet, ut duis lorem provident sed felis blandit, condimentum donec lectus ipsum et mauris, morbi porttitor interdum feugiat nulla donec sodales, vestibulum nisl primis a molestie vestibulum quam, sapien mauris metus risus suspendisse magnis. Augue viverra nulla faucibus egestas eu, a etiam id congue rutrum ante, arcu tincidunt donec quam felis at ornare, iaculis ligula sodales venenatis commodo volutpat neque, suspendisse elit praesent tellus felis mi amet. Inceptos amet tempor lectus lorem est non, ac donec ac libero neque mauris, tellus ante metus eget leo consequat. Scelerisque dolor curabitur pretium blandit ut feugiat, amet lacus pulvinar justo convallis ut, sed natoque ipsum urna posuere nibh eu. Sed at sed vulputate sit orci, facilisis a aliquam tellus quam aliquam, eu aliquam donec at molestie ante, pellentesque mauris lorem ultrices libero faucibus porta, imperdiet adipiscing sit hac diam ut nulla. Lacus enim elit pulvinar donec vehicula dapibus, accumsan purus officia cursus dolor sapien, eu amet dis mauris mi nulla ut. Non accusamus etiam pede non urna tempus, vestibulum aliquam tortor eget pharetra sodales, in vestibulum ut justo orci nulla, lobortis purus sem semper consectetuer magni purus. Dolor a leo vestibulum amet ut sit, arcu ut eaque urna fusce aliquet turpis, sed fermentum sed vestibulum nisl pede, tristique enim lorem posuere in laborum ut. Vestibulum id id justo leo nulla, magna lobortis ullamcorper et dignissim pellentesque, duis suspendisse quis id lorem ante. Vivamus a nullam ante adipiscing amet, mi vel consectetuer nunc aenean pede quisque, eget rhoncus dis porttitor habitant nunc vivamus, duis cubilia blandit non donec justo dictumst, praesent vitae nulla nam pulvinar urna. Adipiscing adipiscing justo urna pulvinar imperdiet nullam, vitae fusce rhoncus proin nonummy suscipit, ullamcorper amet et non potenti platea ultrices, mauris nullam sapien nunc justo vel, eu semper pellentesque arcu fusce augue. Malesuada mauris nibh sit a a scelerisque, velit sem lectus tellus convallis consectetuer, ultricies auctor a ante eros amet sed.</p>\n<p><a class=\"btn\" href=\"https://github.com/ksoichiro/Android-ObservableScrollView\">Android-ObservableScrollView</a></p>\n<p>Risus lacus duis leo platea wisi, felis maecenas rutrum in id in donec, non id a potenti libero eget, posuere elit ea sed pellentesque quis. Sunt lacus urna lorem elit duis, nibh donec purus quisque consectetuer dolor, neque vestibulum proin ornare eros nonummy phasellus. Iaculis cras eu at egestas dolor montes, viverra quisque malesuada consectetuer semper maecenas, a sed vitae donec tempor aliqua metus, ornare mollis suscipit et erat fusce, sit orci aut auctor elementum fames aliquam. Platea dui integer magnis non metus, minus dignissimos ante massa nostra et, rutrum sapien egestas quis sapien donec donec. Erat sit a eros aenean natoque, quam libero id lorem enim proin, lorem ipsum fermentum mattis metus et. Aliquam aliquet suscipit purus conubia at neque, platea vivamus vestibulum nulla quibusdam senectus, et morbi lectus malesuada gravida donec, elementum sit convallis pellentesque velit amet. Et eveniet viverra vehicula consectetuer justo, provident sed commodo non lacinia velit, tempor phasellus vel leo nisl cras, vivamus et arcu interdum dui eu amet. Volutpat wisi rhoncus vel turpis diam quibusdam, dapibus elit est quisque cubilia mauris, nulla elit magna tempor accumsan bibendum, lorem varius sed interdum eget mattis, scelerisque egestas feugiat donec dui molestie. Leo facilisis nisl sit montes ligula sed, enim commodo consectetuer nunc est et, ut sed vehicula dolor luctus elit. Fermentum cras donec eget nibh est vel, sed justo risus et pharetra diam, eu vivamus egestas ligula risus diam, sed justo eget hac ut mauris. Vestibulum diam nec vitae mi eget suspendisse, aenean arcu purus facilisis purus class in, id aliquam sit id scelerisque sapien etiam. Ut nullam sit sed at mauris lobortis, consequat dolor autem ipsum euismod nulla, elit quis proin eget conubia varius, erat arcu massa mus in mauris, scelerisque ut eu sollicitudin libero leo urna.</p>\n<p><a class=\"btn\" href=\"https://github.com/ksoichiro/Android-ObservableScrollView\">Android-ObservableScrollView</a></p>\n<p>Consectetuer luctus tempor elit ut dolor ligula, quis dui per dui hendrerit ante sagittis, in quisque pretium in eleifend enim. Condimentum iaculis vitae feugiat dis tellus vel, lectus dolor nec dui nulla nascetur, et pellentesque curabitur lorem leo velit eget. Id nascetur arcu lobortis suspendisse imperdiet urna, natoque nascetur ante in porta a, interdum hendrerit mi bibendum platea tellus, urna in enim ornare vestibulum faucibus enim. Leo fusce egestas ante nec volutpat, in tempor vel facilisis potenti ut, pede at non lorem a commodo, nulla dolor orci interdum vestibulum nulla. Dui nulla vestibulum quisque a pharetra porta, integer nec ipsum nec sed dui pharetra, magna et dignissim ipsum sed dictum, litora eros vivamus scelerisque libero ipsum. Sed ac ac lorem molestie adipiscing morbi, pellentesque imperdiet nunc quis morbi amet ante, libero dui ligula nec risus neque et, velit nonummy phasellus et facilisi amet, ligula in elementum non sapien pulvinar faucibus. Eu leo ut posuere sed aliquet, tincidunt vel urna volutpat tempus sem, sit felis aliquet vestibulum condimentum sit, amet nibh vel tellus purus ullamcorper libero, nulla vestibulum pede ut vestibulum pretium. Eu nulla vestibulum a neque in metus, quisquam nam sed cursus eget luctus, pede ultrices nec sed dignissim pellentesque, sit class cursus metus nulla placerat mauris, consequat mollis neque vivamus amet pede. Mauris dolor nulla diam eros bibendum, quam ante vestibulum morbi non ligula vel, molestie curabitur rhoncus nulla euismod interdum non. Nulla fringilla lorem mollis ad massa, sit molestie nibh lorem arcu volutpat, accumsan commodo lectus eu et donec, sit tempor tempus rutrum in curabitur amet. Nec urna euismod a tincidunt commodo, eu pede turpis libero vitae viverra, ante vestibulum nam non habitasse potenti, mauris imperdiet in in nunc convallis. Et nostra wisi in est accumsan vehicula, quisque vitae felis mauris sed vulputate nec, ante imperdiet sollicitudin massa iaculis massa sit.</p>\n<p><a class=\"btn\" href=\"https://github.com/ksoichiro/Android-ObservableScrollView\">Android-ObservableScrollView</a></p>\n<p>Quam libero nulla netus eu porta curae, ut nulla bibendum facilisis et urna sed, quis congue vestibulum aliquam interdum etiam. Nulla vel lobortis ullamcorper vitae excepturi, neque urna feugiat lectus vel lacinia, massa pretium orci eu metus neque vulputate. Imperdiet ac velit rhoncus nulla malesuada nullam, nec pulvinar justo gravida lorem rutrum magna, habitasse repudiandae mi eros vestibulum ante, nec euismod dui iaculis in turpis pretium, ac id metus egestas proin lacus lectus. Laoreet lorem nec vitae risus erat arcu, vitae quam ut in ante tristique, porta dolor pede quam et odio nam, arcu lacus sem congue ante cursus massa. Et mattis sagittis erat accumsan fusce quam, vehicula ligula beatae natoque fusce sodales conubia, habitasse metus cum magnis viverra nam cursus, egestas urna wisi primis blandit eu magna, eget libero elit lacus lorem dis aliquam. Ut mauris ante natoque lacus massa, justo a lectus sodales enim adipiscing id, accumsan ut ipsum vestibulum sed enim auctor, vitae congue tincidunt id phasellus lacinia scelerisque, tincidunt sapien nulla euismod volutpat iaculis. Platea sociis nec aliquet nec molestie, in mi et augue sapien in vivamus, integer fames proin vitae in ullamcorper et. Fringilla etiam sapiente rhoncus suspendisse nec id, lobortis cras eget egestas dui ac nec, justo lacus ut lorem bibendum quia eros, eget a gravida id donec nunc suscipit, porta sed in sodales non rutrum. Lectus vel dui elementum pellentesque magna aliquam, vitae non sit pede et fusce nibh, id id deserunt ornare dui sit condimentum, in adipiscing imperdiet turpis nam aliquet, facilisis metus magna lacus wisi facilisis tortor. Vulputate elit accumsan quam amet ligula, suspendisse lacus mi nonummy integer urna, libero nulla nunc varius in odio, laoreet nulla amet placerat amet nec. Consectetuer vel massa hendrerit vitae iaculis id, sed ut ut laudantium odio in, elit vestibulum duis ante maecenas interdum in, neque vehicula ultrices varius in quam, pede tellus pellentesque sed nullam quis.</p>\n</div>\n</body>\n</html>"
  },
  {
    "path": "samples/assets/lipsum.html",
    "content": "<html>\n<head>\n<style type=\"text/css\">\n#container {\npadding: 1em;\n}\n</style>\n</head>\n<body>\n<div id=\"container\">\n<p>Lorem ipsum dolor sit amet, ut duis lorem provident sed felis blandit, condimentum donec lectus ipsum et mauris, morbi porttitor interdum feugiat nulla donec sodales, vestibulum nisl primis a molestie vestibulum quam, sapien mauris metus risus suspendisse magnis. Augue viverra nulla faucibus egestas eu, a etiam id congue rutrum ante, arcu tincidunt donec quam felis at ornare, iaculis ligula sodales venenatis commodo volutpat neque, suspendisse elit praesent tellus felis mi amet. Inceptos amet tempor lectus lorem est non, ac donec ac libero neque mauris, tellus ante metus eget leo consequat. Scelerisque dolor curabitur pretium blandit ut feugiat, amet lacus pulvinar justo convallis ut, sed natoque ipsum urna posuere nibh eu. Sed at sed vulputate sit orci, facilisis a aliquam tellus quam aliquam, eu aliquam donec at molestie ante, pellentesque mauris lorem ultrices libero faucibus porta, imperdiet adipiscing sit hac diam ut nulla. Lacus enim elit pulvinar donec vehicula dapibus, accumsan purus officia cursus dolor sapien, eu amet dis mauris mi nulla ut. Non accusamus etiam pede non urna tempus, vestibulum aliquam tortor eget pharetra sodales, in vestibulum ut justo orci nulla, lobortis purus sem semper consectetuer magni purus. Dolor a leo vestibulum amet ut sit, arcu ut eaque urna fusce aliquet turpis, sed fermentum sed vestibulum nisl pede, tristique enim lorem posuere in laborum ut. Vestibulum id id justo leo nulla, magna lobortis ullamcorper et dignissim pellentesque, duis suspendisse quis id lorem ante. Vivamus a nullam ante adipiscing amet, mi vel consectetuer nunc aenean pede quisque, eget rhoncus dis porttitor habitant nunc vivamus, duis cubilia blandit non donec justo dictumst, praesent vitae nulla nam pulvinar urna. Adipiscing adipiscing justo urna pulvinar imperdiet nullam, vitae fusce rhoncus proin nonummy suscipit, ullamcorper amet et non potenti platea ultrices, mauris nullam sapien nunc justo vel, eu semper pellentesque arcu fusce augue. Malesuada mauris nibh sit a a scelerisque, velit sem lectus tellus convallis consectetuer, ultricies auctor a ante eros amet sed.</p>\n<p>Risus lacus duis leo platea wisi, felis maecenas rutrum in id in donec, non id a potenti libero eget, posuere elit ea sed pellentesque quis. Sunt lacus urna lorem elit duis, nibh donec purus quisque consectetuer dolor, neque vestibulum proin ornare eros nonummy phasellus. Iaculis cras eu at egestas dolor montes, viverra quisque malesuada consectetuer semper maecenas, a sed vitae donec tempor aliqua metus, ornare mollis suscipit et erat fusce, sit orci aut auctor elementum fames aliquam. Platea dui integer magnis non metus, minus dignissimos ante massa nostra et, rutrum sapien egestas quis sapien donec donec. Erat sit a eros aenean natoque, quam libero id lorem enim proin, lorem ipsum fermentum mattis metus et. Aliquam aliquet suscipit purus conubia at neque, platea vivamus vestibulum nulla quibusdam senectus, et morbi lectus malesuada gravida donec, elementum sit convallis pellentesque velit amet. Et eveniet viverra vehicula consectetuer justo, provident sed commodo non lacinia velit, tempor phasellus vel leo nisl cras, vivamus et arcu interdum dui eu amet. Volutpat wisi rhoncus vel turpis diam quibusdam, dapibus elit est quisque cubilia mauris, nulla elit magna tempor accumsan bibendum, lorem varius sed interdum eget mattis, scelerisque egestas feugiat donec dui molestie. Leo facilisis nisl sit montes ligula sed, enim commodo consectetuer nunc est et, ut sed vehicula dolor luctus elit. Fermentum cras donec eget nibh est vel, sed justo risus et pharetra diam, eu vivamus egestas ligula risus diam, sed justo eget hac ut mauris. Vestibulum diam nec vitae mi eget suspendisse, aenean arcu purus facilisis purus class in, id aliquam sit id scelerisque sapien etiam. Ut nullam sit sed at mauris lobortis, consequat dolor autem ipsum euismod nulla, elit quis proin eget conubia varius, erat arcu massa mus in mauris, scelerisque ut eu sollicitudin libero leo urna.</p>\n<p>Consectetuer luctus tempor elit ut dolor ligula, quis dui per dui hendrerit ante sagittis, in quisque pretium in eleifend enim. Condimentum iaculis vitae feugiat dis tellus vel, lectus dolor nec dui nulla nascetur, et pellentesque curabitur lorem leo velit eget. Id nascetur arcu lobortis suspendisse imperdiet urna, natoque nascetur ante in porta a, interdum hendrerit mi bibendum platea tellus, urna in enim ornare vestibulum faucibus enim. Leo fusce egestas ante nec volutpat, in tempor vel facilisis potenti ut, pede at non lorem a commodo, nulla dolor orci interdum vestibulum nulla. Dui nulla vestibulum quisque a pharetra porta, integer nec ipsum nec sed dui pharetra, magna et dignissim ipsum sed dictum, litora eros vivamus scelerisque libero ipsum. Sed ac ac lorem molestie adipiscing morbi, pellentesque imperdiet nunc quis morbi amet ante, libero dui ligula nec risus neque et, velit nonummy phasellus et facilisi amet, ligula in elementum non sapien pulvinar faucibus. Eu leo ut posuere sed aliquet, tincidunt vel urna volutpat tempus sem, sit felis aliquet vestibulum condimentum sit, amet nibh vel tellus purus ullamcorper libero, nulla vestibulum pede ut vestibulum pretium. Eu nulla vestibulum a neque in metus, quisquam nam sed cursus eget luctus, pede ultrices nec sed dignissim pellentesque, sit class cursus metus nulla placerat mauris, consequat mollis neque vivamus amet pede. Mauris dolor nulla diam eros bibendum, quam ante vestibulum morbi non ligula vel, molestie curabitur rhoncus nulla euismod interdum non. Nulla fringilla lorem mollis ad massa, sit molestie nibh lorem arcu volutpat, accumsan commodo lectus eu et donec, sit tempor tempus rutrum in curabitur amet. Nec urna euismod a tincidunt commodo, eu pede turpis libero vitae viverra, ante vestibulum nam non habitasse potenti, mauris imperdiet in in nunc convallis. Et nostra wisi in est accumsan vehicula, quisque vitae felis mauris sed vulputate nec, ante imperdiet sollicitudin massa iaculis massa sit.</p>\n<p>Quam libero nulla netus eu porta curae, ut nulla bibendum facilisis et urna sed, quis congue vestibulum aliquam interdum etiam. Nulla vel lobortis ullamcorper vitae excepturi, neque urna feugiat lectus vel lacinia, massa pretium orci eu metus neque vulputate. Imperdiet ac velit rhoncus nulla malesuada nullam, nec pulvinar justo gravida lorem rutrum magna, habitasse repudiandae mi eros vestibulum ante, nec euismod dui iaculis in turpis pretium, ac id metus egestas proin lacus lectus. Laoreet lorem nec vitae risus erat arcu, vitae quam ut in ante tristique, porta dolor pede quam et odio nam, arcu lacus sem congue ante cursus massa. Et mattis sagittis erat accumsan fusce quam, vehicula ligula beatae natoque fusce sodales conubia, habitasse metus cum magnis viverra nam cursus, egestas urna wisi primis blandit eu magna, eget libero elit lacus lorem dis aliquam. Ut mauris ante natoque lacus massa, justo a lectus sodales enim adipiscing id, accumsan ut ipsum vestibulum sed enim auctor, vitae congue tincidunt id phasellus lacinia scelerisque, tincidunt sapien nulla euismod volutpat iaculis. Platea sociis nec aliquet nec molestie, in mi et augue sapien in vivamus, integer fames proin vitae in ullamcorper et. Fringilla etiam sapiente rhoncus suspendisse nec id, lobortis cras eget egestas dui ac nec, justo lacus ut lorem bibendum quia eros, eget a gravida id donec nunc suscipit, porta sed in sodales non rutrum. Lectus vel dui elementum pellentesque magna aliquam, vitae non sit pede et fusce nibh, id id deserunt ornare dui sit condimentum, in adipiscing imperdiet turpis nam aliquet, facilisis metus magna lacus wisi facilisis tortor. Vulputate elit accumsan quam amet ligula, suspendisse lacus mi nonummy integer urna, libero nulla nunc varius in odio, laoreet nulla amet placerat amet nec. Consectetuer vel massa hendrerit vitae iaculis id, sed ut ut laudantium odio in, elit vestibulum duis ante maecenas interdum in, neque vehicula ultrices varius in quam, pede tellus pellentesque sed nullam quis.</p>\n</div>\n</body>\n</html>"
  },
  {
    "path": "samples/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\n// for using SNAPSHOT\n//repositories {\n//    maven {\n//        url uri('https://oss.sonatype.org/content/repositories/snapshots/')\n//    }\n//}\n\ndependencies {\n    compile 'com.android.support:appcompat-v7:23.1.1'\n    compile 'com.nineoldandroids:library:2.4.0'\n    compile 'com.melnykov:floatingactionbutton:1.0.7'\n    debugCompile project(':library')\n    // Release build uses the synced latest version\n    releaseCompile \"com.github.ksoichiro:android-observablescrollview:${SYNCED_VERSION_NAME}\"\n\n    // for using SNAPSHOT\n    //compile \"com.github.ksoichiro:android-observablescrollview:$VERSION_NAME\"\n}\n\napply from: \"${rootDir}/gradle/version.gradle\"\nproject.ext.versionInfo.releaseVersionName = SYNCED_VERSION_NAME\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.2\"\n\n    defaultConfig {\n        applicationId \"com.github.ksoichiro.android.observablescrollview.samples\"\n        versionCode 5\n        versionName \"1.3.0\"\n    }\n\n    productFlavors {\n        // for development\n        dev {\n        }\n\n        // for Google Play Store release\n        play {\n            // The first version is removed from the Google Play store\n            // due to a violation of the branding guidelines.\n            // Therefore the suffix \"sample\" is replaced to \"sample2\".\n            applicationId \"com.github.ksoichiro.android.observablescrollview.samples2\"\n        }\n    }\n\n    signingConfigs {\n        release {\n            def filePrivateProperties = file(\"private.properties\")\n            if (filePrivateProperties.exists()) {\n                Properties propsPrivate = new Properties()\n                propsPrivate.load(new FileInputStream(filePrivateProperties))\n\n                storeFile file(propsPrivate['key.store'])\n                keyAlias propsPrivate['key.alias']\n                storePassword propsPrivate['key.store.password']\n                keyPassword propsPrivate['key.alias.password']\n            }\n        }\n    }\n\n    buildTypes {\n        debug {\n            applicationIdSuffix \".debug\"\n            versionNameSuffix \"-debug\"\n        }\n\n        release {\n            minifyEnabled true\n            shrinkResources true\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n            def filePrivateProperties = file(\"private.properties\")\n            if (filePrivateProperties.exists()) {\n                signingConfig signingConfigs.release\n            }\n        }\n    }\n\n    sourceSets {\n        main {\n            manifest.srcFile 'AndroidManifest.xml'\n            res.srcDirs = ['res']\n            assets.srcDirs = ['assets']\n        }\n    }\n\n    lintOptions {\n        abortOnError false\n    }\n\n    // Rename APK files\n    applicationVariants.all { variant ->\n        def output = variant.outputs.get(0)\n        File apk = output.outputFile\n        String newName = output.outputFile.name.replace(\".apk\", \"-${variant.mergedFlavor.versionCode}-${variant.mergedFlavor.versionName}-${project.ext.versionInfo.build}.apk\")\n                .replace(\"app-\", \"${variant.mergedFlavor.applicationId}-\")\n        output.outputFile = new File(apk.parentFile, newName)\n    }\n}\n"
  },
  {
    "path": "samples/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Applications/adt-bundle-mac-x86_64-20131030/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "samples/res/color/tab_text_color.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:color=\"#fff\" android:state_selected=\"true\" />\n    <item android:color=\"#9fff\" />\n\n</selector>"
  },
  {
    "path": "samples/res/drawable/gradient_header_background.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <gradient\n        android:angle=\"270\"\n        android:endColor=\"#00616161\"\n        android:startColor=\"#CC616161\" />\n\n</shape>\n"
  },
  {
    "path": "samples/res/drawable/sliding_header_overlay.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <gradient\n        android:angle=\"270\"\n        android:endColor=\"#00009688\"\n        android:startColor=\"#FF009688\" />\n\n</shape>\n"
  },
  {
    "path": "samples/res/layout/activity_about.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fillViewport=\"true\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:padding=\"@dimen/margin_standard\">\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"@dimen/margin_short\"\n            android:gravity=\"center\"\n            android:text=\"@string/app_name\"\n            android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"@dimen/margin_standard\"\n            android:gravity=\"center\"\n            android:text=\"@string/msg_about_description\" />\n\n        <ImageView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:contentDescription=\"@string/desc_about_app\"\n            android:src=\"@drawable/ic_launcher\" />\n\n        <TextView\n            android:id=\"@+id/app_version\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:layout_marginTop=\"@dimen/margin_standard\"\n            android:text=\"@string/msg_app_version\" />\n\n        <TextView\n            android:id=\"@+id/lib_version\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:layout_marginTop=\"@dimen/margin_short\"\n            android:text=\"@string/msg_lib_version\" />\n\n        <TextView\n            android:id=\"@+id/copyright\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"@dimen/margin_standard\"\n            android:layout_marginTop=\"@dimen/margin_short\"\n            android:gravity=\"center\"\n            android:text=\"@string/msg_copyright\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/margin_standard\"\n            android:gravity=\"center\"\n            android:text=\"@string/msg_about_fork_me_on_github\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/margin_short\"\n            android:autoLink=\"web\"\n            android:gravity=\"center\"\n            android:text=\"@string/msg_about_github_url\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/margin_short\"\n            android:gravity=\"center\"\n            android:text=\"@string/msg_about_library_description\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"@dimen/margin_standard\"\n            android:layout_marginTop=\"@dimen/margin_short\"\n            android:gravity=\"center\"\n            android:text=\"@string/msg_android_trademark\" />\n\n        <include layout=\"@layout/divider\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/margin_standard\"\n            android:gravity=\"center\"\n            android:text=\"@string/license\"\n            android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/margin_short\"\n            android:text=\"@string/msg_license\" />\n\n        <LinearLayout\n            android:id=\"@+id/licenses\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/margin_short\"\n            android:orientation=\"vertical\" />\n    </LinearLayout>\n</ScrollView>\n"
  },
  {
    "path": "samples/res/layout/activity_actionbarcontrolgridview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableGridView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/grid\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:numColumns=\"2\"/>\n"
  },
  {
    "path": "samples/res/layout/activity_actionbarcontrollistview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableListView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/list\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n"
  },
  {
    "path": "samples/res/layout/activity_actionbarcontrolrecyclerview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/recycler\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:scrollbars=\"vertical\" />\n"
  },
  {
    "path": "samples/res/layout/activity_actionbarcontrolscrollview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fillViewport=\"true\">\n\n    <TextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"@dimen/activity_vertical_margin\"\n        android:layout_marginLeft=\"@dimen/activity_horizontal_margin\"\n        android:layout_marginRight=\"@dimen/activity_horizontal_margin\"\n        android:layout_marginTop=\"@dimen/activity_vertical_margin\"\n        android:text=\"@string/lipsum\" />\n\n</com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n"
  },
  {
    "path": "samples/res/layout/activity_actionbarcontrolwebview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableWebView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/web\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n"
  },
  {
    "path": "samples/res/layout/activity_fillgap3listview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipChildren=\"false\">\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout\n        android:id=\"@+id/scroll_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:clipChildren=\"false\"\n        android:layout_height=\"match_parent\">\n\n        <com.github.ksoichiro.android.observablescrollview.ObservableListView\n            android:id=\"@+id/scroll\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"@dimen/header_bar_height\"\n            android:background=\"@android:color/white\"\n            android:overScrollMode=\"never\" />\n\n        <FrameLayout\n            android:id=\"@+id/header\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:clipChildren=\"false\">\n\n            <View\n                android:id=\"@+id/header_background\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:background=\"?attr/colorPrimary\"\n                android:minHeight=\"@dimen/header_bar_height\" />\n\n            <LinearLayout\n                android:id=\"@+id/header_bar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:minHeight=\"@dimen/header_bar_height\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    android:id=\"@+id/title\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"?attr/actionBarSize\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center_vertical\"\n                    android:maxLines=\"1\"\n                    android:paddingLeft=\"@dimen/margin_standard\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"20sp\" />\n            </LinearLayout>\n        </FrameLayout>\n    </com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@android:color/transparent\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_fillgap3recyclerview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipChildren=\"false\">\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout\n        android:id=\"@+id/scroll_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:clipChildren=\"false\"\n        android:layout_height=\"match_parent\">\n\n        <com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView\n            android:id=\"@+id/scroll\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"@dimen/header_bar_height\"\n            android:background=\"@android:color/white\"\n            android:overScrollMode=\"never\" />\n\n        <FrameLayout\n            android:id=\"@+id/header\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:clipChildren=\"false\">\n\n            <View\n                android:id=\"@+id/header_background\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:background=\"?attr/colorPrimary\"\n                android:minHeight=\"@dimen/header_bar_height\" />\n\n            <LinearLayout\n                android:id=\"@+id/header_bar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:minHeight=\"@dimen/header_bar_height\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    android:id=\"@+id/title\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"?attr/actionBarSize\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center_vertical\"\n                    android:maxLines=\"1\"\n                    android:paddingLeft=\"@dimen/margin_standard\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"20sp\" />\n            </LinearLayout>\n        </FrameLayout>\n    </com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@android:color/transparent\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_fillgap3scrollview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipChildren=\"false\">\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout\n        android:id=\"@+id/scroll_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:clipChildren=\"false\"\n        android:layout_height=\"match_parent\">\n\n        <com.github.ksoichiro.android.observablescrollview.ObservableScrollView\n            android:id=\"@+id/scroll\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"@dimen/header_bar_height\"\n            android:fillViewport=\"true\"\n            android:overScrollMode=\"never\"\n            android:scrollbars=\"none\">\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@android:color/white\"\n                android:paddingBottom=\"@dimen/activity_vertical_margin\"\n                android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n                android:paddingRight=\"@dimen/activity_horizontal_margin\"\n                android:paddingTop=\"@dimen/activity_vertical_margin\"\n                android:text=\"@string/lipsum_short\" />\n        </com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n\n        <FrameLayout\n            android:id=\"@+id/header\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:clipChildren=\"false\">\n\n            <View\n                android:id=\"@+id/header_background\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:background=\"?attr/colorPrimary\"\n                android:minHeight=\"@dimen/header_bar_height\" />\n\n            <LinearLayout\n                android:id=\"@+id/header_bar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:minHeight=\"@dimen/header_bar_height\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    android:id=\"@+id/title\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"?attr/actionBarSize\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center_vertical\"\n                    android:maxLines=\"1\"\n                    android:paddingLeft=\"@dimen/margin_standard\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"20sp\" />\n            </LinearLayout>\n        </FrameLayout>\n    </com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@android:color/transparent\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_fillgaplistview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipChildren=\"false\">\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <View\n        android:id=\"@+id/list_background\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@android:color/white\" />\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableListView\n        android:id=\"@+id/scroll\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n    <FrameLayout\n        android:id=\"@+id/header\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:clipChildren=\"false\">\n\n        <View\n            android:id=\"@+id/header_background\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/header_bar_height\"\n            android:background=\"?attr/colorPrimary\"\n            android:minHeight=\"@dimen/header_bar_height\" />\n\n        <LinearLayout\n            android:id=\"@+id/header_bar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/header_bar_height\"\n            android:minHeight=\"@dimen/header_bar_height\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:id=\"@+id/title\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:ellipsize=\"end\"\n                android:gravity=\"center_vertical\"\n                android:maxLines=\"1\"\n                android:paddingLeft=\"@dimen/margin_standard\"\n                android:textColor=\"@android:color/white\"\n                android:textSize=\"20sp\" />\n        </LinearLayout>\n    </FrameLayout>\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_fillgaprecyclerview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipChildren=\"false\">\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <View\n        android:id=\"@+id/list_background\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@android:color/white\" />\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView\n        android:id=\"@+id/scroll\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n    <FrameLayout\n        android:id=\"@+id/header\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:clipChildren=\"false\">\n\n        <View\n            android:id=\"@+id/header_background\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/header_bar_height\"\n            android:background=\"?attr/colorPrimary\"\n            android:minHeight=\"@dimen/header_bar_height\" />\n\n        <LinearLayout\n            android:id=\"@+id/header_bar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/header_bar_height\"\n            android:minHeight=\"@dimen/header_bar_height\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:id=\"@+id/title\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:ellipsize=\"end\"\n                android:gravity=\"center_vertical\"\n                android:maxLines=\"1\"\n                android:paddingLeft=\"@dimen/margin_standard\"\n                android:textColor=\"@android:color/white\"\n                android:textSize=\"20sp\" />\n        </LinearLayout>\n    </FrameLayout>\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_fillgapscrollview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipChildren=\"false\">\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableScrollView\n        android:id=\"@+id/scroll\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"@dimen/flexible_space_image_height\"\n        android:fillViewport=\"true\"\n        android:overScrollMode=\"never\"\n        android:scrollbars=\"none\">\n\n        <TextView\n            android:id=\"@+id/container\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@android:color/white\"\n            android:paddingBottom=\"@dimen/activity_vertical_margin\"\n            android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n            android:paddingRight=\"@dimen/activity_horizontal_margin\"\n            android:paddingTop=\"@dimen/activity_vertical_margin\"\n            android:text=\"@string/lipsum\" />\n    </com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n\n    <FrameLayout\n        android:id=\"@+id/header\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:clipChildren=\"false\">\n\n        <View\n            android:id=\"@+id/header_background\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/header_bar_height\"\n            android:background=\"?attr/colorPrimary\"\n            android:minHeight=\"@dimen/header_bar_height\" />\n\n        <LinearLayout\n            android:id=\"@+id/header_bar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/header_bar_height\"\n            android:minHeight=\"@dimen/header_bar_height\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:id=\"@+id/title\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:ellipsize=\"end\"\n                android:gravity=\"center_vertical\"\n                android:maxLines=\"1\"\n                android:paddingLeft=\"@dimen/margin_standard\"\n                android:textColor=\"@android:color/white\"\n                android:textSize=\"20sp\" />\n        </LinearLayout>\n    </FrameLayout>\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_flexiblespacetoolbarscrollview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableScrollView\n        android:id=\"@+id/scroll\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:fillViewport=\"true\"\n        android:scrollbars=\"none\">\n\n        <FrameLayout\n            android:id=\"@+id/body\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingTop=\"@dimen/flexible_space_height\">\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@android:color/white\"\n                android:paddingBottom=\"@dimen/activity_vertical_margin\"\n                android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n                android:paddingRight=\"@dimen/activity_horizontal_margin\"\n                android:paddingTop=\"@dimen/activity_vertical_margin\"\n                android:text=\"@string/lipsum\" />\n        </FrameLayout>\n\n    </com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n\n    <View\n        android:id=\"@+id/flexible_space\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:background=\"@color/primary\" />\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/colorPrimary\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n\n    <!--\n    android:layout_marginLeft on FrameLayout seems to be ignored on Android 2.3\n    so add a parent RelativeLayout and set padding to it.\n    -->\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:paddingLeft=\"@dimen/toolbar_margin_start\">\n\n        <TextView\n            android:id=\"@+id/title\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:gravity=\"center_vertical\"\n            android:maxLines=\"1\"\n            android:minHeight=\"?attr/actionBarSize\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"20sp\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\">\n\n            <View\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                android:background=\"@android:color/transparent\" />\n\n            <View\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/flexible_space_height\"\n                android:background=\"@android:color/transparent\" />\n        </LinearLayout>\n    </RelativeLayout>\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_flexiblespacetoolbarwebview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n             android:layout_width=\"match_parent\"\n             android:layout_height=\"match_parent\">\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableScrollView\n        android:id=\"@+id/scroll\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:scrollbars=\"none\">\n\n        <FrameLayout\n            android:id=\"@+id/webViewContainer\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\">\n\n            <WebView\n                android:id=\"@+id/webView\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@android:color/transparent\"\n                android:scrollbars=\"none\"\n                />\n        </FrameLayout>\n\n    </com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n\n    <View\n        android:id=\"@+id/flexible_space\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:background=\"@color/primary\"/>\n\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/colorPrimary\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\"/>\n\n    <!--\n    android:layout_marginLeft on FrameLayout seems to be ignored on Android 2.3\n    so add a parent RelativeLayout and set padding to it.\n    -->\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:paddingLeft=\"@dimen/toolbar_margin_start\">\n\n        <TextView\n            android:id=\"@+id/title\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:gravity=\"center_vertical\"\n            android:maxLines=\"1\"\n            android:minHeight=\"?attr/actionBarSize\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"20sp\"/>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\">\n\n            <View\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                android:background=\"@android:color/transparent\"/>\n\n            <View\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/flexible_space_height\"\n                android:background=\"@android:color/transparent\"/>\n        </LinearLayout>\n    </RelativeLayout>\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_flexiblespacewithimagegridview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <View\n        android:id=\"@+id/overlay\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:background=\"?attr/colorPrimary\" />\n\n    <View\n        android:id=\"@+id/list_background\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@android:color/white\" />\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableGridView\n        android:id=\"@+id/list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:numColumns=\"2\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:paddingEnd=\"@dimen/margin_standard\"\n        android:paddingLeft=\"@dimen/margin_standard\"\n        android:paddingStart=\"@dimen/margin_standard\">\n\n        <TextView\n            android:id=\"@+id/title\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:gravity=\"center_vertical\"\n            android:maxLines=\"1\"\n            android:minHeight=\"?attr/actionBarSize\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"20sp\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/flexible_space_image_height\"\n            android:background=\"@android:color/transparent\" />\n    </LinearLayout>\n\n    <com.melnykov.fab.FloatingActionButton\n        android:id=\"@+id/fab\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:scaleType=\"center\"\n        android:layout_gravity=\"left|top\"\n        app:fab_colorNormal=\"@color/accentLight\"\n        app:fab_colorPressed=\"@color/accent\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_flexiblespacewithimagelistview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <View\n        android:id=\"@+id/overlay\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:background=\"?attr/colorPrimary\" />\n\n    <View\n        android:id=\"@+id/list_background\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@android:color/white\" />\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableListView\n        android:id=\"@+id/list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:paddingEnd=\"@dimen/margin_standard\"\n        android:paddingLeft=\"@dimen/margin_standard\"\n        android:paddingStart=\"@dimen/margin_standard\">\n\n        <TextView\n            android:id=\"@+id/title\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:gravity=\"center_vertical\"\n            android:maxLines=\"1\"\n            android:minHeight=\"?attr/actionBarSize\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"20sp\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/flexible_space_image_height\"\n            android:background=\"@android:color/transparent\" />\n    </LinearLayout>\n\n    <com.melnykov.fab.FloatingActionButton\n        android:id=\"@+id/fab\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:scaleType=\"center\"\n        android:layout_gravity=\"left|top\"\n        app:fab_colorNormal=\"@color/accentLight\"\n        app:fab_colorPressed=\"@color/accent\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_flexiblespacewithimagerecyclerview.xml",
    "content": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <View\n        android:id=\"@+id/overlay\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:background=\"?attr/colorPrimary\" />\n\n    <View\n        android:id=\"@+id/list_background\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@android:color/white\" />\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView\n            android:id=\"@+id/recycler\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:scrollbars=\"vertical\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:paddingEnd=\"@dimen/margin_standard\"\n        android:paddingLeft=\"@dimen/margin_standard\"\n        android:paddingStart=\"@dimen/margin_standard\">\n\n        <TextView\n            android:id=\"@+id/title\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:gravity=\"center_vertical\"\n            android:maxLines=\"1\"\n            android:minHeight=\"?attr/actionBarSize\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"20sp\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/flexible_space_image_height\"\n            android:background=\"@android:color/transparent\" />\n    </LinearLayout>\n\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_flexiblespacewithimagescrollview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <View\n        android:id=\"@+id/overlay\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:background=\"?attr/colorPrimary\" />\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableScrollView\n        android:id=\"@+id/scroll\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:overScrollMode=\"never\"\n        android:scrollbars=\"none\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\">\n\n            <View\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/flexible_space_image_height\"\n                android:background=\"@android:color/transparent\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@android:color/white\"\n                android:paddingBottom=\"@dimen/activity_vertical_margin\"\n                android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n                android:paddingRight=\"@dimen/activity_horizontal_margin\"\n                android:paddingTop=\"@dimen/activity_vertical_margin\"\n                android:text=\"@string/lipsum\" />\n        </LinearLayout>\n    </com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:paddingLeft=\"@dimen/margin_standard\">\n\n        <TextView\n            android:id=\"@+id/title\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:gravity=\"center_vertical\"\n            android:maxLines=\"1\"\n            android:minHeight=\"?attr/actionBarSize\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"20sp\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/flexible_space_image_height\"\n            android:background=\"@android:color/transparent\" />\n    </LinearLayout>\n\n    <com.melnykov.fab.FloatingActionButton\n        android:id=\"@+id/fab\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"left|top\"\n        android:scaleType=\"center\"\n        app:fab_colorNormal=\"@color/accentLight\"\n        app:fab_colorPressed=\"@color/accent\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_flexiblespacewithimagewithviewpagertab.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/root\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <FrameLayout\n        android:id=\"@+id/pager_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <android.support.v4.view.ViewPager\n            android:id=\"@+id/pager\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\" />\n    </FrameLayout>\n\n    <View\n        android:id=\"@+id/overlay\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:background=\"?attr/colorPrimary\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:paddingEnd=\"@dimen/margin_standard\"\n        android:paddingLeft=\"@dimen/margin_standard\"\n        android:paddingStart=\"@dimen/margin_standard\">\n\n        <TextView\n            android:id=\"@+id/title\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:gravity=\"center_vertical\"\n            android:maxLines=\"1\"\n            android:minHeight=\"?attr/actionBarSize\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"20sp\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/flexible_space_image_height\"\n            android:background=\"@android:color/transparent\" />\n    </LinearLayout>\n\n    <com.google.samples.apps.iosched.ui.widget.SlidingTabLayout\n        android:id=\"@+id/sliding_tabs\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/tab_height\"\n        android:background=\"@android:color/transparent\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_flexiblespacewithimagewithviewpagertab2.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <FrameLayout\n        android:id=\"@+id/pager_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <android.support.v4.view.ViewPager\n            android:id=\"@+id/pager\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\" />\n    </FrameLayout>\n\n    <FrameLayout\n        android:id=\"@+id/header\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/colorPrimary\">\n\n        <ImageView\n            android:id=\"@+id/image\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/flexible_space_image_height\"\n            android:scaleType=\"centerCrop\"\n            android:src=\"@drawable/example\" />\n\n        <View\n            android:id=\"@+id/overlay\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/flexible_space_image_height\"\n            android:background=\"?attr/colorPrimary\" />\n\n        <android.support.v7.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@android:color/transparent\"\n            android:minHeight=\"?attr/actionBarSize\"\n            app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n            app:theme=\"@style/Toolbar\" />\n\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/flexible_space_image_height\"\n            android:paddingEnd=\"@dimen/margin_standard\"\n            android:paddingLeft=\"@dimen/margin_standard\"\n            android:paddingStart=\"@dimen/margin_standard\">\n\n            <TextView\n                android:id=\"@+id/title\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:ellipsize=\"end\"\n                android:layout_alignParentBottom=\"true\"\n                android:layout_marginBottom=\"@dimen/tab_height\"\n                android:gravity=\"center_vertical\"\n                android:maxLines=\"1\"\n                android:minHeight=\"?attr/actionBarSize\"\n                android:textColor=\"@android:color/white\"\n                android:textSize=\"20sp\" />\n\n            <View\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/flexible_space_image_height\"\n                android:background=\"@android:color/transparent\" />\n        </RelativeLayout>\n\n        <com.google.samples.apps.iosched.ui.widget.SlidingTabLayout\n            android:id=\"@+id/sliding_tabs\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/tab_height\"\n            android:background=\"@android:color/transparent\" />\n    </FrameLayout>\n\n</com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>"
  },
  {
    "path": "samples/res/layout/activity_fragmentactionbarcontrol.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n"
  },
  {
    "path": "samples/res/layout/activity_fragmenttransition.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?attr/actionBarSize\"\n        android:layout_weight=\"0\"\n        android:background=\"?attr/colorPrimary\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n\n    <FrameLayout\n        android:id=\"@+id/fragment\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_weight=\"1\" />\n</LinearLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_handletouchgridview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableGridView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:numColumns=\"2\" />\n"
  },
  {
    "path": "samples/res/layout/activity_handletouchlistview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableListView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n"
  },
  {
    "path": "samples/res/layout/activity_handletouchrecyclerview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n"
  },
  {
    "path": "samples/res/layout/activity_handletouchscrollview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fillViewport=\"true\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:paddingBottom=\"@dimen/activity_vertical_margin\"\n        android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n        android:paddingRight=\"@dimen/activity_horizontal_margin\"\n        android:paddingTop=\"@dimen/activity_vertical_margin\">\n\n        <Button\n            android:id=\"@+id/button1\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"Button\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/margin_short\"\n            android:text=\"@string/lipsum_short\" />\n\n        <Button\n            android:id=\"@+id/button2\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/margin_short\"\n            android:text=\"Button\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/margin_short\"\n            android:text=\"@string/lipsum_short\" />\n\n        <Button\n            android:id=\"@+id/button3\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/margin_short\"\n            android:text=\"Button\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/margin_short\"\n            android:text=\"@string/lipsum_short\" />\n    </LinearLayout>\n</com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n"
  },
  {
    "path": "samples/res/layout/activity_handletouchwebview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableWebView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n"
  },
  {
    "path": "samples/res/layout/activity_main.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".MainActivity\"\n    android:orientation=\"vertical\"\n    >\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:minHeight=\"?attr/actionBarSize\"\n        android:background=\"?attr/colorPrimary\"\n        android:elevation=\"@dimen/toolbar_elevation\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\"\n        >\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"match_parent\"\n            android:orientation=\"horizontal\">\n            <View\n                android:layout_width=\"1dp\"\n                android:layout_height=\"match_parent\"\n                android:layout_marginLeft=\"@dimen/margin_short\"\n                android:layout_marginRight=\"@dimen/margin_short\"\n                android:background=\"#99ffffff\" />\n\n            <Spinner\n                style=\"@style/Spinner\"\n                android:id=\"@+id/spinner_toolbar\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"/>\n        </LinearLayout>\n\n    </android.support.v7.widget.Toolbar>\n\n    <FrameLayout\n        android:foreground=\"?android:windowContentOverlay\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n        <ListView\n            android:id=\"@android:id/list\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            />\n    </FrameLayout>\n</LinearLayout>"
  },
  {
    "path": "samples/res/layout/activity_parallaxtoolbargridview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <View\n        android:id=\"@+id/list_background\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@android:color/white\" />\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableGridView\n        android:id=\"@+id/list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:numColumns=\"2\" />\n\n    <include layout=\"@layout/gradient_header\" />\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/colorPrimary\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_parallaxtoolbarlistview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <View\n        android:id=\"@+id/list_background\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@android:color/white\" />\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableListView\n        android:id=\"@+id/list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n    <include layout=\"@layout/gradient_header\" />\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/colorPrimary\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_parallaxtoolbarscrollview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableScrollView\n        android:id=\"@+id/scroll\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:fillViewport=\"true\">\n\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\">\n\n            <ImageView\n                android:id=\"@+id/image\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/parallax_image_height\"\n                android:scaleType=\"centerCrop\"\n                android:src=\"@drawable/example\" />\n\n            <View\n                android:id=\"@+id/anchor\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/parallax_image_height\"\n                android:minHeight=\"@dimen/parallax_image_height\" />\n\n            <TextView\n                android:id=\"@+id/body\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_below=\"@id/anchor\"\n                android:background=\"@android:color/white\"\n                android:paddingBottom=\"@dimen/activity_vertical_margin\"\n                android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n                android:paddingRight=\"@dimen/activity_horizontal_margin\"\n                android:paddingTop=\"@dimen/activity_vertical_margin\"\n                android:text=\"@string/lipsum\" />\n        </RelativeLayout>\n\n    </com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n\n    <include layout=\"@layout/gradient_header\" />\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/colorPrimary\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_slidingupgridview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipChildren=\"false\">\n\n    <!--\n    Dummy background contents.\n    You can replace this to map or something.\n    -->\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#E91E63\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#3F51B5\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#2196F3\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#03A9F4\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#8BC34A\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#FFEB3B\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#FFC107\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#FF9800\" />\n    </LinearLayout>\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout\n        android:id=\"@+id/scroll_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipChildren=\"false\">\n\n        <com.github.ksoichiro.android.observablescrollview.ObservableGridView\n            android:id=\"@+id/scroll\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"@dimen/header_bar_height\"\n            android:background=\"@android:color/white\"\n            android:numColumns=\"2\"\n            android:overScrollMode=\"never\" />\n\n        <FrameLayout\n            android:id=\"@+id/header\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:clipChildren=\"false\">\n\n            <View\n                android:id=\"@+id/header_background\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:background=\"?attr/colorPrimary\"\n                android:minHeight=\"@dimen/header_bar_height\" />\n\n            <LinearLayout\n                android:id=\"@+id/header_bar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:minHeight=\"@dimen/header_bar_height\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    android:id=\"@+id/title\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"?attr/actionBarSize\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center_vertical\"\n                    android:maxLines=\"1\"\n                    android:paddingLeft=\"@dimen/margin_standard\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"20sp\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:id=\"@+id/header_overlay\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:orientation=\"vertical\">\n\n                <View\n                    android:id=\"@+id/header_flexible_space\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"0dp\"\n                    android:background=\"?attr/colorPrimary\" />\n\n                <View\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/sliding_overlay_blur_size\"\n                    android:background=\"@drawable/sliding_header_overlay\"\n                    android:minHeight=\"@dimen/sliding_overlay_blur_size\" />\n            </LinearLayout>\n\n            <com.melnykov.fab.FloatingActionButton\n                android:id=\"@+id/fab\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:scaleType=\"center\"\n                app:fab_colorNormal=\"@color/accentLight\"\n                app:fab_colorPressed=\"@color/accent\" />\n        </FrameLayout>\n    </com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"top\"\n        android:background=\"?attr/colorPrimary\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n\n    <TextView\n        android:id=\"@+id/toolbar_title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?attr/actionBarSize\"\n        android:ellipsize=\"end\"\n        android:gravity=\"center_vertical\"\n        android:maxLines=\"1\"\n        android:paddingLeft=\"@dimen/toolbar_margin_start\"\n        android:paddingRight=\"@dimen/margin_extra_short\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"20sp\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_slidinguplistview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipChildren=\"false\">\n\n    <!--\n    Dummy background contents.\n    You can replace this to map or something.\n    -->\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#E91E63\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#3F51B5\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#2196F3\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#03A9F4\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#8BC34A\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#FFEB3B\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#FFC107\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#FF9800\" />\n    </LinearLayout>\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout\n        android:id=\"@+id/scroll_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:clipChildren=\"false\"\n        android:layout_height=\"match_parent\">\n\n        <com.github.ksoichiro.android.observablescrollview.ObservableListView\n            android:id=\"@+id/scroll\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"@dimen/header_bar_height\"\n            android:background=\"@android:color/white\"\n            android:overScrollMode=\"never\" />\n\n        <FrameLayout\n            android:id=\"@+id/header\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:clipChildren=\"false\">\n\n            <View\n                android:id=\"@+id/header_background\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:background=\"?attr/colorPrimary\"\n                android:minHeight=\"@dimen/header_bar_height\" />\n\n            <LinearLayout\n                android:id=\"@+id/header_bar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:minHeight=\"@dimen/header_bar_height\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    android:id=\"@+id/title\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"?attr/actionBarSize\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center_vertical\"\n                    android:maxLines=\"1\"\n                    android:paddingLeft=\"@dimen/margin_standard\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"20sp\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:id=\"@+id/header_overlay\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:orientation=\"vertical\">\n\n                <View\n                    android:id=\"@+id/header_flexible_space\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"0dp\"\n                    android:background=\"?attr/colorPrimary\" />\n\n                <View\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/sliding_overlay_blur_size\"\n                    android:background=\"@drawable/sliding_header_overlay\"\n                    android:minHeight=\"@dimen/sliding_overlay_blur_size\" />\n            </LinearLayout>\n\n            <com.melnykov.fab.FloatingActionButton\n                android:id=\"@+id/fab\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:scaleType=\"center\"\n                app:fab_colorNormal=\"@color/accentLight\"\n                app:fab_colorPressed=\"@color/accent\" />\n        </FrameLayout>\n    </com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"top\"\n        android:background=\"?attr/colorPrimary\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n\n    <TextView\n        android:id=\"@+id/toolbar_title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?attr/actionBarSize\"\n        android:ellipsize=\"end\"\n        android:gravity=\"center_vertical\"\n        android:maxLines=\"1\"\n        android:paddingLeft=\"@dimen/toolbar_margin_start\"\n        android:paddingRight=\"@dimen/margin_extra_short\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"20sp\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_slidinguprecyclerview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipChildren=\"false\">\n\n    <!--\n    Dummy background contents.\n    You can replace this to map or something.\n    -->\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#E91E63\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#3F51B5\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#2196F3\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#03A9F4\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#8BC34A\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#FFEB3B\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#FFC107\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#FF9800\" />\n    </LinearLayout>\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout\n        android:id=\"@+id/scroll_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipChildren=\"false\">\n\n        <com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView\n            android:id=\"@+id/scroll\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"@dimen/header_bar_height\"\n            android:background=\"@android:color/white\" />\n\n        <FrameLayout\n            android:id=\"@+id/header\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:clipChildren=\"false\">\n\n            <View\n                android:id=\"@+id/header_background\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:background=\"?attr/colorPrimary\"\n                android:minHeight=\"@dimen/header_bar_height\" />\n\n            <LinearLayout\n                android:id=\"@+id/header_bar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:minHeight=\"@dimen/header_bar_height\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    android:id=\"@+id/title\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"?attr/actionBarSize\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center_vertical\"\n                    android:maxLines=\"1\"\n                    android:paddingLeft=\"@dimen/margin_standard\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"20sp\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:id=\"@+id/header_overlay\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:orientation=\"vertical\">\n\n                <View\n                    android:id=\"@+id/header_flexible_space\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"0dp\"\n                    android:background=\"?attr/colorPrimary\" />\n\n                <View\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/sliding_overlay_blur_size\"\n                    android:background=\"@drawable/sliding_header_overlay\"\n                    android:minHeight=\"@dimen/sliding_overlay_blur_size\" />\n            </LinearLayout>\n\n            <com.melnykov.fab.FloatingActionButton\n                android:id=\"@+id/fab\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:scaleType=\"center\"\n                app:fab_colorNormal=\"@color/accentLight\"\n                app:fab_colorPressed=\"@color/accent\" />\n        </FrameLayout>\n    </com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"top\"\n        android:background=\"?attr/colorPrimary\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n\n    <TextView\n        android:id=\"@+id/toolbar_title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?attr/actionBarSize\"\n        android:ellipsize=\"end\"\n        android:gravity=\"center_vertical\"\n        android:maxLines=\"1\"\n        android:paddingLeft=\"@dimen/toolbar_margin_start\"\n        android:paddingRight=\"@dimen/margin_extra_short\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"20sp\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_slidingupscrollview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipChildren=\"false\">\n\n    <!--\n    Dummy background contents.\n    You can replace this to map or something.\n    -->\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#E91E63\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#3F51B5\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#2196F3\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#03A9F4\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#8BC34A\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#FFEB3B\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#FFC107\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#FF9800\" />\n    </LinearLayout>\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout\n        android:id=\"@+id/scroll_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipChildren=\"false\">\n\n        <com.github.ksoichiro.android.observablescrollview.ObservableScrollView\n            android:id=\"@+id/scroll\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"@dimen/header_bar_height\"\n            android:background=\"@android:color/white\"\n            android:fillViewport=\"true\">\n\n            <TextView\n                android:id=\"@+id/container\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingBottom=\"@dimen/activity_vertical_margin\"\n                android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n                android:paddingRight=\"@dimen/activity_horizontal_margin\"\n                android:paddingTop=\"@dimen/activity_vertical_margin\"\n                android:text=\"@string/lipsum\" />\n        </com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n\n        <FrameLayout\n            android:id=\"@+id/header\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:clipChildren=\"false\">\n\n            <View\n                android:id=\"@+id/header_background\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:background=\"?attr/colorPrimary\"\n                android:minHeight=\"@dimen/header_bar_height\" />\n\n            <LinearLayout\n                android:id=\"@+id/header_bar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:minHeight=\"@dimen/header_bar_height\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    android:id=\"@+id/title\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"?attr/actionBarSize\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center_vertical\"\n                    android:maxLines=\"1\"\n                    android:paddingLeft=\"@dimen/margin_standard\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"20sp\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:id=\"@+id/header_overlay\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:orientation=\"vertical\">\n\n                <View\n                    android:id=\"@+id/header_flexible_space\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"0dp\"\n                    android:background=\"?attr/colorPrimary\" />\n\n                <View\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/sliding_overlay_blur_size\"\n                    android:background=\"@drawable/sliding_header_overlay\"\n                    android:minHeight=\"@dimen/sliding_overlay_blur_size\" />\n            </LinearLayout>\n\n            <com.melnykov.fab.FloatingActionButton\n                android:id=\"@+id/fab\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:scaleType=\"center\"\n                app:fab_colorNormal=\"@color/accentLight\"\n                app:fab_colorPressed=\"@color/accent\" />\n        </FrameLayout>\n    </com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"top\"\n        android:background=\"?attr/colorPrimary\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n\n    <TextView\n        android:id=\"@+id/toolbar_title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?attr/actionBarSize\"\n        android:ellipsize=\"end\"\n        android:gravity=\"center_vertical\"\n        android:maxLines=\"1\"\n        android:paddingLeft=\"@dimen/toolbar_margin_start\"\n        android:paddingRight=\"@dimen/margin_extra_short\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"20sp\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_slidingupwebview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipChildren=\"false\">\n\n    <!--\n    Dummy background contents.\n    You can replace this to map or something.\n    -->\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#E91E63\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#3F51B5\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#2196F3\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#03A9F4\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#8BC34A\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#FFEB3B\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#FFC107\" />\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"#FF9800\" />\n    </LinearLayout>\n\n    <ImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/flexible_space_image_height\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/example\" />\n\n    <com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout\n        android:id=\"@+id/scroll_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipChildren=\"false\">\n\n        <com.github.ksoichiro.android.observablescrollview.ObservableWebView\n            android:id=\"@+id/scroll\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"@dimen/header_bar_height\"\n            android:background=\"@android:color/white\"\n            android:overScrollMode=\"never\" />\n\n        <FrameLayout\n            android:id=\"@+id/header\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:clipChildren=\"false\">\n\n            <View\n                android:id=\"@+id/header_background\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:background=\"?attr/colorPrimary\"\n                android:minHeight=\"@dimen/header_bar_height\" />\n\n            <LinearLayout\n                android:id=\"@+id/header_bar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/header_bar_height\"\n                android:minHeight=\"@dimen/header_bar_height\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    android:id=\"@+id/title\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"?attr/actionBarSize\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center_vertical\"\n                    android:maxLines=\"1\"\n                    android:paddingLeft=\"@dimen/margin_standard\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"20sp\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:id=\"@+id/header_overlay\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:orientation=\"vertical\">\n\n                <View\n                    android:id=\"@+id/header_flexible_space\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"0dp\"\n                    android:background=\"?attr/colorPrimary\" />\n\n                <View\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/sliding_overlay_blur_size\"\n                    android:background=\"@drawable/sliding_header_overlay\"\n                    android:minHeight=\"@dimen/sliding_overlay_blur_size\" />\n            </LinearLayout>\n\n            <com.melnykov.fab.FloatingActionButton\n                android:id=\"@+id/fab\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:scaleType=\"center\"\n                app:fab_colorNormal=\"@color/accentLight\"\n                app:fab_colorPressed=\"@color/accent\" />\n        </FrameLayout>\n    </com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"top\"\n        android:background=\"?attr/colorPrimary\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n\n    <TextView\n        android:id=\"@+id/toolbar_title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?attr/actionBarSize\"\n        android:ellipsize=\"end\"\n        android:gravity=\"center_vertical\"\n        android:maxLines=\"1\"\n        android:paddingLeft=\"@dimen/toolbar_margin_start\"\n        android:paddingRight=\"@dimen/margin_extra_short\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"20sp\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_stickyheaderlistview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableListView\n        android:id=\"@+id/list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n    <LinearLayout\n        android:id=\"@+id/header\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@color/primary\"\n        android:orientation=\"vertical\">\n\n        <android.support.v7.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"?attr/colorPrimary\"\n            android:minHeight=\"?attr/actionBarSize\"\n            app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n            app:theme=\"@style/Toolbar\" />\n\n        <TextView\n            android:id=\"@+id/sticky\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:gravity=\"center_vertical\"\n            android:paddingLeft=\"@dimen/margin_standard\"\n            android:paddingRight=\"@dimen/margin_standard\"\n            android:text=\"@string/sticky_header\"\n            android:textAppearance=\"?android:attr/textAppearanceMedium\"\n            android:textColor=\"@android:color/white\" />\n    </LinearLayout>\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_stickyheaderrecyclerview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView\n        android:id=\"@+id/recycler\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:scrollbars=\"vertical\" />\n\n    <LinearLayout\n        android:id=\"@+id/header\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@color/primary\"\n        android:orientation=\"vertical\">\n\n        <android.support.v7.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"?attr/colorPrimary\"\n            android:minHeight=\"?attr/actionBarSize\"\n            app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n            app:theme=\"@style/Toolbar\" />\n\n        <TextView\n            android:id=\"@+id/sticky\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:gravity=\"center_vertical\"\n            android:paddingLeft=\"@dimen/margin_standard\"\n            android:paddingRight=\"@dimen/margin_standard\"\n            android:text=\"@string/sticky_header\"\n            android:textAppearance=\"?android:attr/textAppearanceMedium\"\n            android:textColor=\"@android:color/white\" />\n    </LinearLayout>\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_stickyheaderscrollview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableScrollView\n        android:id=\"@+id/scroll\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:fillViewport=\"true\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\">\n\n            <View\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                android:minHeight=\"?attr/actionBarSize\" />\n\n            <View\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                android:minHeight=\"?attr/actionBarSize\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginBottom=\"@dimen/activity_vertical_margin\"\n                android:layout_marginLeft=\"@dimen/activity_horizontal_margin\"\n                android:layout_marginRight=\"@dimen/activity_horizontal_margin\"\n                android:layout_marginTop=\"@dimen/activity_vertical_margin\"\n                android:text=\"@string/lipsum\" />\n        </LinearLayout>\n\n    </com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n\n    <LinearLayout\n        android:id=\"@+id/header\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@color/primary\"\n        android:orientation=\"vertical\">\n\n        <android.support.v7.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"?attr/colorPrimary\"\n            android:minHeight=\"?attr/actionBarSize\"\n            app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n            app:theme=\"@style/Toolbar\" />\n\n        <TextView\n            android:id=\"@+id/sticky\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:gravity=\"center_vertical\"\n            android:paddingLeft=\"@dimen/margin_standard\"\n            android:paddingRight=\"@dimen/margin_standard\"\n            android:text=\"@string/sticky_header\"\n            android:textAppearance=\"?android:attr/textAppearanceMedium\"\n            android:textColor=\"@android:color/white\" />\n    </LinearLayout>\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_stickyheaderwebview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/root\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableScrollView\n        android:id=\"@+id/scroll\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:orientation=\"vertical\">\n\n            <View\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                android:minHeight=\"?attr/actionBarSize\" />\n\n            <View\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                android:minHeight=\"?attr/actionBarSize\" />\n\n            <com.github.ksoichiro.android.observablescrollview.ObservableWebView\n                android:id=\"@+id/web\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\" />\n        </LinearLayout>\n    </com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n\n    <LinearLayout\n        android:id=\"@+id/header\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@color/primary\"\n        android:orientation=\"vertical\">\n\n        <android.support.v7.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"?attr/colorPrimary\"\n            android:minHeight=\"?attr/actionBarSize\"\n            app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n            app:theme=\"@style/Toolbar\" />\n\n        <TextView\n            android:id=\"@+id/sticky\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:gravity=\"center_vertical\"\n            android:paddingLeft=\"@dimen/margin_standard\"\n            android:paddingRight=\"@dimen/margin_standard\"\n            android:text=\"@string/sticky_header\"\n            android:textAppearance=\"?android:attr/textAppearanceMedium\"\n            android:textColor=\"@android:color/white\" />\n    </LinearLayout>\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_toolbarcontrolgridview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/colorPrimary\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableGridView\n        android:id=\"@+id/scrollable\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/actionBarSize\"\n        android:numColumns=\"2\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_toolbarcontrollistview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/colorPrimary\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableListView\n        android:id=\"@+id/scrollable\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/actionBarSize\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_toolbarcontrolrecyclerview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/colorPrimary\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView\n        android:id=\"@+id/scrollable\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/actionBarSize\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_toolbarcontrolscrollview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/colorPrimary\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableScrollView\n        android:id=\"@+id/scrollable\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/actionBarSize\"\n        android:fillViewport=\"true\">\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"@dimen/activity_vertical_margin\"\n            android:layout_marginLeft=\"@dimen/activity_horizontal_margin\"\n            android:layout_marginRight=\"@dimen/activity_horizontal_margin\"\n            android:layout_marginTop=\"@dimen/activity_vertical_margin\"\n            android:text=\"@string/lipsum\" />\n    </com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_toolbarcontrolwebview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/colorPrimary\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableWebView\n        android:id=\"@+id/scrollable\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/actionBarSize\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_viewpagertab.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/root\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <!--\n    Padding for ViewPager must be set outside the ViewPager itself\n    because with padding, EdgeEffect of ViewPager become strange.\n    -->\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingTop=\"@dimen/tab_height\">\n\n        <android.support.v4.view.ViewPager\n            android:id=\"@+id/pager\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\" />\n    </FrameLayout>\n\n    <LinearLayout\n        android:id=\"@+id/header\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/colorPrimary\"\n        android:orientation=\"vertical\">\n\n        <android.support.v7.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"?attr/colorPrimary\"\n            android:minHeight=\"?attr/actionBarSize\"\n            app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n            app:theme=\"@style/Toolbar\" />\n\n        <com.google.samples.apps.iosched.ui.widget.SlidingTabLayout\n            android:id=\"@+id/sliding_tabs\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/tab_height\"\n            android:background=\"@color/primary\" />\n    </LinearLayout>\n\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/activity_viewpagertab2.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <FrameLayout\n        android:id=\"@+id/pager_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <android.support.v4.view.ViewPager\n            android:id=\"@+id/pager\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\" />\n    </FrameLayout>\n\n    <LinearLayout\n        android:id=\"@+id/header\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/colorPrimary\"\n        android:orientation=\"vertical\">\n\n        <android.support.v7.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"?attr/colorPrimary\"\n            android:minHeight=\"?attr/actionBarSize\"\n            app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n            app:theme=\"@style/Toolbar\" />\n\n        <com.google.samples.apps.iosched.ui.widget.SlidingTabLayout\n            android:id=\"@+id/sliding_tabs\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/tab_height\"\n            android:background=\"@color/primary\" />\n    </LinearLayout>\n\n</com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>"
  },
  {
    "path": "samples/res/layout/activity_viewpagertabfragment.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <FrameLayout\n        android:id=\"@+id/fragment\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?attr/colorPrimary\"\n        android:minHeight=\"?attr/actionBarSize\"\n        app:popupTheme=\"@style/Theme.AppCompat.Light.DarkActionBar\"\n        app:theme=\"@style/Toolbar\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/divider.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<View xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"1px\"\n    android:background=\"?attr/colorPrimary\"\n    android:minHeight=\"1px\" />\n"
  },
  {
    "path": "samples/res/layout/fragment_actionbarcontrollistview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableListView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/list\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n"
  },
  {
    "path": "samples/res/layout/fragment_flexiblespacewithimagegridview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/fragment_root\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <View\n        android:id=\"@+id/list_background\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@android:color/white\" />\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableGridView\n        android:id=\"@+id/scroll\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:numColumns=\"2\"\n        android:scrollbars=\"none\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/fragment_flexiblespacewithimagelistview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/fragment_root\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <View\n        android:id=\"@+id/list_background\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@android:color/white\" />\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableListView\n        android:id=\"@+id/scroll\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:scrollbars=\"none\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/fragment_flexiblespacewithimagerecyclerview.xml",
    "content": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/fragment_root\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <View\n        android:id=\"@+id/list_background\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@android:color/white\" />\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView\n            android:id=\"@+id/scroll\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:scrollbars=\"none\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/fragment_flexiblespacewithimagescrollview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/fragment_root\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableScrollView\n        android:id=\"@+id/scroll\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:overScrollMode=\"never\"\n        android:scrollbars=\"none\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\">\n\n            <View\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/flexible_space_image_height\"\n                android:background=\"@android:color/transparent\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@android:color/white\"\n                android:paddingBottom=\"@dimen/activity_vertical_margin\"\n                android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n                android:paddingRight=\"@dimen/activity_horizontal_margin\"\n                android:paddingTop=\"@dimen/activity_vertical_margin\"\n                android:text=\"@string/lipsum\" />\n        </LinearLayout>\n    </com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/fragment_fragmenttransition_default.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<ListView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/list\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n"
  },
  {
    "path": "samples/res/layout/fragment_fragmenttransition_second.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<TextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/white\"\n    android:gravity=\"center\"\n    android:text=\"This is a fragment\"\n    android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n"
  },
  {
    "path": "samples/res/layout/fragment_gridview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableGridView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:numColumns=\"2\" />\n"
  },
  {
    "path": "samples/res/layout/fragment_listview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableListView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n"
  },
  {
    "path": "samples/res/layout/fragment_recyclerview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:scrollbars=\"vertical\" />\n"
  },
  {
    "path": "samples/res/layout/fragment_scrollview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/white\"\n    android:fillViewport=\"true\"\n    android:overScrollMode=\"never\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\">\n\n        <include layout=\"@layout/padding\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@android:color/white\"\n            android:paddingBottom=\"@dimen/activity_vertical_margin\"\n            android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n            android:paddingRight=\"@dimen/activity_horizontal_margin\"\n            android:paddingTop=\"@dimen/activity_vertical_margin\"\n            android:text=\"@string/lipsum\" />\n    </LinearLayout>\n</com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n"
  },
  {
    "path": "samples/res/layout/fragment_scrollview_noheader.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/white\"\n    android:fillViewport=\"true\"\n    android:overScrollMode=\"never\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\">\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@android:color/white\"\n            android:paddingBottom=\"@dimen/activity_vertical_margin\"\n            android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n            android:paddingRight=\"@dimen/activity_horizontal_margin\"\n            android:paddingTop=\"@dimen/activity_vertical_margin\"\n            android:text=\"@string/lipsum\" />\n    </LinearLayout>\n</com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n"
  },
  {
    "path": "samples/res/layout/fragment_scrollviewwithfab.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.github.ksoichiro.android.observablescrollview.ObservableScrollView\n        android:id=\"@+id/scroll\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"@android:color/white\"\n        android:fillViewport=\"true\"\n        android:overScrollMode=\"never\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\">\n\n            <include layout=\"@layout/padding\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@android:color/white\"\n                android:paddingBottom=\"@dimen/activity_vertical_margin\"\n                android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n                android:paddingRight=\"@dimen/activity_horizontal_margin\"\n                android:paddingTop=\"@dimen/activity_vertical_margin\"\n                android:text=\"@string/lipsum\" />\n        </LinearLayout>\n    </com.github.ksoichiro.android.observablescrollview.ObservableScrollView>\n\n    <com.melnykov.fab.FloatingActionButton\n        android:id=\"@+id/fab\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"left|top\"\n        android:scaleType=\"center\"\n        app:fab_colorNormal=\"@color/accentLight\"\n        app:fab_colorPressed=\"@color/accent\" />\n</FrameLayout>\n"
  },
  {
    "path": "samples/res/layout/fragment_viewpagertabfragment_parent.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/primary\"\n    android:paddingTop=\"?attr/actionBarSize\">\n\n    <!--\n    Padding for ViewPager must be set outside the ViewPager itself\n    because with padding, EdgeEffect of ViewPager become strange.\n    -->\n    <FrameLayout\n        android:id=\"@+id/pager_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingTop=\"@dimen/tab_height\">\n\n        <android.support.v4.view.ViewPager\n            android:id=\"@+id/pager\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:background=\"@android:color/white\" />\n    </FrameLayout>\n\n    <com.google.samples.apps.iosched.ui.widget.SlidingTabLayout\n        android:id=\"@+id/sliding_tabs\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/tab_height\"\n        android:background=\"@color/primary\" />\n\n</com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout>"
  },
  {
    "path": "samples/res/layout/fragment_webview.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<com.github.ksoichiro.android.observablescrollview.ObservableWebView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/scroll\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n"
  },
  {
    "path": "samples/res/layout/gradient_header.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<View xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"?attr/actionBarSize\"\n    android:background=\"@drawable/gradient_header_background\"\n    android:minHeight=\"?attr/actionBarSize\" />\n"
  },
  {
    "path": "samples/res/layout/list_item_handletouch.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:gravity=\"center_vertical\"\n    android:minHeight=\"?android:attr/listPreferredItemHeight\"\n    android:orientation=\"horizontal\"\n    android:padding=\"6dp\">\n\n    <TextView\n        android:id=\"@android:id/text1\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"1\"\n        android:text=\"Item\"\n        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n    <Button\n        android:id=\"@+id/button\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"0\"\n        android:text=\"Button\"\n        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "samples/res/layout/list_item_main.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:gravity=\"center_vertical\"\n    android:minHeight=\"?android:attr/listPreferredItemHeight\"\n    android:orientation=\"vertical\"\n    android:padding=\"6dp\">\n\n    <TextView\n        android:id=\"@+id/className\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"Activity class name\"\n        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n    <TextView\n        android:id=\"@+id/description\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"Description of the sample\"\n        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "samples/res/layout/padding.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<View xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"?attr/actionBarSize\"\n    android:minHeight=\"?attr/actionBarSize\" />\n"
  },
  {
    "path": "samples/res/layout/recycler_header.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n    <include layout=\"@layout/padding\" />\n\n    <include layout=\"@layout/padding\" />\n</LinearLayout>\n"
  },
  {
    "path": "samples/res/layout/tab_indicator.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<TextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@android:id/text1\"\n    style=\"@style/ThemeOverlay.AppCompat.Light\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"48dp\"\n    android:gravity=\"center\"\n    android:paddingLeft=\"16dp\"\n    android:paddingRight=\"16dp\"\n    android:textColor=\"@color/tab_text_color\"\n    android:textSize=\"14sp\" />"
  },
  {
    "path": "samples/res/layout-v11/tab_indicator.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<TextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@android:id/text1\"\n    style=\"@style/ThemeOverlay.AppCompat.Light\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"48dp\"\n    android:background=\"?android:selectableItemBackground\"\n    android:gravity=\"center\"\n    android:paddingLeft=\"16dp\"\n    android:paddingRight=\"16dp\"\n    android:textAllCaps=\"true\"\n    android:textColor=\"@color/tab_text_color\"\n    android:textSize=\"14sp\" />"
  },
  {
    "path": "samples/res/menu/menu_main.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:context=\".MainActivity\">\n\n    <item\n        android:id=\"@+id/menu_about\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/menu_about\"\n        android:icon=\"@drawable/ic_action_info\"\n        app:showAsAction=\"ifRoom\" />\n\n</menu>\n"
  },
  {
    "path": "samples/res/values/colors.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<resources>\n\n    <color name=\"primary\">#009688</color>\n    <color name=\"primaryDark\">#00796b</color>\n    <color name=\"accent\">#eeff41</color>\n    <color name=\"accentLight\">#F4FF81</color>\n\n</resources>\n"
  },
  {
    "path": "samples/res/values/dimens.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n\n    <dimen name=\"margin_standard\">16dp</dimen>\n    <dimen name=\"margin_short\">8dp</dimen>\n    <dimen name=\"margin_extra_short\">4dp</dimen>\n\n    <dimen name=\"toolbar_elevation\">4dp</dimen>\n\n    <dimen name=\"toolbar_margin_start\">72dp</dimen>\n    <dimen name=\"flexible_space_height\">72dp</dimen>\n    <dimen name=\"flexible_space_image_height\">240dp</dimen>\n    <dimen name=\"flexible_space_show_fab_offset\">120dp</dimen>\n\n    <dimen name=\"parallax_image_height\">180dp</dimen>\n    <dimen name=\"tab_height\">48dp</dimen>\n\n    <dimen name=\"header_bar_height\">72dp</dimen>\n    <dimen name=\"intersection_height\">16dp</dimen>\n\n    <dimen name=\"sliding_slop\">32dp</dimen>\n    <dimen name=\"sliding_overlay_blur_size\">4dp</dimen>\n</resources>\n"
  },
  {
    "path": "samples/res/values/strings.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<resources>\n\n    <string name=\"app_name\">ObservableScrollView for Android</string>\n    <string name=\"title_activity_main\">AOSV-Demos</string>\n    <string name=\"title_activity_about\">About this app</string>\n\n    <string name=\"title_activity_actionbarcontrolgridview\">GridView &amp; Action Bar</string>\n    <string name=\"title_activity_actionbarcontrollistview\">ListView &amp; Action Bar</string>\n    <string name=\"title_activity_actionbarcontrolrecyclerview\">RecyclerView &amp; Action Bar</string>\n    <string name=\"title_activity_actionbarcontrolscrollview\">ScrollView &amp; Action Bar</string>\n    <string name=\"title_activity_actionbarcontrolwebview\">WebView &amp; Action Bar</string>\n    <string name=\"title_activity_flexiblespacetoolbarscrollview\">Flexible Space</string>\n    <string name=\"title_activity_flexiblespacewithimagewithviewpagertab\">Flexible Space</string>\n    <string name=\"title_activity_flexiblespacewithimagewithviewpagertab2\">Flexible Space</string>\n    <string name=\"title_activity_flexiblespacewithimagegridview\">Flexible Space</string>\n    <string name=\"title_activity_flexiblespacewithimagescrollview\">Flexible Space</string>\n    <string name=\"title_activity_fillgap3listview\">Another implementation of Fill Gap &amp; ListView</string>\n    <string name=\"title_activity_fillgap3recyclerview\">Another implementation of Fill Gap &amp; RecyclerView</string>\n    <string name=\"title_activity_fillgap3scrollview\">Another implementation of Fill Gap &amp; ScrollView</string>\n    <string name=\"title_activity_fillgaplistview\">Fill Gap &amp; ListView</string>\n    <string name=\"title_activity_fillgaprecyclerview\">Fill Gap &amp; RecyclerView</string>\n    <string name=\"title_activity_fillgapscrollview\">Fill Gap &amp; ScrollView</string>\n    <string name=\"title_activity_flexiblespacewithimageRecyclerview\">Flexible Space</string>\n    <string name=\"title_activity_flexiblespacewithimagelistview\">Flexible Space</string>\n    <string name=\"title_activity_flexiblespacewithwebview\">Flexible Space</string>\n    <string name=\"title_activity_fragmentactionbarcontrollistview\">ListView on Fragment &amp; Action Bar</string>\n    <string name=\"title_activity_fragmenttransition\">ActionBar &amp; Toolbar manipulation with Fragment</string>\n    <string name=\"title_activity_handletouchgridview\">Handling touch with GridView</string>\n    <string name=\"title_activity_handletouchlistview\">Handling touch with ListView</string>\n    <string name=\"title_activity_handletouchrecyclerview\">Handling touch with RecyclerView</string>\n    <string name=\"title_activity_handletouchscrollview\">Handling touch with ScrollView</string>\n    <string name=\"title_activity_handletouchwebview\">Handling touch with WebView</string>\n    <string name=\"title_activity_parallaxtoolbargridview\">Parallax gridView &amp; toolbar</string>\n    <string name=\"title_activity_parallaxtoolbarlistview\">Parallax listView &amp; toolbar</string>\n    <string name=\"title_activity_parallaxtoolbarscrollview\">Parallax scrollView &amp; toolbar</string>\n    <string name=\"title_activity_scrollfrombottomlistview\">ListView, scrolling from bottom</string>\n    <string name=\"title_activity_scrollfrombottomrecyclerview\">RecyclerView, scrolling from bottom</string>\n    <string name=\"title_activity_slidingupgridview\">Sliding up effect with GridView</string>\n    <string name=\"title_activity_slidinguplistview\">Sliding up effect with ListView</string>\n    <string name=\"title_activity_slidinguprecyclerview\">Sliding up effect with RecyclerView</string>\n    <string name=\"title_activity_slidingupscrollview\">Sliding up effect with ScrollView</string>\n    <string name=\"title_activity_slidingupwebview\">Sliding up effect with WebView</string>\n    <string name=\"title_activity_stickyheaderlistview\">ListView &amp; Quick return toolbar &amp; Sticky view</string>\n    <string name=\"title_activity_stickyheaderrecyclerview\">RecyclerView &amp; Quick return toolbar &amp; Sticky view</string>\n    <string name=\"title_activity_stickyheaderscrollview\">ScrollView &amp; Quick return toolbar &amp; Sticky view</string>\n    <string name=\"title_activity_stickyheaderwebview\">WebView &amp; Quick return toolbar &amp; Sticky view</string>\n    <string name=\"title_activity_toolbarcontrolgridview\">GridView &amp; Toolbar</string>\n    <string name=\"title_activity_toolbarcontrollistview\">ListView &amp; Toolbar</string>\n    <string name=\"title_activity_toolbarcontrolrecyclerview\">RecyclerView &amp; Toolbar</string>\n    <string name=\"title_activity_toolbarcontrolscrollview\">ScrollView &amp; Toolbar</string>\n    <string name=\"title_activity_toolbarcontrolwebview\">WebView &amp; Toolbar</string>\n    <string name=\"title_activity_viewpagertabfragment\">ViewPager &amp; Tab on Fragment</string>\n    <string name=\"title_activity_viewpagertab2\">Another implementation of ViewPagerTabActivity</string>\n    <string name=\"title_activity_viewpagertab\">ViewPager &amp; Tab &amp; Different fragments</string>\n    <string name=\"title_activity_viewpagertablistview\">ViewPager &amp; Tab &amp; ListView</string>\n    <string name=\"title_activity_viewpagertabscrollview\">ViewPager &amp; Tab &amp; ScrollView</string>\n    <string name=\"title_activity_viewpagertabscrollviewwithfab\">ViewPager &amp; Tab &amp; ScrollView &amp; FAB</string>\n\n    <string name=\"lipsum\">Lorem ipsum dolor sit amet, ut duis lorem provident sed felis blandit, condimentum donec lectus ipsum et mauris, morbi porttitor interdum feugiat nulla donec sodales, vestibulum nisl primis a molestie vestibulum quam, sapien mauris metus risus suspendisse magnis. Augue viverra nulla faucibus egestas eu, a etiam id congue rutrum ante, arcu tincidunt donec quam felis at ornare, iaculis ligula sodales venenatis commodo volutpat neque, suspendisse elit praesent tellus felis mi amet. Inceptos amet tempor lectus lorem est non, ac donec ac libero neque mauris, tellus ante metus eget leo consequat. Scelerisque dolor curabitur pretium blandit ut feugiat, amet lacus pulvinar justo convallis ut, sed natoque ipsum urna posuere nibh eu. Sed at sed vulputate sit orci, facilisis a aliquam tellus quam aliquam, eu aliquam donec at molestie ante, pellentesque mauris lorem ultrices libero faucibus porta, imperdiet adipiscing sit hac diam ut nulla. Lacus enim elit pulvinar donec vehicula dapibus, accumsan purus officia cursus dolor sapien, eu amet dis mauris mi nulla ut. Non accusamus etiam pede non urna tempus, vestibulum aliquam tortor eget pharetra sodales, in vestibulum ut justo orci nulla, lobortis purus sem semper consectetuer magni purus. Dolor a leo vestibulum amet ut sit, arcu ut eaque urna fusce aliquet turpis, sed fermentum sed vestibulum nisl pede, tristique enim lorem posuere in laborum ut. Vestibulum id id justo leo nulla, magna lobortis ullamcorper et dignissim pellentesque, duis suspendisse quis id lorem ante. Vivamus a nullam ante adipiscing amet, mi vel consectetuer nunc aenean pede quisque, eget rhoncus dis porttitor habitant nunc vivamus, duis cubilia blandit non donec justo dictumst, praesent vitae nulla nam pulvinar urna. Adipiscing adipiscing justo urna pulvinar imperdiet nullam, vitae fusce rhoncus proin nonummy suscipit, ullamcorper amet et non potenti platea ultrices, mauris nullam sapien nunc justo vel, eu semper pellentesque arcu fusce augue. Malesuada mauris nibh sit a a scelerisque, velit sem lectus tellus convallis consectetuer, ultricies auctor a ante eros amet sed.\\n\\n\nRisus lacus duis leo platea wisi, felis maecenas rutrum in id in donec, non id a potenti libero eget, posuere elit ea sed pellentesque quis. Sunt lacus urna lorem elit duis, nibh donec purus quisque consectetuer dolor, neque vestibulum proin ornare eros nonummy phasellus. Iaculis cras eu at egestas dolor montes, viverra quisque malesuada consectetuer semper maecenas, a sed vitae donec tempor aliqua metus, ornare mollis suscipit et erat fusce, sit orci aut auctor elementum fames aliquam. Platea dui integer magnis non metus, minus dignissimos ante massa nostra et, rutrum sapien egestas quis sapien donec donec. Erat sit a eros aenean natoque, quam libero id lorem enim proin, lorem ipsum fermentum mattis metus et. Aliquam aliquet suscipit purus conubia at neque, platea vivamus vestibulum nulla quibusdam senectus, et morbi lectus malesuada gravida donec, elementum sit convallis pellentesque velit amet. Et eveniet viverra vehicula consectetuer justo, provident sed commodo non lacinia velit, tempor phasellus vel leo nisl cras, vivamus et arcu interdum dui eu amet. Volutpat wisi rhoncus vel turpis diam quibusdam, dapibus elit est quisque cubilia mauris, nulla elit magna tempor accumsan bibendum, lorem varius sed interdum eget mattis, scelerisque egestas feugiat donec dui molestie. Leo facilisis nisl sit montes ligula sed, enim commodo consectetuer nunc est et, ut sed vehicula dolor luctus elit. Fermentum cras donec eget nibh est vel, sed justo risus et pharetra diam, eu vivamus egestas ligula risus diam, sed justo eget hac ut mauris. Vestibulum diam nec vitae mi eget suspendisse, aenean arcu purus facilisis purus class in, id aliquam sit id scelerisque sapien etiam. Ut nullam sit sed at mauris lobortis, consequat dolor autem ipsum euismod nulla, elit quis proin eget conubia varius, erat arcu massa mus in mauris, scelerisque ut eu sollicitudin libero leo urna.\\n\\n\nConsectetuer luctus tempor elit ut dolor ligula, quis dui per dui hendrerit ante sagittis, in quisque pretium in eleifend enim. Condimentum iaculis vitae feugiat dis tellus vel, lectus dolor nec dui nulla nascetur, et pellentesque curabitur lorem leo velit eget. Id nascetur arcu lobortis suspendisse imperdiet urna, natoque nascetur ante in porta a, interdum hendrerit mi bibendum platea tellus, urna in enim ornare vestibulum faucibus enim. Leo fusce egestas ante nec volutpat, in tempor vel facilisis potenti ut, pede at non lorem a commodo, nulla dolor orci interdum vestibulum nulla. Dui nulla vestibulum quisque a pharetra porta, integer nec ipsum nec sed dui pharetra, magna et dignissim ipsum sed dictum, litora eros vivamus scelerisque libero ipsum. Sed ac ac lorem molestie adipiscing morbi, pellentesque imperdiet nunc quis morbi amet ante, libero dui ligula nec risus neque et, velit nonummy phasellus et facilisi amet, ligula in elementum non sapien pulvinar faucibus. Eu leo ut posuere sed aliquet, tincidunt vel urna volutpat tempus sem, sit felis aliquet vestibulum condimentum sit, amet nibh vel tellus purus ullamcorper libero, nulla vestibulum pede ut vestibulum pretium. Eu nulla vestibulum a neque in metus, quisquam nam sed cursus eget luctus, pede ultrices nec sed dignissim pellentesque, sit class cursus metus nulla placerat mauris, consequat mollis neque vivamus amet pede. Mauris dolor nulla diam eros bibendum, quam ante vestibulum morbi non ligula vel, molestie curabitur rhoncus nulla euismod interdum non. Nulla fringilla lorem mollis ad massa, sit molestie nibh lorem arcu volutpat, accumsan commodo lectus eu et donec, sit tempor tempus rutrum in curabitur amet. Nec urna euismod a tincidunt commodo, eu pede turpis libero vitae viverra, ante vestibulum nam non habitasse potenti, mauris imperdiet in in nunc convallis. Et nostra wisi in est accumsan vehicula, quisque vitae felis mauris sed vulputate nec, ante imperdiet sollicitudin massa iaculis massa sit.\\n\\n\nQuam libero nulla netus eu porta curae, ut nulla bibendum facilisis et urna sed, quis congue vestibulum aliquam interdum etiam. Nulla vel lobortis ullamcorper vitae excepturi, neque urna feugiat lectus vel lacinia, massa pretium orci eu metus neque vulputate. Imperdiet ac velit rhoncus nulla malesuada nullam, nec pulvinar justo gravida lorem rutrum magna, habitasse repudiandae mi eros vestibulum ante, nec euismod dui iaculis in turpis pretium, ac id metus egestas proin lacus lectus. Laoreet lorem nec vitae risus erat arcu, vitae quam ut in ante tristique, porta dolor pede quam et odio nam, arcu lacus sem congue ante cursus massa. Et mattis sagittis erat accumsan fusce quam, vehicula ligula beatae natoque fusce sodales conubia, habitasse metus cum magnis viverra nam cursus, egestas urna wisi primis blandit eu magna, eget libero elit lacus lorem dis aliquam. Ut mauris ante natoque lacus massa, justo a lectus sodales enim adipiscing id, accumsan ut ipsum vestibulum sed enim auctor, vitae congue tincidunt id phasellus lacinia scelerisque, tincidunt sapien nulla euismod volutpat iaculis. Platea sociis nec aliquet nec molestie, in mi et augue sapien in vivamus, integer fames proin vitae in ullamcorper et. Fringilla etiam sapiente rhoncus suspendisse nec id, lobortis cras eget egestas dui ac nec, justo lacus ut lorem bibendum quia eros, eget a gravida id donec nunc suscipit, porta sed in sodales non rutrum. Lectus vel dui elementum pellentesque magna aliquam, vitae non sit pede et fusce nibh, id id deserunt ornare dui sit condimentum, in adipiscing imperdiet turpis nam aliquet, facilisis metus magna lacus wisi facilisis tortor. Vulputate elit accumsan quam amet ligula, suspendisse lacus mi nonummy integer urna, libero nulla nunc varius in odio, laoreet nulla amet placerat amet nec. Consectetuer vel massa hendrerit vitae iaculis id, sed ut ut laudantium odio in, elit vestibulum duis ante maecenas interdum in, neque vehicula ultrices varius in quam, pede tellus pellentesque sed nullam quis.</string>\n    <string name=\"lipsum_short\">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam cursus sem et ante rhoncus pharetra. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur lacinia mattis quam, non laoreet felis vestibulum ac. Curabitur pharetra vel est quis cursus. Etiam faucibus turpis in nunc semper sollicitudin. Nam fermentum commodo ligula. Aliquam a tellus sollicitudin, ultricies est ac, porta ante. Nam aliquet mi nulla, ut laoreet nunc vestibulum ut. Quisque non purus urna. Aliquam sed porta lectus. In a ipsum varius, imperdiet magna in, hendrerit arcu. Donec porta, nulla non viverra sollicitudin, nisl mi suscipit nisl, nec pellentesque est mi ut neque. Duis viverra laoreet nibh at malesuada.</string>\n    <string name=\"sticky_header\">Sticky</string>\n\n    <string name=\"menu_about\">About</string>\n    <string name=\"msg_app_version\">App Version: %1$s (Build: %2$s)</string>\n    <string name=\"msg_lib_version\">Library Version: %1$s</string>\n    <string name=\"msg_copyright\">© 2014 Soichiro Kashima All rights reserved.</string>\n    <string name=\"msg_about_description\">Demo app for Android-ObservableScrollView.</string>\n    <string name=\"msg_about_fork_me_on_github\">Fork me on GitHub!</string>\n    <string name=\"msg_about_github_url\">https://github.com/ksoichiro/Android-ObservableScrollView</string>\n    <string name=\"msg_about_library_description\">Android-ObservableScrollView is a library to observe scroll events for scrollable views.</string>\n    <string name=\"msg_android_trademark\">Android is a trademark of Google Inc.</string>\n    <string name=\"desc_about_app\">About this app</string>\n\n</resources>\n"
  },
  {
    "path": "samples/res/values/strings_license.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <string name=\"license\">Open source licenses</string>\n    <string name=\"msg_license\">This application is constructed using following software.</string>\n\n    <string-array name=\"software_list\" translatable=\"false\">\n        <item>NineOldAndroids</item>\n        <item>FloatingActionButton</item>\n        <item>Google I/O Android App</item>\n    </string-array>\n\n    <string-array name=\"license_list\" translatable=\"false\">\n        <item><![CDATA[<p>Copyright 2012 Jake Wharton</p>\n\n<p>Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at<br>\n<br>\n   http://www.apache.org/licenses/LICENSE-2.0</p>\n\n<p>Unless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \\\"AS IS\\\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.</p>]]></item>\n        <item tools:ignore=\"TypographyOther\"><![CDATA[<p>The MIT License (MIT)</p>\n\n<p>Copyright (c) 2014 Oleksandr Melnykov</p>\n\n<p>Permission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \\\"Software\\\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:</p>\n\n<p>The above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.</p>\n\n<p>THE SOFTWARE IS PROVIDED \\\"AS IS\\\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.</p>]]></item>\n        <item><![CDATA[<p>Copyright 2014 Google Inc. All rights reserved.</p>\n\n<p>Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at<br>\n<br>\n    http://www.apache.org/licenses/LICENSE-2.0</p>\n\n<p>Unless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \\\"AS IS\\\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.</p>]]></item>\n    </string-array>\n\n</resources>\n"
  },
  {
    "path": "samples/res/values/styles.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<resources>\n\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <item name=\"colorPrimary\">@color/primary</item>\n        <item name=\"colorPrimaryDark\">@color/primaryDark</item>\n        <item name=\"colorAccent\">@color/accent</item>\n    </style>\n\n    <style name=\"AppTheme.Toolbar\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n        <item name=\"colorPrimary\">@color/primary</item>\n        <item name=\"colorPrimaryDark\">@color/primaryDark</item>\n        <item name=\"colorAccent\">@color/accent</item>\n\n        <item name=\"android:spinnerItemStyle\">@style/AppTheme.SpinnerItemStyle</item>\n        <item name=\"android:spinnerDropDownItemStyle\">@style/AppTheme.SpinnerDropDownItemStyle</item>\n    </style>\n\n    <style name=\"Toolbar\" parent=\"Theme.AppCompat\">\n        <item name=\"colorPrimary\">@color/primary</item>\n        <item name=\"colorPrimaryDark\">@color/primaryDark</item>\n        <item name=\"colorAccent\">@color/accent</item>\n    </style>\n\n    <style name=\"Spinner\" parent=\"Widget.AppCompat.Spinner\">\n        <item name=\"android:popupBackground\">@drawable/abc_popup_background_mtrl_mult</item>\n    </style>\n\n    <style name=\"AppTheme.SpinnerItemStyle\" parent=\"Widget.AppCompat.TextView.SpinnerItem\">\n        <item name=\"android:textColor\">#ffffff</item>\n    </style>\n\n    <style name=\"AppTheme.SpinnerDropDownItemStyle\" parent=\"Widget.AppCompat.DropDownItem.Spinner\">\n        <item name=\"android:background\">?attr/selectableItemBackground</item>\n        <item name=\"android:textColor\">#212121</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "samples/res/values-ar/strings.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<resources>\n\n    <string name=\"title_activity_flexiblespacewithimagelistview\">الفضاء مرن</string>\n\n</resources>\n"
  },
  {
    "path": "samples/res/values-w820dp/dimens.xml",
    "content": "<!--\n  Copyright 2014 Soichiro Kashima\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n<resources>\n\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n\n</resources>\n"
  },
  {
    "path": "samples/src/androidTest/java/com/github/ksoichiro/app/ApplicationTest.java",
    "content": "package com.github.ksoichiro.myapplication;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n}"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.os.Bundle;\nimport android.support.v7.app.ActionBar;\nimport android.support.v7.app.AppCompatActivity;\nimport android.text.Html;\nimport android.text.util.Linkify;\nimport android.util.TypedValue;\nimport android.view.LayoutInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\npublic class AboutActivity extends AppCompatActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_about);\n        ActionBar ab = getSupportActionBar();\n        if (ab != null) {\n            ab.setDisplayHomeAsUpEnabled(true);\n            ab.setHomeButtonEnabled(true);\n        }\n        ((TextView) findViewById(R.id.app_version)).setText(getString(R.string.msg_app_version, getVersionName(), VersionInfo.BUILD));\n        ((TextView) findViewById(R.id.lib_version)).setText(getString(R.string.msg_lib_version, VersionInfo.LIBRARY_VERSION));\n\n        initLicenses();\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        if (item.getItemId() == android.R.id.home) {\n            finish();\n            return true;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n    private void initLicenses() {\n        LayoutInflater inflater = LayoutInflater.from(this);\n        LinearLayout content = (LinearLayout) findViewById(R.id.licenses);\n\n        String[] softwareList = getResources().getStringArray(R.array.software_list);\n        String[] licenseList = getResources().getStringArray(R.array.license_list);\n        content.addView(createItemsText(softwareList));\n        for (int i = 0; i < softwareList.length; i++) {\n            content.addView(createDivider(inflater, content));\n            content.addView(createHeader(softwareList[i]));\n            content.addView(createHtmlText(licenseList[i]));\n        }\n    }\n\n    private TextView createHeader(final String name) {\n        String s = \"<big><b>\" + name + \"</b></big>\";\n        return createHtmlText(s, 8);\n    }\n\n    private TextView createItemsText(final String... names) {\n        StringBuilder s = new StringBuilder();\n        for (String name : names) {\n            if (s.length() > 0) {\n                s.append(\"<br>\");\n            }\n            s.append(\"- \");\n            s.append(name);\n        }\n        return createHtmlText(s.toString(), 8);\n    }\n\n    private TextView createHtmlText(final String s) {\n        return createHtmlText(s, 8);\n    }\n\n    private TextView createHtmlText(final String s, final int margin) {\n        TextView text = new TextView(this);\n        text.setAutoLinkMask(Linkify.WEB_URLS | Linkify.EMAIL_ADDRESSES);\n        text.setText(Html.fromHtml(s));\n        LinearLayout.LayoutParams layoutParams =\n                new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,\n                        LinearLayout.LayoutParams.WRAP_CONTENT);\n        int marginPx = (0 < margin) ? (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, margin,\n                getResources().getDisplayMetrics()) : 0;\n        layoutParams.setMargins(0, marginPx, 0, marginPx);\n        text.setLayoutParams(layoutParams);\n        return text;\n    }\n\n    private View createDivider(final LayoutInflater inflater, final ViewGroup parent) {\n        return inflater.inflate(R.layout.divider, parent, false);\n    }\n\n    private String getVersionName() {\n        final PackageManager manager = getPackageManager();\n        String versionName;\n        try {\n            final PackageInfo info = manager.getPackageInfo(getPackageName(), PackageManager.GET_META_DATA);\n            versionName = info.versionName;\n        } catch (PackageManager.NameNotFoundException e) {\n            versionName = \"\";\n        }\n        return versionName;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlGridViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v7.app.ActionBar;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\npublic class ActionBarControlGridViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_actionbarcontrolgridview);\n\n        ObservableGridView gridView = (ObservableGridView) findViewById(R.id.grid);\n        gridView.setScrollViewCallbacks(this);\n        setDummyData(gridView);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        ActionBar ab = getSupportActionBar();\n        if (ab == null) {\n            return;\n        }\n        if (scrollState == ScrollState.UP) {\n            if (ab.isShowing()) {\n                ab.hide();\n            }\n        } else if (scrollState == ScrollState.DOWN) {\n            if (!ab.isShowing()) {\n                ab.show();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlListViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v7.app.ActionBar;\nimport android.util.Log;\nimport android.widget.AbsListView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\npublic class ActionBarControlListViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private static final String TAG = ActionBarControlListViewActivity.class.getSimpleName();\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_actionbarcontrollistview);\n\n        ObservableListView listView = (ObservableListView) findViewById(R.id.list);\n        listView.setScrollViewCallbacks(this);\n        setDummyData(listView);\n\n        // ObservableListView uses setOnScrollListener, but it still works.\n        listView.setOnScrollListener(new AbsListView.OnScrollListener() {\n            @Override\n            public void onScrollStateChanged(AbsListView view, int scrollState) {\n                Log.v(TAG, \"onScrollStateChanged: \" + scrollState);\n            }\n\n            @Override\n            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {\n                Log.v(TAG, \"onScroll: firstVisibleItem: \" + firstVisibleItem + \" visibleItemCount: \" + visibleItemCount + \" totalItemCount: \" + totalItemCount);\n            }\n        });\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        ActionBar ab = getSupportActionBar();\n        if (ab == null) {\n            return;\n        }\n        if (scrollState == ScrollState.UP) {\n            if (ab.isShowing()) {\n                ab.hide();\n            }\n        } else if (scrollState == ScrollState.DOWN) {\n            if (!ab.isShowing()) {\n                ab.show();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlRecyclerViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v7.app.ActionBar;\nimport android.support.v7.widget.LinearLayoutManager;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\npublic class ActionBarControlRecyclerViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_actionbarcontrolrecyclerview);\n\n        ObservableRecyclerView recyclerView = (ObservableRecyclerView) findViewById(R.id.recycler);\n        recyclerView.setLayoutManager(new LinearLayoutManager(this));\n        recyclerView.setHasFixedSize(true);\n        recyclerView.setScrollViewCallbacks(this);\n        setDummyData(recyclerView);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        ActionBar ab = getSupportActionBar();\n        if (ab == null) {\n            return;\n        }\n        if (scrollState == ScrollState.UP) {\n            if (ab.isShowing()) {\n                ab.hide();\n            }\n        } else if (scrollState == ScrollState.DOWN) {\n            if (!ab.isShowing()) {\n                ab.show();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlScrollViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v7.app.ActionBar;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\npublic class ActionBarControlScrollViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_actionbarcontrolscrollview);\n\n        ObservableScrollView scrollView = (ObservableScrollView) findViewById(R.id.scroll);\n        scrollView.setScrollViewCallbacks(this);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        ActionBar ab = getSupportActionBar();\n        if (ab == null) {\n            return;\n        }\n        if (scrollState == ScrollState.UP) {\n            if (ab.isShowing()) {\n                ab.hide();\n            }\n        } else if (scrollState == ScrollState.DOWN) {\n            if (!ab.isShowing()) {\n                ab.show();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlWebViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v7.app.ActionBar;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ObservableWebView;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\npublic class ActionBarControlWebViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_actionbarcontrolwebview);\n\n        ObservableWebView webView = (ObservableWebView) findViewById(R.id.web);\n        webView.setScrollViewCallbacks(this);\n        webView.loadUrl(\"file:///android_asset/lipsum.html\");\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        ActionBar ab = getSupportActionBar();\n        if (ab == null) {\n            return;\n        }\n        if (scrollState == ScrollState.UP) {\n            if (ab.isShowing()) {\n                ab.hide();\n            }\n        } else if (scrollState == ScrollState.DOWN) {\n            if (!ab.isShowing()) {\n                ab.show();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.content.res.TypedArray;\nimport android.support.v7.app.AppCompatActivity;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.TypedValue;\nimport android.view.View;\nimport android.widget.AbsListView;\nimport android.widget.ArrayAdapter;\nimport android.widget.GridView;\nimport android.widget.ListView;\n\nimport java.util.ArrayList;\n\npublic abstract class BaseActivity extends AppCompatActivity {\n    private static final int NUM_OF_ITEMS = 100;\n    private static final int NUM_OF_ITEMS_FEW = 3;\n\n    protected int getActionBarSize() {\n        TypedValue typedValue = new TypedValue();\n        int[] textSizeAttr = new int[]{R.attr.actionBarSize};\n        int indexOfAttrTextSize = 0;\n        TypedArray a = obtainStyledAttributes(typedValue.data, textSizeAttr);\n        int actionBarSize = a.getDimensionPixelSize(indexOfAttrTextSize, -1);\n        a.recycle();\n        return actionBarSize;\n    }\n\n    protected int getScreenHeight() {\n        return findViewById(android.R.id.content).getHeight();\n    }\n\n    public static ArrayList<String> getDummyData() {\n        return getDummyData(NUM_OF_ITEMS);\n    }\n\n    public static ArrayList<String> getDummyData(int num) {\n        ArrayList<String> items = new ArrayList<>();\n        for (int i = 1; i <= num; i++) {\n            items.add(\"Item \" + i);\n        }\n        return items;\n    }\n\n    protected void setDummyData(ListView listView) {\n        setDummyData(listView, NUM_OF_ITEMS);\n    }\n\n    protected void setDummyDataFew(ListView listView) {\n        setDummyData(listView, NUM_OF_ITEMS_FEW);\n    }\n\n    protected void setDummyData(ListView listView, int num) {\n        listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, getDummyData(num)));\n    }\n\n    protected void setDummyDataWithHeader(ListView listView, int headerHeight) {\n        setDummyDataWithHeader(listView, headerHeight, NUM_OF_ITEMS);\n    }\n\n    protected void setDummyDataWithHeader(ListView listView, int headerHeight, int num) {\n        View headerView = new View(this);\n        headerView.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, headerHeight));\n        headerView.setMinimumHeight(headerHeight);\n        // This is required to disable header's list selector effect\n        headerView.setClickable(true);\n        setDummyDataWithHeader(listView, headerView, num);\n    }\n\n    protected void setDummyDataWithHeader(ListView listView, View headerView, int num) {\n        listView.addHeaderView(headerView);\n        setDummyData(listView, num);\n    }\n\n    protected void setDummyData(GridView gridView) {\n        gridView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, getDummyData()));\n    }\n\n    protected void setDummyData(RecyclerView recyclerView) {\n        setDummyData(recyclerView, NUM_OF_ITEMS);\n    }\n\n    protected void setDummyDataFew(RecyclerView recyclerView) {\n        setDummyData(recyclerView, NUM_OF_ITEMS_FEW);\n    }\n\n    protected void setDummyData(RecyclerView recyclerView, int num) {\n        recyclerView.setAdapter(new SimpleRecyclerAdapter(this, getDummyData(num)));\n    }\n\n    protected void setDummyDataWithHeader(RecyclerView recyclerView, int headerHeight) {\n        View headerView = new View(this);\n        headerView.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, headerHeight));\n        headerView.setMinimumHeight(headerHeight);\n        // This is required to disable header's list selector effect\n        headerView.setClickable(true);\n        setDummyDataWithHeader(recyclerView, headerView);\n    }\n\n    protected void setDummyDataWithHeader(RecyclerView recyclerView, View headerView) {\n        recyclerView.setAdapter(new SimpleHeaderRecyclerAdapter(this, getDummyData(), headerView));\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.app.Activity;\nimport android.content.res.TypedArray;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.TypedValue;\nimport android.view.View;\nimport android.widget.ArrayAdapter;\nimport android.widget.GridView;\nimport android.widget.ListView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\n\nimport java.util.ArrayList;\n\npublic abstract class BaseFragment extends Fragment {\n    public static ArrayList<String> getDummyData() {\n        return BaseActivity.getDummyData();\n    }\n\n    protected int getActionBarSize() {\n        Activity activity = getActivity();\n        if (activity == null) {\n            return 0;\n        }\n        TypedValue typedValue = new TypedValue();\n        int[] textSizeAttr = new int[]{R.attr.actionBarSize};\n        int indexOfAttrTextSize = 0;\n        TypedArray a = activity.obtainStyledAttributes(typedValue.data, textSizeAttr);\n        int actionBarSize = a.getDimensionPixelSize(indexOfAttrTextSize, -1);\n        a.recycle();\n        return actionBarSize;\n    }\n\n    protected int getScreenHeight() {\n        Activity activity = getActivity();\n        if (activity == null) {\n            return 0;\n        }\n        return activity.findViewById(android.R.id.content).getHeight();\n    }\n\n    protected void setDummyData(ListView listView) {\n        listView.setAdapter(new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, getDummyData()));\n    }\n\n    protected void setDummyDataWithHeader(ListView listView, View headerView) {\n        listView.addHeaderView(headerView);\n        setDummyData(listView);\n    }\n\n    protected void setDummyData(GridView gridView) {\n        gridView.setAdapter(new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, getDummyData()));\n    }\n\n    protected void setDummyDataWithHeader(ObservableGridView gridView, View headerView) {\n        gridView.addHeaderView(headerView);\n        setDummyData(gridView);\n    }\n\n    protected void setDummyData(RecyclerView recyclerView) {\n        recyclerView.setAdapter(new SimpleRecyclerAdapter(getActivity(), getDummyData()));\n    }\n\n    protected void setDummyDataWithHeader(RecyclerView recyclerView, View headerView) {\n        recyclerView.setAdapter(new SimpleHeaderRecyclerAdapter(getActivity(), getDummyData(), headerView));\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2BaseActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\n\n/**\n * Almost same as FillGapBaseActivity,\n * but in this activity, when swiping up, the filled space shrinks\n * and the header bar moves to the top.\n */\npublic abstract class FillGap2BaseActivity<S extends Scrollable> extends FillGapBaseActivity<S> {\n    protected float getHeaderTranslationY(int scrollY) {\n        return ScrollUtils.getFloat(-scrollY + mFlexibleSpaceImageHeight - mHeaderBar.getHeight(), 0, Float.MAX_VALUE);\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ListViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.nineoldandroids.view.ViewHelper;\n\n/**\n * Warning: This example does not work on Android 2.3.\n */\npublic class FillGap2ListViewActivity extends FillGap2BaseActivity<ObservableListView> implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected ObservableListView createScrollable() {\n        ObservableListView listView = (ObservableListView) findViewById(R.id.scroll);\n        listView.setScrollViewCallbacks(this);\n        setDummyDataWithHeader(listView, mFlexibleSpaceImageHeight);\n        return listView;\n    }\n\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_fillgaplistview;\n    }\n\n    @Override\n    protected void updateViews(int scrollY, boolean animated) {\n        super.updateViews(scrollY, animated);\n\n        // Translate list background\n        ViewHelper.setTranslationY(mListBackgroundView, ViewHelper.getTranslationY(mHeader));\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2RecyclerViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.support.v7.widget.LinearLayoutManager;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.nineoldandroids.view.ViewHelper;\n\n/**\n * Warning: This example does not work on Android 2.3.\n */\npublic class FillGap2RecyclerViewActivity extends FillGap2BaseActivity<ObservableRecyclerView> implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected ObservableRecyclerView createScrollable() {\n        ObservableRecyclerView recyclerView = (ObservableRecyclerView) findViewById(R.id.scroll);\n        recyclerView.setScrollViewCallbacks(this);\n        recyclerView.setLayoutManager(new LinearLayoutManager(this));\n        recyclerView.setHasFixedSize(false);\n        setDummyDataWithHeader(recyclerView, mFlexibleSpaceImageHeight);\n        return recyclerView;\n    }\n\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_fillgaprecyclerview;\n    }\n\n    @Override\n    protected void updateViews(int scrollY, boolean animated) {\n        super.updateViews(scrollY, animated);\n\n        // Translate list background\n        ViewHelper.setTranslationY(mListBackgroundView, ViewHelper.getTranslationY(mHeader));\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ScrollViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\n/**\n * Warning: This example does not work on Android 2.3.\n */\npublic class FillGap2ScrollViewActivity extends FillGap2BaseActivity<ObservableScrollView> implements ObservableScrollViewCallbacks {\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_fillgapscrollview;\n    }\n\n    @Override\n    protected ObservableScrollView createScrollable() {\n        ObservableScrollView scrollView = (ObservableScrollView) findViewById(R.id.scroll);\n        scrollView.setScrollViewCallbacks(this);\n        return scrollView;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3BaseActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.annotation.NonNull;\nimport android.support.v7.widget.Toolbar;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.animation.AccelerateDecelerateInterpolator;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\nimport com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout;\nimport com.nineoldandroids.animation.ValueAnimator;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\n/**\n * Another implementation of FillGap Activity.\n * These examples uses TouchInterceptionFrameLayout to enable scrolling effect\n * even when there are few items and cannot be scrolled.\n *\n * Known issue: this example just moves TouchInterceptionFrameLayout on dragging\n * and it has no velocity, so as soon as the UP/CANCEL event occurred,\n * translation will be stopped.\n */\npublic abstract class FillGap3BaseActivity<S extends Scrollable> extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private static final String STATE_TRANSLATION_Y = \"translationY\";\n\n    protected View mHeader;\n    protected View mHeaderBar;\n    private View mImageView;\n    private View mHeaderBackground;\n    private TextView mTitle;\n    private S mScrollable;\n    private TouchInterceptionFrameLayout mInterceptionLayout;\n\n    // Fields that needs to saved\n    private float mInitialTranslationY;\n\n    // Fields that just keep constants like resource values\n    protected int mActionBarSize;\n    protected int mFlexibleSpaceImageHeight;\n    protected int mIntersectionHeight;\n    private int mHeaderBarHeight;\n\n    // Temporary states\n    private float mScrollYOnDownMotion;\n\n    private float mPrevTranslationY;\n    private boolean mGapIsChanging;\n    private boolean mGapHidden;\n    private boolean mReady;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(getLayoutResId());\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        mFlexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n        mActionBarSize = getActionBarSize();\n        mHeaderBarHeight = getResources().getDimensionPixelSize(R.dimen.header_bar_height);\n\n        // Even when the top gap has began to change, header bar still can move\n        // within mIntersectionHeight.\n        mIntersectionHeight = getResources().getDimensionPixelSize(R.dimen.intersection_height);\n\n        mImageView = findViewById(R.id.image);\n        mHeader = findViewById(R.id.header);\n        mHeaderBar = findViewById(R.id.header_bar);\n        mHeaderBackground = findViewById(R.id.header_background);\n\n        mScrollable = createScrollable();\n\n        mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.scroll_wrapper);\n        mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener);\n        mTitle = (TextView) findViewById(R.id.title);\n        mTitle.setText(getTitle());\n        ViewHelper.setTranslationY(mTitle, (mHeaderBarHeight - mActionBarSize) / 2);\n        setTitle(null);\n\n        if (savedInstanceState == null) {\n            mInitialTranslationY = mFlexibleSpaceImageHeight - mHeaderBarHeight;\n        }\n\n        ScrollUtils.addOnGlobalLayoutListener(mInterceptionLayout, new Runnable() {\n            @Override\n            public void run() {\n                mReady = true;\n                updateViews(mInitialTranslationY, false);\n            }\n        });\n    }\n\n    @Override\n    protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {\n        super.onRestoreInstanceState(savedInstanceState);\n        mInitialTranslationY = savedInstanceState.getFloat(STATE_TRANSLATION_Y);\n    }\n\n    @Override\n    protected void onSaveInstanceState(Bundle outState) {\n        outState.putFloat(STATE_TRANSLATION_Y, ViewHelper.getTranslationY(mInterceptionLayout));\n        super.onSaveInstanceState(outState);\n    }\n\n    protected abstract int getLayoutResId();\n    protected abstract S createScrollable();\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n\n    private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() {\n        @Override\n        public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) {\n            return getMinInterceptionLayoutY() < (int) ViewHelper.getY(mInterceptionLayout)\n                    || (moving && mScrollable.getCurrentScrollY() - diffY < 0);\n        }\n\n        @Override\n        public void onDownMotionEvent(MotionEvent ev) {\n            mScrollYOnDownMotion = mScrollable.getCurrentScrollY();\n        }\n\n        @Override\n        public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) {\n            float translationY = ViewHelper.getTranslationY(mInterceptionLayout) - mScrollYOnDownMotion + diffY;\n            float minTranslationY = getMinInterceptionLayoutY();\n            if (translationY < minTranslationY) {\n                translationY = minTranslationY;\n            } else if (mFlexibleSpaceImageHeight - mHeaderBarHeight < translationY) {\n                translationY = mFlexibleSpaceImageHeight - mHeaderBarHeight;\n            }\n\n            updateViews(translationY, true);\n        }\n\n        @Override\n        public void onUpOrCancelMotionEvent(MotionEvent ev) {\n        }\n    };\n\n    protected void updateViews(float translationY, boolean animated) {\n        // If it's ListView, onScrollChanged is called before ListView is laid out (onGlobalLayout).\n        // This causes weird animation when onRestoreInstanceState occurred,\n        // so we check if it's laid out already.\n        if (!mReady) {\n            return;\n        }\n        ViewHelper.setTranslationY(mInterceptionLayout, translationY);\n\n        // Translate image\n        ViewHelper.setTranslationY(mImageView, (translationY - (mFlexibleSpaceImageHeight - mHeaderBarHeight)) / 2);\n\n        // Translate title\n        ViewHelper.setTranslationY(mTitle, Math.min(mIntersectionHeight, (mHeaderBarHeight - mActionBarSize) / 2));\n\n        // Show/hide gap\n        boolean scrollUp = translationY < mPrevTranslationY;\n        if (scrollUp) {\n            if (translationY <= mActionBarSize) {\n                changeHeaderBackgroundHeightAnimated(false, animated);\n            }\n        } else {\n            if (mActionBarSize <= translationY) {\n                changeHeaderBackgroundHeightAnimated(true, animated);\n            }\n        }\n        mPrevTranslationY = translationY;\n    }\n\n    private void changeHeaderBackgroundHeightAnimated(boolean shouldShowGap, boolean animated) {\n        if (mGapIsChanging) {\n            return;\n        }\n        final int heightOnGapShown = mHeaderBar.getHeight();\n        final int heightOnGapHidden = mHeaderBar.getHeight() + mActionBarSize;\n        final float from = mHeaderBackground.getLayoutParams().height;\n        final float to;\n        if (shouldShowGap) {\n            if (!mGapHidden) {\n                // Already shown\n                return;\n            }\n            to = heightOnGapShown;\n        } else {\n            if (mGapHidden) {\n                // Already hidden\n                return;\n            }\n            to = heightOnGapHidden;\n        }\n        if (animated) {\n            ViewPropertyAnimator.animate(mHeaderBackground).cancel();\n            ValueAnimator a = ValueAnimator.ofFloat(from, to);\n            a.setDuration(100);\n            a.setInterpolator(new AccelerateDecelerateInterpolator());\n            a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n                @Override\n                public void onAnimationUpdate(ValueAnimator animation) {\n                    float height = (float) animation.getAnimatedValue();\n                    changeHeaderBackgroundHeight(height, to, heightOnGapHidden);\n                }\n            });\n            a.start();\n        } else {\n            changeHeaderBackgroundHeight(to, to, heightOnGapHidden);\n        }\n    }\n\n    private void changeHeaderBackgroundHeight(float height, float to, float heightOnGapHidden) {\n        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mHeaderBackground.getLayoutParams();\n        lp.height = (int) height;\n        lp.topMargin = (int) (mHeaderBar.getHeight() - height);\n        mHeaderBackground.requestLayout();\n        mGapIsChanging = (height != to);\n        if (!mGapIsChanging) {\n            mGapHidden = (height == heightOnGapHidden);\n        }\n    }\n\n    private float getMinInterceptionLayoutY() {\n        return mActionBarSize - mIntersectionHeight;\n        // If you want to move header bar to the top, return 0 instead.\n        //return 0;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ListViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\n/**\n * Warning: This example does not work on Android 2.3.\n */\npublic class FillGap3ListViewActivity extends FillGap3BaseActivity<ObservableListView> implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected ObservableListView createScrollable() {\n        ObservableListView listView = (ObservableListView) findViewById(R.id.scroll);\n        listView.setScrollViewCallbacks(this);\n        setDummyDataFew(listView);\n        return listView;\n    }\n\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_fillgap3listview;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3RecyclerViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.support.v7.widget.LinearLayoutManager;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\n/**\n * Warning: This example does not work on Android 2.3.\n */\npublic class FillGap3RecyclerViewActivity extends FillGap3BaseActivity<ObservableRecyclerView> implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected ObservableRecyclerView createScrollable() {\n        ObservableRecyclerView recyclerView = (ObservableRecyclerView) findViewById(R.id.scroll);\n        recyclerView.setScrollViewCallbacks(this);\n        recyclerView.setLayoutManager(new LinearLayoutManager(this));\n        recyclerView.setHasFixedSize(true);\n        setDummyDataFew(recyclerView);\n        return recyclerView;\n    }\n\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_fillgap3recyclerview;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ScrollViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\n/**\n * Warning: This example does not work on Android 2.3.\n */\npublic class FillGap3ScrollViewActivity extends FillGap3BaseActivity<ObservableScrollView> implements ObservableScrollViewCallbacks {\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_fillgap3scrollview;\n    }\n\n    @Override\n    protected ObservableScrollView createScrollable() {\n        ObservableScrollView scrollView = (ObservableScrollView) findViewById(R.id.scroll);\n        scrollView.setScrollViewCallbacks(this);\n        return scrollView;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.view.View;\nimport android.view.animation.AccelerateDecelerateInterpolator;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\nimport com.nineoldandroids.animation.ValueAnimator;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\n/**\n * Warning: This example does not work on Android 2.3.\n *\n * @param <S> Scrollable\n */\npublic abstract class FillGapBaseActivity<S extends Scrollable> extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    protected View mHeader;\n    protected int mFlexibleSpaceImageHeight;\n    protected View mHeaderBar;\n    protected View mListBackgroundView;\n    protected int mActionBarSize;\n    protected int mIntersectionHeight;\n\n    private View mImage;\n    private View mHeaderBackground;\n    private int mPrevScrollY;\n    private boolean mGapIsChanging;\n    private boolean mGapHidden;\n    private boolean mReady;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(getLayoutResId());\n\n        mFlexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n        mActionBarSize = getActionBarSize();\n\n        // Even when the top gap has began to change, header bar still can move\n        // within mIntersectionHeight.\n        mIntersectionHeight = getResources().getDimensionPixelSize(R.dimen.intersection_height);\n\n        mImage = findViewById(R.id.image);\n        mHeader = findViewById(R.id.header);\n        mHeaderBar = findViewById(R.id.header_bar);\n        mHeaderBackground = findViewById(R.id.header_background);\n        mListBackgroundView = findViewById(R.id.list_background);\n\n        final S scrollable = createScrollable();\n\n        ((TextView) findViewById(R.id.title)).setText(getTitle());\n        setTitle(null);\n\n        ScrollUtils.addOnGlobalLayoutListener((View) scrollable, new Runnable() {\n            @Override\n            public void run() {\n                mReady = true;\n                updateViews(scrollable.getCurrentScrollY(), false);\n            }\n        });\n    }\n\n    protected abstract int getLayoutResId();\n    protected abstract S createScrollable();\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        updateViews(scrollY, true);\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n\n    protected void updateViews(int scrollY, boolean animated) {\n        // If it's ListView, onScrollChanged is called before ListView is laid out (onGlobalLayout).\n        // This causes weird animation when onRestoreInstanceState occurred,\n        // so we check if it's laid out already.\n        if (!mReady) {\n            return;\n        }\n        // Translate image\n        ViewHelper.setTranslationY(mImage, -scrollY / 2);\n\n        // Translate header\n        ViewHelper.setTranslationY(mHeader, getHeaderTranslationY(scrollY));\n\n        // Show/hide gap\n        final int headerHeight = mHeaderBar.getHeight();\n        boolean scrollUp = mPrevScrollY < scrollY;\n        if (scrollUp) {\n            if (mFlexibleSpaceImageHeight - headerHeight - mActionBarSize <= scrollY) {\n                changeHeaderBackgroundHeightAnimated(false, animated);\n            }\n        } else {\n            if (scrollY <= mFlexibleSpaceImageHeight - headerHeight - mActionBarSize) {\n                changeHeaderBackgroundHeightAnimated(true, animated);\n            }\n        }\n        mPrevScrollY = scrollY;\n    }\n\n    protected float getHeaderTranslationY(int scrollY) {\n        final int headerHeight = mHeaderBar.getHeight();\n        int headerTranslationY = mActionBarSize - mIntersectionHeight;\n        if (0 <= -scrollY + mFlexibleSpaceImageHeight - headerHeight - mActionBarSize + mIntersectionHeight) {\n            headerTranslationY = -scrollY + mFlexibleSpaceImageHeight - headerHeight;\n        }\n        return headerTranslationY;\n    }\n\n    private void changeHeaderBackgroundHeightAnimated(boolean shouldShowGap, boolean animated) {\n        if (mGapIsChanging) {\n            return;\n        }\n        final int heightOnGapShown = mHeaderBar.getHeight();\n        final int heightOnGapHidden = mHeaderBar.getHeight() + mActionBarSize;\n        final float from = mHeaderBackground.getLayoutParams().height;\n        final float to;\n        if (shouldShowGap) {\n            if (!mGapHidden) {\n                // Already shown\n                return;\n            }\n            to = heightOnGapShown;\n        } else {\n            if (mGapHidden) {\n                // Already hidden\n                return;\n            }\n            to = heightOnGapHidden;\n        }\n        if (animated) {\n            ViewPropertyAnimator.animate(mHeaderBackground).cancel();\n            ValueAnimator a = ValueAnimator.ofFloat(from, to);\n            a.setDuration(100);\n            a.setInterpolator(new AccelerateDecelerateInterpolator());\n            a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n                @Override\n                public void onAnimationUpdate(ValueAnimator animation) {\n                    float height = (float) animation.getAnimatedValue();\n                    changeHeaderBackgroundHeight(height, to, heightOnGapHidden);\n                }\n            });\n            a.start();\n        } else {\n            changeHeaderBackgroundHeight(to, to, heightOnGapHidden);\n        }\n    }\n\n    private void changeHeaderBackgroundHeight(float height, float to, float heightOnGapHidden) {\n        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mHeaderBackground.getLayoutParams();\n        lp.height = (int) height;\n        lp.topMargin = (int) (mHeaderBar.getHeight() - height);\n        mHeaderBackground.requestLayout();\n        mGapIsChanging = (height != to);\n        if (!mGapIsChanging) {\n            mGapHidden = (height == heightOnGapHidden);\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapListViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.nineoldandroids.view.ViewHelper;\n\n/**\n * Warning: This example does not work on Android 2.3.\n */\npublic class FillGapListViewActivity extends FillGapBaseActivity<ObservableListView> implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected ObservableListView createScrollable() {\n        ObservableListView listView = (ObservableListView) findViewById(R.id.scroll);\n        listView.setScrollViewCallbacks(this);\n        setDummyDataWithHeader(listView, mFlexibleSpaceImageHeight);\n        return listView;\n    }\n\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_fillgaplistview;\n    }\n\n    @Override\n    protected void updateViews(int scrollY, boolean animated) {\n        super.updateViews(scrollY, animated);\n\n        // Translate list background\n        ViewHelper.setTranslationY(mListBackgroundView, ViewHelper.getTranslationY(mHeader));\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapRecyclerViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.support.v7.widget.LinearLayoutManager;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.nineoldandroids.view.ViewHelper;\n\n/**\n * Warning: This example does not work on Android 2.3.\n */\npublic class FillGapRecyclerViewActivity extends FillGapBaseActivity<ObservableRecyclerView> implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected ObservableRecyclerView createScrollable() {\n        ObservableRecyclerView recyclerView = (ObservableRecyclerView) findViewById(R.id.scroll);\n        recyclerView.setScrollViewCallbacks(this);\n        recyclerView.setLayoutManager(new LinearLayoutManager(this));\n        recyclerView.setHasFixedSize(false);\n        setDummyDataWithHeader(recyclerView, mFlexibleSpaceImageHeight);\n        return recyclerView;\n    }\n\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_fillgaprecyclerview;\n    }\n\n    @Override\n    protected void updateViews(int scrollY, boolean animated) {\n        super.updateViews(scrollY, animated);\n\n        // Translate list background\n        ViewHelper.setTranslationY(mListBackgroundView, ViewHelper.getTranslationY(mHeader));\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapScrollViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\n/**\n * Warning: This example does not work on Android 2.3.\n */\npublic class FillGapScrollViewActivity extends FillGapBaseActivity<ObservableScrollView> implements ObservableScrollViewCallbacks {\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_fillgapscrollview;\n    }\n\n    @Override\n    protected ObservableScrollView createScrollable() {\n        ObservableScrollView scrollView = (ObservableScrollView) findViewById(R.id.scroll);\n        scrollView.setScrollViewCallbacks(this);\n        return scrollView;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v7.app.ActionBar;\nimport android.support.v7.widget.Toolbar;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\n\npublic class FlexibleSpaceToolbarScrollViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private View mFlexibleSpaceView;\n    private View mToolbarView;\n    private TextView mTitleView;\n    private int mFlexibleSpaceHeight;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_flexiblespacetoolbarscrollview);\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n        ActionBar ab = getSupportActionBar();\n        if (ab != null) {\n            getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        }\n\n        mFlexibleSpaceView = findViewById(R.id.flexible_space);\n        mTitleView = (TextView) findViewById(R.id.title);\n        mTitleView.setText(getTitle());\n        setTitle(null);\n        mToolbarView = findViewById(R.id.toolbar);\n\n        final ObservableScrollView scrollView = (ObservableScrollView) findViewById(R.id.scroll);\n        scrollView.setScrollViewCallbacks(this);\n\n        mFlexibleSpaceHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_height);\n        int flexibleSpaceAndToolbarHeight = mFlexibleSpaceHeight + getActionBarSize();\n\n        findViewById(R.id.body).setPadding(0, flexibleSpaceAndToolbarHeight, 0, 0);\n        mFlexibleSpaceView.getLayoutParams().height = flexibleSpaceAndToolbarHeight;\n\n        ScrollUtils.addOnGlobalLayoutListener(mTitleView, new Runnable() {\n            @Override\n            public void run() {\n                updateFlexibleSpaceText(scrollView.getCurrentScrollY());\n            }\n        });\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        updateFlexibleSpaceText(scrollY);\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n\n    private void updateFlexibleSpaceText(final int scrollY) {\n        ViewHelper.setTranslationY(mFlexibleSpaceView, -scrollY);\n        int adjustedScrollY = (int) ScrollUtils.getFloat(scrollY, 0, mFlexibleSpaceHeight);\n        float maxScale = (float) (mFlexibleSpaceHeight - mToolbarView.getHeight()) / mToolbarView.getHeight();\n        float scale = maxScale * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight;\n\n        ViewHelper.setPivotX(mTitleView, 0);\n        ViewHelper.setPivotY(mTitleView, 0);\n        ViewHelper.setScaleX(mTitleView, 1 + scale);\n        ViewHelper.setScaleY(mTitleView, 1 + scale);\n        int maxTitleTranslationY = mToolbarView.getHeight() + mFlexibleSpaceHeight - (int) (mTitleView.getHeight() * (1 + scale));\n        int titleTranslationY = (int) (maxTitleTranslationY * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight);\n        ViewHelper.setTranslationY(mTitleView, titleTranslationY);\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v7.app.ActionBar;\nimport android.support.v7.widget.Toolbar;\nimport android.view.View;\nimport android.webkit.WebView;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\n\npublic class FlexibleSpaceToolbarWebViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private View mFlexibleSpaceView;\n    private View mToolbarView;\n    private TextView mTitleView;\n    private int mFlexibleSpaceHeight;\n    private View mWebViewContainer;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_flexiblespacetoolbarwebview);\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n        ActionBar ab = getSupportActionBar();\n        if (ab != null) {\n            getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        }\n\n        mFlexibleSpaceView = findViewById(R.id.flexible_space);\n        mTitleView = (TextView) findViewById(R.id.title);\n        mTitleView.setText(getTitle());\n        setTitle(null);\n        mToolbarView = findViewById(R.id.toolbar);\n\n        mWebViewContainer = findViewById(R.id.webViewContainer);\n\n        final ObservableScrollView scrollView = (ObservableScrollView) findViewById(R.id.scroll);\n        scrollView.setScrollViewCallbacks(this);\n\n        WebView webView = (WebView) findViewById(R.id.webView);\n        webView.loadUrl(\"file:///android_asset/lipsum.html\");\n\n        mFlexibleSpaceHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_height);\n        int flexibleSpaceAndToolbarHeight = mFlexibleSpaceHeight + getActionBarSize();\n\n        final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) webView.getLayoutParams();\n        layoutParams.topMargin = flexibleSpaceAndToolbarHeight;\n        webView.setLayoutParams(layoutParams);\n\n        mFlexibleSpaceView.getLayoutParams().height = flexibleSpaceAndToolbarHeight;\n\n        ScrollUtils.addOnGlobalLayoutListener(mTitleView, new Runnable() {\n            @Override\n            public void run() {\n                updateFlexibleSpaceText(scrollView.getCurrentScrollY());\n            }\n        });\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        updateFlexibleSpaceText(scrollY);\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n\n    private void updateFlexibleSpaceText(final int scrollY) {\n        ViewHelper.setTranslationY(mFlexibleSpaceView, -scrollY);\n        int adjustedScrollY = (int) ScrollUtils.getFloat(scrollY, 0, mFlexibleSpaceHeight);\n\n        // Special logic for WebView.\n        adjustTopMargin(mWebViewContainer, adjustedScrollY <= mFlexibleSpaceHeight ? 0 : mFlexibleSpaceHeight + getActionBarSize());\n\n        float maxScale = (float) (mFlexibleSpaceHeight - mToolbarView.getHeight()) / mToolbarView.getHeight();\n        float scale = maxScale * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight;\n\n        ViewHelper.setPivotX(mTitleView, 0);\n        ViewHelper.setPivotY(mTitleView, 0);\n        ViewHelper.setScaleX(mTitleView, 1 + scale);\n        ViewHelper.setScaleY(mTitleView, 1 + scale);\n        int maxTitleTranslationY = mToolbarView.getHeight() + mFlexibleSpaceHeight - (int) (mTitleView.getHeight() * (1 + scale));\n        int titleTranslationY = (int) (maxTitleTranslationY * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight);\n        ViewHelper.setTranslationY(mTitleView, titleTranslationY);\n    }\n\n    private void adjustTopMargin(View view, int topMargin) {\n        final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) view.getLayoutParams();\n\n        if (layoutParams.topMargin == topMargin) {\n            return;\n        }\n\n        layoutParams.topMargin = topMargin;\n\n        view.setLayoutParams(layoutParams);\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.view.View;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\n\npublic abstract class FlexibleSpaceWithImageBaseFragment<S extends Scrollable> extends BaseFragment\n        implements ObservableScrollViewCallbacks {\n\n    public static final String ARG_SCROLL_Y = \"ARG_SCROLL_Y\";\n\n    public void setArguments(int scrollY) {\n        if (0 <= scrollY) {\n            Bundle args = new Bundle();\n            args.putInt(ARG_SCROLL_Y, scrollY);\n            setArguments(args);\n        }\n    }\n\n    public void setScrollY(int scrollY, int threshold) {\n        View view = getView();\n        if (view == null) {\n            return;\n        }\n        Scrollable scrollView = (Scrollable) view.findViewById(R.id.scroll);\n        if (scrollView == null) {\n            return;\n        }\n        scrollView.scrollVerticallyTo(scrollY);\n    }\n\n    protected void updateFlexibleSpace(int scrollY) {\n        updateFlexibleSpace(scrollY, getView());\n    }\n\n    protected abstract void updateFlexibleSpace(int scrollY, View view);\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        if (getView() == null) {\n            return;\n        }\n        updateFlexibleSpace(scrollY, getView());\n    }\n\n    @Override\n    public final void onDownMotionEvent() {\n        // We don't use this callback in this pattern.\n    }\n\n    @Override\n    public final void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        // We don't use this callback in this pattern.\n    }\n\n    protected S getScrollable() {\n        View view = getView();\n        return view == null ? null : (S) view.findViewById(R.id.scroll);\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageGridViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.annotation.TargetApi;\nimport android.content.res.Configuration;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.view.View;\nimport android.widget.AbsListView;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\n/**\n * This example depends on {@code ObservableGridView#addHeaderView()} method introduced in v1.6.0.\n */\npublic class FlexibleSpaceWithImageGridViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private static final float MAX_TEXT_SCALE_DELTA = 0.3f;\n\n    private View mImageView;\n    private View mOverlayView;\n    private View mListBackgroundView;\n    private TextView mTitleView;\n    private View mFab;\n    private int mActionBarSize;\n    private int mFlexibleSpaceShowFabOffset;\n    private int mFlexibleSpaceImageHeight;\n    private int mFabMargin;\n    private boolean mFabIsShown;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_flexiblespacewithimagegridview);\n\n        mFlexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n        mFlexibleSpaceShowFabOffset = getResources().getDimensionPixelSize(R.dimen.flexible_space_show_fab_offset);\n        mActionBarSize = getActionBarSize();\n        mImageView = findViewById(R.id.image);\n        mOverlayView = findViewById(R.id.overlay);\n        ObservableGridView gridView = (ObservableGridView) findViewById(R.id.list);\n        gridView.setScrollViewCallbacks(this);\n\n        // Set padding view for ListView. This is the flexible space.\n        View paddingView = new View(this);\n        AbsListView.LayoutParams lp = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT,\n                mFlexibleSpaceImageHeight);\n        paddingView.setLayoutParams(lp);\n\n        // This is required to disable header's list selector effect\n        paddingView.setClickable(true);\n\n        gridView.addHeaderView(paddingView);\n        setDummyData(gridView);\n        mTitleView = (TextView) findViewById(R.id.title);\n        mTitleView.setText(getTitle());\n        setTitle(null);\n        mFab = findViewById(R.id.fab);\n        mFab.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                Toast.makeText(FlexibleSpaceWithImageGridViewActivity.this, \"FAB is clicked\", Toast.LENGTH_SHORT).show();\n            }\n        });\n        mFabMargin = getResources().getDimensionPixelSize(R.dimen.margin_standard);\n        ViewHelper.setScaleX(mFab, 0);\n        ViewHelper.setScaleY(mFab, 0);\n\n        // mListBackgroundView makes ListView's background except header view.\n        mListBackgroundView = findViewById(R.id.list_background);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        // Translate overlay and image\n        float flexibleRange = mFlexibleSpaceImageHeight - mActionBarSize;\n        int minOverlayTransitionY = mActionBarSize - mOverlayView.getHeight();\n        ViewHelper.setTranslationY(mOverlayView, ScrollUtils.getFloat(-scrollY, minOverlayTransitionY, 0));\n        ViewHelper.setTranslationY(mImageView, ScrollUtils.getFloat(-scrollY / 2, minOverlayTransitionY, 0));\n\n        // Translate list background\n        ViewHelper.setTranslationY(mListBackgroundView, Math.max(0, -scrollY + mFlexibleSpaceImageHeight));\n\n        // Change alpha of overlay\n        ViewHelper.setAlpha(mOverlayView, ScrollUtils.getFloat((float) scrollY / flexibleRange, 0, 1));\n\n        // Scale title text\n        float scale = 1 + ScrollUtils.getFloat((flexibleRange - scrollY) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA);\n        setPivotXToTitle();\n        ViewHelper.setPivotY(mTitleView, 0);\n        ViewHelper.setScaleX(mTitleView, scale);\n        ViewHelper.setScaleY(mTitleView, scale);\n\n        // Translate title text\n        int maxTitleTranslationY = (int) (mFlexibleSpaceImageHeight - mTitleView.getHeight() * scale);\n        int titleTranslationY = maxTitleTranslationY - scrollY;\n        ViewHelper.setTranslationY(mTitleView, titleTranslationY);\n\n        // Translate FAB\n        int maxFabTranslationY = mFlexibleSpaceImageHeight - mFab.getHeight() / 2;\n        float fabTranslationY = ScrollUtils.getFloat(\n                -scrollY + mFlexibleSpaceImageHeight - mFab.getHeight() / 2,\n                mActionBarSize - mFab.getHeight() / 2,\n                maxFabTranslationY);\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {\n            // On pre-honeycomb, ViewHelper.setTranslationX/Y does not set margin,\n            // which causes FAB's OnClickListener not working.\n            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mFab.getLayoutParams();\n            lp.leftMargin = mOverlayView.getWidth() - mFabMargin - mFab.getWidth();\n            lp.topMargin = (int) fabTranslationY;\n            mFab.requestLayout();\n        } else {\n            ViewHelper.setTranslationX(mFab, mOverlayView.getWidth() - mFabMargin - mFab.getWidth());\n            ViewHelper.setTranslationY(mFab, fabTranslationY);\n        }\n\n        // Show/hide FAB\n        if (fabTranslationY < mFlexibleSpaceShowFabOffset) {\n            hideFab();\n        } else {\n            showFab();\n        }\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n\n    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n    private void setPivotXToTitle() {\n        Configuration config = getResources().getConfiguration();\n        if (Build.VERSION_CODES.JELLY_BEAN_MR1 <= Build.VERSION.SDK_INT\n                && config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {\n            ViewHelper.setPivotX(mTitleView, findViewById(android.R.id.content).getWidth());\n        } else {\n            ViewHelper.setPivotX(mTitleView, 0);\n        }\n    }\n\n    private void showFab() {\n        if (!mFabIsShown) {\n            ViewPropertyAnimator.animate(mFab).cancel();\n            ViewPropertyAnimator.animate(mFab).scaleX(1).scaleY(1).setDuration(200).start();\n            mFabIsShown = true;\n        }\n    }\n\n    private void hideFab() {\n        if (mFabIsShown) {\n            ViewPropertyAnimator.animate(mFab).cancel();\n            ViewPropertyAnimator.animate(mFab).scaleX(0).scaleY(0).setDuration(200).start();\n            mFabIsShown = false;\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageGridViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\n\npublic class FlexibleSpaceWithImageGridViewFragment extends FlexibleSpaceWithImageBaseFragment<ObservableGridView> {\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_flexiblespacewithimagegridview, container, false);\n\n        final ObservableGridView gridView = (ObservableGridView) view.findViewById(R.id.scroll);\n        // Set padding view for GridView. This is the flexible space.\n        View paddingView = new View(getActivity());\n        final int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,\n                flexibleSpaceImageHeight);\n        paddingView.setLayoutParams(lp);\n\n        // This is required to disable header's list selector effect\n        paddingView.setClickable(true);\n\n        gridView.addHeaderView(paddingView);\n        setDummyData(gridView);\n        // TouchInterceptionViewGroup should be a parent view other than ViewPager.\n        // This is a workaround for the issue #117:\n        // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117\n        gridView.setTouchInterceptionViewGroup((ViewGroup) view.findViewById(R.id.fragment_root));\n\n        // Scroll to the specified offset after layout\n        Bundle args = getArguments();\n        if (args != null && args.containsKey(ARG_SCROLL_Y)) {\n            final int scrollY = args.getInt(ARG_SCROLL_Y, 0);\n            ScrollUtils.addOnGlobalLayoutListener(gridView, new Runnable() {\n                @SuppressLint(\"NewApi\")\n                @Override\n                public void run() {\n                    int offset = scrollY % flexibleSpaceImageHeight;\n                    setSelectionFromTop(gridView, 0, -offset);\n                }\n            });\n            updateFlexibleSpace(scrollY, view);\n        } else {\n            updateFlexibleSpace(0, view);\n        }\n\n        gridView.setScrollViewCallbacks(this);\n\n        updateFlexibleSpace(0, view);\n\n        return view;\n    }\n\n    @SuppressWarnings(\"NewApi\")\n    @Override\n    public void setScrollY(int scrollY, int threshold) {\n        View view = getView();\n        if (view == null) {\n            return;\n        }\n        ObservableGridView gridView = (ObservableGridView) view.findViewById(R.id.scroll);\n        if (gridView == null) {\n            return;\n        }\n        View firstVisibleChild = gridView.getChildAt(0);\n        if (firstVisibleChild != null) {\n            int offset = scrollY;\n            int position = 0;\n            if (threshold < scrollY) {\n                int baseHeight = firstVisibleChild.getHeight();\n                position = scrollY / baseHeight;\n                offset = scrollY % baseHeight;\n            }\n            setSelectionFromTop(gridView, position, -offset);\n        }\n    }\n\n    @Override\n    protected void updateFlexibleSpace(int scrollY, View view) {\n        int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n\n        View listBackgroundView = view.findViewById(R.id.list_background);\n\n        // Translate list background\n        ViewHelper.setTranslationY(listBackgroundView, Math.max(0, -scrollY + flexibleSpaceImageHeight));\n\n        // Also pass this event to parent Activity\n        FlexibleSpaceWithImageWithViewPagerTabActivity parentActivity =\n                (FlexibleSpaceWithImageWithViewPagerTabActivity) getActivity();\n        if (parentActivity != null) {\n            parentActivity.onScrollChanged(scrollY, (ObservableGridView) view.findViewById(R.id.scroll));\n        }\n    }\n\n    /*\n     * setSelectionFromTop method has been moved from ListView to AbsListView since API level 21,\n     * so for API level 21-, we need to use other method to scroll with offset.\n     * smoothScrollToPositionFromTop seems to work, but it's from API level 11.\n     * We can't use GridView for Gingerbread.\n     */\n    @TargetApi(Build.VERSION_CODES.LOLLIPOP)\n    private void setSelectionFromTop(ObservableGridView gridView, int position, int offset) {\n        if (Build.VERSION_CODES.LOLLIPOP <= Build.VERSION.SDK_INT) {\n            gridView.setSelectionFromTop(position, offset);\n        } else if (Build.VERSION_CODES.HONEYCOMB <= Build.VERSION.SDK_INT) {\n            gridView.smoothScrollToPositionFromTop(position, offset, 0);\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.annotation.TargetApi;\nimport android.content.res.Configuration;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.view.View;\nimport android.widget.AbsListView;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\npublic class FlexibleSpaceWithImageListViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private static final float MAX_TEXT_SCALE_DELTA = 0.3f;\n\n    private View mImageView;\n    private View mOverlayView;\n    private View mListBackgroundView;\n    private TextView mTitleView;\n    private View mFab;\n    private int mActionBarSize;\n    private int mFlexibleSpaceShowFabOffset;\n    private int mFlexibleSpaceImageHeight;\n    private int mFabMargin;\n    private boolean mFabIsShown;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_flexiblespacewithimagelistview);\n\n        mFlexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n        mFlexibleSpaceShowFabOffset = getResources().getDimensionPixelSize(R.dimen.flexible_space_show_fab_offset);\n        mActionBarSize = getActionBarSize();\n        mImageView = findViewById(R.id.image);\n        mOverlayView = findViewById(R.id.overlay);\n        ObservableListView listView = (ObservableListView) findViewById(R.id.list);\n        listView.setScrollViewCallbacks(this);\n\n        // Set padding view for ListView. This is the flexible space.\n        View paddingView = new View(this);\n        AbsListView.LayoutParams lp = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT,\n                mFlexibleSpaceImageHeight);\n        paddingView.setLayoutParams(lp);\n\n        // This is required to disable header's list selector effect\n        paddingView.setClickable(true);\n\n        listView.addHeaderView(paddingView);\n        setDummyData(listView);\n        mTitleView = (TextView) findViewById(R.id.title);\n        mTitleView.setText(getTitle());\n        setTitle(null);\n        mFab = findViewById(R.id.fab);\n        mFab.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                Toast.makeText(FlexibleSpaceWithImageListViewActivity.this, \"FAB is clicked\", Toast.LENGTH_SHORT).show();\n            }\n        });\n        mFabMargin = getResources().getDimensionPixelSize(R.dimen.margin_standard);\n        ViewHelper.setScaleX(mFab, 0);\n        ViewHelper.setScaleY(mFab, 0);\n\n        // mListBackgroundView makes ListView's background except header view.\n        mListBackgroundView = findViewById(R.id.list_background);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        // Translate overlay and image\n        float flexibleRange = mFlexibleSpaceImageHeight - mActionBarSize;\n        int minOverlayTransitionY = mActionBarSize - mOverlayView.getHeight();\n        ViewHelper.setTranslationY(mOverlayView, ScrollUtils.getFloat(-scrollY, minOverlayTransitionY, 0));\n        ViewHelper.setTranslationY(mImageView, ScrollUtils.getFloat(-scrollY / 2, minOverlayTransitionY, 0));\n\n        // Translate list background\n        ViewHelper.setTranslationY(mListBackgroundView, Math.max(0, -scrollY + mFlexibleSpaceImageHeight));\n\n        // Change alpha of overlay\n        ViewHelper.setAlpha(mOverlayView, ScrollUtils.getFloat((float) scrollY / flexibleRange, 0, 1));\n\n        // Scale title text\n        float scale = 1 + ScrollUtils.getFloat((flexibleRange - scrollY) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA);\n        setPivotXToTitle();\n        ViewHelper.setPivotY(mTitleView, 0);\n        ViewHelper.setScaleX(mTitleView, scale);\n        ViewHelper.setScaleY(mTitleView, scale);\n\n        // Translate title text\n        int maxTitleTranslationY = (int) (mFlexibleSpaceImageHeight - mTitleView.getHeight() * scale);\n        int titleTranslationY = maxTitleTranslationY - scrollY;\n        ViewHelper.setTranslationY(mTitleView, titleTranslationY);\n\n        // Translate FAB\n        int maxFabTranslationY = mFlexibleSpaceImageHeight - mFab.getHeight() / 2;\n        float fabTranslationY = ScrollUtils.getFloat(\n                -scrollY + mFlexibleSpaceImageHeight - mFab.getHeight() / 2,\n                mActionBarSize - mFab.getHeight() / 2,\n                maxFabTranslationY);\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {\n            // On pre-honeycomb, ViewHelper.setTranslationX/Y does not set margin,\n            // which causes FAB's OnClickListener not working.\n            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mFab.getLayoutParams();\n            lp.leftMargin = mOverlayView.getWidth() - mFabMargin - mFab.getWidth();\n            lp.topMargin = (int) fabTranslationY;\n            mFab.requestLayout();\n        } else {\n            ViewHelper.setTranslationX(mFab, mOverlayView.getWidth() - mFabMargin - mFab.getWidth());\n            ViewHelper.setTranslationY(mFab, fabTranslationY);\n        }\n\n        // Show/hide FAB\n        if (fabTranslationY < mFlexibleSpaceShowFabOffset) {\n            hideFab();\n        } else {\n            showFab();\n        }\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n\n    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n    private void setPivotXToTitle() {\n        Configuration config = getResources().getConfiguration();\n        if (Build.VERSION_CODES.JELLY_BEAN_MR1 <= Build.VERSION.SDK_INT\n                && config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {\n            ViewHelper.setPivotX(mTitleView, findViewById(android.R.id.content).getWidth());\n        } else {\n            ViewHelper.setPivotX(mTitleView, 0);\n        }\n    }\n\n    private void showFab() {\n        if (!mFabIsShown) {\n            ViewPropertyAnimator.animate(mFab).cancel();\n            ViewPropertyAnimator.animate(mFab).scaleX(1).scaleY(1).setDuration(200).start();\n            mFabIsShown = true;\n        }\n    }\n\n    private void hideFab() {\n        if (mFabIsShown) {\n            ViewPropertyAnimator.animate(mFab).cancel();\n            ViewPropertyAnimator.animate(mFab).scaleX(0).scaleY(0).setDuration(200).start();\n            mFabIsShown = false;\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.annotation.SuppressLint;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AbsListView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\n\npublic class FlexibleSpaceWithImageListViewFragment extends FlexibleSpaceWithImageBaseFragment<ObservableListView> {\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_flexiblespacewithimagelistview, container, false);\n\n        final ObservableListView listView = (ObservableListView) view.findViewById(R.id.scroll);\n        // Set padding view for ListView. This is the flexible space.\n        View paddingView = new View(getActivity());\n        final int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n        AbsListView.LayoutParams lp = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT,\n                flexibleSpaceImageHeight);\n        paddingView.setLayoutParams(lp);\n\n        // This is required to disable header's list selector effect\n        paddingView.setClickable(true);\n\n        listView.addHeaderView(paddingView);\n        setDummyData(listView);\n        // TouchInterceptionViewGroup should be a parent view other than ViewPager.\n        // This is a workaround for the issue #117:\n        // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117\n        listView.setTouchInterceptionViewGroup((ViewGroup) view.findViewById(R.id.fragment_root));\n\n        // Scroll to the specified offset after layout\n        Bundle args = getArguments();\n        if (args != null && args.containsKey(ARG_SCROLL_Y)) {\n            final int scrollY = args.getInt(ARG_SCROLL_Y, 0);\n            ScrollUtils.addOnGlobalLayoutListener(listView, new Runnable() {\n                @SuppressLint(\"NewApi\")\n                @Override\n                public void run() {\n                    int offset = scrollY % flexibleSpaceImageHeight;\n                    listView.setSelectionFromTop(0, -offset);\n                }\n            });\n            updateFlexibleSpace(scrollY, view);\n        } else {\n            updateFlexibleSpace(0, view);\n        }\n\n        listView.setScrollViewCallbacks(this);\n\n        updateFlexibleSpace(0, view);\n\n        return view;\n    }\n\n    @SuppressWarnings(\"NewApi\")\n    @Override\n    public void setScrollY(int scrollY, int threshold) {\n        View view = getView();\n        if (view == null) {\n            return;\n        }\n        ObservableListView listView = (ObservableListView) view.findViewById(R.id.scroll);\n        if (listView == null) {\n            return;\n        }\n        View firstVisibleChild = listView.getChildAt(0);\n        if (firstVisibleChild != null) {\n            int offset = scrollY;\n            int position = 0;\n            if (threshold < scrollY) {\n                int baseHeight = firstVisibleChild.getHeight();\n                position = scrollY / baseHeight;\n                offset = scrollY % baseHeight;\n            }\n            listView.setSelectionFromTop(position, -offset);\n        }\n    }\n\n    @Override\n    protected void updateFlexibleSpace(int scrollY, View view) {\n        int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n\n        View listBackgroundView = view.findViewById(R.id.list_background);\n\n        // Translate list background\n        ViewHelper.setTranslationY(listBackgroundView, Math.max(0, -scrollY + flexibleSpaceImageHeight));\n\n        // Also pass this event to parent Activity\n        FlexibleSpaceWithImageWithViewPagerTabActivity parentActivity =\n                (FlexibleSpaceWithImageWithViewPagerTabActivity) getActivity();\n        if (parentActivity != null) {\n            parentActivity.onScrollChanged(scrollY, (ObservableListView) view.findViewById(R.id.scroll));\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java",
    "content": "package com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.annotation.TargetApi;\nimport android.content.res.Configuration;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\n\npublic class FlexibleSpaceWithImageRecyclerViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private static final float MAX_TEXT_SCALE_DELTA = 0.3f;\n\n    private View mImageView;\n    private View mOverlayView;\n    private View mRecyclerViewBackground;\n    private TextView mTitleView;\n    private int mActionBarSize;\n    private int mFlexibleSpaceImageHeight;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_flexiblespacewithimagerecyclerview);\n\n        mFlexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n        mActionBarSize = getActionBarSize();\n\n        ObservableRecyclerView recyclerView = (ObservableRecyclerView) findViewById(R.id.recycler);\n        recyclerView.setScrollViewCallbacks(this);\n        recyclerView.setLayoutManager(new LinearLayoutManager(this));\n        recyclerView.setHasFixedSize(false);\n        final View headerView = LayoutInflater.from(this).inflate(R.layout.recycler_header, null);\n        headerView.post(new Runnable() {\n            @Override\n            public void run() {\n                headerView.getLayoutParams().height = mFlexibleSpaceImageHeight;\n            }\n        });\n        setDummyDataWithHeader(recyclerView, headerView);\n\n        mImageView = findViewById(R.id.image);\n        mOverlayView = findViewById(R.id.overlay);\n\n        mTitleView = (TextView) findViewById(R.id.title);\n        mTitleView.setText(getTitle());\n        setTitle(null);\n\n        // mRecyclerViewBackground makes RecyclerView's background except header view.\n        mRecyclerViewBackground = findViewById(R.id.list_background);\n\n        //since you cannot programmatically add a header view to a RecyclerView we added an empty view as the header\n        // in the adapter and then are shifting the views OnCreateView to compensate\n        final float scale = 1 + MAX_TEXT_SCALE_DELTA;\n        mRecyclerViewBackground.post(new Runnable() {\n            @Override\n            public void run() {\n                ViewHelper.setTranslationY(mRecyclerViewBackground, mFlexibleSpaceImageHeight);\n            }\n        });\n        ViewHelper.setTranslationY(mOverlayView, mFlexibleSpaceImageHeight);\n        mTitleView.post(new Runnable() {\n            @Override\n            public void run() {\n                ViewHelper.setTranslationY(mTitleView, (int) (mFlexibleSpaceImageHeight - mTitleView.getHeight() * scale));\n                ViewHelper.setPivotX(mTitleView, 0);\n                ViewHelper.setPivotY(mTitleView, 0);\n                ViewHelper.setScaleX(mTitleView, scale);\n                ViewHelper.setScaleY(mTitleView, scale);\n            }\n        });\n    }\n\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        // Translate overlay and image\n        float flexibleRange = mFlexibleSpaceImageHeight - mActionBarSize;\n        int minOverlayTransitionY = mActionBarSize - mOverlayView.getHeight();\n        ViewHelper.setTranslationY(mOverlayView, ScrollUtils.getFloat(-scrollY, minOverlayTransitionY, 0));\n        ViewHelper.setTranslationY(mImageView, ScrollUtils.getFloat(-scrollY / 2, minOverlayTransitionY, 0));\n\n        // Translate list background\n        ViewHelper.setTranslationY(mRecyclerViewBackground, Math.max(0, -scrollY + mFlexibleSpaceImageHeight));\n\n        // Change alpha of overlay\n        ViewHelper.setAlpha(mOverlayView, ScrollUtils.getFloat((float) scrollY / flexibleRange, 0, 1));\n\n        // Scale title text\n        float scale = 1 + ScrollUtils.getFloat((flexibleRange - scrollY) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA);\n        setPivotXToTitle();\n        ViewHelper.setPivotY(mTitleView, 0);\n        ViewHelper.setScaleX(mTitleView, scale);\n        ViewHelper.setScaleY(mTitleView, scale);\n\n        // Translate title text\n        int maxTitleTranslationY = (int) (mFlexibleSpaceImageHeight - mTitleView.getHeight() * scale);\n        int titleTranslationY = maxTitleTranslationY - scrollY;\n        ViewHelper.setTranslationY(mTitleView, titleTranslationY);\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n\n    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n    private void setPivotXToTitle() {\n        Configuration config = getResources().getConfiguration();\n        if (Build.VERSION_CODES.JELLY_BEAN_MR1 <= Build.VERSION.SDK_INT\n                && config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {\n            ViewHelper.setPivotX(mTitleView, findViewById(android.R.id.content).getWidth());\n        } else {\n            ViewHelper.setPivotX(mTitleView, 0);\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.LinearLayout;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\n\npublic class FlexibleSpaceWithImageRecyclerViewFragment extends FlexibleSpaceWithImageBaseFragment<ObservableRecyclerView> {\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_flexiblespacewithimagerecyclerview, container, false);\n\n        final ObservableRecyclerView recyclerView = (ObservableRecyclerView) view.findViewById(R.id.scroll);\n        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));\n        recyclerView.setHasFixedSize(false);\n        final View headerView = LayoutInflater.from(getActivity()).inflate(R.layout.recycler_header, null);\n        final int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n        headerView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, flexibleSpaceImageHeight));\n        setDummyDataWithHeader(recyclerView, headerView);\n\n        // TouchInterceptionViewGroup should be a parent view other than ViewPager.\n        // This is a workaround for the issue #117:\n        // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117\n        recyclerView.setTouchInterceptionViewGroup((ViewGroup) view.findViewById(R.id.fragment_root));\n\n        // Scroll to the specified offset after layout\n        Bundle args = getArguments();\n        if (args != null && args.containsKey(ARG_SCROLL_Y)) {\n            final int scrollY = args.getInt(ARG_SCROLL_Y, 0);\n            ScrollUtils.addOnGlobalLayoutListener(recyclerView, new Runnable() {\n                @Override\n                public void run() {\n                    int offset = scrollY % flexibleSpaceImageHeight;\n                    RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();\n                    if (lm != null && lm instanceof LinearLayoutManager) {\n                        ((LinearLayoutManager) lm).scrollToPositionWithOffset(0, -offset);\n                    }\n                }\n            });\n            updateFlexibleSpace(scrollY, view);\n        } else {\n            updateFlexibleSpace(0, view);\n        }\n\n        recyclerView.setScrollViewCallbacks(this);\n\n        return view;\n    }\n\n    @Override\n    public void setScrollY(int scrollY, int threshold) {\n        View view = getView();\n        if (view == null) {\n            return;\n        }\n        ObservableRecyclerView recyclerView = (ObservableRecyclerView) view.findViewById(R.id.scroll);\n        if (recyclerView == null) {\n            return;\n        }\n        View firstVisibleChild = recyclerView.getChildAt(0);\n        if (firstVisibleChild != null) {\n            int offset = scrollY;\n            int position = 0;\n            if (threshold < scrollY) {\n                int baseHeight = firstVisibleChild.getHeight();\n                position = scrollY / baseHeight;\n                offset = scrollY % baseHeight;\n            }\n            RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();\n            if (lm != null && lm instanceof LinearLayoutManager) {\n                ((LinearLayoutManager) lm).scrollToPositionWithOffset(position, -offset);\n            }\n        }\n    }\n\n    @Override\n    protected void updateFlexibleSpace(int scrollY, View view) {\n        int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n\n        View recyclerViewBackground = view.findViewById(R.id.list_background);\n\n        // Translate list background\n        ViewHelper.setTranslationY(recyclerViewBackground, Math.max(0, -scrollY + flexibleSpaceImageHeight));\n\n        // Also pass this event to parent Activity\n        FlexibleSpaceWithImageWithViewPagerTabActivity parentActivity =\n                (FlexibleSpaceWithImageWithViewPagerTabActivity) getActivity();\n        if (parentActivity != null) {\n            parentActivity.onScrollChanged(scrollY, (ObservableRecyclerView) view.findViewById(R.id.scroll));\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.view.View;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\npublic class FlexibleSpaceWithImageScrollViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private static final float MAX_TEXT_SCALE_DELTA = 0.3f;\n\n    private View mImageView;\n    private View mOverlayView;\n    private ObservableScrollView mScrollView;\n    private TextView mTitleView;\n    private View mFab;\n    private int mActionBarSize;\n    private int mFlexibleSpaceShowFabOffset;\n    private int mFlexibleSpaceImageHeight;\n    private int mFabMargin;\n    private boolean mFabIsShown;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_flexiblespacewithimagescrollview);\n\n        mFlexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n        mFlexibleSpaceShowFabOffset = getResources().getDimensionPixelSize(R.dimen.flexible_space_show_fab_offset);\n        mActionBarSize = getActionBarSize();\n\n        mImageView = findViewById(R.id.image);\n        mOverlayView = findViewById(R.id.overlay);\n        mScrollView = (ObservableScrollView) findViewById(R.id.scroll);\n        mScrollView.setScrollViewCallbacks(this);\n        mTitleView = (TextView) findViewById(R.id.title);\n        mTitleView.setText(getTitle());\n        setTitle(null);\n        mFab = findViewById(R.id.fab);\n        mFab.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                Toast.makeText(FlexibleSpaceWithImageScrollViewActivity.this, \"FAB is clicked\", Toast.LENGTH_SHORT).show();\n            }\n        });\n        mFabMargin = getResources().getDimensionPixelSize(R.dimen.margin_standard);\n        ViewHelper.setScaleX(mFab, 0);\n        ViewHelper.setScaleY(mFab, 0);\n\n        ScrollUtils.addOnGlobalLayoutListener(mScrollView, new Runnable() {\n            @Override\n            public void run() {\n                mScrollView.scrollTo(0, mFlexibleSpaceImageHeight - mActionBarSize);\n\n                // If you'd like to start from scrollY == 0, don't write like this:\n                //mScrollView.scrollTo(0, 0);\n                // The initial scrollY is 0, so it won't invoke onScrollChanged().\n                // To do this, use the following:\n                //onScrollChanged(0, false, false);\n\n                // You can also achieve it with the following codes.\n                // This causes scroll change from 1 to 0.\n                //mScrollView.scrollTo(0, 1);\n                //mScrollView.scrollTo(0, 0);\n            }\n        });\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        // Translate overlay and image\n        float flexibleRange = mFlexibleSpaceImageHeight - mActionBarSize;\n        int minOverlayTransitionY = mActionBarSize - mOverlayView.getHeight();\n        ViewHelper.setTranslationY(mOverlayView, ScrollUtils.getFloat(-scrollY, minOverlayTransitionY, 0));\n        ViewHelper.setTranslationY(mImageView, ScrollUtils.getFloat(-scrollY / 2, minOverlayTransitionY, 0));\n\n        // Change alpha of overlay\n        ViewHelper.setAlpha(mOverlayView, ScrollUtils.getFloat((float) scrollY / flexibleRange, 0, 1));\n\n        // Scale title text\n        float scale = 1 + ScrollUtils.getFloat((flexibleRange - scrollY) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA);\n        ViewHelper.setPivotX(mTitleView, 0);\n        ViewHelper.setPivotY(mTitleView, 0);\n        ViewHelper.setScaleX(mTitleView, scale);\n        ViewHelper.setScaleY(mTitleView, scale);\n\n        // Translate title text\n        int maxTitleTranslationY = (int) (mFlexibleSpaceImageHeight - mTitleView.getHeight() * scale);\n        int titleTranslationY = maxTitleTranslationY - scrollY;\n        ViewHelper.setTranslationY(mTitleView, titleTranslationY);\n\n        // Translate FAB\n        int maxFabTranslationY = mFlexibleSpaceImageHeight - mFab.getHeight() / 2;\n        float fabTranslationY = ScrollUtils.getFloat(\n                -scrollY + mFlexibleSpaceImageHeight - mFab.getHeight() / 2,\n                mActionBarSize - mFab.getHeight() / 2,\n                maxFabTranslationY);\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {\n            // On pre-honeycomb, ViewHelper.setTranslationX/Y does not set margin,\n            // which causes FAB's OnClickListener not working.\n            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mFab.getLayoutParams();\n            lp.leftMargin = mOverlayView.getWidth() - mFabMargin - mFab.getWidth();\n            lp.topMargin = (int) fabTranslationY;\n            mFab.requestLayout();\n        } else {\n            ViewHelper.setTranslationX(mFab, mOverlayView.getWidth() - mFabMargin - mFab.getWidth());\n            ViewHelper.setTranslationY(mFab, fabTranslationY);\n        }\n\n        // Show/hide FAB\n        if (fabTranslationY < mFlexibleSpaceShowFabOffset) {\n            hideFab();\n        } else {\n            showFab();\n        }\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n\n    private void showFab() {\n        if (!mFabIsShown) {\n            ViewPropertyAnimator.animate(mFab).cancel();\n            ViewPropertyAnimator.animate(mFab).scaleX(1).scaleY(1).setDuration(200).start();\n            mFabIsShown = true;\n        }\n    }\n\n    private void hideFab() {\n        if (mFabIsShown) {\n            ViewPropertyAnimator.animate(mFab).cancel();\n            ViewPropertyAnimator.animate(mFab).scaleX(0).scaleY(0).setDuration(200).start();\n            mFabIsShown = false;\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\n\npublic class FlexibleSpaceWithImageScrollViewFragment extends FlexibleSpaceWithImageBaseFragment<ObservableScrollView> {\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_flexiblespacewithimagescrollview, container, false);\n\n        final ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll);\n        // TouchInterceptionViewGroup should be a parent view other than ViewPager.\n        // This is a workaround for the issue #117:\n        // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117\n        scrollView.setTouchInterceptionViewGroup((ViewGroup) view.findViewById(R.id.fragment_root));\n\n        // Scroll to the specified offset after layout\n        Bundle args = getArguments();\n        if (args != null && args.containsKey(ARG_SCROLL_Y)) {\n            final int scrollY = args.getInt(ARG_SCROLL_Y, 0);\n            ScrollUtils.addOnGlobalLayoutListener(scrollView, new Runnable() {\n                @Override\n                public void run() {\n                    scrollView.scrollTo(0, scrollY);\n                }\n            });\n            updateFlexibleSpace(scrollY, view);\n        } else {\n            updateFlexibleSpace(0, view);\n        }\n\n        scrollView.setScrollViewCallbacks(this);\n\n        return view;\n    }\n\n    @Override\n    protected void updateFlexibleSpace(int scrollY) {\n        // Sometimes scrollable.getCurrentScrollY() and the real scrollY has different values.\n        // As a workaround, we should call scrollVerticallyTo() to make sure that they match.\n        Scrollable s = getScrollable();\n        s.scrollVerticallyTo(scrollY);\n\n        // If scrollable.getCurrentScrollY() and the real scrollY has the same values,\n        // calling scrollVerticallyTo() won't invoke scroll (or onScrollChanged()), so we call it here.\n        // Calling this twice is not a problem as long as updateFlexibleSpace(int, View) has idempotence.\n        updateFlexibleSpace(scrollY, getView());\n    }\n\n    @Override\n    protected void updateFlexibleSpace(int scrollY, View view) {\n        ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll);\n\n        // Also pass this event to parent Activity\n        FlexibleSpaceWithImageWithViewPagerTabActivity parentActivity =\n                (FlexibleSpaceWithImageWithViewPagerTabActivity) getActivity();\n        if (parentActivity != null) {\n            parentActivity.onScrollChanged(scrollY, scrollView);\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.annotation.TargetApi;\nimport android.content.res.Configuration;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.view.ViewCompat;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.widget.Toolbar;\nimport android.view.MotionEvent;\nimport android.view.VelocityTracker;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.widget.FrameLayout;\nimport android.widget.OverScroller;\nimport android.widget.TextView;\n\nimport com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\nimport com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout;\nimport com.google.samples.apps.iosched.ui.widget.SlidingTabLayout;\nimport com.nineoldandroids.view.ViewHelper;\n\n/**\n * <p>This uses TouchInterceptionFrameLayout to move Fragments.</p>\n *\n * <p>There is an unsolved problem: it doesn't scroll smoothly\n * when the flexible space is changing.<br>\n * If it's a big problem to you, please also check\n * FlexibleSpaceWithImageWithViewPagerTabActivity.</p>\n *\n * <p>SlidingTabLayout and SlidingTabStrip are from google/iosched:<br>\n * https://github.com/google/iosched</p>\n */\npublic class FlexibleSpaceWithImageWithViewPagerTab2Activity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private static final float MAX_TEXT_SCALE_DELTA = 0.3f;\n    private static final int INVALID_POINTER = -1;\n\n    private View mImageView;\n    private View mOverlayView;\n    private TextView mTitleView;\n    private TouchInterceptionFrameLayout mInterceptionLayout;\n    private ViewPager mPager;\n    private NavigationAdapter mPagerAdapter;\n    private VelocityTracker mVelocityTracker;\n    private OverScroller mScroller;\n    private float mBaseTranslationY;\n    private int mMaximumVelocity;\n    private int mActivePointerId = INVALID_POINTER;\n    private int mSlop;\n    private int mFlexibleSpaceHeight;\n    private int mTabHeight;\n    private boolean mScrolled;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_flexiblespacewithimagewithviewpagertab2);\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        ViewCompat.setElevation(findViewById(R.id.header), getResources().getDimension(R.dimen.toolbar_elevation));\n        mPagerAdapter = new NavigationAdapter(getSupportFragmentManager());\n        mPager = (ViewPager) findViewById(R.id.pager);\n        mPager.setAdapter(mPagerAdapter);\n        mImageView = findViewById(R.id.image);\n        mOverlayView = findViewById(R.id.overlay);\n        // Padding for ViewPager must be set outside the ViewPager itself\n        // because with padding, EdgeEffect of ViewPager become strange.\n        mFlexibleSpaceHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n        mTabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height);\n        findViewById(R.id.pager_wrapper).setPadding(0, mFlexibleSpaceHeight, 0, 0);\n        mTitleView = (TextView) findViewById(R.id.title);\n        mTitleView.setText(getTitle());\n        setTitle(null);\n\n        SlidingTabLayout slidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);\n        slidingTabLayout.setCustomTabView(R.layout.tab_indicator, android.R.id.text1);\n        slidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.accent));\n        slidingTabLayout.setDistributeEvenly(true);\n        slidingTabLayout.setViewPager(mPager);\n        ((FrameLayout.LayoutParams) slidingTabLayout.getLayoutParams()).topMargin = mFlexibleSpaceHeight - mTabHeight;\n\n        ViewConfiguration vc = ViewConfiguration.get(this);\n        mSlop = vc.getScaledTouchSlop();\n        mMaximumVelocity = vc.getScaledMaximumFlingVelocity();\n        mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.container);\n        mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener);\n        mScroller = new OverScroller(getApplicationContext());\n        ScrollUtils.addOnGlobalLayoutListener(mInterceptionLayout, new Runnable() {\n            @Override\n            public void run() {\n                // Extra space is required to move mInterceptionLayout when it's scrolled.\n                // It's better to adjust its height when it's laid out\n                // than to adjust the height when scroll events (onMoveMotionEvent) occur\n                // because it causes lagging.\n                // See #87: https://github.com/ksoichiro/Android-ObservableScrollView/issues/87\n                FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams();\n                lp.height = getScreenHeight() + mFlexibleSpaceHeight;\n                mInterceptionLayout.requestLayout();\n\n                updateFlexibleSpace();\n            }\n        });\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n\n    private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() {\n        @Override\n        public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) {\n            if (!mScrolled && mSlop < Math.abs(diffX) && Math.abs(diffY) < Math.abs(diffX)) {\n                // Horizontal scroll is maybe handled by ViewPager\n                return false;\n            }\n\n            Scrollable scrollable = getCurrentScrollable();\n            if (scrollable == null) {\n                mScrolled = false;\n                return false;\n            }\n\n            // If interceptionLayout can move, it should intercept.\n            // And once it begins to move, horizontal scroll shouldn't work any longer.\n            int flexibleSpace = mFlexibleSpaceHeight - mTabHeight;\n            int translationY = (int) ViewHelper.getTranslationY(mInterceptionLayout);\n            boolean scrollingUp = 0 < diffY;\n            boolean scrollingDown = diffY < 0;\n            if (scrollingUp) {\n                if (translationY < 0) {\n                    mScrolled = true;\n                    return true;\n                }\n            } else if (scrollingDown) {\n                if (-flexibleSpace < translationY) {\n                    mScrolled = true;\n                    return true;\n                }\n            }\n            mScrolled = false;\n            return false;\n        }\n\n        @Override\n        public void onDownMotionEvent(MotionEvent ev) {\n            mActivePointerId = ev.getPointerId(0);\n            mScroller.forceFinished(true);\n            if (mVelocityTracker == null) {\n                mVelocityTracker = VelocityTracker.obtain();\n            } else {\n                mVelocityTracker.clear();\n            }\n            mBaseTranslationY = ViewHelper.getTranslationY(mInterceptionLayout);\n            mVelocityTracker.addMovement(ev);\n        }\n\n        @Override\n        public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) {\n            int flexibleSpace = mFlexibleSpaceHeight - mTabHeight;\n            float translationY = ScrollUtils.getFloat(ViewHelper.getTranslationY(mInterceptionLayout) + diffY, -flexibleSpace, 0);\n            MotionEvent e = MotionEvent.obtainNoHistory(ev);\n            e.offsetLocation(0, translationY - mBaseTranslationY);\n            mVelocityTracker.addMovement(e);\n            updateFlexibleSpace(translationY);\n        }\n\n        @Override\n        public void onUpOrCancelMotionEvent(MotionEvent ev) {\n            mScrolled = false;\n            mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);\n            int velocityY = (int) mVelocityTracker.getYVelocity(mActivePointerId);\n            mActivePointerId = INVALID_POINTER;\n            mScroller.forceFinished(true);\n            int baseTranslationY = (int) ViewHelper.getTranslationY(mInterceptionLayout);\n\n            int minY = -(mFlexibleSpaceHeight - mTabHeight);\n            int maxY = 0;\n            mScroller.fling(0, baseTranslationY, 0, velocityY, 0, 0, minY, maxY);\n            new Handler().post(new Runnable() {\n                @Override\n                public void run() {\n                    updateLayout();\n                }\n            });\n        }\n    };\n\n    private void updateLayout() {\n        boolean needsUpdate = false;\n        float translationY = 0;\n        if (mScroller.computeScrollOffset()) {\n            translationY = mScroller.getCurrY();\n            int flexibleSpace = mFlexibleSpaceHeight - mTabHeight;\n            if (-flexibleSpace <= translationY && translationY <= 0) {\n                needsUpdate = true;\n            } else if (translationY < -flexibleSpace) {\n                translationY = -flexibleSpace;\n                needsUpdate = true;\n            } else if (0 < translationY) {\n                translationY = 0;\n                needsUpdate = true;\n            }\n        }\n\n        if (needsUpdate) {\n            updateFlexibleSpace(translationY);\n\n            new Handler().post(new Runnable() {\n                @Override\n                public void run() {\n                    updateLayout();\n                }\n            });\n        }\n    }\n\n    private Scrollable getCurrentScrollable() {\n        Fragment fragment = getCurrentFragment();\n        if (fragment == null) {\n            return null;\n        }\n        View view = fragment.getView();\n        if (view == null) {\n            return null;\n        }\n        return (Scrollable) view.findViewById(R.id.scroll);\n    }\n\n    private void updateFlexibleSpace() {\n        updateFlexibleSpace(ViewHelper.getTranslationY(mInterceptionLayout));\n    }\n\n    private void updateFlexibleSpace(float translationY) {\n        ViewHelper.setTranslationY(mInterceptionLayout, translationY);\n        int minOverlayTransitionY = getActionBarSize() - mOverlayView.getHeight();\n        ViewHelper.setTranslationY(mImageView, ScrollUtils.getFloat(-translationY / 2, minOverlayTransitionY, 0));\n\n        // Change alpha of overlay\n        float flexibleRange = mFlexibleSpaceHeight - getActionBarSize();\n        ViewHelper.setAlpha(mOverlayView, ScrollUtils.getFloat(-translationY / flexibleRange, 0, 1));\n\n        // Scale title text\n        float scale = 1 + ScrollUtils.getFloat((flexibleRange + translationY - mTabHeight) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA);\n        setPivotXToTitle();\n        ViewHelper.setPivotY(mTitleView, 0);\n        ViewHelper.setScaleX(mTitleView, scale);\n        ViewHelper.setScaleY(mTitleView, scale);\n    }\n\n    private Fragment getCurrentFragment() {\n        return mPagerAdapter.getItemAt(mPager.getCurrentItem());\n    }\n\n    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n    private void setPivotXToTitle() {\n        Configuration config = getResources().getConfiguration();\n        if (Build.VERSION_CODES.JELLY_BEAN_MR1 <= Build.VERSION.SDK_INT\n                && config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {\n            ViewHelper.setPivotX(mTitleView, findViewById(android.R.id.content).getWidth());\n        } else {\n            ViewHelper.setPivotX(mTitleView, 0);\n        }\n    }\n\n    /**\n     * This adapter provides two types of fragments as an example.\n     * {@linkplain #createItem(int)} should be modified if you use this example for your app.\n     */\n    private static class NavigationAdapter extends CacheFragmentStatePagerAdapter {\n\n        private static final String[] TITLES = new String[]{\"Applepie\", \"Butter Cookie\", \"Cupcake\", \"Donut\", \"Eclair\", \"Froyo\", \"Gingerbread\", \"Honeycomb\", \"Ice Cream Sandwich\", \"Jelly Bean\", \"KitKat\", \"Lollipop\"};\n\n        public NavigationAdapter(FragmentManager fm) {\n            super(fm);\n        }\n\n        @Override\n        protected Fragment createItem(int position) {\n            Fragment f;\n            final int pattern = position % 5;\n            switch (pattern) {\n                case 0:\n                    f = new ViewPagerTab2ScrollViewFragment();\n                    break;\n                case 1:\n                    f = new ViewPagerTab2ListViewFragment();\n                    break;\n                case 2:\n                    f = new ViewPagerTab2RecyclerViewFragment();\n                    break;\n                case 3:\n                    f = new ViewPagerTab2GridViewFragment();\n                    break;\n                case 4:\n                default:\n                    f = new ViewPagerTab2WebViewFragment();\n                    break;\n            }\n            return f;\n        }\n\n        @Override\n        public int getCount() {\n            return TITLES.length;\n        }\n\n        @Override\n        public CharSequence getPageTitle(int position) {\n            return TITLES[position];\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.annotation.TargetApi;\nimport android.content.res.Configuration;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.view.ViewPager;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\nimport com.google.samples.apps.iosched.ui.widget.SlidingTabLayout;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\n/**\n * <p>Another implementation of FlexibleImage pattern + ViewPager.</p>\n * <p/>\n * <p>This is a completely different approach comparing to FlexibleImageWithViewPager2Activity.\n * <p/>\n * <p>Descriptions of this pattern:</p>\n * <ul>\n * <li>When the current tab is changed, tabs will be translated in Y-axis\n * using scrollY of the new page's Fragment.</li>\n * <li>The parent Activity and children Fragments strongly depend on each other,\n * so if you need to use this pattern, maybe you should extract some interfaces from them.<br>\n * (This is just an example, so we won't do it here.)</li>\n * <li>The parent Activity and children Fragments communicate bidirectionally:\n * the parent Activity will update the Fragment's state when the tab is changed,\n * and Fragments will tell the parent Activity to update the tab's translationY.</li>\n * </ul>\n * <p/>\n * <p>SlidingTabLayout and SlidingTabStrip are from google/iosched:<br>\n * https://github.com/google/iosched</p>\n */\npublic class FlexibleSpaceWithImageWithViewPagerTabActivity extends BaseActivity {\n\n    protected static final float MAX_TEXT_SCALE_DELTA = 0.3f;\n\n    private ViewPager mPager;\n    private NavigationAdapter mPagerAdapter;\n    private SlidingTabLayout mSlidingTabLayout;\n    private int mFlexibleSpaceHeight;\n    private int mTabHeight;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_flexiblespacewithimagewithviewpagertab);\n\n        mPagerAdapter = new NavigationAdapter(getSupportFragmentManager());\n        mPager = (ViewPager) findViewById(R.id.pager);\n        mPager.setAdapter(mPagerAdapter);\n        mFlexibleSpaceHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n        mTabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height);\n\n        TextView titleView = (TextView) findViewById(R.id.title);\n        titleView.setText(R.string.title_activity_flexiblespacewithimagewithviewpagertab);\n\n        mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);\n        mSlidingTabLayout.setCustomTabView(R.layout.tab_indicator, android.R.id.text1);\n        mSlidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.accent));\n        mSlidingTabLayout.setDistributeEvenly(true);\n        mSlidingTabLayout.setViewPager(mPager);\n\n        // Initialize the first Fragment's state when layout is completed.\n        ScrollUtils.addOnGlobalLayoutListener(mSlidingTabLayout, new Runnable() {\n            @Override\n            public void run() {\n                translateTab(0, false);\n            }\n        });\n    }\n\n    /**\n     * Called by children Fragments when their scrollY are changed.\n     * They all call this method even when they are inactive\n     * but this Activity should listen only the active child,\n     * so each Fragments will pass themselves for Activity to check if they are active.\n     *\n     * @param scrollY scroll position of Scrollable\n     * @param s       caller Scrollable view\n     */\n    public void onScrollChanged(int scrollY, Scrollable s) {\n        FlexibleSpaceWithImageBaseFragment fragment =\n                (FlexibleSpaceWithImageBaseFragment) mPagerAdapter.getItemAt(mPager.getCurrentItem());\n        if (fragment == null) {\n            return;\n        }\n        View view = fragment.getView();\n        if (view == null) {\n            return;\n        }\n        Scrollable scrollable = (Scrollable) view.findViewById(R.id.scroll);\n        if (scrollable == null) {\n            return;\n        }\n        if (scrollable == s) {\n            // This method is called by not only the current fragment but also other fragments\n            // when their scrollY is changed.\n            // So we need to check the caller(S) is the current fragment.\n            int adjustedScrollY = Math.min(scrollY, mFlexibleSpaceHeight - mTabHeight);\n            translateTab(adjustedScrollY, false);\n            propagateScroll(adjustedScrollY);\n        }\n    }\n\n    private void translateTab(int scrollY, boolean animated) {\n        int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n        int tabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height);\n        View imageView = findViewById(R.id.image);\n        View overlayView = findViewById(R.id.overlay);\n        TextView titleView = (TextView) findViewById(R.id.title);\n\n        // Translate overlay and image\n        float flexibleRange = flexibleSpaceImageHeight - getActionBarSize();\n        int minOverlayTransitionY = tabHeight - overlayView.getHeight();\n        ViewHelper.setTranslationY(overlayView, ScrollUtils.getFloat(-scrollY, minOverlayTransitionY, 0));\n        ViewHelper.setTranslationY(imageView, ScrollUtils.getFloat(-scrollY / 2, minOverlayTransitionY, 0));\n\n        // Change alpha of overlay\n        ViewHelper.setAlpha(overlayView, ScrollUtils.getFloat((float) scrollY / flexibleRange, 0, 1));\n\n        // Scale title text\n        float scale = 1 + ScrollUtils.getFloat((flexibleRange - scrollY - tabHeight) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA);\n        setPivotXToTitle(titleView);\n        ViewHelper.setPivotY(titleView, 0);\n        ViewHelper.setScaleX(titleView, scale);\n        ViewHelper.setScaleY(titleView, scale);\n\n        // Translate title text\n        int maxTitleTranslationY = flexibleSpaceImageHeight - tabHeight - getActionBarSize();\n        int titleTranslationY = maxTitleTranslationY - scrollY;\n        ViewHelper.setTranslationY(titleView, titleTranslationY);\n\n        // If tabs are moving, cancel it to start a new animation.\n        ViewPropertyAnimator.animate(mSlidingTabLayout).cancel();\n        // Tabs will move between the top of the screen to the bottom of the image.\n        float translationY = ScrollUtils.getFloat(-scrollY + mFlexibleSpaceHeight - mTabHeight, 0, mFlexibleSpaceHeight - mTabHeight);\n        if (animated) {\n            // Animation will be invoked only when the current tab is changed.\n            ViewPropertyAnimator.animate(mSlidingTabLayout)\n                    .translationY(translationY)\n                    .setDuration(200)\n                    .start();\n        } else {\n            // When Fragments' scroll, translate tabs immediately (without animation).\n            ViewHelper.setTranslationY(mSlidingTabLayout, translationY);\n        }\n    }\n\n    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n    private void setPivotXToTitle(View view) {\n        final TextView mTitleView = (TextView) view.findViewById(R.id.title);\n        Configuration config = getResources().getConfiguration();\n        if (Build.VERSION_CODES.JELLY_BEAN_MR1 <= Build.VERSION.SDK_INT\n                && config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {\n            ViewHelper.setPivotX(mTitleView, view.findViewById(android.R.id.content).getWidth());\n        } else {\n            ViewHelper.setPivotX(mTitleView, 0);\n        }\n    }\n\n    private void propagateScroll(int scrollY) {\n        // Set scrollY for the fragments that are not created yet\n        mPagerAdapter.setScrollY(scrollY);\n\n        // Set scrollY for the active fragments\n        for (int i = 0; i < mPagerAdapter.getCount(); i++) {\n            // Skip current item\n            if (i == mPager.getCurrentItem()) {\n                continue;\n            }\n\n            // Skip destroyed or not created item\n            FlexibleSpaceWithImageBaseFragment f =\n                    (FlexibleSpaceWithImageBaseFragment) mPagerAdapter.getItemAt(i);\n            if (f == null) {\n                continue;\n            }\n\n            View view = f.getView();\n            if (view == null) {\n                continue;\n            }\n            f.setScrollY(scrollY, mFlexibleSpaceHeight);\n            f.updateFlexibleSpace(scrollY);\n        }\n    }\n\n    /**\n     * This adapter provides three types of fragments as an example.\n     * {@linkplain #createItem(int)} should be modified if you use this example for your app.\n     */\n    private static class NavigationAdapter extends CacheFragmentStatePagerAdapter {\n\n        private static final String[] TITLES = new String[]{\"Applepie\", \"Butter Cookie\", \"Cupcake\", \"Donut\", \"Eclair\", \"Froyo\", \"Gingerbread\", \"Honeycomb\", \"Ice Cream Sandwich\", \"Jelly Bean\", \"KitKat\", \"Lollipop\"};\n\n        private int mScrollY;\n\n        public NavigationAdapter(FragmentManager fm) {\n            super(fm);\n        }\n\n        public void setScrollY(int scrollY) {\n            mScrollY = scrollY;\n        }\n\n        @Override\n        protected Fragment createItem(int position) {\n            FlexibleSpaceWithImageBaseFragment f;\n            final int pattern = position % 4;\n            switch (pattern) {\n                case 0: {\n                    f = new FlexibleSpaceWithImageScrollViewFragment();\n                    break;\n                }\n                case 1: {\n                    f = new FlexibleSpaceWithImageListViewFragment();\n                    break;\n                }\n                case 2: {\n                    f = new FlexibleSpaceWithImageRecyclerViewFragment();\n                    break;\n                }\n                case 3:\n                default: {\n                    f = new FlexibleSpaceWithImageGridViewFragment();\n                    break;\n                }\n            }\n            f.setArguments(mScrollY);\n            return f;\n        }\n\n        @Override\n        public int getCount() {\n            return TITLES.length;\n        }\n\n        @Override\n        public CharSequence getPageTitle(int position) {\n            return TITLES[position];\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.app.FragmentTransaction;\n\npublic class FragmentActionBarControlListViewActivity extends BaseActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_fragmentactionbarcontrol);\n\n        FragmentManager fm = getSupportFragmentManager();\n        if (fm.findFragmentByTag(FragmentTransitionDefaultFragment.FRAGMENT_TAG) == null) {\n            FragmentTransaction ft = fm.beginTransaction();\n            ft.add(R.id.container, new FragmentActionBarControlListViewFragment(),\n                    FragmentActionBarControlListViewFragment.FRAGMENT_TAG);\n            ft.commit();\n            fm.executePendingTransactions();\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v7.app.ActionBar;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\n/**\n * This fragment handles scroll events by itself, and controls its parent activity's ActionBar.\n */\npublic class FragmentActionBarControlListViewFragment extends BaseFragment implements ObservableScrollViewCallbacks {\n\n    public static final String FRAGMENT_TAG = \"actionBarControl\";\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_actionbarcontrollistview, container, false);\n\n        ObservableListView listView = (ObservableListView) view.findViewById(R.id.list);\n        listView.setScrollViewCallbacks(this);\n        setDummyData(listView);\n\n        return view;\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        AppCompatActivity activity = (AppCompatActivity) getActivity();\n        if (activity == null) {\n            return;\n        }\n        ActionBar ab = activity.getSupportActionBar();\n        if (ab == null) {\n            return;\n        }\n        if (scrollState == ScrollState.UP) {\n            if (ab.isShowing()) {\n                ab.hide();\n            }\n        } else if (scrollState == ScrollState.DOWN) {\n            if (!ab.isShowing()) {\n                ab.show();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.app.FragmentTransaction;\nimport android.support.v7.widget.Toolbar;\n\n/**\n * This doesn't include ObservableScrollViews,\n * just shows how to show/hide Toolbar on the parent Activity of Fragment\n * to help you implement a screen that uses Fragments.\n */\npublic class FragmentTransitionActivity extends BaseActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_fragmenttransition);\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        initFragment();\n    }\n\n    /**\n     * Fragment should be added programmatically.\n     * Using fragment tag in XML causes IllegalStateException on rotation of screen\n     * or restoring states of activity.\n     */\n    private void initFragment() {\n        FragmentManager fm = getSupportFragmentManager();\n        if (fm.findFragmentByTag(FragmentTransitionDefaultFragment.FRAGMENT_TAG) == null) {\n            FragmentTransaction ft = fm.beginTransaction();\n            ft.add(R.id.fragment, new FragmentTransitionDefaultFragment(), FragmentTransitionDefaultFragment.FRAGMENT_TAG);\n            ft.commit();\n            fm.executePendingTransactions();\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionDefaultFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.FragmentActivity;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.app.FragmentTransaction;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AdapterView;\nimport android.widget.ListView;\n\n/**\n * This is a fragment to cause fragment transition(replacement).\n */\npublic class FragmentTransitionDefaultFragment extends BaseFragment {\n\n    public static final String FRAGMENT_TAG = \"default\";\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_fragmenttransition_default, container, false);\n\n        ListView listView = (ListView) view.findViewById(R.id.list);\n        setDummyData(listView);\n        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {\n            @Override\n            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n                showNextFragment();\n            }\n        });\n        return view;\n    }\n\n    private void showNextFragment() {\n        FragmentActivity activity = getActivity();\n        if (activity == null) {\n            return;\n        }\n        FragmentManager fm = activity.getSupportFragmentManager();\n        FragmentTransaction ft = fm.beginTransaction();\n        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);\n        ft.replace(R.id.fragment, new FragmentTransitionSecondFragment(), FragmentTransitionSecondFragment.FRAGMENT_TAG);\n        ft.addToBackStack(null);\n        ft.commit();\n        fm.executePendingTransactions();\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionSecondFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v7.app.ActionBar;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\n/**\n * This fragment shows how to show/hide toolbar of the parent Activity on fragment transitions.\n */\npublic class FragmentTransitionSecondFragment extends BaseFragment {\n\n    public static final String FRAGMENT_TAG = \"second\";\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        hideToolbar();\n        return inflater.inflate(R.layout.fragment_fragmenttransition_second, container, false);\n    }\n\n    @Override\n    public void onDestroyView() {\n        showToolbar();\n        super.onDestroyView();\n    }\n\n    private void showToolbar() {\n        AppCompatActivity activity = (AppCompatActivity) getActivity();\n        if (activity != null) {\n            ActionBar ab = activity.getSupportActionBar();\n            if (ab != null) {\n                ab.show();\n            }\n        }\n    }\n\n    private void hideToolbar() {\n        AppCompatActivity activity = (AppCompatActivity) getActivity();\n        if (activity != null) {\n            ActionBar ab = activity.getSupportActionBar();\n            if (ab != null) {\n                ab.hide();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchGridViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ArrayAdapter;\nimport android.widget.Toast;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\nimport java.util.List;\n\n/**\n * This sample doesn't include any scrolling effects.\n * With this sample, you can check how the callbacks occur\n * when the children views handle touch events.\n * <p/>\n * At least, ScrollView has an issue: when we touch a child which has OnClickListener\n * and drag it to expect its parent ScrollView generates onDownMotionEvent() and\n * onScrollChanged([WHATEVER], true, true), but they won't be generated.\n * <p/>\n * https://github.com/ksoichiro/Android-ObservableScrollView/issues/18\n */\npublic class HandleTouchGridViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n    private static final String TAG = HandleTouchGridViewActivity.class.getSimpleName();\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_handletouchgridview);\n\n        ObservableGridView gridView = (ObservableGridView) findViewById(R.id.scroll);\n        gridView.setScrollViewCallbacks(this);\n        gridView.setAdapter(new CustomAdapter(this, getDummyData()));\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        Log.v(TAG, \"onScrollChanged: scrollY: \" + scrollY + \" firstScroll: \" + firstScroll + \" dragging: \" + dragging);\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n        Log.v(TAG, \"onDownMotionEvent\");\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        Log.v(TAG, \"onUpOrCancelMotionEvent: scrollState: \" + scrollState);\n    }\n\n    public static class CustomAdapter extends ArrayAdapter<String> {\n        public CustomAdapter(Context context, List<String> objects) {\n            super(context, R.layout.list_item_handletouch, android.R.id.text1, objects);\n        }\n\n        @Override\n        public View getView(final int position, View convertView, ViewGroup parent) {\n            View view = super.getView(position, convertView, parent);\n            view.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    click(position + 1);\n                }\n            });\n            return view;\n        }\n\n        private void click(int i) {\n            String message = \"Button \" + i + \" is clicked\";\n            Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();\n            Log.v(TAG, \"click: \" + message);\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchListViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ArrayAdapter;\nimport android.widget.Toast;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\nimport java.util.List;\n\n/**\n * This sample doesn't include any scrolling effects.\n * With this sample, you can check how the callbacks occur\n * when the children views handle touch events.\n * <p/>\n * At least, ScrollView has an issue: when we touch a child which has OnClickListener\n * and drag it to expect its parent ScrollView generates onDownMotionEvent() and\n * onScrollChanged([WHATEVER], true, true), but they won't be generated.\n * <p/>\n * https://github.com/ksoichiro/Android-ObservableScrollView/issues/18\n */\npublic class HandleTouchListViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n    private static final String TAG = HandleTouchListViewActivity.class.getSimpleName();\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_handletouchlistview);\n\n        ObservableListView listView = (ObservableListView) findViewById(R.id.scroll);\n        listView.setScrollViewCallbacks(this);\n        listView.setAdapter(new CustomAdapter(this, getDummyData()));\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        Log.v(TAG, \"onScrollChanged: scrollY: \" + scrollY + \" firstScroll: \" + firstScroll + \" dragging: \" + dragging);\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n        Log.v(TAG, \"onDownMotionEvent\");\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        Log.v(TAG, \"onUpOrCancelMotionEvent: scrollState: \" + scrollState);\n    }\n\n    public static class CustomAdapter extends ArrayAdapter<String> {\n        public CustomAdapter(Context context, List<String> objects) {\n            super(context, R.layout.list_item_handletouch, android.R.id.text1, objects);\n        }\n\n        @Override\n        public View getView(final int position, View convertView, ViewGroup parent) {\n            View view = super.getView(position, convertView, parent);\n            view.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    click(position + 1);\n                }\n            });\n            return view;\n        }\n\n        private void click(int i) {\n            String message = \"Button \" + i + \" is clicked\";\n            Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();\n            Log.v(TAG, \"click: \" + message);\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchRecyclerViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\nimport java.util.ArrayList;\n\n/**\n * This sample doesn't include any scrolling effects.\n * With this sample, you can check how the callbacks occur\n * when the children views handle touch events.\n * <p/>\n * At least, ScrollView has an issue: when we touch a child which has OnClickListener\n * and drag it to expect its parent ScrollView generates onDownMotionEvent() and\n * onScrollChanged([WHATEVER], true, true), but they won't be generated.\n * <p/>\n * https://github.com/ksoichiro/Android-ObservableScrollView/issues/18\n */\npublic class HandleTouchRecyclerViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n    private static final String TAG = HandleTouchRecyclerViewActivity.class.getSimpleName();\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_handletouchrecyclerview);\n\n        ObservableRecyclerView recyclerView = (ObservableRecyclerView) findViewById(R.id.scroll);\n        recyclerView.setLayoutManager(new LinearLayoutManager(this));\n        recyclerView.setHasFixedSize(true);\n        recyclerView.setScrollViewCallbacks(this);\n        recyclerView.setAdapter(new CustomAdapter(this, getDummyData()));\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        Log.v(TAG, \"onScrollChanged: scrollY: \" + scrollY + \" firstScroll: \" + firstScroll + \" dragging: \" + dragging);\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n        Log.v(TAG, \"onDownMotionEvent\");\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        Log.v(TAG, \"onUpOrCancelMotionEvent: scrollState: \" + scrollState);\n    }\n\n    public static class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {\n        private Context mContext;\n        private LayoutInflater mInflater;\n        private ArrayList<String> mItems;\n\n        public CustomAdapter(Context context, ArrayList<String> items) {\n            mContext = context;\n            mInflater = LayoutInflater.from(context);\n            mItems = items;\n        }\n\n        @Override\n        public int getItemCount() {\n            return mItems.size();\n        }\n\n        @Override\n        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n            return new ViewHolder(mContext, mInflater.inflate(R.layout.list_item_handletouch, parent, false));\n        }\n\n        @Override\n        public void onBindViewHolder(ViewHolder viewHolder, int position) {\n            viewHolder.textView.setText(mItems.get(position));\n        }\n\n        static class ViewHolder extends RecyclerView.ViewHolder {\n            TextView textView;\n            Context context;\n\n            public ViewHolder(Context context, View view) {\n                super(view);\n                this.context = context;\n                this.textView = (TextView) view.findViewById(android.R.id.text1);\n                view.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        click(getPosition() + 1);\n                    }\n                });\n            }\n\n            private void click(int i) {\n                String message = \"Button \" + i + \" is clicked\";\n                Toast.makeText(context, message, Toast.LENGTH_SHORT).show();\n                Log.v(TAG, \"click: \" + message);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchScrollViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.Toast;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\n/**\n * This sample doesn't include any scrolling effects.\n * With this sample, you can check how the callbacks occur\n * when the children views handle touch events.\n * <p/>\n * At least, ScrollView has an issue: when we touch a child which has OnClickListener\n * and drag it to expect its parent ScrollView generates onDownMotionEvent() and\n * onScrollChanged([WHATEVER], true, true), but they won't be generated.\n * <p/>\n * https://github.com/ksoichiro/Android-ObservableScrollView/issues/18\n */\npublic class HandleTouchScrollViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n    private static final String TAG = HandleTouchScrollViewActivity.class.getSimpleName();\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_handletouchscrollview);\n\n        ObservableScrollView scrollView = (ObservableScrollView) findViewById(R.id.scroll);\n        scrollView.setScrollViewCallbacks(this);\n\n        int[] ids = new int[]{R.id.button1, R.id.button2, R.id.button3};\n        for (int i = 0; i < ids.length; i++) {\n            final int number = i + 1;\n            findViewById(ids[i]).setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    click(number);\n                }\n            });\n        }\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        Log.v(TAG, \"onScrollChanged: scrollY: \" + scrollY + \" firstScroll: \" + firstScroll + \" dragging: \" + dragging);\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n        Log.v(TAG, \"onDownMotionEvent\");\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        Log.v(TAG, \"onUpOrCancelMotionEvent: scrollState: \" + scrollState);\n    }\n\n    private void click(int i) {\n        String message = \"Button \" + i + \" is clicked\";\n        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();\n        Log.v(TAG, \"click: \" + message);\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchWebViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.util.Log;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ObservableWebView;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\n\n/**\n * This sample doesn't include any scrolling effects.\n * With this sample, you can check how the callbacks occur\n * when the children views handle touch events.\n * <p/>\n * At least, ScrollView has an issue: when we touch a child which has OnClickListener\n * and drag it to expect its parent ScrollView generates onDownMotionEvent() and\n * onScrollChanged([WHATEVER], true, true), but they won't be generated.\n * <p/>\n * https://github.com/ksoichiro/Android-ObservableScrollView/issues/18\n */\npublic class HandleTouchWebViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n    private static final String TAG = HandleTouchWebViewActivity.class.getSimpleName();\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_handletouchwebview);\n\n        ObservableWebView webView = (ObservableWebView) findViewById(R.id.scroll);\n        webView.setScrollViewCallbacks(this);\n        webView.loadUrl(\"file:///android_asset/handletouch.html\");\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        Log.v(TAG, \"onScrollChanged: scrollY: \" + scrollY + \" firstScroll: \" + firstScroll + \" dragging: \" + dragging);\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n        Log.v(TAG, \"onDownMotionEvent\");\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        Log.v(TAG, \"onUpOrCancelMotionEvent: scrollState: \" + scrollState);\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ResolveInfo;\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\nimport android.support.v7.widget.Toolbar;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.ArrayAdapter;\nimport android.widget.ListView;\nimport android.widget.SimpleAdapter;\nimport android.widget.Spinner;\n\nimport java.text.Collator;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n\npublic class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {\n    private static final String CATEGORY_SAMPLES = MainActivity.class.getPackage().getName();\n    private static final String TAG_CLASS_NAME = \"className\";\n    private static final String TAG_DESCRIPTION = \"description\";\n    private static final String TAG_INTENT = \"intent\";\n\n    private static final Comparator<Map<String, Object>> DISPLAY_NAME_COMPARATOR = new Comparator<Map<String, Object>>() {\n        private final Collator collator = Collator.getInstance();\n\n        @Override\n        public int compare(Map<String, Object> lhs, Map<String, Object> rhs) {\n            return collator.compare(lhs.get(\"className\"), rhs.get(\"className\"));\n        }\n    };\n    private ListView listView;\n\n    // Quickly navigate through the examples.\n    enum Filter {\n        All,\n        GridView,\n        RecyclerView,\n        ScrollView,\n        ListView,\n        WebView,\n        Toolbar,\n        ActionBar,\n        FlexibleSpace,\n        Parallax,\n        ViewPager,\n    }\n\n    Filter currentFilter = Filter.All;\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n\n        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);\n        setSupportActionBar(toolbar);\n\n        listView = (ListView) findViewById(android.R.id.list);\n        listView.setOnItemClickListener(this);\n\n        Spinner spinner = (Spinner) findViewById(R.id.spinner_toolbar);\n        spinner.setAdapter(new FilterAdapter(this));\n        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {\n            @Override\n            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {\n                currentFilter = Filter.values()[position];\n                refreshData();\n            }\n\n            @Override\n            public void onNothingSelected(AdapterView<?> parent) {\n            }\n        });\n    }\n\n    private void refreshData() {\n        listView.setAdapter(new SimpleAdapter(this, getData(),\n                R.layout.list_item_main,\n                new String[]{TAG_CLASS_NAME, TAG_DESCRIPTION,},\n                new int[]{R.id.className, R.id.description,}));\n    }\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        getMenuInflater().inflate(R.menu.menu_main, menu);\n        return true;\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(final MenuItem menu) {\n        int id = menu.getItemId();\n        if (id == R.id.menu_about) {\n            startActivity(new Intent(getApplicationContext(), AboutActivity.class));\n            return true;\n        }\n        return false;\n    }\n\n    private List<Map<String, Object>> getData() {\n        List<Map<String, Object>> data = new ArrayList<>();\n\n        Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);\n        mainIntent.setPackage(getApplicationContext().getPackageName());\n        mainIntent.addCategory(CATEGORY_SAMPLES);\n\n        PackageManager pm = getPackageManager();\n        List<ResolveInfo> list = pm.queryIntentActivities(mainIntent, 0);\n\n        if (list == null) {\n            return data;\n        }\n\n        for (ResolveInfo info : list) {\n            CharSequence labelSeq = info.loadLabel(pm);\n            String label = labelSeq != null\n                    ? labelSeq.toString()\n                    : info.activityInfo.name;\n\n            String[] labelPath = label.split(\"/\");\n\n            String nextLabel = labelPath[0];\n\n            if (labelPath.length == 1) {\n                String nameLabel = info.activityInfo.name.replace(info.activityInfo.packageName + \"\", \"\");\n                // Remove package and get simple class name\n                if (nameLabel.contains(\".\")) {\n                    nameLabel = nameLabel.replaceAll(\"[^.]*\\\\.\", \"\");\n                }\n\n                // Filter logic.\n                if (currentFilter == Filter.All || nameLabel.contains(currentFilter.name())) {\n                    addItem(data,\n                            nameLabel,\n                            nextLabel,\n                            activityIntent(\n                                    info.activityInfo.applicationInfo.packageName,\n                                    info.activityInfo.name));\n                }\n            }\n        }\n\n        Collections.sort(data, DISPLAY_NAME_COMPARATOR);\n\n        return data;\n    }\n\n    protected Intent activityIntent(String pkg, String componentName) {\n        Intent result = new Intent();\n        result.setClassName(pkg, componentName);\n        return result;\n    }\n\n    protected void addItem(List<Map<String, Object>> data, String className, String description,\n                           Intent intent) {\n        Map<String, Object> temp = new HashMap<>();\n        temp.put(TAG_CLASS_NAME, className);\n        temp.put(TAG_DESCRIPTION, description);\n        temp.put(TAG_INTENT, intent);\n        data.add(temp);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n        Map<String, Object> map = (Map<String, Object>) parent.getItemAtPosition(position);\n\n        Intent intent = (Intent) map.get(TAG_INTENT);\n        startActivity(intent);\n    }\n\n    private class FilterAdapter extends ArrayAdapter<Filter> {\n        public FilterAdapter(Context context) {\n            super(context, android.R.layout.simple_spinner_item, Filter.values());\n            setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);\n        }\n    }\n}"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarGridViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v7.widget.Toolbar;\nimport android.view.View;\nimport android.widget.AbsListView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\n\n/**\n * This example depends on {@code ObservableGridView#addHeaderView()} method introduced in v1.6.0.\n */\npublic class ParallaxToolbarGridViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private View mImageView;\n    private View mToolbarView;\n    private View mListBackgroundView;\n    private ObservableGridView mGridView;\n    private int mParallaxImageHeight;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_parallaxtoolbargridview) ;\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        mImageView = findViewById(R.id.image);\n        mToolbarView = findViewById(R.id.toolbar);\n        mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(0, getResources().getColor(R.color.primary)));\n\n        mParallaxImageHeight = getResources().getDimensionPixelSize(R.dimen.parallax_image_height);\n\n        mGridView = (ObservableGridView) findViewById(R.id.list);\n        mGridView.setScrollViewCallbacks(this);\n        // Set padding view for ListView. This is the flexible space.\n        View paddingView = new View(this);\n        AbsListView.LayoutParams lp = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT,\n                mParallaxImageHeight);\n        paddingView.setLayoutParams(lp);\n\n        // This is required to disable header's list selector effect\n        paddingView.setClickable(true);\n\n        mGridView.addHeaderView(paddingView);\n        setDummyData(mGridView);\n\n        // mListBackgroundView makes ListView's background except header view.\n        mListBackgroundView = findViewById(R.id.list_background);\n    }\n\n    @Override\n    protected void onRestoreInstanceState(Bundle savedInstanceState) {\n        super.onRestoreInstanceState(savedInstanceState);\n        onScrollChanged(mGridView.getCurrentScrollY(), false, false);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        int baseColor = getResources().getColor(R.color.primary);\n        float alpha = Math.min(1, (float) scrollY / mParallaxImageHeight);\n        mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(alpha, baseColor));\n        ViewHelper.setTranslationY(mImageView, -scrollY / 2);\n\n        // Translate list background\n        ViewHelper.setTranslationY(mListBackgroundView, Math.max(0, -scrollY + mParallaxImageHeight));\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarListViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v7.widget.Toolbar;\nimport android.view.View;\nimport android.widget.AbsListView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\n\npublic class ParallaxToolbarListViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private View mImageView;\n    private View mToolbarView;\n    private View mListBackgroundView;\n    private ObservableListView mListView;\n    private int mParallaxImageHeight;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_parallaxtoolbarlistview) ;\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        mImageView = findViewById(R.id.image);\n        mToolbarView = findViewById(R.id.toolbar);\n        mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(0, getResources().getColor(R.color.primary)));\n\n        mParallaxImageHeight = getResources().getDimensionPixelSize(R.dimen.parallax_image_height);\n\n        mListView = (ObservableListView) findViewById(R.id.list);\n        mListView.setScrollViewCallbacks(this);\n        // Set padding view for ListView. This is the flexible space.\n        View paddingView = new View(this);\n        AbsListView.LayoutParams lp = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT,\n                mParallaxImageHeight);\n        paddingView.setLayoutParams(lp);\n\n        // This is required to disable header's list selector effect\n        paddingView.setClickable(true);\n\n        mListView.addHeaderView(paddingView);\n        setDummyData(mListView);\n\n        // mListBackgroundView makes ListView's background except header view.\n        mListBackgroundView = findViewById(R.id.list_background);\n    }\n\n    @Override\n    protected void onRestoreInstanceState(Bundle savedInstanceState) {\n        super.onRestoreInstanceState(savedInstanceState);\n        onScrollChanged(mListView.getCurrentScrollY(), false, false);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        int baseColor = getResources().getColor(R.color.primary);\n        float alpha = Math.min(1, (float) scrollY / mParallaxImageHeight);\n        mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(alpha, baseColor));\n        ViewHelper.setTranslationY(mImageView, -scrollY / 2);\n\n        // Translate list background\n        ViewHelper.setTranslationY(mListBackgroundView, Math.max(0, -scrollY + mParallaxImageHeight));\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarScrollViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v7.widget.Toolbar;\nimport android.view.View;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\n\npublic class ParallaxToolbarScrollViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private View mImageView;\n    private View mToolbarView;\n    private ObservableScrollView mScrollView;\n    private int mParallaxImageHeight;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_parallaxtoolbarscrollview);\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        mImageView = findViewById(R.id.image);\n        mToolbarView = findViewById(R.id.toolbar);\n        mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(0, getResources().getColor(R.color.primary)));\n\n        mScrollView = (ObservableScrollView) findViewById(R.id.scroll);\n        mScrollView.setScrollViewCallbacks(this);\n\n        mParallaxImageHeight = getResources().getDimensionPixelSize(R.dimen.parallax_image_height);\n    }\n\n    @Override\n    protected void onRestoreInstanceState(Bundle savedInstanceState) {\n        super.onRestoreInstanceState(savedInstanceState);\n        onScrollChanged(mScrollView.getCurrentScrollY(), false, false);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        int baseColor = getResources().getColor(R.color.primary);\n        float alpha = Math.min(1, (float) scrollY / mParallaxImageHeight);\n        mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(alpha, baseColor));\n        ViewHelper.setTranslationY(mImageView, scrollY / 2);\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ScrollFromBottomListViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.view.ViewCompat;\nimport android.support.v7.widget.Toolbar;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\n/**\n * This is a sample of using ListView that scrolls from the bottom.\n * Currently we cannot get the correct 'scrollY', but it's now almost correct with the fix in the issue #3.\n * We can also use 'AbsListView#computeVerticalScrollOffset()' instead of 'scrollY' from 'onScrollChanged()'.\n * But please note that 'computeVerticalScrollOffset' is not the scroll position but the scroll bar's thumb,\n * so it cannot be used in some cases.\n * See the discussion below for details:\n * https://github.com/ksoichiro/Android-ObservableScrollView/issues/3\n */\npublic class ScrollFromBottomListViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private View mHeaderView;\n    private View mToolbarView;\n    private ObservableListView mListView;\n    private int mBaseTranslationY;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_stickyheaderlistview);\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        mHeaderView = findViewById(R.id.header);\n        ViewCompat.setElevation(mHeaderView, getResources().getDimension(R.dimen.toolbar_elevation));\n        mToolbarView = findViewById(R.id.toolbar);\n\n        mListView = (ObservableListView) findViewById(R.id.list);\n        mListView.setScrollViewCallbacks(this);\n\n        LayoutInflater inflater = LayoutInflater.from(this);\n        mListView.addHeaderView(inflater.inflate(R.layout.padding, mListView, false)); // toolbar\n        mListView.addHeaderView(inflater.inflate(R.layout.padding, mListView, false)); // sticky view\n        setDummyData(mListView);\n\n        ScrollUtils.addOnGlobalLayoutListener(mListView, new Runnable() {\n            @Override\n            public void run() {\n                int count = mListView.getAdapter().getCount() - 1;\n                int position = count == 0 ? 1 : count > 0 ? count : 0;\n                mListView.smoothScrollToPosition(position);\n                mListView.setSelection(position);\n            }\n        });\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        int toolbarHeight = mToolbarView.getHeight();\n        if (dragging || scrollY < toolbarHeight) {\n            if (firstScroll) {\n                float currentHeaderTranslationY = ViewHelper.getTranslationY(mHeaderView);\n                if (-toolbarHeight < currentHeaderTranslationY && toolbarHeight < scrollY) {\n                    mBaseTranslationY = scrollY;\n                }\n            }\n            float headerTranslationY = ScrollUtils.getFloat(-(scrollY - mBaseTranslationY), -toolbarHeight, 0);\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewHelper.setTranslationY(mHeaderView, headerTranslationY);\n        }\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        mBaseTranslationY = 0;\n\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        int toolbarHeight = mToolbarView.getHeight();\n        if (scrollState == ScrollState.UP) {\n            if (toolbarHeight < mListView.getCurrentScrollY()) {\n                if (headerTranslationY != -toolbarHeight) {\n                    ViewPropertyAnimator.animate(mHeaderView).cancel();\n                    ViewPropertyAnimator.animate(mHeaderView).translationY(-toolbarHeight).setDuration(200).start();\n                }\n            }\n        } else if (scrollState == ScrollState.DOWN) {\n            if (toolbarHeight < mListView.getCurrentScrollY()) {\n                if (headerTranslationY != 0) {\n                    ViewPropertyAnimator.animate(mHeaderView).cancel();\n                    ViewPropertyAnimator.animate(mHeaderView).translationY(0).setDuration(200).start();\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ScrollFromBottomRecyclerViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.view.ViewCompat;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.Toolbar;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\n/**\n * This is a sample of using RecyclerView that scrolls from the bottom.\n * It returns incorrect scrollY and commit 'a99a0de' fixed a part of this problem.\n * (Related to the issue #3)\n * It still returns incorrect scrollY and this is a known issue.\n * Please don't submit it as a new issue.\n * (Pull request to fix this is greatly appreciated!)\n */\npublic class ScrollFromBottomRecyclerViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private View mHeaderView;\n    private View mToolbarView;\n    private ObservableRecyclerView mRecyclerView;\n    private int mBaseTranslationY;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_stickyheaderrecyclerview);\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        mHeaderView = findViewById(R.id.header);\n        ViewCompat.setElevation(mHeaderView, getResources().getDimension(R.dimen.toolbar_elevation));\n        mToolbarView = findViewById(R.id.toolbar);\n\n        mRecyclerView = (ObservableRecyclerView) findViewById(R.id.recycler);\n        mRecyclerView.setScrollViewCallbacks(this);\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));\n        mRecyclerView.setHasFixedSize(false);\n        View headerView = LayoutInflater.from(this).inflate(R.layout.recycler_header, null);\n        setDummyDataWithHeader(mRecyclerView, headerView);\n\n        ScrollUtils.addOnGlobalLayoutListener(mRecyclerView, new Runnable() {\n            @Override\n            public void run() {\n                int count = mRecyclerView.getAdapter().getItemCount() - 1;\n                int position = count == 0 ? 1 : count > 0 ? count : 0;\n                mRecyclerView.scrollToPosition(position);\n            }\n        });\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        int toolbarHeight = mToolbarView.getHeight();\n        if (dragging || scrollY < toolbarHeight) {\n            if (firstScroll) {\n                float currentHeaderTranslationY = ViewHelper.getTranslationY(mHeaderView);\n                if (-toolbarHeight < currentHeaderTranslationY && toolbarHeight < scrollY) {\n                    mBaseTranslationY = scrollY;\n                }\n            }\n            float headerTranslationY = ScrollUtils.getFloat(-(scrollY - mBaseTranslationY), -toolbarHeight, 0);\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewHelper.setTranslationY(mHeaderView, headerTranslationY);\n        }\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        mBaseTranslationY = 0;\n\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        int toolbarHeight = mToolbarView.getHeight();\n        if (scrollState == ScrollState.UP) {\n            if (toolbarHeight < mRecyclerView.getCurrentScrollY()) {\n                if (headerTranslationY != -toolbarHeight) {\n                    ViewPropertyAnimator.animate(mHeaderView).cancel();\n                    ViewPropertyAnimator.animate(mHeaderView).translationY(-toolbarHeight).setDuration(200).start();\n                }\n            }\n        } else if (scrollState == ScrollState.DOWN) {\n            if (toolbarHeight < mRecyclerView.getCurrentScrollY()) {\n                if (headerTranslationY != 0) {\n                    ViewPropertyAnimator.animate(mHeaderView).cancel();\n                    ViewPropertyAnimator.animate(mHeaderView).translationY(0).setDuration(200).start();\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SimpleHeaderRecyclerAdapter.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.content.Context;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\nimport java.util.ArrayList;\n\npublic class SimpleHeaderRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {\n    private static final int VIEW_TYPE_HEADER = 0;\n    private static final int VIEW_TYPE_ITEM = 1;\n\n    private LayoutInflater mInflater;\n    private ArrayList<String> mItems;\n    private View mHeaderView;\n\n    public SimpleHeaderRecyclerAdapter(Context context, ArrayList<String> items, View headerView) {\n        mInflater = LayoutInflater.from(context);\n        mItems = items;\n        mHeaderView = headerView;\n    }\n\n    @Override\n    public int getItemCount() {\n        if (mHeaderView == null) {\n            return mItems.size();\n        } else {\n            return mItems.size() + 1;\n        }\n    }\n\n    @Override\n    public int getItemViewType(int position) {\n        return (position == 0) ? VIEW_TYPE_HEADER : VIEW_TYPE_ITEM;\n    }\n\n    @Override\n    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n        if (viewType == VIEW_TYPE_HEADER) {\n            return new HeaderViewHolder(mHeaderView);\n        } else {\n            return new ItemViewHolder(mInflater.inflate(android.R.layout.simple_list_item_1, parent, false));\n        }\n    }\n\n    @Override\n    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {\n        if (viewHolder instanceof ItemViewHolder) {\n            ((ItemViewHolder) viewHolder).textView.setText(mItems.get(position - 1));\n        }\n    }\n\n    static class HeaderViewHolder extends RecyclerView.ViewHolder {\n        public HeaderViewHolder(View view) {\n            super(view);\n        }\n    }\n\n    static class ItemViewHolder extends RecyclerView.ViewHolder {\n        TextView textView;\n\n        public ItemViewHolder(View view) {\n            super(view);\n            textView = (TextView) view.findViewById(android.R.id.text1);\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SimpleRecyclerAdapter.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.content.Context;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\nimport java.util.ArrayList;\n\npublic class SimpleRecyclerAdapter extends RecyclerView.Adapter<SimpleRecyclerAdapter.ViewHolder> {\n    private LayoutInflater mInflater;\n    private ArrayList<String> mItems;\n\n    public SimpleRecyclerAdapter(Context context, ArrayList<String> items) {\n        mInflater = LayoutInflater.from(context);\n        mItems = items;\n    }\n\n    @Override\n    public int getItemCount() {\n        return mItems.size();\n    }\n\n    @Override\n    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n        return new ViewHolder(mInflater.inflate(android.R.layout.simple_list_item_1, parent, false));\n    }\n\n    @Override\n    public void onBindViewHolder(ViewHolder viewHolder, int position) {\n        viewHolder.textView.setText(mItems.get(position));\n    }\n\n    static class ViewHolder extends RecyclerView.ViewHolder {\n        TextView textView;\n\n        public ViewHolder(View view) {\n            super(view);\n            textView = (TextView) view.findViewById(android.R.id.text1);\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.graphics.Color;\nimport android.graphics.Rect;\nimport android.os.Bundle;\nimport android.support.v7.app.ActionBar;\nimport android.support.v7.widget.Toolbar;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\nimport com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout;\nimport com.nineoldandroids.animation.ValueAnimator;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\npublic abstract class SlidingUpBaseActivity<S extends Scrollable> extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private static final String STATE_SLIDING_STATE = \"slidingState\";\n    private static final int SLIDING_STATE_TOP = 0;\n    private static final int SLIDING_STATE_MIDDLE = 1;\n    private static final int SLIDING_STATE_BOTTOM = 2;\n\n    private View mHeader;\n    private View mHeaderBar;\n    private View mHeaderOverlay;\n    private View mHeaderFlexibleSpace;\n    private TextView mTitle;\n    private TextView mToolbarTitle;\n    private View mImageView;\n    private View mFab;\n    private Toolbar mToolbar;\n    private S mScrollable;\n    private TouchInterceptionFrameLayout mInterceptionLayout;\n\n    // Fields that just keep constants like resource values\n    private int mActionBarSize;\n    private int mIntersectionHeight;\n    private int mHeaderBarHeight;\n    private int mSlidingSlop;\n    private int mSlidingHeaderBlueSize;\n    private int mColorPrimary;\n    private int mFlexibleSpaceImageHeight;\n    private int mToolbarColor;\n    private int mFabMargin;\n\n    // Fields that needs to saved\n    private int mSlidingState;\n\n    // Temporary states\n    private boolean mFabIsShown;\n    private boolean mMoved;\n    private float mInitialY;\n    private float mMovedDistanceY;\n    private float mScrollYOnDownMotion;\n\n    // These flags are used for changing header colors.\n    private boolean mHeaderColorIsChanging;\n    private boolean mHeaderColorChangedToBottom;\n    private boolean mHeaderIsAtBottom;\n    private boolean mHeaderIsNotAtBottom;\n    private View.OnClickListener fabClickListener = new View.OnClickListener() {\n        @Override\n        public void onClick(View v) {\n            Toast.makeText(SlidingUpBaseActivity.this, \"floating action button clicked\", Toast.LENGTH_SHORT).show();\n        }\n    };\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(getLayoutResId());\n\n        mToolbar = (Toolbar) findViewById(R.id.toolbar);\n\n        setSupportActionBar(mToolbar);\n        ViewHelper.setScaleY(mToolbar, 0);\n        ActionBar ab = getSupportActionBar();\n        if (ab != null) {\n            ab.setHomeButtonEnabled(true);\n            ab.setDisplayHomeAsUpEnabled(true);\n            ab.setTitle(\"\");\n        }\n\n        mToolbarColor = getResources().getColor(R.color.primary);\n        mToolbar.setBackgroundColor(Color.TRANSPARENT);\n        mToolbar.setTitle(\"\");\n\n        mFlexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);\n        mIntersectionHeight = getResources().getDimensionPixelSize(R.dimen.intersection_height);\n        mHeaderBarHeight = getResources().getDimensionPixelSize(R.dimen.header_bar_height);\n        mSlidingSlop = getResources().getDimensionPixelSize(R.dimen.sliding_slop);\n        mActionBarSize = getActionBarSize();\n        mColorPrimary = getResources().getColor(R.color.primary);\n        mSlidingHeaderBlueSize = getResources().getDimensionPixelSize(R.dimen.sliding_overlay_blur_size);\n\n        mHeader = findViewById(R.id.header);\n        mHeaderBar = findViewById(R.id.header_bar);\n        mHeaderOverlay = findViewById(R.id.header_overlay);\n        mHeaderFlexibleSpace = findViewById(R.id.header_flexible_space);\n        mImageView = findViewById(R.id.image);\n        mImageView.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                slideOnClick();\n            }\n        });\n        mScrollable = createScrollable();\n\n        mFab = findViewById(R.id.fab);\n        mFab.setOnClickListener(fabClickListener);\n        mFabMargin = getResources().getDimensionPixelSize(R.dimen.margin_standard);\n\n        mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.scroll_wrapper);\n        mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener);\n        mTitle = (TextView) findViewById(R.id.title);\n        mTitle.setText(getTitle());\n        mToolbarTitle = (TextView) findViewById(R.id.toolbar_title);\n        mToolbarTitle.setText(mTitle.getText());\n        ViewHelper.setAlpha(mToolbarTitle, 0);\n        ViewHelper.setTranslationY(mTitle, (mHeaderBarHeight - mActionBarSize) / 2);\n\n        if (savedInstanceState == null) {\n            mSlidingState = SLIDING_STATE_BOTTOM;\n        }\n\n        ScrollUtils.addOnGlobalLayoutListener(mInterceptionLayout, new Runnable() {\n            @Override\n            public void run() {\n                if (mFab != null) {\n                    ViewHelper.setTranslationX(mFab, mTitle.getWidth() - mFabMargin - mFab.getWidth());\n                    ViewHelper.setTranslationY(mFab, ViewHelper.getX(mTitle) - (mFab.getHeight() / 2));\n                }\n                changeSlidingState(mSlidingState, false);\n            }\n        });\n    }\n\n    @Override\n    protected void onRestoreInstanceState(Bundle savedInstanceState) {\n        super.onRestoreInstanceState(savedInstanceState);\n        // All the related temporary states can be restored by slidingState\n        mSlidingState = savedInstanceState.getInt(STATE_SLIDING_STATE);\n    }\n\n    @Override\n    protected void onSaveInstanceState(Bundle outState) {\n        outState.putInt(STATE_SLIDING_STATE, mSlidingState);\n        super.onSaveInstanceState(outState);\n    }\n\n    protected abstract int getLayoutResId();\n\n    protected abstract S createScrollable();\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n    }\n\n    private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() {\n        @Override\n        public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) {\n            final int minInterceptionLayoutY = -mIntersectionHeight;\n\n            // slight fix for untappable floating action button for larger screens\n            Rect fabRect = new Rect();\n            mFab.getHitRect(fabRect);\n            // if the user's touch is within the floating action button's touch area, don't intercept\n            if (fabRect.contains((int) ev.getX(), (int) ev.getY())) {\n                return false;\n            } else {\n                return minInterceptionLayoutY < (int) ViewHelper.getY(mInterceptionLayout)\n                        || (moving && mScrollable.getCurrentScrollY() - diffY < 0);\n            }\n        }\n\n        @Override\n        public void onDownMotionEvent(MotionEvent ev) {\n            mScrollYOnDownMotion = mScrollable.getCurrentScrollY();\n            mInitialY = ViewHelper.getTranslationY(mInterceptionLayout);\n        }\n\n        @Override\n        public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) {\n            mMoved = true;\n            float translationY = ViewHelper.getTranslationY(mInterceptionLayout) - mScrollYOnDownMotion + diffY;\n            if (translationY < -mIntersectionHeight) {\n                translationY = -mIntersectionHeight;\n            } else if (getScreenHeight() - mHeaderBarHeight < translationY) {\n                translationY = getScreenHeight() - mHeaderBarHeight;\n            }\n\n            slideTo(translationY, true);\n\n            mMovedDistanceY = ViewHelper.getTranslationY(mInterceptionLayout) - mInitialY;\n        }\n\n        @Override\n        public void onUpOrCancelMotionEvent(MotionEvent ev) {\n            if (!mMoved) {\n                // Invoke slide animation only on header view\n                Rect outRect = new Rect();\n                mHeader.getHitRect(outRect);\n                if (outRect.contains((int) ev.getX(), (int) ev.getY())) {\n                    slideOnClick();\n                }\n            } else {\n                stickToAnchors();\n            }\n            mMoved = false;\n        }\n    };\n\n    private void changeSlidingState(final int slidingState, boolean animated) {\n        mSlidingState = slidingState;\n        float translationY = 0;\n        switch (slidingState) {\n            case SLIDING_STATE_TOP:\n                translationY = 0;\n                break;\n            case SLIDING_STATE_MIDDLE:\n                translationY = getAnchorYImage();\n                break;\n            case SLIDING_STATE_BOTTOM:\n                translationY = getAnchorYBottom();\n                break;\n        }\n        if (animated) {\n            slideWithAnimation(translationY);\n        } else {\n            slideTo(translationY, false);\n        }\n    }\n\n    private void slideOnClick() {\n        float translationY = ViewHelper.getTranslationY(mInterceptionLayout);\n        if (translationY == getAnchorYBottom()) {\n            changeSlidingState(SLIDING_STATE_MIDDLE, true);\n        } else if (translationY == getAnchorYImage()) {\n            changeSlidingState(SLIDING_STATE_BOTTOM, true);\n        }\n    }\n\n    private void stickToAnchors() {\n        // Slide to some points automatically\n        if (0 < mMovedDistanceY) {\n            // Sliding down\n            if (mSlidingSlop < mMovedDistanceY) {\n                // Sliding down to an anchor\n                if (getAnchorYImage() < ViewHelper.getTranslationY(mInterceptionLayout)) {\n                    changeSlidingState(SLIDING_STATE_BOTTOM, true);\n                } else {\n                    changeSlidingState(SLIDING_STATE_MIDDLE, true);\n                }\n            } else {\n                // Sliding up(back) to an anchor\n                if (getAnchorYImage() < ViewHelper.getTranslationY(mInterceptionLayout)) {\n                    changeSlidingState(SLIDING_STATE_MIDDLE, true);\n                } else {\n                    changeSlidingState(SLIDING_STATE_TOP, true);\n                }\n            }\n        } else if (mMovedDistanceY < 0) {\n            // Sliding up\n            if (mMovedDistanceY < -mSlidingSlop) {\n                // Sliding up to an anchor\n                if (getAnchorYImage() < ViewHelper.getTranslationY(mInterceptionLayout)) {\n                    changeSlidingState(SLIDING_STATE_MIDDLE, true);\n                } else {\n                    changeSlidingState(SLIDING_STATE_TOP, true);\n                }\n            } else {\n                // Sliding down(back) to an anchor\n                if (getAnchorYImage() < ViewHelper.getTranslationY(mInterceptionLayout)) {\n                    changeSlidingState(SLIDING_STATE_BOTTOM, true);\n                } else {\n                    changeSlidingState(SLIDING_STATE_MIDDLE, true);\n                }\n            }\n        }\n    }\n\n    private void slideTo(float translationY, final boolean animated) {\n        ViewHelper.setTranslationY(mInterceptionLayout, translationY);\n\n        if (translationY < 0) {\n            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams();\n            lp.height = (int) -translationY + getScreenHeight();\n            mInterceptionLayout.requestLayout();\n        }\n\n        // Translate title\n        float hiddenHeight = translationY < 0 ? -translationY : 0;\n        ViewHelper.setTranslationY(mTitle, Math.min(mIntersectionHeight, (mHeaderBarHeight + hiddenHeight - mActionBarSize) / 2));\n\n        // Translate image\n        float imageAnimatableHeight = getScreenHeight() - mHeaderBarHeight;\n        float imageTranslationScale = imageAnimatableHeight / (imageAnimatableHeight - mImageView.getHeight());\n        float imageTranslationY = Math.max(0, imageAnimatableHeight - (imageAnimatableHeight - translationY) * imageTranslationScale);\n        ViewHelper.setTranslationY(mImageView, imageTranslationY);\n\n        // Show/hide FAB\n        if (ViewHelper.getTranslationY(mInterceptionLayout) < mFlexibleSpaceImageHeight) {\n            hideFab(animated);\n        } else {\n            if (animated) {\n                ViewPropertyAnimator.animate(mToolbar).scaleY(0).setDuration(200).start();\n            } else {\n                ViewHelper.setScaleY(mToolbar, 0);\n            }\n            showFab(animated);\n        }\n        if (ViewHelper.getTranslationY(mInterceptionLayout) <= mFlexibleSpaceImageHeight) {\n            if (animated) {\n                ViewPropertyAnimator.animate(mToolbar).scaleY(1).setDuration(200).start();\n            } else {\n                ViewHelper.setScaleY(mToolbar, 1);\n            }\n            mToolbar.setBackgroundColor(ScrollUtils.getColorWithAlpha(0, mToolbarColor));\n        }\n\n        changeToolbarTitleVisibility();\n        changeHeaderBarColorAnimated(animated);\n        changeHeaderOverlay();\n    }\n\n    private void slideWithAnimation(float toY) {\n        float layoutTranslationY = ViewHelper.getTranslationY(mInterceptionLayout);\n        if (layoutTranslationY != toY) {\n            ValueAnimator animator = ValueAnimator.ofFloat(ViewHelper.getTranslationY(mInterceptionLayout), toY).setDuration(200);\n            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n                @Override\n                public void onAnimationUpdate(ValueAnimator animation) {\n                    slideTo((float) animation.getAnimatedValue(), true);\n                }\n            });\n            animator.start();\n        }\n    }\n\n    private void changeToolbarTitleVisibility() {\n        if (ViewHelper.getTranslationY(mInterceptionLayout) <= mIntersectionHeight) {\n            if (ViewHelper.getAlpha(mToolbarTitle) != 1) {\n                ViewPropertyAnimator.animate(mToolbarTitle).cancel();\n                ViewPropertyAnimator.animate(mToolbarTitle).alpha(1).setDuration(200).start();\n            }\n        } else if (ViewHelper.getAlpha(mToolbarTitle) != 0) {\n            ViewPropertyAnimator.animate(mToolbarTitle).cancel();\n            ViewPropertyAnimator.animate(mToolbarTitle).alpha(0).setDuration(200).start();\n        } else {\n            ViewHelper.setAlpha(mToolbarTitle, 0);\n        }\n    }\n\n    private void changeHeaderBarColorAnimated(boolean animated) {\n        if (mHeaderColorIsChanging) {\n            return;\n        }\n        boolean shouldBeWhite = getAnchorYBottom() == ViewHelper.getTranslationY(mInterceptionLayout);\n        if (!mHeaderIsAtBottom && !mHeaderColorChangedToBottom && shouldBeWhite) {\n            mHeaderIsAtBottom = true;\n            mHeaderIsNotAtBottom = false;\n            if (animated) {\n                ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(100);\n                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n                    @Override\n                    public void onAnimationUpdate(ValueAnimator animation) {\n                        float alpha = (float) animation.getAnimatedValue();\n                        mHeaderColorIsChanging = (alpha != 1);\n                        changeHeaderBarColor(alpha);\n                    }\n                });\n                animator.start();\n            } else {\n                changeHeaderBarColor(1);\n            }\n        } else if (!mHeaderIsNotAtBottom && !shouldBeWhite) {\n            mHeaderIsAtBottom = false;\n            mHeaderIsNotAtBottom = true;\n            if (animated) {\n                ValueAnimator animator = ValueAnimator.ofFloat(1, 0).setDuration(100);\n                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n                    @Override\n                    public void onAnimationUpdate(ValueAnimator animation) {\n                        float alpha = (float) animation.getAnimatedValue();\n                        mHeaderColorIsChanging = (alpha != 0);\n                        changeHeaderBarColor(alpha);\n                    }\n                });\n                animator.start();\n            } else {\n                changeHeaderBarColor(0);\n            }\n        }\n    }\n\n    private void changeHeaderBarColor(float alpha) {\n        mHeaderBar.setBackgroundColor(ScrollUtils.mixColors(mColorPrimary, Color.WHITE, alpha));\n        mTitle.setTextColor(ScrollUtils.mixColors(Color.WHITE, Color.BLACK, alpha));\n        mHeaderColorChangedToBottom = (alpha == 1);\n    }\n\n    private void changeHeaderOverlay() {\n        final float translationY = ViewHelper.getTranslationY(mInterceptionLayout);\n        if (translationY <= mToolbar.getHeight() - mSlidingHeaderBlueSize) {\n            mHeaderOverlay.setVisibility(View.VISIBLE);\n            mHeaderFlexibleSpace.getLayoutParams().height = (int) (mToolbar.getHeight() - mSlidingHeaderBlueSize - translationY);\n            mHeaderFlexibleSpace.requestLayout();\n            mHeaderOverlay.requestLayout();\n        } else {\n            mHeaderOverlay.setVisibility(View.INVISIBLE);\n        }\n    }\n\n    private void showFab(boolean animated) {\n        if (mFab == null) {\n            return;\n        }\n        if (!mFabIsShown) {\n            if (animated) {\n                ViewPropertyAnimator.animate(mFab).cancel();\n                ViewPropertyAnimator.animate(mFab).scaleX(1).scaleY(1).setDuration(200).start();\n            } else {\n                ViewHelper.setScaleX(mFab, 1);\n                ViewHelper.setScaleY(mFab, 1);\n            }\n            mFabIsShown = true;\n        } else {\n            // Ensure that FAB is shown\n            ViewHelper.setScaleX(mFab, 1);\n            ViewHelper.setScaleY(mFab, 1);\n        }\n    }\n\n    private void hideFab(boolean animated) {\n        if (mFab == null) {\n            return;\n        }\n        if (mFabIsShown) {\n            if (animated) {\n                ViewPropertyAnimator.animate(mFab).cancel();\n                ViewPropertyAnimator.animate(mFab).scaleX(0).scaleY(0).setDuration(200).start();\n            } else {\n                ViewHelper.setScaleX(mFab, 0);\n                ViewHelper.setScaleY(mFab, 0);\n            }\n            mFabIsShown = false;\n        } else {\n            // Ensure that FAB is hidden\n            ViewHelper.setScaleX(mFab, 0);\n            ViewHelper.setScaleY(mFab, 0);\n        }\n    }\n\n    private float getAnchorYBottom() {\n        return getScreenHeight() - mHeaderBarHeight;\n    }\n\n    private float getAnchorYImage() {\n        return mImageView.getHeight();\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpGridViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.Toast;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\npublic class SlidingUpGridViewActivity extends SlidingUpBaseActivity<ObservableGridView> implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_slidingupgridview;\n    }\n\n    @Override\n    protected ObservableGridView createScrollable() {\n        ObservableGridView gridView = (ObservableGridView) findViewById(R.id.scroll);\n        gridView.setScrollViewCallbacks(this);\n        setDummyData(gridView);\n        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {\n            @Override\n            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n                Toast.makeText(SlidingUpGridViewActivity.this, \"Item \" + (position + 1) + \" clicked\", Toast.LENGTH_SHORT).show();\n            }\n        });\n        return gridView;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpListViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.Toast;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\npublic class SlidingUpListViewActivity extends SlidingUpBaseActivity<ObservableListView> implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_slidinguplistview;\n    }\n\n    @Override\n    protected ObservableListView createScrollable() {\n        ObservableListView listView = (ObservableListView) findViewById(R.id.scroll);\n        listView.setScrollViewCallbacks(this);\n        setDummyData(listView);\n        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {\n            @Override\n            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n                Toast.makeText(SlidingUpListViewActivity.this, \"Item \" + (position + 1) + \" clicked\", Toast.LENGTH_SHORT).show();\n            }\n        });\n        return listView;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpRecyclerViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.content.Context;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\nimport java.util.ArrayList;\n\npublic class SlidingUpRecyclerViewActivity extends SlidingUpBaseActivity<ObservableRecyclerView> implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_slidinguprecyclerview;\n    }\n\n    @Override\n    protected ObservableRecyclerView createScrollable() {\n        ObservableRecyclerView recyclerView = (ObservableRecyclerView) findViewById(R.id.scroll);\n        recyclerView.setScrollViewCallbacks(this);\n        recyclerView.setLayoutManager(new LinearLayoutManager(this));\n        recyclerView.setHasFixedSize(true);\n        recyclerView.setAdapter(new CustomAdapter(this, getDummyData()));\n        return recyclerView;\n    }\n\n    public static class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {\n        private Context mContext;\n        private LayoutInflater mInflater;\n        private ArrayList<String> mItems;\n\n        public CustomAdapter(Context context, ArrayList<String> items) {\n            mContext = context;\n            mInflater = LayoutInflater.from(context);\n            mItems = items;\n        }\n\n        @Override\n        public int getItemCount() {\n            return mItems.size();\n        }\n\n        @Override\n        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n            return new ViewHolder(mContext, mInflater.inflate(android.R.layout.simple_list_item_1, parent, false));\n        }\n\n        @Override\n        public void onBindViewHolder(ViewHolder viewHolder, int position) {\n            viewHolder.textView.setText(mItems.get(position));\n        }\n\n        static class ViewHolder extends RecyclerView.ViewHolder {\n            TextView textView;\n            Context context;\n\n            public ViewHolder(Context context, View view) {\n                super(view);\n                this.context = context;\n                this.textView = (TextView) view.findViewById(android.R.id.text1);\n                this.textView.setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        click(getPosition() + 1);\n                    }\n                });\n            }\n\n            private void click(int i) {\n                Toast.makeText(context, \"Button \" + i + \" is clicked\", Toast.LENGTH_SHORT).show();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpScrollViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\npublic class SlidingUpScrollViewActivity extends SlidingUpBaseActivity<ObservableScrollView> implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_slidingupscrollview;\n    }\n\n    @Override\n    protected ObservableScrollView createScrollable() {\n        ObservableScrollView scrollView = (ObservableScrollView) findViewById(R.id.scroll);\n        scrollView.setScrollViewCallbacks(this);\n        return scrollView;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpWebViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ObservableWebView;\n\npublic class SlidingUpWebViewActivity extends SlidingUpBaseActivity<ObservableWebView> implements ObservableScrollViewCallbacks {\n\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_slidingupwebview;\n    }\n\n    @Override\n    protected ObservableWebView createScrollable() {\n        ObservableWebView webView = (ObservableWebView) findViewById(R.id.scroll);\n        webView.setScrollViewCallbacks(this);\n        webView.loadUrl(\"file:///android_asset/lipsum.html\");\n        return webView;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderListViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.view.ViewCompat;\nimport android.support.v7.widget.Toolbar;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.AbsListView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\npublic class StickyHeaderListViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private static final String TAG = StickyHeaderListViewActivity.class.getSimpleName();\n    private View mHeaderView;\n    private View mToolbarView;\n    private ObservableListView mListView;\n    private int mBaseTranslationY;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_stickyheaderlistview);\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        mHeaderView = findViewById(R.id.header);\n        ViewCompat.setElevation(mHeaderView, getResources().getDimension(R.dimen.toolbar_elevation));\n        mToolbarView = findViewById(R.id.toolbar);\n\n        mListView = (ObservableListView) findViewById(R.id.list);\n        mListView.setScrollViewCallbacks(this);\n\n        LayoutInflater inflater = LayoutInflater.from(this);\n        mListView.addHeaderView(inflater.inflate(R.layout.padding, mListView, false)); // toolbar\n        mListView.addHeaderView(inflater.inflate(R.layout.padding, mListView, false)); // sticky view\n        setDummyData(mListView);\n\n        // ObservableListView uses setOnScrollListener, but it still works.\n        mListView.setOnScrollListener(new AbsListView.OnScrollListener() {\n            @Override\n            public void onScrollStateChanged(AbsListView view, int scrollState) {\n                Log.v(TAG, \"onScrollStateChanged: \" + scrollState);\n            }\n\n            @Override\n            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {\n                Log.v(TAG, \"onScroll: firstVisibleItem: \" + firstVisibleItem + \" visibleItemCount: \" + visibleItemCount + \" totalItemCount: \" + totalItemCount);\n            }\n        });\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        if (dragging) {\n            int toolbarHeight = mToolbarView.getHeight();\n            if (firstScroll) {\n                float currentHeaderTranslationY = ViewHelper.getTranslationY(mHeaderView);\n                if (-toolbarHeight < currentHeaderTranslationY) {\n                    mBaseTranslationY = scrollY;\n                }\n            }\n            float headerTranslationY = ScrollUtils.getFloat(-(scrollY - mBaseTranslationY), -toolbarHeight, 0);\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewHelper.setTranslationY(mHeaderView, headerTranslationY);\n        }\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        mBaseTranslationY = 0;\n\n        if (scrollState == ScrollState.DOWN) {\n            showToolbar();\n        } else if (scrollState == ScrollState.UP) {\n            int toolbarHeight = mToolbarView.getHeight();\n            int scrollY = mListView.getCurrentScrollY();\n            if (toolbarHeight <= scrollY) {\n                hideToolbar();\n            } else {\n                showToolbar();\n            }\n        } else {\n            // Even if onScrollChanged occurs without scrollY changing, toolbar should be adjusted\n            if (!toolbarIsShown() && !toolbarIsHidden()) {\n                // Toolbar is moving but doesn't know which to move:\n                // you can change this to hideToolbar()\n                showToolbar();\n            }\n        }\n    }\n\n    private boolean toolbarIsShown() {\n        return ViewHelper.getTranslationY(mHeaderView) == 0;\n    }\n\n    private boolean toolbarIsHidden() {\n        return ViewHelper.getTranslationY(mHeaderView) == -mToolbarView.getHeight();\n    }\n\n    private void showToolbar() {\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        if (headerTranslationY != 0) {\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewPropertyAnimator.animate(mHeaderView).translationY(0).setDuration(200).start();\n        }\n    }\n\n    private void hideToolbar() {\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        int toolbarHeight = mToolbarView.getHeight();\n        if (headerTranslationY != -toolbarHeight) {\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewPropertyAnimator.animate(mHeaderView).translationY(-toolbarHeight).setDuration(200).start();\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderRecyclerViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.view.ViewCompat;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.Toolbar;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\npublic class StickyHeaderRecyclerViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private View mHeaderView;\n    private View mToolbarView;\n    private ObservableRecyclerView mRecyclerView;\n    private int mBaseTranslationY;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_stickyheaderrecyclerview);\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        mHeaderView = findViewById(R.id.header);\n        ViewCompat.setElevation(mHeaderView, getResources().getDimension(R.dimen.toolbar_elevation));\n        mToolbarView = findViewById(R.id.toolbar);\n\n        mRecyclerView = (ObservableRecyclerView) findViewById(R.id.recycler);\n        mRecyclerView.setScrollViewCallbacks(this);\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));\n        mRecyclerView.setHasFixedSize(false);\n        View headerView = LayoutInflater.from(this).inflate(R.layout.recycler_header, null);\n        setDummyDataWithHeader(mRecyclerView, headerView);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        if (dragging) {\n            int toolbarHeight = mToolbarView.getHeight();\n            if (firstScroll) {\n                float currentHeaderTranslationY = ViewHelper.getTranslationY(mHeaderView);\n                if (-toolbarHeight < currentHeaderTranslationY) {\n                    mBaseTranslationY = scrollY;\n                }\n            }\n            float headerTranslationY = ScrollUtils.getFloat(-(scrollY - mBaseTranslationY), -toolbarHeight, 0);\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewHelper.setTranslationY(mHeaderView, headerTranslationY);\n        }\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        mBaseTranslationY = 0;\n\n        if (scrollState == ScrollState.DOWN) {\n            showToolbar();\n        } else if (scrollState == ScrollState.UP) {\n            int toolbarHeight = mToolbarView.getHeight();\n            int scrollY = mRecyclerView.getCurrentScrollY();\n            if (toolbarHeight <= scrollY) {\n                hideToolbar();\n            } else {\n                showToolbar();\n            }\n        } else {\n            // Even if onScrollChanged occurs without scrollY changing, toolbar should be adjusted\n            if (!toolbarIsShown() && !toolbarIsHidden()) {\n                // Toolbar is moving but doesn't know which to move:\n                // you can change this to hideToolbar()\n                showToolbar();\n            }\n        }\n    }\n\n    private boolean toolbarIsShown() {\n        return ViewHelper.getTranslationY(mHeaderView) == 0;\n    }\n\n    private boolean toolbarIsHidden() {\n        return ViewHelper.getTranslationY(mHeaderView) == -mToolbarView.getHeight();\n    }\n\n    private void showToolbar() {\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        if (headerTranslationY != 0) {\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewPropertyAnimator.animate(mHeaderView).translationY(0).setDuration(200).start();\n        }\n    }\n\n    private void hideToolbar() {\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        int toolbarHeight = mToolbarView.getHeight();\n        if (headerTranslationY != -toolbarHeight) {\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewPropertyAnimator.animate(mHeaderView).translationY(-toolbarHeight).setDuration(200).start();\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderScrollViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.view.ViewCompat;\nimport android.support.v7.widget.Toolbar;\nimport android.view.View;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\npublic class StickyHeaderScrollViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private View mHeaderView;\n    private View mToolbarView;\n    private ObservableScrollView mScrollView;\n    private int mBaseTranslationY;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_stickyheaderscrollview);\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        mHeaderView = findViewById(R.id.header);\n        ViewCompat.setElevation(mHeaderView, getResources().getDimension(R.dimen.toolbar_elevation));\n        mToolbarView = findViewById(R.id.toolbar);\n\n        mScrollView = (ObservableScrollView) findViewById(R.id.scroll);\n        mScrollView.setScrollViewCallbacks(this);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        if (dragging) {\n            int toolbarHeight = mToolbarView.getHeight();\n            if (firstScroll) {\n                float currentHeaderTranslationY = ViewHelper.getTranslationY(mHeaderView);\n                if (-toolbarHeight < currentHeaderTranslationY) {\n                    mBaseTranslationY = scrollY;\n                }\n            }\n            float headerTranslationY = ScrollUtils.getFloat(-(scrollY - mBaseTranslationY), -toolbarHeight, 0);\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewHelper.setTranslationY(mHeaderView, headerTranslationY);\n        }\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        mBaseTranslationY = 0;\n\n        if (scrollState == ScrollState.DOWN) {\n            showToolbar();\n        } else if (scrollState == ScrollState.UP) {\n            int toolbarHeight = mToolbarView.getHeight();\n            int scrollY = mScrollView.getCurrentScrollY();\n            if (toolbarHeight <= scrollY) {\n                hideToolbar();\n            } else {\n                showToolbar();\n            }\n        } else {\n            // Even if onScrollChanged occurs without scrollY changing, toolbar should be adjusted\n            if (!toolbarIsShown() && !toolbarIsHidden()) {\n                // Toolbar is moving but doesn't know which to move:\n                // you can change this to hideToolbar()\n                showToolbar();\n            }\n        }\n    }\n\n    private boolean toolbarIsShown() {\n        return ViewHelper.getTranslationY(mHeaderView) == 0;\n    }\n\n    private boolean toolbarIsHidden() {\n        return ViewHelper.getTranslationY(mHeaderView) == -mToolbarView.getHeight();\n    }\n\n    private void showToolbar() {\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        if (headerTranslationY != 0) {\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewPropertyAnimator.animate(mHeaderView).translationY(0).setDuration(200).start();\n        }\n    }\n\n    private void hideToolbar() {\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        int toolbarHeight = mToolbarView.getHeight();\n        if (headerTranslationY != -toolbarHeight) {\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewPropertyAnimator.animate(mHeaderView).translationY(-toolbarHeight).setDuration(200).start();\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderWebViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.view.ViewCompat;\nimport android.support.v7.widget.Toolbar;\nimport android.view.View;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ObservableWebView;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\npublic class StickyHeaderWebViewActivity extends BaseActivity {\n\n    private View mHeaderView;\n    private View mToolbarView;\n    private ObservableScrollView mScrollView;\n    private boolean mFirstScroll;\n    private boolean mDragging;\n    private int mBaseTranslationY;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_stickyheaderwebview);\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        mHeaderView = findViewById(R.id.header);\n        ViewCompat.setElevation(mHeaderView, getResources().getDimension(R.dimen.toolbar_elevation));\n        mToolbarView = findViewById(R.id.toolbar);\n\n        mScrollView = (ObservableScrollView) findViewById(R.id.scroll);\n        mScrollView.setScrollViewCallbacks(mScrollViewScrollCallbacks);\n\n        ObservableWebView mWebView = (ObservableWebView) findViewById(R.id.web);\n        mWebView.setScrollViewCallbacks(mWebViewScrollCallbacks);\n        mWebView.loadUrl(\"file:///android_asset/lipsum.html\");\n    }\n\n    private ObservableScrollViewCallbacks mScrollViewScrollCallbacks = new ObservableScrollViewCallbacks() {\n        @Override\n        public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n            if (mDragging) {\n                int toolbarHeight = mToolbarView.getHeight();\n                if (mFirstScroll) {\n                    mFirstScroll = false;\n                    float currentHeaderTranslationY = ViewHelper.getTranslationY(mHeaderView);\n                    if (-toolbarHeight < currentHeaderTranslationY) {\n                        mBaseTranslationY = scrollY;\n                    }\n                }\n                float headerTranslationY = ScrollUtils.getFloat(-(scrollY - mBaseTranslationY), -toolbarHeight, 0);\n                ViewPropertyAnimator.animate(mHeaderView).cancel();\n                ViewHelper.setTranslationY(mHeaderView, headerTranslationY);\n            }\n        }\n\n        @Override\n        public void onDownMotionEvent() {\n        }\n\n        @Override\n        public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n            mDragging = false;\n            mBaseTranslationY = 0;\n\n            if (scrollState == ScrollState.DOWN) {\n                showToolbar();\n            } else if (scrollState == ScrollState.UP) {\n                int toolbarHeight = mToolbarView.getHeight();\n                int scrollY = mScrollView.getCurrentScrollY();\n                if (toolbarHeight <= scrollY) {\n                    hideToolbar();\n                } else {\n                    showToolbar();\n                }\n            } else {\n                // Even if onScrollChanged occurs without scrollY changing, toolbar should be adjusted\n                if (!toolbarIsShown() && !toolbarIsHidden()) {\n                    // Toolbar is moving but doesn't know which to move:\n                    // you can change this to hideToolbar()\n                    showToolbar();\n                }\n            }\n        }\n    };\n\n    private ObservableScrollViewCallbacks mWebViewScrollCallbacks = new ObservableScrollViewCallbacks() {\n        @Override\n        public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        }\n\n        @Override\n        public void onDownMotionEvent() {\n            // Workaround: WebView inside a ScrollView absorbs down motion events, so observing\n            // down motion event from the WebView is required.\n            mFirstScroll = mDragging = true;\n        }\n\n        @Override\n        public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        }\n    };\n\n    private boolean toolbarIsShown() {\n        return ViewHelper.getTranslationY(mHeaderView) == 0;\n    }\n\n    private boolean toolbarIsHidden() {\n        return ViewHelper.getTranslationY(mHeaderView) == -mToolbarView.getHeight();\n    }\n\n    private void showToolbar() {\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        if (headerTranslationY != 0) {\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewPropertyAnimator.animate(mHeaderView).translationY(0).setDuration(200).start();\n        }\n    }\n\n    private void hideToolbar() {\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        int toolbarHeight = mToolbarView.getHeight();\n        if (headerTranslationY != -toolbarHeight) {\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewPropertyAnimator.animate(mHeaderView).translationY(-toolbarHeight).setDuration(200).start();\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlBaseActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v7.widget.Toolbar;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.FrameLayout;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\nimport com.nineoldandroids.animation.ValueAnimator;\nimport com.nineoldandroids.view.ViewHelper;\n\npublic abstract class ToolbarControlBaseActivity<S extends Scrollable> extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private Toolbar mToolbar;\n    private S mScrollable;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(getLayoutResId());\n\n        mToolbar = (Toolbar) findViewById(R.id.toolbar);\n        setSupportActionBar(mToolbar);\n\n        mScrollable = createScrollable();\n        mScrollable.setScrollViewCallbacks(this);\n    }\n\n    protected abstract int getLayoutResId();\n\n    protected abstract S createScrollable();\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        Log.e(\"DEBUG\", \"onUpOrCancelMotionEvent: \" + scrollState);\n        if (scrollState == ScrollState.UP) {\n            if (toolbarIsShown()) {\n                hideToolbar();\n            }\n        } else if (scrollState == ScrollState.DOWN) {\n            if (toolbarIsHidden()) {\n                showToolbar();\n            }\n        }\n    }\n\n    private boolean toolbarIsShown() {\n        return ViewHelper.getTranslationY(mToolbar) == 0;\n    }\n\n    private boolean toolbarIsHidden() {\n        return ViewHelper.getTranslationY(mToolbar) == -mToolbar.getHeight();\n    }\n\n    private void showToolbar() {\n        moveToolbar(0);\n    }\n\n    private void hideToolbar() {\n        moveToolbar(-mToolbar.getHeight());\n    }\n\n    private void moveToolbar(float toTranslationY) {\n        if (ViewHelper.getTranslationY(mToolbar) == toTranslationY) {\n            return;\n        }\n        ValueAnimator animator = ValueAnimator.ofFloat(ViewHelper.getTranslationY(mToolbar), toTranslationY).setDuration(200);\n        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                float translationY = (float) animation.getAnimatedValue();\n                ViewHelper.setTranslationY(mToolbar, translationY);\n                ViewHelper.setTranslationY((View) mScrollable, translationY);\n                FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) ((View) mScrollable).getLayoutParams();\n                lp.height = (int) -translationY + getScreenHeight() - lp.topMargin;\n                ((View) mScrollable).requestLayout();\n            }\n        });\n        animator.start();\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlGridViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\n\npublic class ToolbarControlGridViewActivity extends ToolbarControlBaseActivity<ObservableGridView> {\n\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_toolbarcontrolgridview;\n    }\n\n    @Override\n    protected ObservableGridView createScrollable() {\n        ObservableGridView gridView = (ObservableGridView) findViewById(R.id.scrollable);\n        setDummyData(gridView);\n        return gridView;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlListViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.util.Log;\nimport android.widget.AbsListView;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\n\npublic class ToolbarControlListViewActivity extends ToolbarControlBaseActivity<ObservableListView> {\n\n    private static final String TAG = ToolbarControlListViewActivity.class.getSimpleName();\n\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_toolbarcontrollistview;\n    }\n\n    @Override\n    protected ObservableListView createScrollable() {\n        ObservableListView listView = (ObservableListView) findViewById(R.id.scrollable);\n        setDummyData(listView);\n\n        // ObservableListView uses setOnScrollListener, but it still works.\n        listView.setOnScrollListener(new AbsListView.OnScrollListener() {\n            @Override\n            public void onScrollStateChanged(AbsListView view, int scrollState) {\n                Log.v(TAG, \"onScrollStateChanged: \" + scrollState);\n            }\n\n            @Override\n            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {\n                Log.v(TAG, \"onScroll: firstVisibleItem: \" + firstVisibleItem + \" visibleItemCount: \" + visibleItemCount + \" totalItemCount: \" + totalItemCount);\n            }\n        });\n        return listView;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlRecyclerViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.support.v7.widget.LinearLayoutManager;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\n\npublic class ToolbarControlRecyclerViewActivity extends ToolbarControlBaseActivity<ObservableRecyclerView> {\n\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_toolbarcontrolrecyclerview;\n    }\n\n    @Override\n    protected ObservableRecyclerView createScrollable() {\n        ObservableRecyclerView recyclerView = (ObservableRecyclerView) findViewById(R.id.scrollable);\n        recyclerView.setLayoutManager(new LinearLayoutManager(this));\n        recyclerView.setHasFixedSize(true);\n        setDummyData(recyclerView);\n        return recyclerView;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlScrollViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\n\npublic class ToolbarControlScrollViewActivity extends ToolbarControlBaseActivity<ObservableScrollView> {\n\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_toolbarcontrolscrollview;\n    }\n\n    @Override\n    protected ObservableScrollView createScrollable() {\n        return (ObservableScrollView) findViewById(R.id.scrollable);\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlWebViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableWebView;\n\npublic class ToolbarControlWebViewActivity extends ToolbarControlBaseActivity<ObservableWebView> {\n\n    @Override\n    protected int getLayoutResId() {\n        return R.layout.activity_toolbarcontrolwebview;\n    }\n\n    @Override\n    protected ObservableWebView createScrollable() {\n        ObservableWebView webView = (ObservableWebView) findViewById(R.id.scrollable);\n        webView.loadUrl(\"file:///android_asset/lipsum.html\");\n        return webView;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2Activity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.view.ViewCompat;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.widget.Toolbar;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.widget.FrameLayout;\n\nimport com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\nimport com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout;\nimport com.google.samples.apps.iosched.ui.widget.SlidingTabLayout;\nimport com.nineoldandroids.animation.ValueAnimator;\nimport com.nineoldandroids.view.ViewHelper;\n\n/**\n * Another implementation of ViewPagerTabActivity.\n * This uses TouchInterceptionFrameLayout to move Fragments.\n * <p/>\n * SlidingTabLayout and SlidingTabStrip are from google/iosched:\n * https://github.com/google/iosched\n */\npublic class ViewPagerTab2Activity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private View mToolbarView;\n    private TouchInterceptionFrameLayout mInterceptionLayout;\n    private ViewPager mPager;\n    private NavigationAdapter mPagerAdapter;\n    private int mSlop;\n    private boolean mScrolled;\n    private ScrollState mLastScrollState;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_viewpagertab2);\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        ViewCompat.setElevation(findViewById(R.id.header), getResources().getDimension(R.dimen.toolbar_elevation));\n        mToolbarView = findViewById(R.id.toolbar);\n        mPagerAdapter = new NavigationAdapter(getSupportFragmentManager());\n        mPager = (ViewPager) findViewById(R.id.pager);\n        mPager.setAdapter(mPagerAdapter);\n        // Padding for ViewPager must be set outside the ViewPager itself\n        // because with padding, EdgeEffect of ViewPager become strange.\n        final int tabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height);\n        findViewById(R.id.pager_wrapper).setPadding(0, getActionBarSize() + tabHeight, 0, 0);\n\n        SlidingTabLayout slidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);\n        slidingTabLayout.setCustomTabView(R.layout.tab_indicator, android.R.id.text1);\n        slidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.accent));\n        slidingTabLayout.setDistributeEvenly(true);\n        slidingTabLayout.setViewPager(mPager);\n\n        ViewConfiguration vc = ViewConfiguration.get(this);\n        mSlop = vc.getScaledTouchSlop();\n        mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.container);\n        mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener);\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        if (!mScrolled) {\n            // This event can be used only when TouchInterceptionFrameLayout\n            // doesn't handle the consecutive events.\n            adjustToolbar(scrollState);\n        }\n    }\n\n    private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() {\n        @Override\n        public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) {\n            if (!mScrolled && mSlop < Math.abs(diffX) && Math.abs(diffY) < Math.abs(diffX)) {\n                // Horizontal scroll is maybe handled by ViewPager\n                return false;\n            }\n\n            Scrollable scrollable = getCurrentScrollable();\n            if (scrollable == null) {\n                mScrolled = false;\n                return false;\n            }\n\n            // If interceptionLayout can move, it should intercept.\n            // And once it begins to move, horizontal scroll shouldn't work any longer.\n            int toolbarHeight = mToolbarView.getHeight();\n            int translationY = (int) ViewHelper.getTranslationY(mInterceptionLayout);\n            boolean scrollingUp = 0 < diffY;\n            boolean scrollingDown = diffY < 0;\n            if (scrollingUp) {\n                if (translationY < 0) {\n                    mScrolled = true;\n                    mLastScrollState = ScrollState.UP;\n                    return true;\n                }\n            } else if (scrollingDown) {\n                if (-toolbarHeight < translationY) {\n                    mScrolled = true;\n                    mLastScrollState = ScrollState.DOWN;\n                    return true;\n                }\n            }\n            mScrolled = false;\n            return false;\n        }\n\n        @Override\n        public void onDownMotionEvent(MotionEvent ev) {\n        }\n\n        @Override\n        public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) {\n            float translationY = ScrollUtils.getFloat(ViewHelper.getTranslationY(mInterceptionLayout) + diffY, -mToolbarView.getHeight(), 0);\n            ViewHelper.setTranslationY(mInterceptionLayout, translationY);\n            if (translationY < 0) {\n                FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams();\n                lp.height = (int) (-translationY + getScreenHeight());\n                mInterceptionLayout.requestLayout();\n            }\n        }\n\n        @Override\n        public void onUpOrCancelMotionEvent(MotionEvent ev) {\n            mScrolled = false;\n            adjustToolbar(mLastScrollState);\n        }\n    };\n\n    private Scrollable getCurrentScrollable() {\n        Fragment fragment = getCurrentFragment();\n        if (fragment == null) {\n            return null;\n        }\n        View view = fragment.getView();\n        if (view == null) {\n            return null;\n        }\n        return (Scrollable) view.findViewById(R.id.scroll);\n    }\n\n    private void adjustToolbar(ScrollState scrollState) {\n        int toolbarHeight = mToolbarView.getHeight();\n        final Scrollable scrollable = getCurrentScrollable();\n        if (scrollable == null) {\n            return;\n        }\n        int scrollY = scrollable.getCurrentScrollY();\n        if (scrollState == ScrollState.DOWN) {\n            showToolbar();\n        } else if (scrollState == ScrollState.UP) {\n            if (toolbarHeight <= scrollY) {\n                hideToolbar();\n            } else {\n                showToolbar();\n            }\n        } else if (!toolbarIsShown() && !toolbarIsHidden()) {\n            // Toolbar is moving but doesn't know which to move:\n            // you can change this to hideToolbar()\n            showToolbar();\n        }\n    }\n\n    private Fragment getCurrentFragment() {\n        return mPagerAdapter.getItemAt(mPager.getCurrentItem());\n    }\n\n    private boolean toolbarIsShown() {\n        return ViewHelper.getTranslationY(mInterceptionLayout) == 0;\n    }\n\n    private boolean toolbarIsHidden() {\n        return ViewHelper.getTranslationY(mInterceptionLayout) == -mToolbarView.getHeight();\n    }\n\n    private void showToolbar() {\n        animateToolbar(0);\n    }\n\n    private void hideToolbar() {\n        animateToolbar(-mToolbarView.getHeight());\n    }\n\n    private void animateToolbar(final float toY) {\n        float layoutTranslationY = ViewHelper.getTranslationY(mInterceptionLayout);\n        if (layoutTranslationY != toY) {\n            ValueAnimator animator = ValueAnimator.ofFloat(ViewHelper.getTranslationY(mInterceptionLayout), toY).setDuration(200);\n            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n                @Override\n                public void onAnimationUpdate(ValueAnimator animation) {\n                    float translationY = (float) animation.getAnimatedValue();\n                    ViewHelper.setTranslationY(mInterceptionLayout, translationY);\n                    if (translationY < 0) {\n                        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams();\n                        lp.height = (int) (-translationY + getScreenHeight());\n                        mInterceptionLayout.requestLayout();\n                    }\n                }\n            });\n            animator.start();\n        }\n    }\n\n    /**\n     * This adapter provides two types of fragments as an example.\n     * {@linkplain #createItem(int)} should be modified if you use this example for your app.\n     */\n    private static class NavigationAdapter extends CacheFragmentStatePagerAdapter {\n\n        private static final String[] TITLES = new String[]{\"Applepie\", \"Butter Cookie\", \"Cupcake\", \"Donut\", \"Eclair\", \"Froyo\", \"Gingerbread\", \"Honeycomb\", \"Ice Cream Sandwich\", \"Jelly Bean\", \"KitKat\", \"Lollipop\"};\n\n        public NavigationAdapter(FragmentManager fm) {\n            super(fm);\n        }\n\n        @Override\n        protected Fragment createItem(int position) {\n            Fragment f;\n            final int pattern = position % 5;\n            switch (pattern) {\n                case 0:\n                    f = new ViewPagerTab2ScrollViewFragment();\n                    break;\n                case 1:\n                    f = new ViewPagerTab2ListViewFragment();\n                    break;\n                case 2:\n                    f = new ViewPagerTab2RecyclerViewFragment();\n                    break;\n                case 3:\n                    f = new ViewPagerTab2GridViewFragment();\n                    break;\n                case 4:\n                default:\n                    f = new ViewPagerTab2WebViewFragment();\n                    break;\n            }\n            return f;\n        }\n\n        @Override\n        public int getCount() {\n            return TITLES.length;\n        }\n\n        @Override\n        public CharSequence getPageTitle(int position) {\n            return TITLES[position];\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2GridViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\npublic class ViewPagerTab2GridViewFragment extends BaseFragment {\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_gridview, container, false);\n\n        Activity parentActivity = getActivity();\n        final ObservableGridView gridView = (ObservableGridView) view.findViewById(R.id.scroll);\n        setDummyData(gridView);\n        gridView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.container));\n\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            gridView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2ListViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\npublic class ViewPagerTab2ListViewFragment extends BaseFragment {\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_listview, container, false);\n\n        Activity parentActivity = getActivity();\n        final ObservableListView listView = (ObservableListView) view.findViewById(R.id.scroll);\n        setDummyData(listView);\n        listView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.container));\n\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            listView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2RecyclerViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\npublic class ViewPagerTab2RecyclerViewFragment extends BaseFragment {\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_recyclerview, container, false);\n\n        Activity parentActivity = getActivity();\n        final ObservableRecyclerView recyclerView = (ObservableRecyclerView) view.findViewById(R.id.scroll);\n        recyclerView.setLayoutManager(new LinearLayoutManager(parentActivity));\n        recyclerView.setHasFixedSize(false);\n        setDummyData(recyclerView);\n        recyclerView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.container));\n\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            recyclerView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2ScrollViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\npublic class ViewPagerTab2ScrollViewFragment extends BaseFragment {\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_scrollview_noheader, container, false);\n\n        final ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll);\n        Activity parentActivity = getActivity();\n        scrollView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.container));\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            scrollView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2WebViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ObservableWebView;\n\npublic class ViewPagerTab2WebViewFragment extends BaseFragment {\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_webview, container, false);\n\n        final ObservableWebView webView = (ObservableWebView) view.findViewById(R.id.scroll);\n        webView.loadUrl(\"file:///android_asset/lipsum.html\");\n        Activity parentActivity = getActivity();\n        webView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.container));\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            webView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.view.ViewCompat;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.widget.Toolbar;\nimport android.view.View;\n\nimport com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\nimport com.google.samples.apps.iosched.ui.widget.SlidingTabLayout;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\n/**\n * This is an example of ViewPager + SlidingTab + ListView/ScrollView.\n * This example shows how to handle scroll events for several different fragments.\n * <p/>\n * SlidingTabLayout and SlidingTabStrip are from google/iosched:\n * https://github.com/google/iosched\n */\npublic class ViewPagerTabActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private View mHeaderView;\n    private View mToolbarView;\n    private int mBaseTranslationY;\n    private ViewPager mPager;\n    private NavigationAdapter mPagerAdapter;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_viewpagertab);\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        mHeaderView = findViewById(R.id.header);\n        ViewCompat.setElevation(mHeaderView, getResources().getDimension(R.dimen.toolbar_elevation));\n        mToolbarView = findViewById(R.id.toolbar);\n        mPagerAdapter = new NavigationAdapter(getSupportFragmentManager());\n        mPager = (ViewPager) findViewById(R.id.pager);\n        mPager.setAdapter(mPagerAdapter);\n\n        SlidingTabLayout slidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);\n        slidingTabLayout.setCustomTabView(R.layout.tab_indicator, android.R.id.text1);\n        slidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.accent));\n        slidingTabLayout.setDistributeEvenly(true);\n        slidingTabLayout.setViewPager(mPager);\n\n        // When the page is selected, other fragments' scrollY should be adjusted\n        // according to the toolbar status(shown/hidden)\n        slidingTabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {\n            @Override\n            public void onPageScrolled(int i, float v, int i2) {\n            }\n\n            @Override\n            public void onPageSelected(int i) {\n                propagateToolbarState(toolbarIsShown());\n            }\n\n            @Override\n            public void onPageScrollStateChanged(int i) {\n            }\n        });\n\n        propagateToolbarState(toolbarIsShown());\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        if (dragging) {\n            int toolbarHeight = mToolbarView.getHeight();\n            float currentHeaderTranslationY = ViewHelper.getTranslationY(mHeaderView);\n            if (firstScroll) {\n                if (-toolbarHeight < currentHeaderTranslationY) {\n                    mBaseTranslationY = scrollY;\n                }\n            }\n            float headerTranslationY = ScrollUtils.getFloat(-(scrollY - mBaseTranslationY), -toolbarHeight, 0);\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewHelper.setTranslationY(mHeaderView, headerTranslationY);\n        }\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        mBaseTranslationY = 0;\n\n        Fragment fragment = getCurrentFragment();\n        if (fragment == null) {\n            return;\n        }\n        View view = fragment.getView();\n        if (view == null) {\n            return;\n        }\n\n        // ObservableXxxViews have same API\n        // but currently they don't have any common interfaces.\n        adjustToolbar(scrollState, view);\n    }\n\n    private void adjustToolbar(ScrollState scrollState, View view) {\n        int toolbarHeight = mToolbarView.getHeight();\n        final Scrollable scrollView = (Scrollable) view.findViewById(R.id.scroll);\n        if (scrollView == null) {\n            return;\n        }\n        int scrollY = scrollView.getCurrentScrollY();\n        if (scrollState == ScrollState.DOWN) {\n            showToolbar();\n        } else if (scrollState == ScrollState.UP) {\n            if (toolbarHeight <= scrollY) {\n                hideToolbar();\n            } else {\n                showToolbar();\n            }\n        } else {\n            // Even if onScrollChanged occurs without scrollY changing, toolbar should be adjusted\n            if (toolbarIsShown() || toolbarIsHidden()) {\n                // Toolbar is completely moved, so just keep its state\n                // and propagate it to other pages\n                propagateToolbarState(toolbarIsShown());\n            } else {\n                // Toolbar is moving but doesn't know which to move:\n                // you can change this to hideToolbar()\n                showToolbar();\n            }\n        }\n    }\n\n    private Fragment getCurrentFragment() {\n        return mPagerAdapter.getItemAt(mPager.getCurrentItem());\n    }\n\n    private void propagateToolbarState(boolean isShown) {\n        int toolbarHeight = mToolbarView.getHeight();\n\n        // Set scrollY for the fragments that are not created yet\n        mPagerAdapter.setScrollY(isShown ? 0 : toolbarHeight);\n\n        // Set scrollY for the active fragments\n        for (int i = 0; i < mPagerAdapter.getCount(); i++) {\n            // Skip current item\n            if (i == mPager.getCurrentItem()) {\n                continue;\n            }\n\n            // Skip destroyed or not created item\n            Fragment f = mPagerAdapter.getItemAt(i);\n            if (f == null) {\n                continue;\n            }\n\n            View view = f.getView();\n            if (view == null) {\n                continue;\n            }\n            propagateToolbarState(isShown, view, toolbarHeight);\n        }\n    }\n\n    private void propagateToolbarState(boolean isShown, View view, int toolbarHeight) {\n        Scrollable scrollView = (Scrollable) view.findViewById(R.id.scroll);\n        if (scrollView == null) {\n            return;\n        }\n        if (isShown) {\n            // Scroll up\n            if (0 < scrollView.getCurrentScrollY()) {\n                scrollView.scrollVerticallyTo(0);\n            }\n        } else {\n            // Scroll down (to hide padding)\n            if (scrollView.getCurrentScrollY() < toolbarHeight) {\n                scrollView.scrollVerticallyTo(toolbarHeight);\n            }\n        }\n    }\n\n    private boolean toolbarIsShown() {\n        return ViewHelper.getTranslationY(mHeaderView) == 0;\n    }\n\n    private boolean toolbarIsHidden() {\n        return ViewHelper.getTranslationY(mHeaderView) == -mToolbarView.getHeight();\n    }\n\n    private void showToolbar() {\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        if (headerTranslationY != 0) {\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewPropertyAnimator.animate(mHeaderView).translationY(0).setDuration(200).start();\n        }\n        propagateToolbarState(true);\n    }\n\n    private void hideToolbar() {\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        int toolbarHeight = mToolbarView.getHeight();\n        if (headerTranslationY != -toolbarHeight) {\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewPropertyAnimator.animate(mHeaderView).translationY(-toolbarHeight).setDuration(200).start();\n        }\n        propagateToolbarState(false);\n    }\n\n    /**\n     * This adapter provides two types of fragments as an example.\n     * {@linkplain #createItem(int)} should be modified if you use this example for your app.\n     */\n    private static class NavigationAdapter extends CacheFragmentStatePagerAdapter {\n\n        private static final String[] TITLES = new String[]{\"Applepie\", \"Butter Cookie\", \"Cupcake\", \"Donut\", \"Eclair\", \"Froyo\", \"Gingerbread\", \"Honeycomb\", \"Ice Cream Sandwich\", \"Jelly Bean\", \"KitKat\", \"Lollipop\"};\n\n        private int mScrollY;\n\n        public NavigationAdapter(FragmentManager fm) {\n            super(fm);\n        }\n\n        public void setScrollY(int scrollY) {\n            mScrollY = scrollY;\n        }\n\n        @Override\n        protected Fragment createItem(int position) {\n            // Initialize fragments.\n            // Please be sure to pass scroll position to each fragments using setArguments.\n            Fragment f;\n            final int pattern = position % 4;\n            switch (pattern) {\n                case 0: {\n                    f = new ViewPagerTabScrollViewFragment();\n                    if (0 <= mScrollY) {\n                        Bundle args = new Bundle();\n                        args.putInt(ViewPagerTabScrollViewFragment.ARG_SCROLL_Y, mScrollY);\n                        f.setArguments(args);\n                    }\n                    break;\n                }\n                case 1: {\n                    f = new ViewPagerTabListViewFragment();\n                    if (0 < mScrollY) {\n                        Bundle args = new Bundle();\n                        args.putInt(ViewPagerTabListViewFragment.ARG_INITIAL_POSITION, 1);\n                        f.setArguments(args);\n                    }\n                    break;\n                }\n                case 2: {\n                    f = new ViewPagerTabRecyclerViewFragment();\n                    if (0 < mScrollY) {\n                        Bundle args = new Bundle();\n                        args.putInt(ViewPagerTabRecyclerViewFragment.ARG_INITIAL_POSITION, 1);\n                        f.setArguments(args);\n                    }\n                    break;\n                }\n                case 3:\n                default: {\n                    f = new ViewPagerTabGridViewFragment();\n                    if (0 < mScrollY) {\n                        Bundle args = new Bundle();\n                        args.putInt(ViewPagerTabGridViewFragment.ARG_INITIAL_POSITION, 1);\n                        f.setArguments(args);\n                    }\n                    break;\n                }\n            }\n            return f;\n        }\n\n        @Override\n        public int getCount() {\n            return TITLES.length;\n        }\n\n        @Override\n        public CharSequence getPageTitle(int position) {\n            return TITLES[position];\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.app.FragmentTransaction;\nimport android.support.v7.widget.Toolbar;\n\n/**\n * This activity just provides a toolbar.\n * Toolbar is manipulated by ViewPagerTabFragmentParentFragment.\n */\npublic class ViewPagerTabFragmentActivity extends BaseActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_viewpagertabfragment);\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        FragmentManager fm = getSupportFragmentManager();\n        if (fm.findFragmentByTag(ViewPagerTabFragmentParentFragment.FRAGMENT_TAG) == null) {\n            FragmentTransaction ft = fm.beginTransaction();\n            ft.add(R.id.fragment, new ViewPagerTabFragmentParentFragment(),\n                    ViewPagerTabFragmentParentFragment.FRAGMENT_TAG);\n            ft.commit();\n            fm.executePendingTransactions();\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentGridViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\n/**\n * Fragment for ViewPagerTabFragmentActivity.\n * ScrollView callbacks are handled by its parent fragment, not its parent activity.\n */\npublic class ViewPagerTabFragmentGridViewFragment extends BaseFragment {\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_gridview, container, false);\n\n        final ObservableGridView gridView = (ObservableGridView) view.findViewById(R.id.scroll);\n        setDummyData(gridView);\n\n        Fragment parentFragment = getParentFragment();\n        ViewGroup viewGroup = (ViewGroup) parentFragment.getView();\n        if (viewGroup != null) {\n            gridView.setTouchInterceptionViewGroup((ViewGroup) viewGroup.findViewById(R.id.container));\n            if (parentFragment instanceof ObservableScrollViewCallbacks) {\n                gridView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentFragment);\n            }\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentListViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\n/**\n * Fragment for ViewPagerTabFragmentActivity.\n * ScrollView callbacks are handled by its parent fragment, not its parent activity.\n */\npublic class ViewPagerTabFragmentListViewFragment extends BaseFragment {\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_listview, container, false);\n\n        final ObservableListView listView = (ObservableListView) view.findViewById(R.id.scroll);\n        setDummyData(listView);\n\n        Fragment parentFragment = getParentFragment();\n        ViewGroup viewGroup = (ViewGroup) parentFragment.getView();\n        if (viewGroup != null) {\n            listView.setTouchInterceptionViewGroup((ViewGroup) viewGroup.findViewById(R.id.container));\n            if (parentFragment instanceof ObservableScrollViewCallbacks) {\n                listView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentFragment);\n            }\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentParentFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.LayoutInflater;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\n\nimport com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.github.ksoichiro.android.observablescrollview.Scrollable;\nimport com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout;\nimport com.google.samples.apps.iosched.ui.widget.SlidingTabLayout;\nimport com.nineoldandroids.animation.ValueAnimator;\nimport com.nineoldandroids.view.ViewHelper;\n\n/**\n * This fragment manages ViewPager and its child Fragments.\n * Scrolling techniques are basically the same as ViewPagerTab2Activity.\n */\npublic class ViewPagerTabFragmentParentFragment extends BaseFragment implements ObservableScrollViewCallbacks {\n    public static final String FRAGMENT_TAG = \"fragment\";\n\n    private TouchInterceptionFrameLayout mInterceptionLayout;\n    private ViewPager mPager;\n    private NavigationAdapter mPagerAdapter;\n    private int mSlop;\n    private boolean mScrolled;\n    private ScrollState mLastScrollState;\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_viewpagertabfragment_parent, container, false);\n\n        AppCompatActivity parentActivity = (AppCompatActivity) getActivity();\n        mPagerAdapter = new NavigationAdapter(getChildFragmentManager());\n        mPager = (ViewPager) view.findViewById(R.id.pager);\n        mPager.setAdapter(mPagerAdapter);\n\n        SlidingTabLayout slidingTabLayout = (SlidingTabLayout) view.findViewById(R.id.sliding_tabs);\n        slidingTabLayout.setCustomTabView(R.layout.tab_indicator, android.R.id.text1);\n        slidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.accent));\n        slidingTabLayout.setDistributeEvenly(true);\n        slidingTabLayout.setViewPager(mPager);\n\n        ViewConfiguration vc = ViewConfiguration.get(parentActivity);\n        mSlop = vc.getScaledTouchSlop();\n        mInterceptionLayout = (TouchInterceptionFrameLayout) view.findViewById(R.id.container);\n        mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener);\n\n        return view;\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        if (!mScrolled) {\n            // This event can be used only when TouchInterceptionFrameLayout\n            // doesn't handle the consecutive events.\n            adjustToolbar(scrollState);\n        }\n    }\n\n    private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() {\n        @Override\n        public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) {\n            if (!mScrolled && mSlop < Math.abs(diffX) && Math.abs(diffY) < Math.abs(diffX)) {\n                // Horizontal scroll is maybe handled by ViewPager\n                return false;\n            }\n\n            Scrollable scrollable = getCurrentScrollable();\n            if (scrollable == null) {\n                mScrolled = false;\n                return false;\n            }\n\n            // If interceptionLayout can move, it should intercept.\n            // And once it begins to move, horizontal scroll shouldn't work any longer.\n            View toolbarView = getActivity().findViewById(R.id.toolbar);\n            int toolbarHeight = toolbarView.getHeight();\n            int translationY = (int) ViewHelper.getTranslationY(mInterceptionLayout);\n            boolean scrollingUp = 0 < diffY;\n            boolean scrollingDown = diffY < 0;\n            if (scrollingUp) {\n                if (translationY < 0) {\n                    mScrolled = true;\n                    mLastScrollState = ScrollState.UP;\n                    return true;\n                }\n            } else if (scrollingDown) {\n                if (-toolbarHeight < translationY) {\n                    mScrolled = true;\n                    mLastScrollState = ScrollState.DOWN;\n                    return true;\n                }\n            }\n            mScrolled = false;\n            return false;\n        }\n\n        @Override\n        public void onDownMotionEvent(MotionEvent ev) {\n        }\n\n        @Override\n        public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) {\n            View toolbarView = getActivity().findViewById(R.id.toolbar);\n            float translationY = ScrollUtils.getFloat(ViewHelper.getTranslationY(mInterceptionLayout) + diffY, -toolbarView.getHeight(), 0);\n            ViewHelper.setTranslationY(mInterceptionLayout, translationY);\n            ViewHelper.setTranslationY(toolbarView, translationY);\n            if (translationY < 0) {\n                FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams();\n                lp.height = (int) (-translationY + getScreenHeight());\n                mInterceptionLayout.requestLayout();\n            }\n        }\n\n        @Override\n        public void onUpOrCancelMotionEvent(MotionEvent ev) {\n            mScrolled = false;\n            adjustToolbar(mLastScrollState);\n        }\n    };\n\n    private Scrollable getCurrentScrollable() {\n        Fragment fragment = getCurrentFragment();\n        if (fragment == null) {\n            return null;\n        }\n        View view = fragment.getView();\n        if (view == null) {\n            return null;\n        }\n        return (Scrollable) view.findViewById(R.id.scroll);\n    }\n\n    private void adjustToolbar(ScrollState scrollState) {\n        View toolbarView = getActivity().findViewById(R.id.toolbar);\n        int toolbarHeight = toolbarView.getHeight();\n        final Scrollable scrollable = getCurrentScrollable();\n        if (scrollable == null) {\n            return;\n        }\n        int scrollY = scrollable.getCurrentScrollY();\n        if (scrollState == ScrollState.DOWN) {\n            showToolbar();\n        } else if (scrollState == ScrollState.UP) {\n            if (toolbarHeight <= scrollY) {\n                hideToolbar();\n            } else {\n                showToolbar();\n            }\n        } else if (!toolbarIsShown() && !toolbarIsHidden()) {\n            // Toolbar is moving but doesn't know which to move:\n            // you can change this to hideToolbar()\n            showToolbar();\n        }\n    }\n\n    private Fragment getCurrentFragment() {\n        return mPagerAdapter.getItemAt(mPager.getCurrentItem());\n    }\n\n    private boolean toolbarIsShown() {\n        return ViewHelper.getTranslationY(mInterceptionLayout) == 0;\n    }\n\n    private boolean toolbarIsHidden() {\n        View view = getView();\n        if (view == null) {\n            return false;\n        }\n        View toolbarView = getActivity().findViewById(R.id.toolbar);\n        return ViewHelper.getTranslationY(mInterceptionLayout) == -toolbarView.getHeight();\n    }\n\n    private void showToolbar() {\n        animateToolbar(0);\n    }\n\n    private void hideToolbar() {\n        View toolbarView = getActivity().findViewById(R.id.toolbar);\n        animateToolbar(-toolbarView.getHeight());\n    }\n\n    private void animateToolbar(final float toY) {\n        float layoutTranslationY = ViewHelper.getTranslationY(mInterceptionLayout);\n        if (layoutTranslationY != toY) {\n            ValueAnimator animator = ValueAnimator.ofFloat(ViewHelper.getTranslationY(mInterceptionLayout), toY).setDuration(200);\n            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n                @Override\n                public void onAnimationUpdate(ValueAnimator animation) {\n                    float translationY = (float) animation.getAnimatedValue();\n                    View toolbarView = getActivity().findViewById(R.id.toolbar);\n                    ViewHelper.setTranslationY(mInterceptionLayout, translationY);\n                    ViewHelper.setTranslationY(toolbarView, translationY);\n                    if (translationY < 0) {\n                        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams();\n                        lp.height = (int) (-translationY + getScreenHeight());\n                        mInterceptionLayout.requestLayout();\n                    }\n                }\n            });\n            animator.start();\n        }\n    }\n\n    /**\n     * This adapter provides two types of fragments as an example.\n     * {@linkplain #createItem(int)} should be modified if you use this example for your app.\n     */\n    private static class NavigationAdapter extends CacheFragmentStatePagerAdapter {\n\n        private static final String[] TITLES = new String[]{\"Applepie\", \"Butter Cookie\", \"Cupcake\", \"Donut\", \"Eclair\", \"Froyo\", \"Gingerbread\", \"Honeycomb\", \"Ice Cream Sandwich\", \"Jelly Bean\", \"KitKat\", \"Lollipop\"};\n\n        public NavigationAdapter(FragmentManager fm) {\n            super(fm);\n        }\n\n        @Override\n        protected Fragment createItem(int position) {\n            Fragment f;\n            final int pattern = position % 5;\n            switch (pattern) {\n                case 0:\n                    f = new ViewPagerTabFragmentScrollViewFragment();\n                    break;\n                case 1:\n                    f = new ViewPagerTabFragmentListViewFragment();\n                    break;\n                case 2:\n                    f = new ViewPagerTabFragmentRecyclerViewFragment();\n                    break;\n                case 3:\n                    f = new ViewPagerTabFragmentGridViewFragment();\n                    break;\n                case 4:\n                default:\n                    f = new ViewPagerTabFragmentWebViewFragment();\n                    break;\n            }\n            return f;\n        }\n\n        @Override\n        public int getCount() {\n            return TITLES.length;\n        }\n\n        @Override\n        public CharSequence getPageTitle(int position) {\n            return TITLES[position];\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentRecyclerViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\n/**\n * Fragment for ViewPagerTabFragmentActivity.\n * ScrollView callbacks are handled by its parent fragment, not its parent activity.\n */\npublic class ViewPagerTabFragmentRecyclerViewFragment extends BaseFragment {\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_recyclerview, container, false);\n\n        final ObservableRecyclerView recyclerView = (ObservableRecyclerView) view.findViewById(R.id.scroll);\n        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));\n        recyclerView.setHasFixedSize(false);\n        setDummyData(recyclerView);\n\n        Fragment parentFragment = getParentFragment();\n        ViewGroup viewGroup = (ViewGroup) parentFragment.getView();\n        if (viewGroup != null) {\n            recyclerView.setTouchInterceptionViewGroup((ViewGroup) viewGroup.findViewById(R.id.container));\n            if (parentFragment instanceof ObservableScrollViewCallbacks) {\n                recyclerView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentFragment);\n            }\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentScrollViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\n\n/**\n * Fragment for ViewPagerTabFragmentActivity.\n * ScrollView callbacks are handled by its parent fragment, not its parent activity.\n */\npublic class ViewPagerTabFragmentScrollViewFragment extends BaseFragment {\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_scrollview_noheader, container, false);\n\n        final ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll);\n        Fragment parentFragment = getParentFragment();\n        ViewGroup viewGroup = (ViewGroup) parentFragment.getView();\n        if (viewGroup != null) {\n            scrollView.setTouchInterceptionViewGroup((ViewGroup) viewGroup.findViewById(R.id.container));\n            if (parentFragment instanceof ObservableScrollViewCallbacks) {\n                scrollView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentFragment);\n            }\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentWebViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ObservableWebView;\n\n/**\n * Fragment for ViewPagerTabFragmentActivity.\n * ScrollView callbacks are handled by its parent fragment, not its parent activity.\n */\npublic class ViewPagerTabFragmentWebViewFragment extends BaseFragment {\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_webview, container, false);\n\n        final ObservableWebView webView = (ObservableWebView) view.findViewById(R.id.scroll);\n        webView.loadUrl(\"file:///android_asset/lipsum.html\");\n        Fragment parentFragment = getParentFragment();\n        ViewGroup viewGroup = (ViewGroup) parentFragment.getView();\n        if (viewGroup != null) {\n            webView.setTouchInterceptionViewGroup((ViewGroup) viewGroup.findViewById(R.id.container));\n            if (parentFragment instanceof ObservableScrollViewCallbacks) {\n                webView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentFragment);\n            }\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabGridViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableGridView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\n\npublic class ViewPagerTabGridViewFragment extends BaseFragment {\n\n    public static final String ARG_INITIAL_POSITION = \"ARG_INITIAL_POSITION\";\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_gridview, container, false);\n\n        Activity parentActivity = getActivity();\n        final ObservableGridView gridView = (ObservableGridView) view.findViewById(R.id.scroll);\n        setDummyDataWithHeader(gridView, inflater.inflate(R.layout.padding, gridView, false));\n\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            // Scroll to the specified position after layout\n            Bundle args = getArguments();\n            if (args != null && args.containsKey(ARG_INITIAL_POSITION)) {\n                final int initialPosition = args.getInt(ARG_INITIAL_POSITION, 0);\n                ScrollUtils.addOnGlobalLayoutListener(gridView, new Runnable() {\n                    @Override\n                    public void run() {\n                        // scrollTo() doesn't work, should use setSelection()\n                        gridView.setSelection(initialPosition);\n                    }\n                });\n            }\n\n            // TouchInterceptionViewGroup should be a parent view other than ViewPager.\n            // This is a workaround for the issue #117:\n            // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117\n            gridView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.root));\n\n            gridView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.view.ViewCompat;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.widget.Toolbar;\nimport android.view.View;\n\nimport com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter;\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.google.samples.apps.iosched.ui.widget.SlidingTabLayout;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\n/**\n * SlidingTabLayout and SlidingTabStrip are from google/iosched:\n * https://github.com/google/iosched\n */\npublic class ViewPagerTabListViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private View mHeaderView;\n    private View mToolbarView;\n    private int mBaseTranslationY;\n    private ViewPager mPager;\n    private NavigationAdapter mPagerAdapter;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_viewpagertab);\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        mHeaderView = findViewById(R.id.header);\n        ViewCompat.setElevation(mHeaderView, getResources().getDimension(R.dimen.toolbar_elevation));\n        mToolbarView = findViewById(R.id.toolbar);\n        mPagerAdapter = new NavigationAdapter(getSupportFragmentManager());\n        mPager = (ViewPager) findViewById(R.id.pager);\n        mPager.setAdapter(mPagerAdapter);\n\n        SlidingTabLayout slidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);\n        slidingTabLayout.setCustomTabView(R.layout.tab_indicator, android.R.id.text1);\n        slidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.accent));\n        slidingTabLayout.setDistributeEvenly(true);\n        slidingTabLayout.setViewPager(mPager);\n\n        // When the page is selected, other fragments' scrollY should be adjusted\n        // according to the toolbar status(shown/hidden)\n        slidingTabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {\n            @Override\n            public void onPageScrolled(int i, float v, int i2) {\n            }\n\n            @Override\n            public void onPageSelected(int i) {\n                propagateToolbarState(toolbarIsShown());\n            }\n\n            @Override\n            public void onPageScrollStateChanged(int i) {\n            }\n        });\n\n        propagateToolbarState(toolbarIsShown());\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        if (dragging) {\n            int toolbarHeight = mToolbarView.getHeight();\n            float currentHeaderTranslationY = ViewHelper.getTranslationY(mHeaderView);\n            if (firstScroll) {\n                if (-toolbarHeight < currentHeaderTranslationY) {\n                    mBaseTranslationY = scrollY;\n                }\n            }\n            float headerTranslationY = ScrollUtils.getFloat(-(scrollY - mBaseTranslationY), -toolbarHeight, 0);\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewHelper.setTranslationY(mHeaderView, headerTranslationY);\n        }\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        mBaseTranslationY = 0;\n\n        Fragment fragment = getCurrentFragment();\n        if (fragment == null) {\n            return;\n        }\n        View view = fragment.getView();\n        if (view == null) {\n            return;\n        }\n\n        int toolbarHeight = mToolbarView.getHeight();\n        final ObservableListView listView = (ObservableListView) view.findViewById(R.id.scroll);\n        if (listView == null) {\n            return;\n        }\n        int scrollY = listView.getCurrentScrollY();\n        if (scrollState == ScrollState.DOWN) {\n            showToolbar();\n        } else if (scrollState == ScrollState.UP) {\n            if (toolbarHeight <= scrollY) {\n                hideToolbar();\n            } else {\n                showToolbar();\n            }\n        } else {\n            // Even if onScrollChanged occurs without scrollY changing, toolbar should be adjusted\n            if (toolbarIsShown() || toolbarIsHidden()) {\n                // Toolbar is completely moved, so just keep its state\n                // and propagate it to other pages\n                propagateToolbarState(toolbarIsShown());\n            } else {\n                // Toolbar is moving but doesn't know which to move:\n                // you can change this to hideToolbar()\n                showToolbar();\n            }\n        }\n    }\n\n    private Fragment getCurrentFragment() {\n        return mPagerAdapter.getItemAt(mPager.getCurrentItem());\n    }\n\n    private void propagateToolbarState(boolean isShown) {\n        int toolbarHeight = mToolbarView.getHeight();\n\n        // Set scrollY for the fragments that are not created yet\n        mPagerAdapter.setScrollY(isShown ? 0 : toolbarHeight);\n\n        // Set scrollY for the active fragments\n        for (int i = 0; i < mPagerAdapter.getCount(); i++) {\n            // Skip current item\n            if (i == mPager.getCurrentItem()) {\n                continue;\n            }\n\n            // Skip destroyed or not created item\n            Fragment f = mPagerAdapter.getItemAt(i);\n            if (f == null) {\n                continue;\n            }\n\n            View view = f.getView();\n            if (view == null) {\n                continue;\n            }\n            ObservableListView listView = (ObservableListView) view.findViewById(R.id.scroll);\n            if (isShown) {\n                // Scroll up\n                if (0 < listView.getCurrentScrollY()) {\n                    listView.setSelection(0);\n                }\n            } else {\n                // Scroll down (to hide padding)\n                if (listView.getCurrentScrollY() < toolbarHeight) {\n                    listView.setSelection(1);\n                }\n            }\n        }\n    }\n\n    private boolean toolbarIsShown() {\n        return ViewHelper.getTranslationY(mHeaderView) == 0;\n    }\n\n    private boolean toolbarIsHidden() {\n        return ViewHelper.getTranslationY(mHeaderView) == -mToolbarView.getHeight();\n    }\n\n    private void showToolbar() {\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        if (headerTranslationY != 0) {\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewPropertyAnimator.animate(mHeaderView).translationY(0).setDuration(200).start();\n        }\n        propagateToolbarState(true);\n    }\n\n    private void hideToolbar() {\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        int toolbarHeight = mToolbarView.getHeight();\n        if (headerTranslationY != -toolbarHeight) {\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewPropertyAnimator.animate(mHeaderView).translationY(-toolbarHeight).setDuration(200).start();\n        }\n        propagateToolbarState(false);\n    }\n\n    private static class NavigationAdapter extends CacheFragmentStatePagerAdapter {\n\n        private static final String[] TITLES = new String[]{\"Applepie\", \"Butter Cookie\", \"Cupcake\", \"Donut\", \"Eclair\", \"Froyo\", \"Gingerbread\", \"Honeycomb\", \"Ice Cream Sandwich\", \"Jelly Bean\", \"KitKat\", \"Lollipop\"};\n\n        private int mScrollY;\n\n        public NavigationAdapter(FragmentManager fm) {\n            super(fm);\n        }\n\n        public void setScrollY(int scrollY) {\n            mScrollY = scrollY;\n        }\n\n        @Override\n        protected Fragment createItem(int position) {\n            Fragment f = new ViewPagerTabListViewFragment();\n            if (0 < mScrollY) {\n                Bundle args = new Bundle();\n                args.putInt(ViewPagerTabListViewFragment.ARG_INITIAL_POSITION, 1);\n                f.setArguments(args);\n            }\n            return f;\n        }\n\n        @Override\n        public int getCount() {\n            return TITLES.length;\n        }\n\n        @Override\n        public CharSequence getPageTitle(int position) {\n            return TITLES[position];\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableListView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\n\npublic class ViewPagerTabListViewFragment extends BaseFragment {\n\n    public static final String ARG_INITIAL_POSITION = \"ARG_INITIAL_POSITION\";\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_listview, container, false);\n\n        Activity parentActivity = getActivity();\n        final ObservableListView listView = (ObservableListView) view.findViewById(R.id.scroll);\n        setDummyDataWithHeader(listView, inflater.inflate(R.layout.padding, listView, false));\n\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            // Scroll to the specified position after layout\n            Bundle args = getArguments();\n            if (args != null && args.containsKey(ARG_INITIAL_POSITION)) {\n                final int initialPosition = args.getInt(ARG_INITIAL_POSITION, 0);\n                ScrollUtils.addOnGlobalLayoutListener(listView, new Runnable() {\n                    @Override\n                    public void run() {\n                        // scrollTo() doesn't work, should use setSelection()\n                        listView.setSelection(initialPosition);\n                    }\n                });\n            }\n\n            // TouchInterceptionViewGroup should be a parent view other than ViewPager.\n            // This is a workaround for the issue #117:\n            // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117\n            listView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.root));\n\n            listView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabRecyclerViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\n\npublic class ViewPagerTabRecyclerViewFragment extends BaseFragment {\n\n    public static final String ARG_INITIAL_POSITION = \"ARG_INITIAL_POSITION\";\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_recyclerview, container, false);\n\n        Activity parentActivity = getActivity();\n        final ObservableRecyclerView recyclerView = (ObservableRecyclerView) view.findViewById(R.id.scroll);\n        recyclerView.setLayoutManager(new LinearLayoutManager(parentActivity));\n        recyclerView.setHasFixedSize(false);\n        View headerView = LayoutInflater.from(parentActivity).inflate(R.layout.padding, null);\n        setDummyDataWithHeader(recyclerView, headerView);\n\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            // Scroll to the specified offset after layout\n            Bundle args = getArguments();\n            if (args != null && args.containsKey(ARG_INITIAL_POSITION)) {\n                final int initialPosition = args.getInt(ARG_INITIAL_POSITION, 0);\n                ScrollUtils.addOnGlobalLayoutListener(recyclerView, new Runnable() {\n                    @Override\n                    public void run() {\n                        recyclerView.scrollVerticallyToPosition(initialPosition);\n                    }\n                });\n            }\n\n            // TouchInterceptionViewGroup should be a parent view other than ViewPager.\n            // This is a workaround for the issue #117:\n            // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117\n            recyclerView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.root));\n\n            recyclerView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.view.ViewCompat;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.widget.Toolbar;\nimport android.view.View;\n\nimport com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.google.samples.apps.iosched.ui.widget.SlidingTabLayout;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\n/**\n * SlidingTabLayout and SlidingTabStrip are from google/iosched:\n * https://github.com/google/iosched\n */\npublic class ViewPagerTabScrollViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {\n\n    private View mHeaderView;\n    private View mToolbarView;\n    private int mBaseTranslationY;\n    private ViewPager mPager;\n    private NavigationAdapter mPagerAdapter;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_viewpagertab);\n\n        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));\n\n        mHeaderView = findViewById(R.id.header);\n        ViewCompat.setElevation(mHeaderView, getResources().getDimension(R.dimen.toolbar_elevation));\n        mToolbarView = findViewById(R.id.toolbar);\n        mPagerAdapter = newViewPagerAdapter();\n        mPager = (ViewPager) findViewById(R.id.pager);\n        mPager.setAdapter(mPagerAdapter);\n\n        SlidingTabLayout slidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);\n        slidingTabLayout.setCustomTabView(R.layout.tab_indicator, android.R.id.text1);\n        slidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.accent));\n        slidingTabLayout.setDistributeEvenly(true);\n        slidingTabLayout.setViewPager(mPager);\n\n        // When the page is selected, other fragments' scrollY should be adjusted\n        // according to the toolbar status(shown/hidden)\n        slidingTabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {\n            @Override\n            public void onPageScrolled(int i, float v, int i2) {\n            }\n\n            @Override\n            public void onPageSelected(int i) {\n                propagateToolbarState(toolbarIsShown());\n            }\n\n            @Override\n            public void onPageScrollStateChanged(int i) {\n            }\n        });\n\n        propagateToolbarState(toolbarIsShown());\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        if (dragging) {\n            int toolbarHeight = mToolbarView.getHeight();\n            float currentHeaderTranslationY = ViewHelper.getTranslationY(mHeaderView);\n            if (firstScroll) {\n                if (-toolbarHeight < currentHeaderTranslationY) {\n                    mBaseTranslationY = scrollY;\n                }\n            }\n            float headerTranslationY = ScrollUtils.getFloat(-(scrollY - mBaseTranslationY), -toolbarHeight, 0);\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewHelper.setTranslationY(mHeaderView, headerTranslationY);\n        }\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        mBaseTranslationY = 0;\n\n        Fragment fragment = getCurrentFragment();\n        if (fragment == null) {\n            return;\n        }\n        View view = fragment.getView();\n        if (view == null) {\n            return;\n        }\n\n        int toolbarHeight = mToolbarView.getHeight();\n        final ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll);\n        if (scrollView == null) {\n            return;\n        }\n        int scrollY = scrollView.getCurrentScrollY();\n        if (scrollState == ScrollState.DOWN) {\n            showToolbar();\n        } else if (scrollState == ScrollState.UP) {\n            if (toolbarHeight <= scrollY) {\n                hideToolbar();\n            } else {\n                showToolbar();\n            }\n        } else {\n            // Even if onScrollChanged occurs without scrollY changing, toolbar should be adjusted\n            if (toolbarIsShown() || toolbarIsHidden()) {\n                // Toolbar is completely moved, so just keep its state\n                // and propagate it to other pages\n                propagateToolbarState(toolbarIsShown());\n            } else {\n                // Toolbar is moving but doesn't know which to move:\n                // you can change this to hideToolbar()\n                showToolbar();\n            }\n        }\n    }\n\n    private Fragment getCurrentFragment() {\n        return mPagerAdapter.getItemAt(mPager.getCurrentItem());\n    }\n\n    private void propagateToolbarState(boolean isShown) {\n        int toolbarHeight = mToolbarView.getHeight();\n\n        // Set scrollY for the fragments that are not created yet\n        mPagerAdapter.setScrollY(isShown ? 0 : toolbarHeight);\n\n        // Set scrollY for the active fragments\n        for (int i = 0; i < mPagerAdapter.getCount(); i++) {\n            // Skip current item\n            if (i == mPager.getCurrentItem()) {\n                continue;\n            }\n\n            // Skip destroyed or not created item\n            Fragment f = mPagerAdapter.getItemAt(i);\n            if (f == null) {\n                continue;\n            }\n\n            View view = f.getView();\n            if (view == null) {\n                continue;\n            }\n            ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll);\n            if (isShown) {\n                // Scroll up\n                if (0 < scrollView.getCurrentScrollY()) {\n                    scrollView.scrollTo(0, 0);\n                }\n            } else {\n                // Scroll down (to hide padding)\n                if (scrollView.getCurrentScrollY() < toolbarHeight) {\n                    scrollView.scrollTo(0, toolbarHeight);\n                }\n            }\n        }\n    }\n\n    protected NavigationAdapter newViewPagerAdapter() {\n        return new NavigationAdapter(getSupportFragmentManager());\n    }\n\n    private boolean toolbarIsShown() {\n        return ViewHelper.getTranslationY(mHeaderView) == 0;\n    }\n    private boolean toolbarIsHidden() {\n        return ViewHelper.getTranslationY(mHeaderView) == -mToolbarView.getHeight();\n    }\n\n    private void showToolbar() {\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        if (headerTranslationY != 0) {\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewPropertyAnimator.animate(mHeaderView).translationY(0).setDuration(200).start();\n        }\n        propagateToolbarState(true);\n    }\n\n    private void hideToolbar() {\n        float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);\n        int toolbarHeight = mToolbarView.getHeight();\n        if (headerTranslationY != -toolbarHeight) {\n            ViewPropertyAnimator.animate(mHeaderView).cancel();\n            ViewPropertyAnimator.animate(mHeaderView).translationY(-toolbarHeight).setDuration(200).start();\n        }\n        propagateToolbarState(false);\n    }\n\n    protected static class NavigationAdapter extends CacheFragmentStatePagerAdapter {\n\n        private static final String[] TITLES = new String[]{\"Applepie\", \"Butter Cookie\", \"Cupcake\", \"Donut\", \"Eclair\", \"Froyo\", \"Gingerbread\", \"Honeycomb\", \"Ice Cream Sandwich\", \"Jelly Bean\", \"KitKat\", \"Lollipop\"};\n\n        private int mScrollY;\n\n        public NavigationAdapter(FragmentManager fm) {\n            super(fm);\n        }\n\n        public void setScrollY(int scrollY) {\n            mScrollY = scrollY;\n        }\n\n        protected Fragment newFragment() {\n            return new ViewPagerTabScrollViewFragment();\n        }\n\n        @Override\n        protected Fragment createItem(int position) {\n            Fragment f = newFragment();\n            if (0 <= mScrollY) {\n                Bundle args = new Bundle();\n                args.putInt(ViewPagerTabScrollViewFragment.ARG_SCROLL_Y, mScrollY);\n                f.setArguments(args);\n            }\n            return f;\n        }\n\n        @Override\n        public int getCount() {\n            return TITLES.length;\n        }\n\n        @Override\n        public CharSequence getPageTitle(int position) {\n            return TITLES[position];\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\n\npublic class ViewPagerTabScrollViewFragment extends BaseFragment {\n\n    public static final String ARG_SCROLL_Y = \"ARG_SCROLL_Y\";\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_scrollview, container, false);\n\n        final ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll);\n        Activity parentActivity = getActivity();\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            // Scroll to the specified offset after layout\n            Bundle args = getArguments();\n            if (args != null && args.containsKey(ARG_SCROLL_Y)) {\n                final int scrollY = args.getInt(ARG_SCROLL_Y, 0);\n                ScrollUtils.addOnGlobalLayoutListener(scrollView, new Runnable() {\n                    @Override\n                    public void run() {\n                        scrollView.scrollTo(0, scrollY);\n                    }\n                });\n            }\n\n            // TouchInterceptionViewGroup should be a parent view other than ViewPager.\n            // This is a workaround for the issue #117:\n            // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117\n            scrollView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.root));\n\n            scrollView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);\n        }\n        return view;\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewWithFabActivity.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\n\n/**\n * This example shows how to handle scroll events on both the parent Activity and Fragments.\n * (Handling FAB is not the main purpose)\n *\n * SlidingTabLayout and SlidingTabStrip are from google/iosched:\n * https://github.com/google/iosched\n */\npublic class ViewPagerTabScrollViewWithFabActivity extends ViewPagerTabScrollViewActivity {\n\n    @Override\n    protected NavigationAdapter newViewPagerAdapter() {\n        return new NavigationAdapter(getSupportFragmentManager());\n    }\n\n    private static class NavigationAdapter extends ViewPagerTabScrollViewActivity.NavigationAdapter {\n        public NavigationAdapter(FragmentManager fm) {\n            super(fm);\n        }\n\n        @Override\n        protected Fragment newFragment() {\n            return new ViewPagerTabScrollViewWithFabFragment();\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewWithFabFragment.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.github.ksoichiro.android.observablescrollview.samples;\n\nimport android.app.Activity;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\n\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollView;\nimport com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;\nimport com.github.ksoichiro.android.observablescrollview.ScrollState;\nimport com.github.ksoichiro.android.observablescrollview.ScrollUtils;\nimport com.nineoldandroids.view.ViewHelper;\nimport com.nineoldandroids.view.ViewPropertyAnimator;\n\n/**\n * This example shows how to handle scroll events on both the parent Activity and Fragments.\n * (Handling FAB is not the main purpose)\n */\npublic class ViewPagerTabScrollViewWithFabFragment extends BaseFragment implements ObservableScrollViewCallbacks {\n\n    public static final String ARG_SCROLL_Y = \"ARG_SCROLL_Y\";\n    private View mFab;\n    private int mFabMargin;\n    private boolean mFabIsShown;\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        final View view = inflater.inflate(R.layout.fragment_scrollviewwithfab, container, false);\n        mFab = view.findViewById(R.id.fab);\n        mFabMargin = getResources().getDimensionPixelSize(R.dimen.margin_standard);\n        mFabIsShown = true;\n\n        // Translate FAB\n        ScrollUtils.addOnGlobalLayoutListener(mFab, new Runnable() {\n            @Override\n            public void run() {\n                float fabTranslationY = view.getHeight() - mFabMargin - mFab.getHeight();\n                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {\n                    // On pre-honeycomb, ViewHelper.setTranslationX/Y does not set margin,\n                    // which causes FAB's OnClickListener not working.\n                    FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mFab.getLayoutParams();\n                    lp.leftMargin = view.getWidth() - mFabMargin - mFab.getWidth();\n                    lp.topMargin = (int) fabTranslationY;\n                    mFab.requestLayout();\n                } else {\n                    ViewHelper.setTranslationX(mFab, view.getWidth() - mFabMargin - mFab.getWidth());\n                    ViewHelper.setTranslationY(mFab, fabTranslationY);\n                }\n            }\n        });\n\n        final ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll);\n        Activity parentActivity = getActivity();\n        if (parentActivity instanceof ObservableScrollViewCallbacks) {\n            // Scroll to the specified offset after layout\n            Bundle args = getArguments();\n            if (args != null && args.containsKey(ARG_SCROLL_Y)) {\n                final int scrollY = args.getInt(ARG_SCROLL_Y, 0);\n                ScrollUtils.addOnGlobalLayoutListener(scrollView, new Runnable() {\n                    @Override\n                    public void run() {\n                        scrollView.scrollTo(0, scrollY);\n                    }\n                });\n            }\n\n            // TouchInterceptionViewGroup should be a parent view other than ViewPager.\n            // This is a workaround for the issue #117:\n            // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117\n            scrollView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.root));\n        }\n        scrollView.setScrollViewCallbacks(this);\n\n        return view;\n    }\n\n    @Override\n    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {\n        if (getActivity() != null && getActivity() instanceof ObservableScrollViewCallbacks) {\n            ((ObservableScrollViewCallbacks) getActivity()).onScrollChanged(scrollY, firstScroll, dragging);\n        }\n    }\n\n    @Override\n    public void onDownMotionEvent() {\n        if (getActivity() != null && getActivity() instanceof ObservableScrollViewCallbacks) {\n            ((ObservableScrollViewCallbacks) getActivity()).onDownMotionEvent();\n        }\n    }\n\n    @Override\n    public void onUpOrCancelMotionEvent(ScrollState scrollState) {\n        if (getActivity() != null && getActivity() instanceof ObservableScrollViewCallbacks) {\n            ((ObservableScrollViewCallbacks) getActivity()).onUpOrCancelMotionEvent(scrollState);\n        }\n\n        if (scrollState == ScrollState.UP) {\n            hideFab();\n        } else if (scrollState == ScrollState.DOWN) {\n            showFab();\n        }\n    }\n\n    private void showFab() {\n        if (!mFabIsShown) {\n            ViewPropertyAnimator.animate(mFab).cancel();\n            ViewPropertyAnimator.animate(mFab).scaleX(1).scaleY(1).setDuration(200).start();\n            mFabIsShown = true;\n        }\n    }\n\n    private void hideFab() {\n        if (mFabIsShown) {\n            ViewPropertyAnimator.animate(mFab).cancel();\n            ViewPropertyAnimator.animate(mFab).scaleX(0).scaleY(0).setDuration(200).start();\n            mFabIsShown = false;\n        }\n    }\n}\n"
  },
  {
    "path": "samples/src/main/java/com/google/samples/apps/iosched/ui/widget/SlidingTabLayout.java",
    "content": "/*\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.samples.apps.iosched.ui.widget;\n\nimport android.content.Context;\nimport android.graphics.Typeface;\nimport android.support.v4.view.PagerAdapter;\nimport android.support.v4.view.ViewPager;\nimport android.util.AttributeSet;\nimport android.util.SparseArray;\nimport android.util.TypedValue;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.HorizontalScrollView;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\n/**\n * To be used with ViewPager to provide a tab indicator component which give constant feedback as to\n * the user's scroll progress.\n * <p>\n * To use the component, simply add it to your view hierarchy. Then in your\n * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call\n * {@link #setViewPager(android.support.v4.view.ViewPager)} providing it the ViewPager this layout is being used for.\n * <p>\n * The colors can be customized in two ways. The first and simplest is to provide an array of colors\n * via {@link #setSelectedIndicatorColors(int...)}. The\n * alternative is via the {@link com.google.samples.apps.iosched.ui.widget.SlidingTabLayout.TabColorizer} interface which provides you complete control over\n * which color is used for any individual position.\n * <p>\n * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},\n * providing the layout ID of your custom layout.\n */\npublic class SlidingTabLayout extends HorizontalScrollView {\n    /**\n     * Allows complete control over the colors drawn in the tab layout. Set with\n     * {@link #setCustomTabColorizer(com.google.samples.apps.iosched.ui.widget.SlidingTabLayout.TabColorizer)}.\n     */\n    public interface TabColorizer {\n\n        /**\n         * @return return the color of the indicator used when {@code position} is selected.\n         */\n        int getIndicatorColor(int position);\n\n    }\n\n    private static final int TITLE_OFFSET_DIPS = 24;\n    private static final int TAB_VIEW_PADDING_DIPS = 16;\n    private static final int TAB_VIEW_TEXT_SIZE_SP = 12;\n\n    private int mTitleOffset;\n\n    private int mTabViewLayoutId;\n    private int mTabViewTextViewId;\n    private boolean mDistributeEvenly;\n\n    private ViewPager mViewPager;\n    private SparseArray<String> mContentDescriptions = new SparseArray<String>();\n    private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;\n\n    private final SlidingTabStrip mTabStrip;\n\n    public SlidingTabLayout(Context context) {\n        this(context, null);\n    }\n\n    public SlidingTabLayout(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n\n        // Disable the Scroll Bar\n        setHorizontalScrollBarEnabled(false);\n        // Make sure that the Tab Strips fills this View\n        setFillViewport(true);\n\n        mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);\n\n        mTabStrip = new SlidingTabStrip(context);\n        addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);\n    }\n\n    /**\n     * Set the custom {@link com.google.samples.apps.iosched.ui.widget.SlidingTabLayout.TabColorizer} to be used.\n     *\n     * If you only require simple custmisation then you can use\n     * {@link #setSelectedIndicatorColors(int...)} to achieve\n     * similar effects.\n     */\n    public void setCustomTabColorizer(TabColorizer tabColorizer) {\n        mTabStrip.setCustomTabColorizer(tabColorizer);\n    }\n\n    public void setDistributeEvenly(boolean distributeEvenly) {\n        mDistributeEvenly = distributeEvenly;\n    }\n\n    /**\n     * Sets the colors to be used for indicating the selected tab. These colors are treated as a\n     * circular array. Providing one color will mean that all tabs are indicated with the same color.\n     */\n    public void setSelectedIndicatorColors(int... colors) {\n        mTabStrip.setSelectedIndicatorColors(colors);\n    }\n\n    /**\n     * Set the {@link android.support.v4.view.ViewPager.OnPageChangeListener}. When using {@link com.google.samples.apps.iosched.ui.widget.SlidingTabLayout} you are\n     * required to set any {@link android.support.v4.view.ViewPager.OnPageChangeListener} through this method. This is so\n     * that the layout can update it's scroll position correctly.\n     *\n     * @see android.support.v4.view.ViewPager#setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener)\n     */\n    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {\n        mViewPagerPageChangeListener = listener;\n    }\n\n    /**\n     * Set the custom layout to be inflated for the tab views.\n     *\n     * @param layoutResId Layout id to be inflated\n     * @param textViewId id of the {@link android.widget.TextView} in the inflated view\n     */\n    public void setCustomTabView(int layoutResId, int textViewId) {\n        mTabViewLayoutId = layoutResId;\n        mTabViewTextViewId = textViewId;\n    }\n\n    /**\n     * Sets the associated view pager. Note that the assumption here is that the pager content\n     * (number of tabs and tab titles) does not change after this call has been made.\n     */\n    public void setViewPager(ViewPager viewPager) {\n        mTabStrip.removeAllViews();\n\n        mViewPager = viewPager;\n        if (viewPager != null) {\n            viewPager.setOnPageChangeListener(new InternalViewPagerListener());\n            populateTabStrip();\n        }\n    }\n\n    /**\n     * Create a default view to be used for tabs. This is called if a custom tab view is not set via\n     * {@link #setCustomTabView(int, int)}.\n     */\n    protected TextView createDefaultTabView(Context context) {\n        TextView textView = new TextView(context);\n        textView.setGravity(Gravity.CENTER);\n        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);\n        textView.setTypeface(Typeface.DEFAULT_BOLD);\n        textView.setLayoutParams(new LinearLayout.LayoutParams(\n                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));\n\n        TypedValue outValue = new TypedValue();\n        getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,\n                outValue, true);\n        textView.setBackgroundResource(outValue.resourceId);\n        textView.setAllCaps(true);\n\n        int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);\n        textView.setPadding(padding, padding, padding, padding);\n\n        return textView;\n    }\n\n    private void populateTabStrip() {\n        final PagerAdapter adapter = mViewPager.getAdapter();\n        final OnClickListener tabClickListener = new TabClickListener();\n\n        for (int i = 0; i < adapter.getCount(); i++) {\n            View tabView = null;\n            TextView tabTitleView = null;\n\n            if (mTabViewLayoutId != 0) {\n                // If there is a custom tab view layout id set, try and inflate it\n                tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,\n                        false);\n                tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);\n            }\n\n            if (tabView == null) {\n                tabView = createDefaultTabView(getContext());\n            }\n\n            if (tabTitleView == null && TextView.class.isInstance(tabView)) {\n                tabTitleView = (TextView) tabView;\n            }\n\n            if (mDistributeEvenly) {\n                LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) tabView.getLayoutParams();\n                lp.width = 0;\n                lp.weight = 1;\n            }\n\n            tabTitleView.setText(adapter.getPageTitle(i));\n            tabView.setOnClickListener(tabClickListener);\n            String desc = mContentDescriptions.get(i, null);\n            if (desc != null) {\n                tabView.setContentDescription(desc);\n            }\n\n            mTabStrip.addView(tabView);\n            if (i == mViewPager.getCurrentItem()) {\n                tabView.setSelected(true);\n            }\n        }\n    }\n\n    public void setContentDescription(int i, String desc) {\n        mContentDescriptions.put(i, desc);\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n\n        if (mViewPager != null) {\n            scrollToTab(mViewPager.getCurrentItem(), 0);\n        }\n    }\n\n    private void scrollToTab(int tabIndex, int positionOffset) {\n        final int tabStripChildCount = mTabStrip.getChildCount();\n        if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {\n            return;\n        }\n\n        View selectedChild = mTabStrip.getChildAt(tabIndex);\n        if (selectedChild != null) {\n            int targetScrollX = selectedChild.getLeft() + positionOffset;\n\n            if (tabIndex > 0 || positionOffset > 0) {\n                // If we're not at the first child and are mid-scroll, make sure we obey the offset\n                targetScrollX -= mTitleOffset;\n            }\n\n            scrollTo(targetScrollX, 0);\n        }\n    }\n\n    private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {\n        private int mScrollState;\n\n        @Override\n        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {\n            int tabStripChildCount = mTabStrip.getChildCount();\n            if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {\n                return;\n            }\n\n            mTabStrip.onViewPagerPageChanged(position, positionOffset);\n\n            View selectedTitle = mTabStrip.getChildAt(position);\n            int extraOffset = (selectedTitle != null)\n                    ? (int) (positionOffset * selectedTitle.getWidth())\n                    : 0;\n            scrollToTab(position, extraOffset);\n\n            if (mViewPagerPageChangeListener != null) {\n                mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,\n                        positionOffsetPixels);\n            }\n        }\n\n        @Override\n        public void onPageScrollStateChanged(int state) {\n            mScrollState = state;\n\n            if (mViewPagerPageChangeListener != null) {\n                mViewPagerPageChangeListener.onPageScrollStateChanged(state);\n            }\n        }\n\n        @Override\n        public void onPageSelected(int position) {\n            if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {\n                mTabStrip.onViewPagerPageChanged(position, 0f);\n                scrollToTab(position, 0);\n            }\n            for (int i = 0; i < mTabStrip.getChildCount(); i++) {\n                mTabStrip.getChildAt(i).setSelected(position == i);\n            }\n            if (mViewPagerPageChangeListener != null) {\n                mViewPagerPageChangeListener.onPageSelected(position);\n            }\n        }\n\n    }\n\n    private class TabClickListener implements OnClickListener {\n        @Override\n        public void onClick(View v) {\n            for (int i = 0; i < mTabStrip.getChildCount(); i++) {\n                if (v == mTabStrip.getChildAt(i)) {\n                    mViewPager.setCurrentItem(i);\n                    return;\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "samples/src/main/java/com/google/samples/apps/iosched/ui/widget/SlidingTabStrip.java",
    "content": "/*\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.samples.apps.iosched.ui.widget;\n\nimport android.R;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.util.AttributeSet;\nimport android.util.TypedValue;\nimport android.view.View;\nimport android.widget.LinearLayout;\n\nclass SlidingTabStrip extends LinearLayout {\n\n    private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 0;\n    private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;\n    private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 3;\n    private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;\n\n    private final int mBottomBorderThickness;\n    private final Paint mBottomBorderPaint;\n\n    private final int mSelectedIndicatorThickness;\n    private final Paint mSelectedIndicatorPaint;\n\n    private final int mDefaultBottomBorderColor;\n\n    private int mSelectedPosition;\n    private float mSelectionOffset;\n\n    private SlidingTabLayout.TabColorizer mCustomTabColorizer;\n    private final SimpleTabColorizer mDefaultTabColorizer;\n\n    SlidingTabStrip(Context context) {\n        this(context, null);\n    }\n\n    SlidingTabStrip(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        setWillNotDraw(false);\n\n        final float density = getResources().getDisplayMetrics().density;\n\n        TypedValue outValue = new TypedValue();\n        context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);\n        final int themeForegroundColor =  outValue.data;\n\n        mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,\n                DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);\n\n        mDefaultTabColorizer = new SimpleTabColorizer();\n        mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);\n\n        mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);\n        mBottomBorderPaint = new Paint();\n        mBottomBorderPaint.setColor(mDefaultBottomBorderColor);\n\n        mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);\n        mSelectedIndicatorPaint = new Paint();\n    }\n\n    void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {\n        mCustomTabColorizer = customTabColorizer;\n        invalidate();\n    }\n\n    void setSelectedIndicatorColors(int... colors) {\n        // Make sure that the custom colorizer is removed\n        mCustomTabColorizer = null;\n        mDefaultTabColorizer.setIndicatorColors(colors);\n        invalidate();\n    }\n\n    void onViewPagerPageChanged(int position, float positionOffset) {\n        mSelectedPosition = position;\n        mSelectionOffset = positionOffset;\n        invalidate();\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        final int height = getHeight();\n        final int childCount = getChildCount();\n        final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null\n                ? mCustomTabColorizer\n                : mDefaultTabColorizer;\n\n        // Thick colored underline below the current selection\n        if (childCount > 0) {\n            View selectedTitle = getChildAt(mSelectedPosition);\n            int left = selectedTitle.getLeft();\n            int right = selectedTitle.getRight();\n            int color = tabColorizer.getIndicatorColor(mSelectedPosition);\n\n            if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {\n                int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);\n                if (color != nextColor) {\n                    color = blendColors(nextColor, color, mSelectionOffset);\n                }\n\n                // Draw the selection partway between the tabs\n                View nextTitle = getChildAt(mSelectedPosition + 1);\n                left = (int) (mSelectionOffset * nextTitle.getLeft() +\n                        (1.0f - mSelectionOffset) * left);\n                right = (int) (mSelectionOffset * nextTitle.getRight() +\n                        (1.0f - mSelectionOffset) * right);\n            }\n\n            mSelectedIndicatorPaint.setColor(color);\n\n            canvas.drawRect(left, height - mSelectedIndicatorThickness, right,\n                    height, mSelectedIndicatorPaint);\n        }\n\n        // Thin underline along the entire bottom edge\n        canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);\n    }\n\n    /**\n     * Set the alpha value of the {@code color} to be the given {@code alpha} value.\n     */\n    private static int setColorAlpha(int color, byte alpha) {\n        return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));\n    }\n\n    /**\n     * Blend {@code color1} and {@code color2} using the given ratio.\n     *\n     * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,\n     *              0.0 will return {@code color2}.\n     */\n    private static int blendColors(int color1, int color2, float ratio) {\n        final float inverseRation = 1f - ratio;\n        float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);\n        float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);\n        float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);\n        return Color.rgb((int) r, (int) g, (int) b);\n    }\n\n    private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {\n        private int[] mIndicatorColors;\n\n        @Override\n        public final int getIndicatorColor(int position) {\n            return mIndicatorColors[position % mIndicatorColors.length];\n        }\n\n        void setIndicatorColors(int... colors) {\n            mIndicatorColors = colors;\n        }\n    }\n}\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':library', ':samples'\n"
  },
  {
    "path": "website/.bowerrc",
    "content": "{\n  \"directory\": \"bower_components\"\n}\n"
  },
  {
    "path": "website/.gitignore",
    "content": "node_modules/\nbower_components/\npublic/lib/\npublic/docs/\nwww/\nrepo/\n*.log\n"
  },
  {
    "path": "website/bower.json",
    "content": "{\n  \"name\": \"Android-ObservableScrollView\",\n  \"description\": \"bower components for Android-ObservableScrollView\",\n  \"dependencies\": {\n    \"jquery\": \"1.11.2\",\n    \"bootstrap\": \"3.3.4\",\n    \"roboto-fontface\": \"0.4.0\",\n    \"highlightjs\": \"8.5.0\",\n    \"respond-minmax\": \"1.4.2\",\n    \"html5shiv\": \"3.7.2\",\n    \"octicons\": \"2.2.2\"\n  },\n  \"overrides\": {\n    \"jquery\": {\n      \"main\": \"dist/*.min.*\"\n    },\n    \"bootstrap\": {\n      \"main\": [\"dist/css/*.min.css\", \"dist/js/*.min.js\", \"dist/fonts/*\"]\n    },\n    \"roboto-fontface\": {\n      \"main\": [\"fonts/Roboto-Thin*\", \"fonts/Roboto-Light*\", \"fonts/Roboto-Regular*\"]\n    },\n    \"highlightjs\": {\n      \"main\": [\"*.js\"]\n    },\n    \"respond-minmax\": {\n      \"main\": \"dest/*.min.js\"\n    },\n    \"html5shiv\": {\n      \"main\": \"dist/*.min.js\"\n    },\n    \"octicons\": {\n      \"main\": [\"octicons/*.css\", \"octicons/*.svg\", \"octicons/*.woff\", \"octicons/*.ttf\", \"octicons/*.eot\"]\n    }\n  }\n}\n"
  },
  {
    "path": "website/gulpfile.js",
    "content": "var gulp = require('gulp');\nvar mainBowerFiles = require('main-bower-files');\nvar git = require('gulp-git');\nvar harp = require('harp');\nvar del = require('del');\nvar gutil = require('gulp-util');\n\nvar ghToken = process.env.GH_TOKEN;\n\n// You should replace this configs to your project's values.\nvar project = {\n    githubRepoOwner: 'ksoichiro',\n    name: 'Android-ObservableScrollView',\n};\n\nif (process.env.GH_TOKEN) {\n    // Travis CI\n    var gitBaseUrl = 'github.com/' + project.githubRepoOwner + '/' + project.name + '.git';\n    project['gitUrl'] = 'https://' + gitBaseUrl;\n    project['gitPushUrl'] = 'https://' + ghToken + '@' + gitBaseUrl;\n} else {\n    // Local\n    var gitBaseUrl = 'github.com:' + project.githubRepoOwner + '/' + project.name + '.git';\n    project['gitUrl'] = 'git@' + gitBaseUrl;\n    project['gitPushUrl'] = 'origin';\n}\n\nvar paths = {\n    bower: \"./bower_components\",\n    harp: {\n        project: \".\",\n        output: \"./www\"\n    },\n    docs: \"../docs\",\n    dest: {\n        root: \"./www\",\n        lib: \"./public/lib\",\n        docs: \"./public/docs\"\n    },\n    repo: \"repo\"\n};\n\nvar port = 9000;\n\ngulp.task('cleanDocs', function(cb) {\n    del([paths.dest.docs], cb);\n});\n\ngulp.task('cleanBowerFiles', function(cb) {\n    del([paths.dest.lib], cb);\n});\n\ngulp.task('clean', ['cleanDocs', 'cleanBowerFiles'], function(cb) {\n    del([paths.dest.root, paths.repo], cb);\n});\n\ngulp.task('build', ['copy'], function(cb) {\n    // This task is for production, so BASE_URL should be a project name.\n    // $BASE_URL is referenced in harp.json, and it will be replaced by harp (envy).\n    process.env.BASE_URL = '/' + project.name\n    harp.compile(paths.harp.project, paths.harp.output, function(err) {\n        if (err) {\n            gutil.log('build failed: ' + err);\n        }\n        gutil.log('Compile done');\n        cb();\n    });\n});\n\ngulp.task('copyDocs', ['cleanDocs'], function() {\n    return gulp.src(paths.docs + '/**/*')\n        .pipe(gulp.dest(paths.dest.docs));\n});\n\ngulp.task('copyBowerFiles', ['cleanBowerFiles'], function() {\n    // Return streams so that the dependent tasks can detect when it has done.\n    return gulp.src(mainBowerFiles(), { base: paths.bower })\n        .pipe(gulp.dest(paths.dest.lib));\n});\n\ngulp.task('copy', ['copyDocs', 'copyBowerFiles'], function() {\n});\n\ngulp.task('remove-repo', ['build'], function(cb) {\n    // Remove old directory, if exists.\n    gutil.log('removing repo..');\n    del([paths.repo], cb);\n});\n\ngulp.task('git-clone', ['remove-repo'], function(cb) {\n    // Clone to build directory, commit files to gh-pages branch, and push it.\n    git.clone(project.gitUrl, {args: paths.repo}, function(err) {\n        if (err) {\n            gutil.log('Failed to clone: ' + err);\n        } else {\n            gutil.log('clone done');\n        }\n        cb();\n    });\n});\n\ngulp.task('deploy', ['git-clone'], function(cb) {\n    gutil.log('check out gh-pages...');\n    git.checkout('origin/gh-pages', {args: '-b gh-pages', cwd: paths.repo}, function(err) {\n        if (err) {\n            gutil.log('Failed to check out branch: ' + err);\n            throw err;\n        } else {\n            gutil.log('copying files...');\n            gulp.src(paths.harp.output + '/**')\n                .pipe(gulp.dest(paths.repo))\n                .on('finish', function() {\n                    gutil.log('finished to copy');\n\n                    git.exec({args: 'status --porcelain', cwd: paths.repo}, function (err, stdout) {\n                        if (err) {\n                            gutil.log('Failed to check diff: ' + err);\n                            throw err;\n                        } else if (stdout) {\n                            // There are some changes for gh-pages\n                            gutil.log('There are some changes to commit.');\n                            gulp.src(paths.repo)\n                                .pipe(git.add({args: '-A', cwd: paths.repo}))\n                                .pipe(git.commit('Updated website.', {cwd: paths.repo}))\n                                .on('end', function() {\n                                    gutil.log('finish');\n                                    git.push(project.gitPushUrl, 'gh-pages', {args: '--quiet', /*quiet: true, */cwd: paths.repo}, function(err) {\n                                        if (err) {\n                                            gutil.log('Failed to push: ' + err);\n                                            throw err;\n                                        } else {\n                                            gutil.log('Pushed successfully.');\n                                        }\n                                        cb();\n                                    });\n                                });\n                        } else {\n                            gutil.log('Nothing to commit.');\n                            cb();\n                        }\n                    });\n                });\n        }\n    });\n});\n\ngulp.task('start', ['copy'], function() {\n    // This task is for development locally, so BASE_URL should be empty.\n    // $BASE_URL is referenced in harp.json, and it will be replaced by harp (envy).\n    process.env.BASE_URL = ''\n    harp.server(paths.harp.project, { port: port }, function(err) {\n        if (err) {\n            gutil.log('Failed to start');\n            gutil.log(err);\n        } else {\n            gulp.watch([paths.docs + '/**/*', '!' + paths.docs + '/**/*.sw*'], ['copyDocs']);\n            gulp.watch(paths.bower + '/**/*', ['copyBowerFiles']);\n            gutil.log('Started server: http://localhost:' + port);\n            gutil.log('Press Ctrl+C to quit');\n        }\n    });\n});\n"
  },
  {
    "path": "website/harp.json",
    "content": "{\n  \"globals\": {\n    \"baseUrl\": \"$BASE_URL\",\n    \"title\": \"Android-ObservableScrollView\",\n    \"name\": \"Android-ObservableScrollView\",\n    \"description\": \"Android library to observe scroll events on scrollable views.\",\n    \"githubUser\": \"ksoichiro\",\n    \"githubRepo\": \"Android-ObservableScrollView\",\n    \"githubUrl\": \"https://github.com/ksoichiro/Android-ObservableScrollView\",\n    \"ogType\": \"website\",\n    \"ogUrl\": \"http://ksoichiro.github.io/Android-ObservableScrollView\",\n    \"copyrightYear\": \"2014\",\n    \"copyrightHolder\": \"Soichiro Kashima\"\n  }\n}\n"
  },
  {
    "path": "website/package.json",
    "content": "{\n  \"name\": \"Android-ObservableScrollView\",\n  \"description\": \"Web site for Android-ObservableScrollView\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/ksoichiro/Android-ObservableScrollView.git\"\n  },\n  \"dependencies\": {\n  },\n  \"devDependencies\": {\n    \"bower\": \"1.3.12\",\n    \"gulp\": \"3.8.11\",\n    \"del\": \"1.1.1\",\n    \"harp\": \"0.17.0\",\n    \"main-bower-files\": \"2.7.0\",\n    \"gulp-git\": \"1.2.1\",\n    \"gulp-util\": \"3.0.4\"\n  },\n  \"scripts\": {\n    \"prepublish\": \"bower install --config.interactive=false\",\n    \"start\": \"gulp start\",\n    \"copy\": \"gulp copy\",\n    \"build\": \"gulp build\",\n    \"deploy\": \"gulp deploy\",\n    \"clean\": \"gulp clean\"\n  }\n}\n"
  },
  {
    "path": "website/public/404.ejs",
    "content": "<h1>404</h1>\n<h2>Page Not Found</h2>\n"
  },
  {
    "path": "website/public/_data.json",
    "content": "{\n  \"index\": {\n    \"title\": \"Android-ObservableScrollView\",\n    \"layout\": false\n  },\n  \"quick-start\": {\n    \"title\": \"Quick start\"\n  },\n  \"example\": {\n    \"title\": \"Try the example app\"\n  },\n  \"basic\": {\n    \"title\": \"Basic techniques\"\n  },\n  \"advanced\": {\n    \"title\": \"Advanced techniques\"\n  },\n  \"contributor\": {\n    \"title\": \"For contributors\"\n  },\n  \"reference\": {\n    \"title\": \"Reference\"\n  }\n}\n"
  },
  {
    "path": "website/public/_footer.ejs",
    "content": "<div class=\"footer\">\n    <div class=\"btns\">\n        <iframe class=\"ghstar\" src=\"https://ghbtns.com/github-btn.html?user=<%- githubUser %>&repo=<%- githubRepo %>&type=star&count=true\" frameborder=\"0\" scrolling=\"0\" ></iframe>\n        <iframe class=\"ghfork\" src=\"https://ghbtns.com/github-btn.html?user=<%- githubUser %>&repo=<%- githubRepo %>&type=fork&count=true\" frameborder=\"0\" scrolling=\"0\" ></iframe>\n    </div>\n    <div class=\"copyright\">\n        Android is a trademark of Google Inc.<br />\n        © Copyright <%- copyrightYear %>, <%- copyrightHolder %>.\n    </div>\n</div>\n\n<script src=\"<%- baseUrl %>/lib/jquery/dist/jquery.min.js\"></script>\n<script src=\"<%- baseUrl %>/lib/bootstrap/dist/js/bootstrap.min.js\"></script>\n<script src=\"<%- baseUrl %>/lib/highlightjs/highlight.pack.js\"></script>\n<script src=\"<%- baseUrl %>/js/main.js\"></script>\n<script>hljs.initHighlightingOnLoad();</script>\n"
  },
  {
    "path": "website/public/_head.ejs",
    "content": "<meta charset=\"UTF-8\" />\n<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n<meta http-equiv=\"Content-Language\" content=\"en\" />\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n<title><%- title %> | <%- title === name ? description : name %></title>\n<meta name=\"description\" content=\"<%- description %>\" />\n<meta name=\"robots\" content=\"index\" />\n<meta property=\"og:type\" content=\"<%- ogType %>\" />\n<meta property=\"og:site_name\" content=\"<%- name %> | <%- description %>\" />\n<meta property=\"og:title\" content=\"<%- title %> | <%- title === name ? description : name %>\" />\n<meta property=\"og:url\" content=\"<%- ogUrl %>\" />\n<meta property=\"og:description\" content=\"<%- description %>\" />\n<meta property=\"og:image\" content=\"<%- baseUrl %>/thumbnail.png\" />\n<meta property=\"og:locale\" content=\"en\" />\n<link rel=\"stylesheet\" type=\"text/css\" href=\"<%- baseUrl %>/lib/bootstrap/dist/css/bootstrap.min.css\" />\n<link rel=\"stylesheet\" type=\"text/css\" href=\"<%- baseUrl %>/lib/octicons/octicons/octicons.css\" />\n<link rel=\"stylesheet\" type=\"text/css\" href=\"<%- baseUrl %>/css/main.css\" />\n<!--[if lt IE 9]>\n<script src=\"<%- baseUrl %>/lib/html5shiv/dist/html5shiv.min.js\"></script>\n<script src=\"<%- baseUrl %>/lib/respond-minmax/dest/respond.min.js\"></script>\n<![endif]-->\n<link rel=\"apple-touch-icon\" sizes=\"57x57\" href=\"<%- baseUrl %>/apple-icon-57x57.png\" />\n<link rel=\"apple-touch-icon\" sizes=\"60x60\" href=\"<%- baseUrl %>/apple-icon-60x60.png\" />\n<link rel=\"apple-touch-icon\" sizes=\"72x72\" href=\"<%- baseUrl %>/apple-icon-72x72.png\" />\n<link rel=\"apple-touch-icon\" sizes=\"76x76\" href=\"<%- baseUrl %>/apple-icon-76x76.png\" />\n<link rel=\"apple-touch-icon\" sizes=\"114x114\" href=\"<%- baseUrl %>/apple-icon-114x114.png\" />\n<link rel=\"apple-touch-icon\" sizes=\"120x120\" href=\"<%- baseUrl %>/apple-icon-120x120.png\" />\n<link rel=\"apple-touch-icon\" sizes=\"144x144\" href=\"<%- baseUrl %>/apple-icon-144x144.png\" />\n<link rel=\"apple-touch-icon\" sizes=\"152x152\" href=\"<%- baseUrl %>/apple-icon-152x152.png\" />\n<link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"<%- baseUrl %>/apple-icon-180x180.png\" />\n<link rel=\"icon\" type=\"image/png\" sizes=\"192x192\"  href=\"<%- baseUrl %>/android-icon-192x192.png\" />\n<link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"<%- baseUrl %>/favicon-32x32.png\" />\n<link rel=\"icon\" type=\"image/png\" sizes=\"96x96\" href=\"<%- baseUrl %>/favicon-96x96.png\" />\n<link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"<%- baseUrl %>/favicon-16x16.png\" />\n<link rel=\"manifest\" href=\"<%- baseUrl %>/manifest.json\" />\n<meta name=\"msapplication-TileColor\" content=\"#ffffff\" />\n<meta name=\"msapplication-TileImage\" content=\"<%- baseUrl %>/ms-icon-144x144.png\" />\n<meta name=\"theme-color\" content=\"#ffffff\" />\n<meta name=\"base\" content=\"<%- baseUrl %>\" />\n<script>\n  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){\n  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),\n  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)\n  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');\n  ga('create', 'UA-62586232-1', 'auto');\n  ga('send', 'pageview');\n</script>\n"
  },
  {
    "path": "website/public/_layout.ejs",
    "content": "<!DOCTYPE html>\n<html prefix=\"og: http://ogp.me/ns#\">\n<head>\n<%- partial(\"_head\") %>\n</head>\n<body>\n\n<%- partial(\"_nav\") %>\n\n<div class=\"container\">\n<div id=\"content\">\n<div id=\"main-content\">\n<%- yield %>\n</div>\n</div>\n</div>\n\n<%- partial(\"_footer\") %>\n</body>\n</html>\n"
  },
  {
    "path": "website/public/_nav.ejs",
    "content": "<nav class=\"navbar navbar-inverse navbar-fixed-top\">\n  <div class=\"container\">\n    <div class=\"navbar-header\">\n      <button type=\"button\" class=\"navbar-toggle collapsed\" data-toggle=\"collapse\" data-target=\"#navbar\" aria-expanded=\"false\" aria-controls=\"navbar\">\n        <span class=\"sr-only\">Toggle navigation</span>\n        <span class=\"icon-bar\"></span>\n        <span class=\"icon-bar\"></span>\n        <span class=\"icon-bar\"></span>\n      </button>\n      <a class=\"navbar-brand\" href=\"<%- baseUrl %>/\"><%- name %></a>\n    </div>\n    <div id=\"navbar\" class=\"collapse navbar-collapse\">\n      <ul class=\"nav navbar-nav\">\n        <li><a href=\"<%- baseUrl %>/docs/overview\">Documentation</a></li>\n        <li><a href=\"<%- baseUrl %>/docs/faq\">FAQ</a></li>\n        <li><a href=\"<%- githubUrl %>\" title=\"GitHub\"><span class=\"octicon octicon-mark-github\"></span></a></li>\n      </ul>\n    </div>\n  </div>\n</nav>\n"
  },
  {
    "path": "website/public/browserconfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<browserconfig><msapplication><tile><square70x70logo src=\"/ms-icon-70x70.png\"/><square150x150logo src=\"/ms-icon-150x150.png\"/><square310x310logo src=\"/ms-icon-310x310.png\"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>"
  },
  {
    "path": "website/public/css/_code.less",
    "content": "// Based on androidstudio.css\n// made by Author: Pedro Oliveira <kanytu@gmail . com>\n\n@code-bg-color: #263238; // Blue Grey 900\npre {\n  border-width: 0px;\n  border-radius: 0px;\n  background: @code-bg-color;\n  @code-bg-shadow-alpha: 0.6;\n  -webkit-box-shadow: 0 4px 6px 2px rgba(24, 24, 24, @code-bg-shadow-alpha) inset;\n  -moz-box-shadow: 0 4px 6px 2px rgba(24, 24, 24, @code-bg-shadow-alpha) inset;\n  box-shadow: 0 4px 6px 2px rgba(24, 24, 24, @code-bg-shadow-alpha) inset;\n}\n.nohighlight,\n.hljs {\n  color: #A9B7C6;\n  background: @code-bg-color;\n  display: block;\n  overflow-x: auto;\n  padding: 12px;\n  webkit-text-size-adjust: none;\n}\n.hljs-number {\n  color: #6897BB;\n}\n\n.hljs-keyword, .hljs-deletion {\n  color: #CC7832;\n}\n.hljs-javadoc {\n  color: #629755;\n}\n.hljs-comment {\n  color: #808080;\n}\n.hljs-annotation {\n  color: #BBB529;\n}\n.hljs-string, .hljs-addition {\n  color: #6A8759;\n}\n.hljs-function .hljs-title, .hljs-change {\n  color: #FFC66D;\n}\n.hljs-tag .hljs-title, .hljs-doctype {\n  color: #E8BF6A;\n}\n.hljs-tag .hljs-attribute {\n  color: #BABABA;\n}\n.hljs-tag .hljs-value {\n  color: #A5C261;\n}\n"
  },
  {
    "path": "website/public/css/_colors.less",
    "content": "@theme-main-color: #009688; // 500\n@theme-focus-color: #00897B; // 600\n@theme-brand-color: #E0F2F1; // 50\n@theme-focus-border-color: #00695C; // 800\n@theme-active-color: #00695C; // 800\n.navbar-inverse {\n  background-color: @theme-main-color;\n  border-color: @theme-main-color;\n  .navbar-brand,\n  .navbar-nav>li>a {\n    color: @theme-brand-color;\n  }\n  .navbar-nav>.active>a,\n  .navbar-nav>.active>a:focus,\n  .navbar-nav>.active>a:hover {\n    background-color: @theme-active-color;\n  }\n  .navbar-nav>.open>a,\n  .navbar-nav>.open>a:focus,\n  .navbar-nav>.open>a:hover {\n    background-color: @theme-focus-color;\n  }\n  .navbar-toggle {\n    background-color: @theme-main-color;\n  }\n  .navbar-toggle {\n    border-color: @theme-focus-border-color;\n    &:hover,\n    &:focus {\n      background-color: @theme-focus-color;\n    }\n  }\n  .navbar-collapse {\n    border-color: @theme-focus-border-color;\n  }\n}\ncode {\n  color: @theme-main-color;\n  background-color: lighten(@theme-brand-color, 7%);\n}\na,\na:hover {\n  color: @theme-main-color;\n}\na:hover {\n  text-decoration: none;\n}\nh1, h2 {\n  color: @theme-main-color;\n}\n"
  },
  {
    "path": "website/public/css/_fonts.less",
    "content": "@import '_roboto-fonts.less';\n\nbody, h1, h2, h3, h4, h5, h6, p {\n  font-family: Roboto, \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-weight: 300;\n  color: #212121;\n}\nbody {\n  font-size: 14px;\n}\nh1 {\n  font-size: 28px;\n  font-weight: 400;\n}\nh2 {\n  font-size: 22px;\n  font-weight: 400;\n}\nh3 {\n  font-size: 20px;\n}\n#sidebar-main-content {\n  font-size: 16px;\n  h1 {\n    font-size: 32px;\n  }\n  h2 {\n    font-size: 28px;\n  }\n  h3 {\n    font-size: 22px;\n    font-weight: 400;\n  }\n  h4 {\n    font-size: 18px;\n    font-weight: 400;\n  }\n  h5 {\n    font-size: 16px;\n    font-weight: 400;\n  }\n}\n"
  },
  {
    "path": "website/public/css/_footer.less",
    "content": "@import (reference) '_mixins.less';\n\n// Footer\n.footer {\n  .container;\n  .override-container;\n  padding: 15px;\n  border-top: 1px solid lighten(#E0F2F1, 7%);\n  .btns {\n    text-align: right;\n    padding-bottom: 15px;\n    .ghstar {\n      width: 100px;\n      height: 20px;\n    }\n    .ghfork {\n      width: 100px;\n      height: 20px;\n    }\n  }\n  .copyright {\n    .text-muted;\n    .clearfix;\n    text-align: right;\n    font-size: 12px;\n    line-height: 150%;\n  }\n}\n"
  },
  {
    "path": "website/public/css/_layout.less",
    "content": "@import (reference) '_mixins.less';\n\nhtml,\nbody {\n  overflow-x: hidden;\n}\n\nbody {\n  padding-top: 70px;\n}\n\nh1 {\n  margin-top: 8px;\n}\n\n// Markdown fix\n// Apply .table to all <table>s\ntable {\n  .table;\n}\n\n.container {\n  .override-container;\n}\n\n#sidebar-main-content {\n  h1 {\n    margin-bottom: 16px;\n  }\n  h2 {\n    margin-top: 34px;\n    margin-bottom: 18px;\n  }\n  h3 {\n    margin-top: 26px;\n    margin-bottom: 16px;\n  }\n  h4 {\n    margin-top: 22px;\n    margin-bottom: 16px;\n  }\n  h5 {\n    margin-top: 20px;\n    margin-bottom: 16px;\n  }\n  p, ol, ul, pre {\n    margin-bottom: 16px;\n  }\n}\n"
  },
  {
    "path": "website/public/css/_misc.less",
    "content": "// Showing fragment links\n#sidebar-main-content {\n  h2, h3, h4, h5 {\n    position: relative;\n    .anchor {\n      visibility: hidden;\n      padding-left: 4px;\n    }\n    &:hover {\n      .anchor {\n        visibility: visible;\n      }\n    }\n  }\n  .marker {\n    position: absolute;\n    top: -70px;\n    left: 0;\n    padding: 0;\n    margin: 0;\n  }\n  h2 .anchor {\n    font-size: 20px;\n  }\n  h3 .anchor {\n    font-size: 16px;\n    color: #212121;\n  }\n  h4 .anchor {\n    font-size: 14px;\n    color: #212121;\n  }\n  h5 .anchor {\n    font-size: 12px;\n    color: #212121;\n  }\n  img {\n    -webkit-box-shadow: 0 2px 4px 2px rgba(0, 0, 0, 0.3);\n    -moz-box-shadow: 0 2px 4px 2px rgba(0, 0, 0, 0.3);\n    box-shadow: 0 2px 4px 2px rgba(0, 0, 0, 0.3);\n  }\n  a>img {\n    -webkit-box-shadow: none;\n    -moz-box-shadow: none;\n    box-shadow: none;\n  }\n}\n"
  },
  {
    "path": "website/public/css/_mixins.less",
    "content": "// Overwrite bootstrap's default\n.override-container(@width: 970px) {\n  @media (min-width: 1200px) {\n    & {\n      width: @width;\n    }\n  }\n}\n"
  },
  {
    "path": "website/public/css/_navbar.less",
    "content": ".navbar-shadow() {\n  @shadow-h-offset: 0px;\n  @shadow-v-offset: -3px;\n  @shadow-blur-radius: 6px;\n  @shadow-spread-radius: 6px;\n  @shadow-color: rgba(32, 32, 32, 0.4);\n  -moz-box-shadow:    @shadow-h-offset @shadow-v-offset @shadow-blur-radius @shadow-spread-radius @shadow-color;\n  -webkit-box-shadow: @shadow-h-offset @shadow-v-offset @shadow-blur-radius @shadow-spread-radius @shadow-color;\n  box-shadow:         @shadow-h-offset @shadow-v-offset @shadow-blur-radius @shadow-spread-radius @shadow-color;\n}\n.navbar-shadow-clear() {\n  -moz-box-shadow:    none;\n  -webkit-box-shadow: none;\n  box-shadow:         none;\n}\n\nnav.navbar {\n  .navbar-shadow();\n  .navbar-brand {\n    background-image: url('../images/logo.svg');\n    background-repeat: no-repeat;\n    background-position: 12px center;\n    padding-left: 64px;\n  }\n}\n\n@media screen and (max-width: 767px) {\n  nav.navbar {\n    .navbar-brand {\n      background-position: 4px center;\n      padding-left: 56px;\n      padding-right: 8px;\n    }\n  }\n}\n"
  },
  {
    "path": "website/public/css/_roboto-fonts.less",
    "content": "@roboto-font-path: '../lib/roboto-fontface/fonts';\n\n.roboto-font(@type, @weight, @style) {\n    @font-face {\n        font-family: 'Roboto';\n        src: url('@{roboto-font-path}/Roboto-@{type}.eot');\n        src: url('@{roboto-font-path}/Roboto-@{type}.eot?#iefix') format('embedded-opentype'),\n             url('@{roboto-font-path}/Roboto-@{type}.woff2') format('woff2'),\n             url('@{roboto-font-path}/Roboto-@{type}.woff') format('woff'),\n             url('@{roboto-font-path}/Roboto-@{type}.ttf') format('truetype'),\n             url('@{roboto-font-path}/Roboto-@{type}.svg#Roboto') format('svg');\n        font-weight: @weight;\n        font-style: @style;\n    }\n\n    @font-face {\n        font-family: 'Roboto-@{type}';\n        src: url('@{roboto-font-path}/Roboto-@{type}.eot');\n        src: url('@{roboto-font-path}/Roboto-@{type}.eot?#iefix') format('embedded-opentype'),\n             url('@{roboto-font-path}/Roboto-@{type}.woff2') format('woff2'),\n             url('@{roboto-font-path}/Roboto-@{type}.woff') format('woff'),\n             url('@{roboto-font-path}/Roboto-@{type}.ttf') format('truetype'),\n             url('@{roboto-font-path}/Roboto-@{type}.svg#Roboto') format('svg');\n    }\n}\n\n.roboto-font-pair(@type, @weight) {\n    .roboto-font('@{type}', @weight, normal);\n    .roboto-font('@{type}Italic', @weight, italic);\n}\n\n.roboto-font-pair('Thin', 100);\n.roboto-font-pair('Light', 300);\n.roboto-font-pair('Regular', 400);\n"
  },
  {
    "path": "website/public/css/_sidebar.less",
    "content": "@import (reference) '../../bower_components/bootstrap/less/bootstrap.less';\n\n// Page with sidebar\n.btn-sidebar-toggle {\n  .btn;\n  .btn-default;\n  .btn-xs;\n}\n#grid-content {\n  .make-row();\n  #sidebar {\n    .make-xs-column(6);\n    .make-sm-column(3);\n\n    ul {\n      padding-left: 0px;\n    }\n    &>ul {\n      border-right: 1px solid #E0E0E0;\n      padding-right: 15px;\n      li {\n        position: relative;\n        display: block;\n      }\n      &>li {\n        padding: 8px;\n      }\n      .section {\n        font-size: 14px;\n        font-weight: 400;\n        margin: 0px;\n        margin-bottom: 8px;\n        &,\n        &>a {\n          color: #009688;\n          text-decoration: none;\n        }\n      }\n      .topic {\n        &>a {\n          color: #212121;\n        }\n      }\n    }\n  }\n  #sidebar-main-content {\n    .make-xs-column(12);\n    .make-sm-column(9);\n  }\n}\n\n@media screen and (max-width: 767px) {\n  #grid-content {\n    position: relative;\n    -webkit-transition: all .25s ease-out;\n    -o-transition: all .25s ease-out;\n    transition: all .25s ease-out;\n    left: 0;\n\n    &.active {\n      left: 50%;\n    }\n\n    #sidebar {\n      left: -50%;\n      position: absolute;\n      top: 0;\n      width: 50%;\n    }\n  }\n}\n\n"
  },
  {
    "path": "website/public/css/_site-top.less",
    "content": "@import (reference) '_mixins.less';\n@import (reference) '_colors.less';\n\n@content-header-height: 300px;\nbody#site-top {\n  padding-top: 0px;\n  #content-header {\n    padding-top: 100px;\n    height: @content-header-height;\n    background-color: #ffffff;\n    #site-title {\n      font-weight: 100;\n      padding-top: 50px;\n      color: @theme-main-color;\n    }\n  }\n  #main-content {\n    padding-top: 30px;\n    h1 {\n      display: none;\n    }\n  }\n}\n\n@media screen and (min-width: 768px) {\n  body#site-top {\n    padding-top: 0px;\n    nav.navbar {\n      background: none;\n      border-color: transparent;\n      .navbar-shadow-clear();\n      &.sticky {\n        background-color: @theme-main-color;\n        .navbar-shadow();\n      }\n      .navbar-brand {\n        background-image: none;\n        background-position: left center;\n        padding-left: 0px;\n        visibility: hidden;\n        &.visible {\n          visibility: visible;\n          padding-left: 15px;\n        }\n      }\n    }\n    nav.navbar {\n      #navbar {\n        &.right {\n          @translate-amount: -250px;\n          -moz-transform:    translateX(@translate-amount);\n          -webkit-transform: translateX(@translate-amount);\n          -o-transform:      translateX(@translate-amount);\n          -ms-transform:     translateX(@translate-amount);\n        }\n      }\n    }\n\n    padding-top: 0px;\n    #content-header {\n      display: block;\n      padding-top: 100px;\n      height: @content-header-height;\n      background-color: @theme-main-color;\n      #site-title {\n        font-size: 48px;\n        color: #ffffff;\n        visibility: hidden;\n        &.visible {\n          visibility: visible;\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "website/public/css/main.less",
    "content": "@import (reference) '../../bower_components/bootstrap/less/bootstrap.less';\n@import (reference) '_mixins.less';\n@import '_fonts.less';\n@import '_colors.less';\n@import '_layout.less';\n@import '_navbar.less';\n@import '_footer.less';\n@import '_sidebar.less';\n@import '_site-top.less';\n@import '_code.less';\n@import '_misc.less';\n"
  },
  {
    "path": "website/public/index.ejs",
    "content": "<!DOCTYPE html>\n<html prefix=\"og: http://ogp.me/ns#\">\n<head>\n<%- partial(\"_head\") %>\n</head>\n<body id=\"site-top\">\n\n<nav class=\"navbar navbar-inverse navbar-fixed-top\">\n  <div class=\"container\">\n    <div class=\"navbar-header\">\n      <button type=\"button\" class=\"navbar-toggle collapsed\" data-toggle=\"collapse\" data-target=\"#navbar\" aria-expanded=\"false\" aria-controls=\"navbar\">\n        <span class=\"sr-only\">Toggle navigation</span>\n        <span class=\"icon-bar\"></span>\n        <span class=\"icon-bar\"></span>\n        <span class=\"icon-bar\"></span>\n      </button>\n      <a class=\"navbar-brand\" href=\"<%- baseUrl %>/\"><%- name %></a>\n    </div>\n    <div id=\"navbar\" class=\"collapse navbar-collapse right\">\n      <ul class=\"nav navbar-nav\">\n        <li><a href=\"<%- baseUrl %>/docs/overview\">Documentation</a></li>\n        <li><a href=\"<%- baseUrl %>/docs/faq\">FAQ</a></li>\n        <li><a href=\"<%- githubUrl %>\" title=\"GitHub\"><span class=\"octicon octicon-mark-github\"></span></a></li>\n      </ul>\n    </div>\n  </div>\n</nav>\n\n<div id=\"content-header\">\n<div class=\"container\">\n<h1 id=\"site-title\" class=\"visible\"><%- title %></h1>\n</div>\n</div>\n\n<div class=\"container\">\n<div id=\"content\">\n\n<div id=\"main-content\">\n<%- partial('../../README') %>\n</div>\n</div>\n</div>\n\n<%- partial(\"_footer\") %>\n</body>\n</html>\n"
  },
  {
    "path": "website/public/js/main.coffee",
    "content": "# Disable highlight for license quotation\n$('.language-license').addClass 'nohighlight'\n\n# Remove .md, except external links\n$(\"a[href$='.md']\").not(\"[href^='http']\").each ->\n  @.href = @.href.replace /\\.md$/, \"\"\n\n# Insert subdirectory for links\nbase = $(\"meta[name='base']\").attr('content')\nif base != \"\"\n  $(\"a[href$='.md']\").not(\"[href^='http']\").each ->\n    @.href = @.href.replace 'docs/', \"#{base}/docs/\"\n\n# Toggling sidebar\n$(document).ready ->\n  $('[data-toggle=\"offcanvas\"]').click ->\n    $('#grid-content').toggleClass('active')\n    if $('#grid-content').hasClass('active')\n      $('[data-toggle=\"offcanvas\"]').text 'Hide menu'\n    else\n      $('[data-toggle=\"offcanvas\"]').text 'Show menu'\n\nif $('#site-top')\n  $(window).scroll ->\n    if 70 < $(document).scrollTop()\n      $('.navbar-brand').addClass('visible')\n      $('#site-title').removeClass('visible')\n      $('#navbar').removeClass('right')\n    else\n      $('.navbar-brand').removeClass('visible')\n      $('#site-title').addClass('visible')\n      $('#navbar').addClass('right')\n\n    if 250 < $(document).scrollTop()\n      $('nav').addClass('sticky')\n    else\n      $('nav').removeClass('sticky')\n\n# Create fragment links\n$(document).ready ->\n  $('#sidebar-main-content h2, #sidebar-main-content h3, #sidebar-main-content h4, #sidebar-main-content h5').each ->\n    fragment = $(@).text().toLowerCase().replace(/[ _\\.\\/]/g, '-').replace(/--+/g, '-').replace(/([,':()\\?!]|-+ |-+$)/g, '')\n    $(@).html($(@).html() + '<a id=\"' + fragment + '\" class=\"marker\"></a><a href=\"#' + fragment + '\" class=\"anchor\">#</a>')\n"
  },
  {
    "path": "website/public/manifest.json",
    "content": "{\n \"name\": \"App\",\n \"icons\": [\n  {\n   \"src\": \"\\/android-icon-36x36.png\",\n   \"sizes\": \"36x36\",\n   \"type\": \"image\\/png\",\n   \"density\": \"0.75\"\n  },\n  {\n   \"src\": \"\\/android-icon-48x48.png\",\n   \"sizes\": \"48x48\",\n   \"type\": \"image\\/png\",\n   \"density\": \"1.0\"\n  },\n  {\n   \"src\": \"\\/android-icon-72x72.png\",\n   \"sizes\": \"72x72\",\n   \"type\": \"image\\/png\",\n   \"density\": \"1.5\"\n  },\n  {\n   \"src\": \"\\/android-icon-96x96.png\",\n   \"sizes\": \"96x96\",\n   \"type\": \"image\\/png\",\n   \"density\": \"2.0\"\n  },\n  {\n   \"src\": \"\\/android-icon-144x144.png\",\n   \"sizes\": \"144x144\",\n   \"type\": \"image\\/png\",\n   \"density\": \"3.0\"\n  },\n  {\n   \"src\": \"\\/android-icon-192x192.png\",\n   \"sizes\": \"192x192\",\n   \"type\": \"image\\/png\",\n   \"density\": \"4.0\"\n  }\n ]\n}"
  },
  {
    "path": "wercker.yml",
    "content": "box: wercker/android\n# Build definition\nbuild:\n  # The steps that will be executed on build\n  steps:\n    - script:\n        name: show base information\n        code: |\n          ./gradlew -v\n          echo $ANDROID_HOME\n          echo $ANDROID_SDK_VERSION\n          echo $ANDROID_BUILD_TOOLS\n          echo $ANDROID_UPDATE_FILTER\n          echo $ANDROID_NDK_HOME\n    - android-sdk-update:\n        filter: tools,platform-tools\n    - android-sdk-update:\n        filter: android-21,android-22,android-23,build-tools-23.0.2,extra-android-support,extra-android-m2repository\n    # A step that executes `gradle build` command\n    - script:\n        name: run gradle\n        code: |\n          ./gradlew --full-stacktrace -q --project-cache-dir=$WERCKER_CACHE_DIR assembleDebug\n  after-steps:\n    - script:\n        name: inspect build result\n        code: |\n          ls -la ./samples/build/outputs/apk\n          cp ./samples/build/outputs/apk/*.apk $WERCKER_REPORT_ARTIFACTS_DIR\n          rm -f $WERCKER_REPORT_ARTIFACTS_DIR/*-unaligned.apk\n"
  }
]