[
  {
    "path": ".gitignore",
    "content": "# built application files\n*.apk\n*.ap_\n\n# files for the dex VM\n*.dex\n\n# Java class files\n*.class\n\n# generated files\nbin/\ngen/\nbuild/\n\n# Gradle files\n.gradle/\nsubProjects/facebook/build\n\n# Buck files\nbuck-out/\n.buckd/\n\n# Intellij project files\n.idea/\n.idea/libraries\n.idea/.name\n.idea/compiler.xml\n.idea/gradle.xml\n.idea/modules.xml\n.idea/runConfigurations.xml\n.idea/vcs.xml\n.idea/workspace.xml\n.idea/misc.xml\ngen-external-apklibs/\n*.iml\n*.iws\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Mac-specific stuff\n.DS_Store\n\n#Maven\ntarget\nrelease.properties\npom.xml.*\n\n#Ant\nbuild.xml\nant.properties\nprofiles_settings.xml\n"
  },
  {
    "path": "LICENSE",
    "content": "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."
  },
  {
    "path": "README.md",
    "content": "Learning RxJava for Android by example\n==============\n\nThis is a repository with real-world useful examples of using RxJava with Android. [It usually will be in a constant state of \"Work in Progress\" (WIP)](https://kau.sh/blog/learning-rxjava-with-android-by-example/).\n\nI've also been giving talks about Learning Rx using many of the examples listed in this repo.\n\n* [Learning RxJava For Android by Example : Part 1](https://www.youtube.com/watch?v=k3D0cWyNno4) \\[[slides](https://speakerdeck.com/kaushikgopal/learning-rxjava-for-android-by-example)\\] (SF Android Meetup 2015)\n* [Learning Rx by Example : Part 2](https://vimeo.com/190922794) \\[[slides](https://speakerdeck.com/kaushikgopal/learning-rx-by-example-2)\\] (Øredev 2016)\n\n## Examples:\n\n1. [Background work & concurrency (using Schedulers)](#1-background-work--concurrency-using-schedulers)\n2. [Accumulate calls (using buffer)](#2-accumulate-calls-using-buffer)\n3. [Instant/Auto searching text listeners (using Subjects & debounce)](#3-instantauto-searching-text-listeners-using-subjects--debounce)\n4. [Networking with Retrofit & RxJava (using zip, flatmap)](#4-networking-with-retrofit--rxjava-using-zip-flatmap)\n5. [Two-way data binding for TextViews (using PublishSubject)](#5-two-way-data-binding-for-textviews-using-publishsubject)\n6. [Simple and Advanced polling (using interval and repeatWhen)](#6-simple-and-advanced-polling-using-interval-and-repeatwhen)\n7. [Simple and Advanced exponential backoff (using delay and retryWhen)](#7-simple-and-advanced-exponential-backoff-using-delay-and-retrywhen)\n8. [Form validation (using combineLatest)](#8-form-validation-using-combinelatest)\n9. [Pseudo caching : retrieve data first from a cache, then a network call (using concat, concatEager, merge or publish)](#9-pseudo-caching--retrieve-data-first-from-a-cache-then-a-network-call-using-concat-concateager-merge-or-publish)\n10. [Simple timing demos (using timer, interval or delay)](#10-simple-timing-demos-using-timer-interval-and-delay)\n11. [RxBus : event bus using RxJava (using RxRelay (never terminating Subjects) and debouncedBuffer)](#11-rxbus--event-bus-using-rxjava-using-rxrelay-never-terminating-subjects-and-debouncedbuffer)\n12. [Persist data on Activity rotations (using Subjects and retained Fragments)](#12-persist-data-on-activity-rotations-using-subjects-and-retained-fragments)\n13. [Networking with Volley](#13-networking-with-volley)\n14. [Pagination with Rx (using Subjects)](#14-pagination-with-rx-using-subjects)\n15. [Orchestrating Observable: make parallel network calls, then combine the result into a single data point (using flatmap & zip)](#15-orchestrating-observable-make-parallel-network-calls-then-combine-the-result-into-a-single-data-point-using-flatmap--zip)\n16. [Simple Timeout example (using timeout)](#16-simple-timeout-example-using-timeout)\n17. [Setup and teardown resources (using `using`)](#17-setup-and-teardown-resources-using-using)\n18. [Multicast playground](#18-multicast-playground)\n\n## Description\n\n### 1. Background work & concurrency (using Schedulers)\n\nA common requirement is to offload lengthy heavy I/O intensive operations to a background thread (non-UI thread) and feed the results back to the UI/main thread, on completion. This is a demo of how long-running operations can be offloaded to a background thread. After the operation is done, we resume back on the main thread. All using RxJava! Think of this as a replacement to AsyncTasks.\n\nThe long operation is simulated by a blocking Thread.sleep call (since this is done in a background thread, our UI is never interrupted).\n\nTo really see this example shine. Hit the button multiple times and see how the button click (which is a UI operation) is never blocked because the long operation only runs in the background.\n\n### 2. Accumulate calls (using buffer)\n\nThis is a demo of how events can be accumulated using the \"buffer\" operation.\n\nA button is provided and we accumulate the number of clicks on that button, over a span of time and then spit out the final results.\n\nIf you hit the button once, you'll get a message saying the button was hit once. If you hit it 5 times continuously within a span of 2 seconds, then you get a single log, saying you hit that button 5 times (vs 5 individual logs saying \"Button hit once\").\n\nNote:\n\nIf you're looking for a more foolproof solution that accumulates \"continuous\" taps vs just the number of taps within a time span, look at the [EventBus Demo](https://github.com/kaushikgopal/Android-RxJava/blob/master/app/src/main/java/com/morihacky/android/rxjava/rxbus/RxBusDemo_Bottom3Fragment.java) where a combo of the `publish` and `buffer` operators is used. For a more detailed explanation, you can also have a look at this [blog post](https://kau.sh/blog/debouncedbuffer-with-rxjava/).\n\n### 3. Instant/Auto searching text listeners (using Subjects & debounce)\n\nThis is a demo of how events can be swallowed in a way that only the last one is respected. A typical example of this is instant search result boxes. As you type the word \"Bruce Lee\", you don't want to execute searches for B, Br, Bru, Bruce, Bruce, Bruce L ... etc. But rather intelligently wait for a couple of moments, make sure the user has finished typing the whole word, and then shoot out a single call for \"Bruce Lee\".\n\nAs you type in the input box, it will not shoot out log messages at every single input character change, but rather only pick the lastly emitted event (i.e. input) and log that.\n\nThis is the debounce/throttleWithTimeout method in RxJava.\n\n### 4. Networking with Retrofit & RxJava (using zip, flatmap)\n\n[Retrofit from Square](http://square.github.io/retrofit/) is an amazing library that helps with easy networking (even if you haven't made the jump to RxJava just yet, you really should check it out). It works even better with RxJava and these are examples hitting the GitHub API, taken straight up from the android demigod-developer Jake Wharton's talk at Netflix. You can [watch the talk](https://www.youtube.com/watch?v=aEuNBk1b5OE#t=2480) at this link. Incidentally, my motivation to use RxJava was from attending this talk at Netflix.\n\n(Note: you're most likely to hit the GitHub API quota pretty fast so send in an OAuth-token as a parameter if you want to keep running these examples often).\n\n### 5. Two-way data binding for TextViews (using PublishSubject)\n\nAuto-updating views are a pretty cool thing. If you've dealt with Angular JS before, they have a pretty nifty concept called \"two-way data binding\", so when an HTML element is bound to a model/entity object, it constantly \"listens\" to changes on that entity and auto-updates its state based on the model. Using the technique in this example, you could potentially use a pattern like the [Presentation View Model pattern](http://martinfowler.com/eaaDev/PresentationModel.html) with great ease.\n\nWhile the example here is pretty rudimentary, the technique used to achieve the double binding using a `Publish Subject` is much more interesting.\n\n### 6. Simple and Advanced polling (using interval and repeatWhen)\n\nThis is an example of polling using RxJava Schedulers. This is useful in cases, where you want to constantly poll a server and possibly get new data. The network call is \"simulated\" so it forces a delay before return a resultant string.\n\nThere are two variants for this:\n\n1. Simple Polling: say when you want to execute a certain task every 5 seconds\n2. Increasing Delayed Polling: say when you want to execute a task first in 1 second, then in 2 seconds, then 3 and so on.\n\nThe second example is basically a variant of [Exponential Backoff](https://github.com/kaushikgopal/RxJava-Android-Samples#exponential-backoff).\n\nInstead of using a RetryWithDelay, we use a RepeatWithDelay here. To understand the difference between Retry(When) and Repeat(When) I wouuld suggest Dan's [fantastic post on the subject](http://blog.danlew.net/2016/01/25/rxjavas-repeatwhen-and-retrywhen-explained/).\n\nAn alternative approach to delayed polling without the use of `repeatWhen` would be using chained nested delay observables. See [startExecutingWithExponentialBackoffDelay in the ExponentialBackOffFragment example](https://github.com/kaushikgopal/RxJava-Android-Samples/blob/master/app/src/main/java/com/morihacky/android/rxjava/fragments/ExponentialBackoffFragment.java#L111).\n\n### 7. Simple and Advanced exponential backoff (using delay and retryWhen)\n\n[Exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff) is a strategy where based on feedback from a certain output, we alter the rate of a process (usually reducing the number of retries or increasing the wait time before retrying or re-executing a certain process).\n\nThe concept makes more sense with examples. RxJava makes it (relatively) simple to implement such a strategy. My thanks to [Mike](https://twitter.com/m_evans10) for suggesting the idea.\n\n#### Retry (if error) with exponential backoff\n\nSay you have a network failure. A sensible strategy would be to NOT keep retrying your network call every 1 second. It would be smart instead (nay... elegant!) to retry with increasing delays. So you try at second 1 to execute the network call, no dice? try after 10 seconds... negatory? try after 20 seconds, no cookie? try after 1 minute. If this thing is still failing, you got to give up on the network yo!\n\nWe simulate this behaviour using RxJava with the [`retryWhen` operator](http://reactivex.io/documentation/operators/retry.html).\n\n`RetryWithDelay` code snippet courtesy:\n\n* http://stackoverflow.com/a/25292833/159825\n* Another excellent implementation via @[sddamico](https://github.com/sddamico) : https://gist.github.com/sddamico/c45d7cdabc41e663bea1\n* This one includes support for jittering, by @[leandrofavarin](https://github.com/leandrofavarin) : http://leandrofavarin.com/exponential-backoff-rxjava-operator-with-jitter\n\nAlso look at the [Polling example](https://github.com/kaushikgopal/RxJava-Android-Samples#polling-with-schedulers) where we use a very similar Exponential backoff mechanism.\n\n#### \"Repeat\" with exponential backoff\n\nAnother variant of the exponential backoff strategy is to execute an operation for a given number of times but with delayed intervals. So you execute a certain operation 1 second from now, then you execute it again 10 seconds from now, then you execute the operation 20 seconds from now. After a grand total of 3 times you stop executing.\n\nSimulating this behavior is actually way more simpler than the prevoius retry mechanism. You can use a variant of the `delay` operator to achieve this.\n\n\n### 8. Form validation (using [`.combineLatest`](http://reactivex.io/documentation/operators/combinelatest.html))\n\nThanks to Dan Lew for giving me this idea in the [fragmented podcast - episode #4](http://fragmentedpodcast.com/episodes/4/) (around the 4:30 mark).\n\n`.combineLatest` allows you to monitor the state of multiple observables at once compactly at a single location. The example demonstrated shows how you can use `.combineLatest` to validate a basic form. There are 3 primary inputs for this form to be considered \"valid\" (an email, a password and a number). The form will turn valid (the text below turns blue :P) once all the inputs are valid. If they are not, an error is shown against the invalid inputs.\n\nWe have 3 independent observables that track the text/input changes for each of the form fields (RxAndroid's `WidgetObservable` comes in handy to monitor the text changes). After an event change is noticed from **all** 3 inputs, the result is \"combined\" and the form is evaluated for validity.\n\nNote that the `Func3` function that checks for validity, kicks in only after ALL 3 inputs have received a text change event.\n\nThe value of this technique becomes more apparent when you have more number of input fields in a form. Handling it otherwise with a bunch of booleans makes the code cluttered and kind of difficult to follow. But using `.combineLatest` all that logic is concentrated in a nice compact block of code (I still use booleans but that was to make the example more readable).\n\n\n### 9. Pseudo caching : retrieve data first from a cache, then a network call (using concat, concatEager, merge or publish)\n\nWe have two source Observables: a disk (fast) cache and a network (fresh) call. Typically the disk Observable is much faster than the network Observable. But in order to demonstrate the working, we've also used a fake \"slower\" disk cache just to see how the operators behave.\n\nThis is demonstrated using 4 techniques:\n\n1. [`.concat`](http://reactivex.io/documentation/operators/concat.html)\n2. [`.concatEager`](http://reactivex.io/RxJava/javadoc/rx/Observable.html#concatEager(java.lang.Iterable))\n3. [`.merge`](http://reactivex.io/documentation/operators/merge.html)\n4. [`.publish`](http://reactivex.io/RxJava/javadoc/rx/Observable.html#publish(rx.functions.Func1)) selector + merge + takeUntil\n\nThe 4th technique is probably what you want to use eventually but it's interesting to go through the progression of techniques, to understand why.\n\n`concat` is great. It retrieves information from the first Observable (disk cache in our case) and then the subsequent network Observable. Since the disk cache is presumably faster, all appears well and the disk cache is loaded up fast, and once the network call finishes we swap out the \"fresh\" results.\n\nThe problem with `concat` is that the subsequent observable doesn't even start until the first Observable completes. That can be a problem. We want all observables to start simultaneously but produce the results in a way we expect. Thankfully RxJava introduced `concatEager` which does exactly that. It starts both observables but buffers the result from the latter one until the former Observable finishes. This is a completely viable option.\n\nSometimes though, you just want to start showing the results immediately. Assuming the first observable (for some strange reason) takes really long to run through all its items, even if the first few items from the second observable have come down the wire it will forcibly be queued. You don't necessarily want to \"wait\" on any Observable. In these situations, we could use the `merge` operator. It interleaves items as they are emitted. This works great and starts to spit out the results as soon as they're shown.\n\nSimilar to the `concat` operator, if your first Observable is always faster than the second Observable you won't run into any problems. However the problem with `merge` is: if for some strange reason an item is emitted by the cache or slower observable *after* the newer/fresher observable, it will overwrite the newer content. Click the \"MERGE (SLOWER DISK)\" button in the example to see this problem in action. @JakeWharton and @swankjesse contributions go to 0! In the real world this could be bad, as it would mean the fresh data would get overridden by stale disk data.\n\nTo solve this problem you can use merge in combination with the super nifty `publish` operator which takes in a \"selector\". I wrote about this usage in a [blog post](https://kau.sh/blog/rxjava-tip-for-the-day-share-publish-refcount-and-all-that-jazz/) but I have [Jedi JW](https://twitter.com/JakeWharton/status/786363146990649345) to thank for reminding of this technique. We `publish` the network observable and provide it a selector which starts emitting from the disk cache, up until the point that the network observable starts emitting. Once the network observable starts emitting, it ignores all results from the disk observable. This is perfect and handles any problems we might have.\n\nPreviously, I was using the `merge` operator but overcoming the problem of results being overwritten by monitoring the \"resultAge\". See the old `PseudoCacheMergeFragment` example if you're curious to see this old implementation.\n\n### 10. Simple timing demos (using timer, interval and delay)\n\nThis is a super simple and straightforward example which shows you how to use RxJava's `timer`, `interval` and `delay` operators to handle a bunch of cases where you want to run a task at specific intervals. Basically say NO to Android `TimerTask`s.\n\nCases demonstrated here:\n\n1. run a single task after a delay of 2s, then complete\n2. run a task constantly every 1s (there's a delay of 1s before the first task fires off)\n3. run a task constantly every 1s (same as above but there's no delay before the first task fires off)\n4. run a task constantly every 3s, but after running it 5 times, terminate automatically\n5. run a task A, pause for sometime, then execute Task B, then terminate\n\n### 11. RxBus : event bus using RxJava (using RxRelay (never terminating Subjects) and debouncedBuffer)\n\nThere are accompanying blog posts that do a much better job of explaining the details on this demo:\n\n1. [Implementing an event bus with RxJava](https://kau.sh/blog/implementing-an-event-bus-with-rxjava-rxbus/)\n2. [DebouncedBuffer used for the fancier variant of the demo](https://kau.sh/blog/debouncedbuffer-with-rxjava/)\n3. [share/publish/refcount](https://kau.sh/blog/rxjava-tip-for-the-day-share-publish-refcount-and-all-that-jazz/)\n\n### 12. Persist data on Activity rotations (using Subjects and retained Fragments)\n\nA common question that's asked when using RxJava in Android is, \"how do i resume the work of an observable if a configuration change occurs (activity rotation, language locale change etc.)?\".\n\nThis example shows you one strategy viz. using retained Fragments. I started using retained fragments as \"worker fragments\" after reading this [fantastic post by Alex Lockwood](http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html) quite sometime back.\n\nHit the start button and rotate the screen to your heart's content; you'll see the observable continue from where it left off.\n\n*There are certain quirks about the \"hotness\" of the source observable used in this example. Check [my blog post](https://kau.sh/blog/a-note-about-the-warmth-share-operator/) out where I explain the specifics.*\n\nI have since rewritten this example using an alternative approach. While the [`ConnectedObservable` approach worked](https://github.com/kaushikgopal/RxJava-Android-Samples/blob/master/app/src/main/java/com/morihacky/android/rxjava/fragments/RotationPersist1WorkerFragment.java#L20) it enters the lands of \"multicasting\" which can be tricky (thread-safety, .refcount etc.). Subjects on the other hand are far more simple.  You can see it rewritten [using a `Subject` here](https://github.com/kaushikgopal/RxJava-Android-Samples/blob/master/app/src/main/java/com/morihacky/android/rxjava/fragments/RotationPersist2WorkerFragment.java#L22).\n\nI wrote [another blog post](https://tech.instacart.com/how-to-think-about-subjects-part-1/) on how to think about Subjects where I go into some specifics.\n\n\n### 13. Networking with Volley\n\n[Volley](http://developer.android.com/training/volley/index.html) is another networking library introduced by [Google at IO '13](https://www.youtube.com/watch?v=yhv8l9F44qo). A kind citizen of github contributed this example so we know how to integrate Volley with RxJava.\n\n### 14. Pagination with Rx (using Subjects)\n\nI leverage the simple use of a Subject here. Honestly, if you don't have your items coming down via an `Observable` already (like through Retrofit or a network request), there's no good reason to use Rx and complicate things.\n\nThis example basically sends the page number to a Subject, and the subject handles adding the items. Notice the use of `concatMap` and the return of an `Observable<List>` from `_itemsFromNetworkCall`.\n\nFor kicks, I've also included a `PaginationAutoFragment` example, this \"auto-paginates\" without us requiring to hit a button. It should be simple to follow if you got how the previous example works.\n\nHere are some other fancy implementations (while i enjoyed reading them, i didn't land up using them for my real world app cause personally i don't think it's necessary):\n\n* [Matthias example of an Rx based pager](https://gist.github.com/mttkay/24881a0ce986f6ec4b4d)\n* [Eugene's very comprehensive Pagination sample](https://github.com/matzuk/PaginationSample)\n* [Recursive Paging example](http://stackoverflow.com/questions/28047272/handle-paging-with-rxjava)\n\n### 15. Orchestrating Observable: make parallel network calls, then combine the result into a single data point (using flatmap & zip)\n\nThe below ascii diagram expresses the intention of our next example with panache. f1,f2,f3,f4,f5 are essentially network calls that when made, give back a result that's needed for a future calculation.\n\n\n             (flatmap)\n    f1 ___________________ f3 _______\n             (flatmap)               |    (zip)\n    f2 ___________________ f4 _______| ___________  final output\n            \\                        |\n             \\____________ f5 _______|\n\nThe code for this example has already been written by one Mr.skehlet in the interwebs. Head over to [the gist](https://gist.github.com/skehlet/9418379) for the code. It's written in pure Java (6) so it's pretty comprehensible if you've understood the previous examples. I'll flush it out here again when time permits or I've run out of other compelling examples.\n\n### 16. Simple Timeout example (using timeout)\n\nThis is a simple example demonstrating the use of the `.timeout` operator. Button 1 will complete the task before the timeout constraint, while Button 2 will force a timeout error.\n\nNotice how we can provide a custom Observable that indicates how to react under a timeout Exception.\n\n### 17. Setup and teardown resources (using `using`)\n\nThe [operator `using`](http://reactivex.io/documentation/operators/using.html) is relatively less known and notoriously hard to Google. It's a beautiful API that helps to setup a (costly) resource, use it and then dispose off in a clean way.\n\nThe nice thing about this operator is that it provides a mechansim to use potentially costly resources in a tightly scoped manner. using -> setup, use and dispose. Think DB connections (like Realm instances), socket connections, thread locks etc.\n\n### 18. Multicast Playground\n\nMulticasting in Rx is like a dark art. Not too many folks know how to pull it off without concern. This example condiers two subscribers (in the forms of buttons) and allows you to add/remove subscribers at different points of time and see how the different operators behave under those circumstances.\n\nThe source observale is a timer (`interval`) observable and the reason this was chosen was to intentionally pick a non-terminating observable, so you can test/confirm if your multicast experiment will leak.\n\n_I also gave a talk about [Multicasting in detail at 360|Andev](https://speakerdeck.com/kaushikgopal/rx-by-example-volume-3-the-multicast-edition). If you have the inclination and time, I highly suggest watching that talk first (specifically the Multicast operator permutation segment) and then messing around with the example here._\n\n## Rx 2.x\n\nAll the examples here have been migrated to use RxJava 2.X.\n\n* Have a look at [PR #83 to see the diff of changes between RxJava 1 and 2](https://github.com/kaushikgopal/RxJava-Android-Samples/pull/83/files)\n* [What's different in Rx 2.x](https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0)\n\nWe use [David Karnok's Interop library](https://github.com/akarnokd/RxJava2Interop) in some cases as certain libraries like RxBindings, RxRelays, RxJava-Math etc. have not been ported yet to 2.x.\n\n## Contributing:\n\nI try to ensure the examples are not overly contrived but reflect a real-world usecase. If you have similar useful examples demonstrating the use of RxJava, feel free to send in a pull request.\n\nI'm wrapping my head around RxJava too so if you feel there's a better way of doing one of the examples mentioned above, open up an issue explaining how. Even better, send a pull request.\n\n\n## Sponsorship (Memory Management/Profiling)\n\nRx threading is messy business. To help, this project uses YourKit tools for analysis.\n\n![Yourkit](https://www.yourkit.com/images/yklogo.png)\n\n\nYourKit supports open source projects with innovative and intelligent tools\nfor monitoring and profiling Java applications. YourKit is the creator of <a href=\"https://www.yourkit.com/java/profiler/\">YourKit Java Profiler</a>.\n\n\n## License\n\nLicensed under the Apache License, Version 2.0 (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\nYou agree that all contributions to this repository, in the form of fixes, pull-requests, new examples etc. follow the above-mentioned license.\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "buildscript {\n    repositories {\n//        mavenCentral()\n        jcenter()\n    }\n    dependencies {\n        classpath 'me.tatarka:gradle-retrolambda:3.6.0'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}\"\n    }\n\n    // Exclude the lombok version that the android plugin depends on.\n    configurations.classpath.exclude group: 'com.android.tools.external.lombok'\n}\n\napply plugin: 'com.android.application'\napply plugin: 'me.tatarka.retrolambda'\napply plugin: 'com.f2prateek.javafmt'\napply plugin: 'kotlin-android'\n\ndependencies {\n    compile 'com.android.support:multidex:1.0.1'\n    compile \"com.android.support:support-v13:${supportLibVersion}\"\n    compile \"com.android.support:appcompat-v7:${supportLibVersion}\"\n    compile \"com.android.support:recyclerview-v7:${supportLibVersion}\"\n\n    compile 'com.github.kaushikgopal:CoreTextUtils:c703fa12b6'\n    compile \"com.jakewharton:butterknife:${butterKnifeVersion}\"\n    kapt \"com.jakewharton:butterknife-compiler:${butterKnifeVersion}\"\n    compile 'com.jakewharton.timber:timber:4.5.1'\n    compile \"com.squareup.retrofit2:retrofit:${retrofitVersion}\"\n    compile \"com.squareup.retrofit2:converter-gson:${retrofitVersion}\"\n    compile \"com.squareup.okhttp3:okhttp:${okhttpVersion}\"\n    compile \"com.squareup.okhttp3:okhttp-urlconnection:${okhttpVersion}\"\n    compile 'com.mcxiaoke.volley:library:1.0.19'\n\n    compile \"org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}\"\n    compile \"com.nhaarman:mockito-kotlin:${mockitoKotlinVersion}\"\n\n    compile \"android.arch.lifecycle:runtime:${archComponentsVersion}\"\n    compile \"android.arch.lifecycle:extensions:${archComponentsVersion}\"\n    kapt  \"android.arch.lifecycle:compiler:${archComponentsVersion}\"\n\n    // ----------------------------------\n    //  Rx dependencies\n\n    compile 'io.reactivex.rxjava2:rxjava:2.0.7'\n\n    // Because RxAndroid releases are few and far between, it is recommended you also\n    // explicitly depend on RxJava's latest version for bug fixes and new features.\n    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'\n\n    compile 'com.jakewharton.rx:replaying-share-kotlin:2.0.0'\n    compile \"com.github.akarnokd:rxjava2-extensions:0.16.0\"\n    compile 'com.jakewharton.rxrelay2:rxrelay:2.0.0'\n\n    compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0'\n    compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'\n\n    // ----------------------------------\n\n    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'\n    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'\n}\n\nandroid {\n    compileSdkVersion sdkVersion\n    buildToolsVersion buildToolsVrs\n\n    defaultConfig {\n        applicationId \"com.morihacky.android.rxjava\"\n        minSdkVersion 15\n        targetSdkVersion sdkVersion\n        versionCode 2\n        versionName \"1.2\"\n        multiDexEnabled true\n    }\n    buildTypes {\n        release {\n            minifyEnabled true\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    sourceSets {\n        main.java.srcDirs += 'src/main/kotlin'\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    packagingOptions {\n        pickFirst  'META-INF/rxjava.properties'\n    }\n}"
  },
  {
    "path": "app/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/Android Studio.app/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": "app/src/androidTest/java/com/morihacky/android/rxjava/app/ApplicationTest.java",
    "content": "package com.morihacky.android.rxjava.app;\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": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          package=\"com.morihacky.android.rxjava\"\n    >\n\n    <application\n        android:name=\".MyApp\"\n        android:allowBackup=\"true\"\n        android:icon=\"@drawable/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\"\n        >\n        <activity\n            android:name=\"com.morihacky.android.rxjava.MainActivity\"\n            android:label=\"@string/app_name\"\n            >\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    </application>\n\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>\n\n</manifest>\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/MainActivity.java",
    "content": "package com.morihacky.android.rxjava;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.AppCompatActivity;\nimport com.morihacky.android.rxjava.fragments.MainFragment;\nimport com.morihacky.android.rxjava.fragments.RotationPersist1WorkerFragment;\nimport com.morihacky.android.rxjava.fragments.RotationPersist2WorkerFragment;\nimport com.morihacky.android.rxjava.rxbus.RxBus;\n\npublic class MainActivity extends AppCompatActivity {\n\n  private RxBus _rxBus = null;\n\n  @Override\n  public void onBackPressed() {\n    super.onBackPressed();\n    _removeWorkerFragments();\n  }\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n\n    if (savedInstanceState == null) {\n      getSupportFragmentManager()\n          .beginTransaction()\n          .replace(android.R.id.content, new MainFragment(), this.toString())\n          .commit();\n    }\n  }\n\n  // This is better done with a DI Library like Dagger\n  public RxBus getRxBusSingleton() {\n    if (_rxBus == null) {\n      _rxBus = new RxBus();\n    }\n\n    return _rxBus;\n  }\n\n  private void _removeWorkerFragments() {\n    Fragment frag =\n        getSupportFragmentManager()\n            .findFragmentByTag(RotationPersist1WorkerFragment.class.getName());\n\n    if (frag != null) {\n      getSupportFragmentManager().beginTransaction().remove(frag).commit();\n    }\n\n    frag =\n        getSupportFragmentManager()\n            .findFragmentByTag(RotationPersist2WorkerFragment.class.getName());\n\n    if (frag != null) {\n      getSupportFragmentManager().beginTransaction().remove(frag).commit();\n    }\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/MyApp.java",
    "content": "package com.morihacky.android.rxjava;\n\nimport android.support.multidex.MultiDexApplication;\nimport com.morihacky.android.rxjava.volley.MyVolley;\nimport com.squareup.leakcanary.LeakCanary;\nimport com.squareup.leakcanary.RefWatcher;\nimport timber.log.Timber;\n\npublic class MyApp extends MultiDexApplication {\n\n  private static MyApp _instance;\n  private RefWatcher _refWatcher;\n\n  public static MyApp get() {\n    return _instance;\n  }\n\n  public static RefWatcher getRefWatcher() {\n    return MyApp.get()._refWatcher;\n  }\n\n  @Override\n  public void onCreate() {\n    super.onCreate();\n\n    if (LeakCanary.isInAnalyzerProcess(this)) {\n      // This process is dedicated to LeakCanary for heap analysis.\n      // You should not init your app in this process.\n      return;\n    }\n\n    _instance = (MyApp) getApplicationContext();\n    _refWatcher = LeakCanary.install(this);\n\n    // for better RxJava debugging\n    //RxJavaHooks.enableAssemblyTracking();\n\n    // Initialize Volley\n    MyVolley.init(this);\n\n    Timber.plant(new Timber.DebugTree());\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/BaseFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport android.support.v4.app.Fragment;\nimport com.morihacky.android.rxjava.MyApp;\nimport com.squareup.leakcanary.RefWatcher;\n\npublic class BaseFragment extends Fragment {\n\n  @Override\n  public void onDestroy() {\n    super.onDestroy();\n    RefWatcher refWatcher = MyApp.getRefWatcher();\n    refWatcher.watch(this);\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/BufferDemoFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.support.annotation.Nullable;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Button;\nimport android.widget.ListView;\n\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.wiring.LogAdapter;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.Unbinder;\nimport io.reactivex.android.schedulers.AndroidSchedulers;\nimport io.reactivex.disposables.Disposable;\nimport io.reactivex.observers.DisposableObserver;\nimport timber.log.Timber;\n\n/**\n * This is a demonstration of the `buffer` Observable.\n *\n * <p>The buffer observable allows taps to be collected only within a time span. So taps outside the\n * 2s limit imposed by buffer will get accumulated in the next log statement.\n *\n * <p>If you're looking for a more foolproof solution that accumulates \"continuous\" taps vs a more\n * dumb solution as show below (i.e. number of taps within a timespan) look at {@link\n * com.morihacky.android.rxjava.rxbus.RxBusDemo_Bottom3Fragment} where a combo of `publish` and\n * `buffer` is used.\n *\n * <p>Also http://nerds.weddingpartyapp.com/tech/2015/01/05/debouncedbuffer-used-in-rxbus-example/\n * if you're looking for words instead of code\n */\npublic class BufferDemoFragment extends BaseFragment {\n\n  @BindView(R.id.list_threading_log)\n  ListView _logsList;\n\n  @BindView(R.id.btn_start_operation)\n  Button _tapBtn;\n\n  private LogAdapter _adapter;\n  private List<String> _logs;\n\n  private Disposable _disposable;\n  private Unbinder unbinder;\n\n  @Override\n  public void onResume() {\n    super.onResume();\n    _disposable = _getBufferedDisposable();\n  }\n\n  @Override\n  public void onPause() {\n    super.onPause();\n    _disposable.dispose();\n  }\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n    _setupLogger();\n  }\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_buffer, container, false);\n    unbinder = ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @Override\n  public void onDestroyView() {\n    super.onDestroyView();\n    unbinder.unbind();\n  }\n\n  // -----------------------------------------------------------------------------------\n  // Main Rx entities\n\n  private Disposable _getBufferedDisposable() {\n    return RxView.clicks(_tapBtn)\n        .map(\n            onClickEvent -> {\n              Timber.d(\"--------- GOT A TAP\");\n              _log(\"GOT A TAP\");\n              return 1;\n            })\n        .buffer(2, TimeUnit.SECONDS)\n        .observeOn(AndroidSchedulers.mainThread())\n        .subscribeWith(\n            new DisposableObserver<List<Integer>>() {\n\n              @Override\n              public void onComplete() {\n                // fyi: you'll never reach here\n                Timber.d(\"----- onCompleted\");\n              }\n\n              @Override\n              public void onError(Throwable e) {\n                Timber.e(e, \"--------- Woops on error!\");\n                _log(\"Dang error! check your logs\");\n              }\n\n              @Override\n              public void onNext(List<Integer> integers) {\n                Timber.d(\"--------- onNext\");\n                if (integers.size() > 0) {\n                  _log(String.format(\"%d taps\", integers.size()));\n                } else {\n                  Timber.d(\"--------- No taps received \");\n                }\n              }\n            });\n  }\n\n  // -----------------------------------------------------------------------------------\n  // Methods that help wiring up the example (irrelevant to RxJava)\n\n  private void _setupLogger() {\n    _logs = new ArrayList<>();\n    _adapter = new LogAdapter(getActivity(), new ArrayList<>());\n    _logsList.setAdapter(_adapter);\n  }\n\n  private void _log(String logMsg) {\n\n    if (_isCurrentlyOnMainThread()) {\n      _logs.add(0, logMsg + \" (main thread) \");\n      _adapter.clear();\n      _adapter.addAll(_logs);\n    } else {\n      _logs.add(0, logMsg + \" (NOT main thread) \");\n\n      // You can only do below stuff on main thread.\n      new Handler(Looper.getMainLooper())\n          .post(\n              () -> {\n                _adapter.clear();\n                _adapter.addAll(_logs);\n              });\n    }\n  }\n\n  private boolean _isCurrentlyOnMainThread() {\n    return Looper.myLooper() == Looper.getMainLooper();\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/ConcurrencyWithSchedulersDemoFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.support.annotation.Nullable;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ArrayAdapter;\nimport android.widget.ListView;\nimport android.widget.ProgressBar;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport com.morihacky.android.rxjava.R;\n\nimport butterknife.Unbinder;\nimport io.reactivex.Observable;\nimport io.reactivex.android.schedulers.AndroidSchedulers;\nimport io.reactivex.disposables.CompositeDisposable;\nimport io.reactivex.observers.DisposableObserver;\nimport io.reactivex.schedulers.Schedulers;\nimport java.util.ArrayList;\nimport java.util.List;\nimport timber.log.Timber;\n\npublic class ConcurrencyWithSchedulersDemoFragment extends BaseFragment {\n\n  @BindView(R.id.progress_operation_running)\n  ProgressBar _progress;\n\n  @BindView(R.id.list_threading_log)\n  ListView _logsList;\n\n  private LogAdapter _adapter;\n  private List<String> _logs;\n  private CompositeDisposable _disposables = new CompositeDisposable();\n  private Unbinder unbinder;\n\n  @Override\n  public void onDestroy() {\n    super.onDestroy();\n    unbinder.unbind();\n    _disposables.clear();\n  }\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n    _setupLogger();\n  }\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_concurrency_schedulers, container, false);\n    unbinder = ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @OnClick(R.id.btn_start_operation)\n  public void startLongOperation() {\n\n    _progress.setVisibility(View.VISIBLE);\n    _log(\"Button Clicked\");\n\n    DisposableObserver<Boolean> d = _getDisposableObserver();\n\n    _getObservable()\n        .subscribeOn(Schedulers.io())\n        .observeOn(AndroidSchedulers.mainThread())\n        .subscribe(d);\n\n    _disposables.add(d);\n  }\n\n  private Observable<Boolean> _getObservable() {\n    return Observable.just(true)\n        .map(\n            aBoolean -> {\n              _log(\"Within Observable\");\n              _doSomeLongOperation_thatBlocksCurrentThread();\n              return aBoolean;\n            });\n  }\n\n  /**\n   * Observer that handles the result through the 3 important actions:\n   *\n   * <p>1. onCompleted 2. onError 3. onNext\n   */\n  private DisposableObserver<Boolean> _getDisposableObserver() {\n    return new DisposableObserver<Boolean>() {\n\n      @Override\n      public void onComplete() {\n        _log(\"On complete\");\n        _progress.setVisibility(View.INVISIBLE);\n      }\n\n      @Override\n      public void onError(Throwable e) {\n        Timber.e(e, \"Error in RxJava Demo concurrency\");\n        _log(String.format(\"Boo! Error %s\", e.getMessage()));\n        _progress.setVisibility(View.INVISIBLE);\n      }\n\n      @Override\n      public void onNext(Boolean bool) {\n        _log(String.format(\"onNext with return value \\\"%b\\\"\", bool));\n      }\n    };\n  }\n\n  // -----------------------------------------------------------------------------------\n  // Method that help wiring up the example (irrelevant to RxJava)\n\n  private void _doSomeLongOperation_thatBlocksCurrentThread() {\n    _log(\"performing long operation\");\n\n    try {\n      Thread.sleep(3000);\n    } catch (InterruptedException e) {\n      Timber.d(\"Operation was interrupted\");\n    }\n  }\n\n  private void _log(String logMsg) {\n\n    if (_isCurrentlyOnMainThread()) {\n      _logs.add(0, logMsg + \" (main thread) \");\n      _adapter.clear();\n      _adapter.addAll(_logs);\n    } else {\n      _logs.add(0, logMsg + \" (NOT main thread) \");\n\n      // You can only do below stuff on main thread.\n      new Handler(Looper.getMainLooper())\n          .post(\n              () -> {\n                _adapter.clear();\n                _adapter.addAll(_logs);\n              });\n    }\n  }\n\n  private void _setupLogger() {\n    _logs = new ArrayList<>();\n    _adapter = new LogAdapter(getActivity(), new ArrayList<>());\n    _logsList.setAdapter(_adapter);\n  }\n\n  private boolean _isCurrentlyOnMainThread() {\n    return Looper.myLooper() == Looper.getMainLooper();\n  }\n\n  private class LogAdapter extends ArrayAdapter<String> {\n\n    public LogAdapter(Context context, List<String> logs) {\n      super(context, R.layout.item_log, R.id.item_log, logs);\n    }\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/DebounceSearchEmitterFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.support.annotation.Nullable;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ArrayAdapter;\nimport android.widget.EditText;\nimport android.widget.ListView;\n\nimport com.jakewharton.rxbinding2.widget.RxTextView;\nimport com.jakewharton.rxbinding2.widget.TextViewTextChangeEvent;\nimport com.morihacky.android.rxjava.R;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport butterknife.Unbinder;\nimport io.reactivex.android.schedulers.AndroidSchedulers;\nimport io.reactivex.disposables.Disposable;\nimport io.reactivex.observers.DisposableObserver;\nimport timber.log.Timber;\n\nimport static co.kaush.core.util.CoreNullnessUtils.isNotNullOrEmpty;\nimport static java.lang.String.format;\n\npublic class DebounceSearchEmitterFragment extends BaseFragment {\n\n  @BindView(R.id.list_threading_log)\n  ListView _logsList;\n\n  @BindView(R.id.input_txt_debounce)\n  EditText _inputSearchText;\n\n  private LogAdapter _adapter;\n  private List<String> _logs;\n\n  private Disposable _disposable;\n  private Unbinder unbinder;\n\n  @Override\n  public void onDestroy() {\n    super.onDestroy();\n    _disposable.dispose();\n    unbinder.unbind();\n  }\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_debounce, container, false);\n    unbinder = ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @OnClick(R.id.clr_debounce)\n  public void onClearLog() {\n    _logs = new ArrayList<>();\n    _adapter.clear();\n  }\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n\n    super.onActivityCreated(savedInstanceState);\n    _setupLogger();\n\n    _disposable =\n        RxTextView.textChangeEvents(_inputSearchText)\n            .debounce(400, TimeUnit.MILLISECONDS) // default Scheduler is Computation\n            .filter(changes -> isNotNullOrEmpty(changes.text().toString()))\n            .observeOn(AndroidSchedulers.mainThread())\n            .subscribeWith(_getSearchObserver());\n  }\n\n  // -----------------------------------------------------------------------------------\n  // Main Rx entities\n\n  private DisposableObserver<TextViewTextChangeEvent> _getSearchObserver() {\n    return new DisposableObserver<TextViewTextChangeEvent>() {\n      @Override\n      public void onComplete() {\n        Timber.d(\"--------- onComplete\");\n      }\n\n      @Override\n      public void onError(Throwable e) {\n        Timber.e(e, \"--------- Woops on error!\");\n        _log(\"Dang error. check your logs\");\n      }\n\n      @Override\n      public void onNext(TextViewTextChangeEvent onTextChangeEvent) {\n        _log(format(\"Searching for %s\", onTextChangeEvent.text().toString()));\n      }\n    };\n  }\n\n  // -----------------------------------------------------------------------------------\n  // Method that help wiring up the example (irrelevant to RxJava)\n\n  private void _setupLogger() {\n    _logs = new ArrayList<>();\n    _adapter = new LogAdapter(getActivity(), new ArrayList<>());\n    _logsList.setAdapter(_adapter);\n  }\n\n  private void _log(String logMsg) {\n\n    if (_isCurrentlyOnMainThread()) {\n      _logs.add(0, logMsg + \" (main thread) \");\n      _adapter.clear();\n      _adapter.addAll(_logs);\n    } else {\n      _logs.add(0, logMsg + \" (NOT main thread) \");\n\n      // You can only do below stuff on main thread.\n      new Handler(Looper.getMainLooper())\n          .post(\n              () -> {\n                _adapter.clear();\n                _adapter.addAll(_logs);\n              });\n    }\n  }\n\n  private boolean _isCurrentlyOnMainThread() {\n    return Looper.myLooper() == Looper.getMainLooper();\n  }\n\n  private class LogAdapter extends ArrayAdapter<String> {\n\n    public LogAdapter(Context context, List<String> logs) {\n      super(context, R.layout.item_log, R.id.item_log, logs);\n    }\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/DoubleBindingTextViewFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.EditText;\nimport android.widget.TextView;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnTextChanged;\nimport com.morihacky.android.rxjava.R;\n\nimport butterknife.Unbinder;\nimport io.reactivex.disposables.Disposable;\nimport io.reactivex.processors.PublishProcessor;\n\nimport static android.text.TextUtils.isEmpty;\n\npublic class DoubleBindingTextViewFragment extends BaseFragment {\n\n  @BindView(R.id.double_binding_num1)\n  EditText _number1;\n\n  @BindView(R.id.double_binding_num2)\n  EditText _number2;\n\n  @BindView(R.id.double_binding_result)\n  TextView _result;\n\n  Disposable _disposable;\n  PublishProcessor<Float> _resultEmitterSubject;\n  private Unbinder unbinder;\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_double_binding_textview, container, false);\n    unbinder = ButterKnife.bind(this, layout);\n\n    _resultEmitterSubject = PublishProcessor.create();\n\n    _disposable =\n        _resultEmitterSubject.subscribe(\n            aFloat -> {\n              _result.setText(String.valueOf(aFloat));\n            });\n\n    onNumberChanged();\n    _number2.requestFocus();\n\n    return layout;\n  }\n\n  @OnTextChanged({R.id.double_binding_num1, R.id.double_binding_num2})\n  public void onNumberChanged() {\n    float num1 = 0;\n    float num2 = 0;\n\n    if (!isEmpty(_number1.getText().toString())) {\n      num1 = Float.parseFloat(_number1.getText().toString());\n    }\n\n    if (!isEmpty(_number2.getText().toString())) {\n      num2 = Float.parseFloat(_number2.getText().toString());\n    }\n\n    _resultEmitterSubject.onNext(num1 + num2);\n  }\n\n  @Override\n  public void onDestroyView() {\n    super.onDestroyView();\n    _disposable.dispose();\n    unbinder.unbind();\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/ExponentialBackoffFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.support.annotation.Nullable;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ListView;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.wiring.LogAdapter;\n\nimport butterknife.Unbinder;\nimport hu.akarnokd.rxjava2.math.MathFlowable;\nimport io.reactivex.Flowable;\nimport io.reactivex.disposables.CompositeDisposable;\nimport io.reactivex.functions.Function;\nimport io.reactivex.subscribers.DisposableSubscriber;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\nimport org.reactivestreams.Publisher;\nimport timber.log.Timber;\n\nimport static android.os.Looper.getMainLooper;\n\npublic class ExponentialBackoffFragment extends BaseFragment {\n\n  @BindView(R.id.list_threading_log)\n  ListView _logList;\n\n  private LogAdapter _adapter;\n  private CompositeDisposable _disposables = new CompositeDisposable();\n  private List<String> _logs;\n  Unbinder unbinder;\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_exponential_backoff, container, false);\n    unbinder = ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n    _setupLogger();\n  }\n\n  @Override\n  public void onPause() {\n    super.onPause();\n\n    _disposables.clear();\n  }\n\n  @Override\n  public void onDestroyView() {\n    super.onDestroyView();\n    unbinder.unbind();\n  }\n\n  // -----------------------------------------------------------------------------------\n\n  @OnClick(R.id.btn_eb_retry)\n  public void startRetryingWithExponentialBackoffStrategy() {\n    _logs = new ArrayList<>();\n    _adapter.clear();\n\n    DisposableSubscriber<Object> disposableSubscriber =\n        new DisposableSubscriber<Object>() {\n          @Override\n          public void onNext(Object aVoid) {\n            Timber.d(\"on Next\");\n          }\n\n          @Override\n          public void onComplete() {\n            Timber.d(\"on Completed\");\n          }\n\n          @Override\n          public void onError(Throwable e) {\n            _log(\"Error: I give up!\");\n          }\n        };\n\n    Flowable.error(new RuntimeException(\"testing\")) // always fails\n        .retryWhen(new RetryWithDelay(5, 1000)) // notice this is called only onError (onNext\n        // values sent are ignored)\n        .doOnSubscribe(subscription -> _log(\"Attempting the impossible 5 times in intervals of 1s\"))\n        .subscribe(disposableSubscriber);\n\n    _disposables.add(disposableSubscriber);\n  }\n\n  @OnClick(R.id.btn_eb_delay)\n  public void startExecutingWithExponentialBackoffDelay() {\n\n    _logs = new ArrayList<>();\n    _adapter.clear();\n\n    DisposableSubscriber<Integer> disposableSubscriber =\n        new DisposableSubscriber<Integer>() {\n          @Override\n          public void onNext(Integer integer) {\n            Timber.d(\"executing Task %d [xx:%02d]\", integer, _getSecondHand());\n            _log(String.format(\"executing Task %d  [xx:%02d]\", integer, _getSecondHand()));\n          }\n\n          @Override\n          public void onError(Throwable e) {\n            Timber.d(e, \"arrrr. Error\");\n            _log(\"Error\");\n          }\n\n          @Override\n          public void onComplete() {\n            Timber.d(\"onCompleted\");\n            _log(\"Completed\");\n          }\n        };\n\n    Flowable.range(1, 4)\n        .delay(\n            integer -> {\n              // Rx-y way of doing the Fibonnaci :P\n              return MathFlowable.sumInt(Flowable.range(1, integer))\n                  .flatMap(\n                      targetSecondDelay ->\n                          Flowable.just(integer).delay(targetSecondDelay, TimeUnit.SECONDS));\n            })\n        .doOnSubscribe(\n            s ->\n                _log(\n                    String.format(\n                        \"Execute 4 tasks with delay - time now: [xx:%02d]\", _getSecondHand())))\n        .subscribe(disposableSubscriber);\n\n    _disposables.add(disposableSubscriber);\n  }\n\n  // -----------------------------------------------------------------------------------\n\n  private int _getSecondHand() {\n    long millis = System.currentTimeMillis();\n    return (int)\n        (TimeUnit.MILLISECONDS.toSeconds(millis)\n            - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));\n  }\n\n  // -----------------------------------------------------------------------------------\n\n  private void _setupLogger() {\n    _logs = new ArrayList<>();\n    _adapter = new LogAdapter(getActivity(), new ArrayList<>());\n    _logList.setAdapter(_adapter);\n  }\n\n  private void _log(String logMsg) {\n    _logs.add(logMsg);\n\n    // You can only do below stuff on main thread.\n    new Handler(getMainLooper())\n        .post(\n            () -> {\n              _adapter.clear();\n              _adapter.addAll(_logs);\n            });\n  }\n\n  // -----------------------------------------------------------------------------------\n\n  // CAUTION:\n  // --------------------------------------\n  // THIS notificationHandler class HAS NO BUSINESS BEING non-static\n  // I ONLY did this cause i wanted access to the `_log` method from inside here\n  // for the purpose of demonstration. In the real world, make it static and LET IT BE!!\n\n  // It's 12am in the morning and i feel lazy dammit !!!\n\n  //public static class RetryWithDelay\n  public class RetryWithDelay implements Function<Flowable<? extends Throwable>, Publisher<?>> {\n\n    private final int _maxRetries;\n    private final int _retryDelayMillis;\n    private int _retryCount;\n\n    public RetryWithDelay(final int maxRetries, final int retryDelayMillis) {\n      _maxRetries = maxRetries;\n      _retryDelayMillis = retryDelayMillis;\n      _retryCount = 0;\n    }\n\n    // this is a notificationhandler, all that is cared about here\n    // is the emission \"type\" not emission \"content\"\n    // only onNext triggers a re-subscription (onError + onComplete kills it)\n\n    @Override\n    public Publisher<?> apply(Flowable<? extends Throwable> inputObservable) {\n\n      // it is critical to use inputObservable in the chain for the result\n      // ignoring it and doing your own thing will break the sequence\n\n      return inputObservable.flatMap(\n          new Function<Throwable, Publisher<?>>() {\n            @Override\n            public Publisher<?> apply(Throwable throwable) {\n              if (++_retryCount < _maxRetries) {\n\n                // When this Observable calls onNext, the original\n                // Observable will be retried (i.e. re-subscribed)\n\n                Timber.d(\"Retrying in %d ms\", _retryCount * _retryDelayMillis);\n                _log(String.format(\"Retrying in %d ms\", _retryCount * _retryDelayMillis));\n\n                return Flowable.timer(_retryCount * _retryDelayMillis, TimeUnit.MILLISECONDS);\n              }\n\n              Timber.d(\"Argh! i give up\");\n\n              // Max retries hit. Pass an error so the chain is forcibly completed\n              // only onNext triggers a re-subscription (onError + onComplete kills it)\n              return Flowable.error(throwable);\n            }\n          });\n    }\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/FormValidationCombineLatestFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport static android.text.TextUtils.isEmpty;\nimport static android.util.Patterns.EMAIL_ADDRESS;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v4.content.ContextCompat;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.EditText;\nimport android.widget.TextView;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.Unbinder;\nimport com.jakewharton.rxbinding2.widget.RxTextView;\nimport com.morihacky.android.rxjava.R;\nimport io.reactivex.BackpressureStrategy;\nimport io.reactivex.Flowable;\nimport io.reactivex.subscribers.DisposableSubscriber;\nimport timber.log.Timber;\n\npublic class FormValidationCombineLatestFragment extends BaseFragment {\n\n  @BindView(R.id.btn_demo_form_valid)\n  TextView _btnValidIndicator;\n\n  @BindView(R.id.demo_combl_email)\n  EditText _email;\n\n  @BindView(R.id.demo_combl_password)\n  EditText _password;\n\n  @BindView(R.id.demo_combl_num)\n  EditText _number;\n\n  private DisposableSubscriber<Boolean> _disposableObserver = null;\n  private Flowable<CharSequence> _emailChangeObservable;\n  private Flowable<CharSequence> _numberChangeObservable;\n  private Flowable<CharSequence> _passwordChangeObservable;\n  private Unbinder unbinder;\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_form_validation_comb_latest, container, false);\n    unbinder = ButterKnife.bind(this, layout);\n\n    _emailChangeObservable =\n        RxTextView.textChanges(_email).skip(1).toFlowable(BackpressureStrategy.LATEST);\n    _passwordChangeObservable =\n        RxTextView.textChanges(_password).skip(1).toFlowable(BackpressureStrategy.LATEST);\n    _numberChangeObservable =\n        RxTextView.textChanges(_number).skip(1).toFlowable(BackpressureStrategy.LATEST);\n\n    _combineLatestEvents();\n\n    return layout;\n  }\n\n  @Override\n  public void onDestroyView() {\n    super.onDestroyView();\n    unbinder.unbind();\n    _disposableObserver.dispose();\n  }\n\n  private void _combineLatestEvents() {\n\n    _disposableObserver =\n        new DisposableSubscriber<Boolean>() {\n          @Override\n          public void onNext(Boolean formValid) {\n            if (formValid) {\n              _btnValidIndicator.setBackgroundColor(\n                  ContextCompat.getColor(getContext(), R.color.blue));\n            } else {\n              _btnValidIndicator.setBackgroundColor(\n                  ContextCompat.getColor(getContext(), R.color.gray));\n            }\n          }\n\n          @Override\n          public void onError(Throwable e) {\n            Timber.e(e, \"there was an error\");\n          }\n\n          @Override\n          public void onComplete() {\n            Timber.d(\"completed\");\n          }\n        };\n\n    Flowable.combineLatest(\n            _emailChangeObservable,\n            _passwordChangeObservable,\n            _numberChangeObservable,\n            (newEmail, newPassword, newNumber) -> {\n              boolean emailValid = !isEmpty(newEmail) && EMAIL_ADDRESS.matcher(newEmail).matches();\n              if (!emailValid) {\n                _email.setError(\"Invalid Email!\");\n              }\n\n              boolean passValid = !isEmpty(newPassword) && newPassword.length() > 8;\n              if (!passValid) {\n                _password.setError(\"Invalid Password!\");\n              }\n\n              boolean numValid = !isEmpty(newNumber);\n              if (numValid) {\n                int num = Integer.parseInt(newNumber.toString());\n                numValid = num > 0 && num <= 100;\n              }\n              if (!numValid) {\n                _number.setError(\"Invalid Number!\");\n              }\n\n              return emailValid && passValid && numValid;\n            })\n        .subscribe(_disposableObserver);\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/MainFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport android.os.Bundle;\nimport android.support.annotation.NonNull;\nimport android.support.annotation.Nullable;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport butterknife.Unbinder;\n\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.pagination.PaginationAutoFragment;\nimport com.morihacky.android.rxjava.rxbus.RxBusDemoFragment;\nimport com.morihacky.android.rxjava.volley.VolleyDemoFragment;\n\npublic class MainFragment extends BaseFragment {\n\n  private Unbinder unbinder;\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_main, container, false);\n    unbinder = ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @Override\n  public void onDestroyView() {\n    super.onDestroyView();\n    unbinder.unbind();\n  }\n\n  @OnClick(R.id.btn_demo_schedulers)\n  void demoConcurrencyWithSchedulers() {\n    clickedOn(new ConcurrencyWithSchedulersDemoFragment());\n  }\n\n  @OnClick(R.id.btn_demo_buffer)\n  void demoBuffer() {\n    clickedOn(new BufferDemoFragment());\n  }\n\n  @OnClick(R.id.btn_demo_debounce)\n  void demoThrottling() {\n    clickedOn(new DebounceSearchEmitterFragment());\n  }\n\n  @OnClick(R.id.btn_demo_retrofit)\n  void demoRetrofitCalls() {\n    clickedOn(new RetrofitFragment());\n  }\n\n  @OnClick(R.id.btn_demo_polling)\n  void demoPolling() {\n    clickedOn(new PollingFragment());\n  }\n\n  @OnClick(R.id.btn_demo_double_binding_textview)\n  void demoDoubleBindingWithPublishSubject() {\n    clickedOn(new DoubleBindingTextViewFragment());\n  }\n\n  @OnClick(R.id.btn_demo_rxbus)\n  void demoRxBus() {\n    clickedOn(new RxBusDemoFragment());\n  }\n\n  @OnClick(R.id.btn_demo_form_validation_combinel)\n  void formValidation() {\n    clickedOn(new FormValidationCombineLatestFragment());\n  }\n\n  @OnClick(R.id.btn_demo_pseudo_cache)\n  void pseudoCacheDemo() {\n    clickedOn(new PseudoCacheFragment());\n  }\n\n  @OnClick(R.id.btn_demo_timing)\n  void demoTimerIntervalDelays() {\n    clickedOn(new TimingDemoFragment());\n  }\n\n  @OnClick(R.id.btn_demo_timeout)\n  void demoTimeout() {\n    clickedOn(new TimeoutDemoFragment());\n  }\n\n  @OnClick(R.id.btn_demo_exponential_backoff)\n  void demoExponentialBackoff() {\n    clickedOn(new ExponentialBackoffFragment());\n  }\n\n  @OnClick(R.id.btn_demo_rotation_persist)\n  void demoRotationPersist() {\n    clickedOn(new RotationPersist3Fragment());\n    // clickedOn(new RotationPersist2Fragment());\n    // clickedOn(new RotationPersist1Fragment());\n  }\n\n  @OnClick(R.id.btn_demo_pagination)\n  void demoPaging() {\n    clickedOn(new PaginationAutoFragment());\n    //clickedOn(new PaginationFragment());\n  }\n\n  @OnClick(R.id.btn_demo_volley)\n  void demoVolleyRequest() {\n    clickedOn(new VolleyDemoFragment());\n  }\n\n  @OnClick(R.id.btn_demo_networkDetector)\n  void demoNetworkDetector() {\n    clickedOn(new NetworkDetectorFragment());\n  }\n\n  @OnClick(R.id.btn_demo_using)\n  void demoUsing() {\n    clickedOn(new UsingFragment());\n  }\n\n  @OnClick(R.id.btn_demo_multicastPlayground)\n  void demoMulticastPlayground() {\n    clickedOn(new MulticastPlaygroundFragment());\n  }\n\n  private void clickedOn(@NonNull Fragment fragment) {\n    final String tag = fragment.getClass().toString();\n    getActivity()\n        .getSupportFragmentManager()\n        .beginTransaction()\n        .addToBackStack(tag)\n        .replace(android.R.id.content, fragment, tag)\n        .commit();\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/NetworkDetectorFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.net.ConnectivityManager;\nimport android.net.NetworkInfo;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.support.annotation.Nullable;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ArrayAdapter;\nimport android.widget.ListView;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport com.morihacky.android.rxjava.R;\n\nimport butterknife.Unbinder;\nimport io.reactivex.android.schedulers.AndroidSchedulers;\nimport io.reactivex.disposables.Disposable;\nimport io.reactivex.processors.PublishProcessor;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class NetworkDetectorFragment extends BaseFragment {\n\n  @BindView(R.id.list_threading_log)\n  ListView logsList;\n\n  private LogAdapter adapter;\n  private BroadcastReceiver broadcastReceiver;\n  private List<String> logs;\n  private Disposable disposable;\n  private PublishProcessor<Boolean> publishProcessor;\n  private Unbinder unbinder;\n\n  @Override\n  public void onDestroy() {\n    super.onDestroy();\n    unbinder.unbind();\n  }\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_network_detector, container, false);\n    unbinder = ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n    setupLogger();\n  }\n\n  @Override\n  public void onStart() {\n    super.onStart();\n\n    publishProcessor = PublishProcessor.create();\n\n    disposable =\n        publishProcessor\n            .startWith(getConnectivityStatus(getActivity()))\n            .distinctUntilChanged()\n            .observeOn(AndroidSchedulers.mainThread())\n            .subscribe(\n                online -> {\n                  if (online) {\n                    log(\"You are online\");\n                  } else {\n                    log(\"You are offline\");\n                  }\n                });\n\n    listenToNetworkConnectivity();\n  }\n\n  @Override\n  public void onStop() {\n    super.onStop();\n\n    disposable.dispose();\n    getActivity().unregisterReceiver(broadcastReceiver);\n  }\n\n  private void listenToNetworkConnectivity() {\n\n    broadcastReceiver =\n        new BroadcastReceiver() {\n          @Override\n          public void onReceive(Context context, Intent intent) {\n            publishProcessor.onNext(getConnectivityStatus(context));\n          }\n        };\n\n    final IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);\n    getActivity().registerReceiver(broadcastReceiver, intentFilter);\n  }\n\n  private boolean getConnectivityStatus(Context context) {\n    ConnectivityManager cm =\n        (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);\n    NetworkInfo networkInfo = cm.getActiveNetworkInfo();\n    return networkInfo != null && networkInfo.isConnected();\n  }\n\n  // -----------------------------------------------------------------------------------\n  // Method that help wiring up the example (irrelevant to RxJava)\n\n  private void log(String logMsg) {\n\n    if (isCurrentlyOnMainThread()) {\n      logs.add(0, logMsg + \" (main thread) \");\n      adapter.clear();\n      adapter.addAll(logs);\n    } else {\n      logs.add(0, logMsg + \" (NOT main thread) \");\n\n      // You can only do below stuff on main thread.\n      new Handler(Looper.getMainLooper())\n          .post(\n              () -> {\n                adapter.clear();\n                adapter.addAll(logs);\n              });\n    }\n  }\n\n  private void setupLogger() {\n    logs = new ArrayList<>();\n    adapter = new LogAdapter(getActivity(), new ArrayList<>());\n    logsList.setAdapter(adapter);\n  }\n\n  private boolean isCurrentlyOnMainThread() {\n    return Looper.myLooper() == Looper.getMainLooper();\n  }\n\n  private class LogAdapter extends ArrayAdapter<String> {\n\n    public LogAdapter(Context context, List<String> logs) {\n      super(context, R.layout.item_log, R.id.item_log, logs);\n    }\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/PollingFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.support.annotation.Nullable;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ArrayAdapter;\nimport android.widget.ListView;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport com.morihacky.android.rxjava.R;\n\nimport butterknife.Unbinder;\nimport io.reactivex.Flowable;\nimport io.reactivex.disposables.CompositeDisposable;\nimport io.reactivex.disposables.Disposable;\nimport io.reactivex.functions.Function;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.concurrent.TimeUnit;\nimport org.reactivestreams.Publisher;\nimport timber.log.Timber;\n\npublic class PollingFragment extends BaseFragment {\n\n  private static final int INITIAL_DELAY = 0;\n  private static final int POLLING_INTERVAL = 1000;\n  private static final int POLL_COUNT = 8;\n\n  @BindView(R.id.list_threading_log)\n  ListView _logsList;\n\n  private LogAdapter _adapter;\n  private int _counter = 0;\n  private CompositeDisposable _disposables;\n  private List<String> _logs;\n  private Unbinder unbinder;\n\n  @Override\n  public void onCreate(@Nullable Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n\n    _disposables = new CompositeDisposable();\n  }\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_polling, container, false);\n    unbinder = ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n    _setupLogger();\n  }\n\n  @Override\n  public void onDestroy() {\n    super.onDestroy();\n    _disposables.clear();\n    unbinder.unbind();\n  }\n\n  @OnClick(R.id.btn_start_simple_polling)\n  public void onStartSimplePollingClicked() {\n\n    final int pollCount = POLL_COUNT;\n\n    Disposable d =\n        Flowable.interval(INITIAL_DELAY, POLLING_INTERVAL, TimeUnit.MILLISECONDS)\n            .map(this::_doNetworkCallAndGetStringResult)\n            .take(pollCount)\n            .doOnSubscribe(\n                subscription -> {\n                  _log(String.format(\"Start simple polling - %s\", _counter));\n                })\n            .subscribe(\n                taskName -> {\n                  _log(\n                      String.format(\n                          Locale.US,\n                          \"Executing polled task [%s] now time : [xx:%02d]\",\n                          taskName,\n                          _getSecondHand()));\n                });\n\n    _disposables.add(d);\n  }\n\n  @OnClick(R.id.btn_start_increasingly_delayed_polling)\n  public void onStartIncreasinglyDelayedPolling() {\n    _setupLogger();\n\n    final int pollingInterval = POLLING_INTERVAL;\n    final int pollCount = POLL_COUNT;\n\n    _log(\n        String.format(\n            Locale.US, \"Start increasingly delayed polling now time: [xx:%02d]\", _getSecondHand()));\n\n    _disposables.add(\n        Flowable.just(1L)\n            .repeatWhen(new RepeatWithDelay(pollCount, pollingInterval))\n            .subscribe(\n                o ->\n                    _log(\n                        String.format(\n                            Locale.US,\n                            \"Executing polled task now time : [xx:%02d]\",\n                            _getSecondHand())),\n                e -> Timber.d(e, \"arrrr. Error\")));\n  }\n\n  // -----------------------------------------------------------------------------------\n\n  // CAUTION:\n  // --------------------------------------\n  // THIS notificationHandler class HAS NO BUSINESS BEING non-static\n  // I ONLY did this cause i wanted access to the `_log` method from inside here\n  // for the purpose of demonstration. In the real world, make it static and LET IT BE!!\n\n  // It's 12am in the morning and i feel lazy dammit !!!\n\n  private String _doNetworkCallAndGetStringResult(long attempt) {\n    try {\n      if (attempt == 4) {\n        // randomly make one event super long so we test that the repeat logic waits\n        // and accounts for this.\n        Thread.sleep(9000);\n      } else {\n        Thread.sleep(3000);\n      }\n\n    } catch (InterruptedException e) {\n      Timber.d(\"Operation was interrupted\");\n    }\n    _counter++;\n\n    return String.valueOf(_counter);\n  }\n\n  // -----------------------------------------------------------------------------------\n  // Method that help wiring up the example (irrelevant to RxJava)\n\n  private int _getSecondHand() {\n    long millis = System.currentTimeMillis();\n    return (int)\n        (TimeUnit.MILLISECONDS.toSeconds(millis)\n            - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));\n  }\n\n  private void _log(String logMsg) {\n    if (_isCurrentlyOnMainThread()) {\n      _logs.add(0, logMsg + \" (main thread) \");\n      _adapter.clear();\n      _adapter.addAll(_logs);\n    } else {\n      _logs.add(0, logMsg + \" (NOT main thread) \");\n\n      // You can only do below stuff on main thread.\n      new Handler(Looper.getMainLooper())\n          .post(\n              () -> {\n                _adapter.clear();\n                _adapter.addAll(_logs);\n              });\n    }\n  }\n\n  private void _setupLogger() {\n    _logs = new ArrayList<>();\n    _adapter = new LogAdapter(getActivity(), new ArrayList<>());\n    _logsList.setAdapter(_adapter);\n    _counter = 0;\n  }\n\n  private boolean _isCurrentlyOnMainThread() {\n    return Looper.myLooper() == Looper.getMainLooper();\n  }\n\n  //public static class RepeatWithDelay\n  public class RepeatWithDelay implements Function<Flowable<Object>, Publisher<Long>> {\n\n    private final int _repeatLimit;\n    private final int _pollingInterval;\n    private int _repeatCount = 1;\n\n    RepeatWithDelay(int repeatLimit, int pollingInterval) {\n      _pollingInterval = pollingInterval;\n      _repeatLimit = repeatLimit;\n    }\n\n    // this is a notificationhandler, all we care about is\n    // the emission \"type\" not emission \"content\"\n    // only onNext triggers a re-subscription\n\n    @Override\n    public Publisher<Long> apply(Flowable<Object> inputFlowable) throws Exception {\n      // it is critical to use inputObservable in the chain for the result\n      // ignoring it and doing your own thing will break the sequence\n\n      return inputFlowable.flatMap(\n          new Function<Object, Publisher<Long>>() {\n            @Override\n            public Publisher<Long> apply(Object o) throws Exception {\n              if (_repeatCount >= _repeatLimit) {\n                // terminate the sequence cause we reached the limit\n                _log(\"Completing sequence\");\n                return Flowable.empty();\n              }\n\n              // since we don't get an input\n              // we store state in this handler to tell us the point of time we're firing\n              _repeatCount++;\n\n              return Flowable.timer(_repeatCount * _pollingInterval, TimeUnit.MILLISECONDS);\n            }\n          });\n    }\n  }\n\n  private class LogAdapter extends ArrayAdapter<String> {\n\n    public LogAdapter(Context context, List<String> logs) {\n      super(context, R.layout.item_log, R.id.item_log, logs);\n    }\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/PseudoCacheFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.support.annotation.Nullable;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ArrayAdapter;\nimport android.widget.ListView;\nimport android.widget.TextView;\n\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.retrofit.Contributor;\nimport com.morihacky.android.rxjava.retrofit.GithubApi;\nimport com.morihacky.android.rxjava.retrofit.GithubService;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport butterknife.Unbinder;\nimport io.reactivex.Observable;\nimport io.reactivex.android.schedulers.AndroidSchedulers;\nimport io.reactivex.observers.DisposableObserver;\nimport io.reactivex.schedulers.Schedulers;\nimport timber.log.Timber;\n\npublic class PseudoCacheFragment extends BaseFragment {\n\n  @BindView(R.id.info_pseudoCache_demo)\n  TextView infoText;\n\n  @BindView(R.id.info_pseudoCache_listSubscription)\n  ListView listSubscriptionInfo;\n\n  @BindView(R.id.info_pseudoCache_listDtl)\n  ListView listDetail;\n\n  private ArrayAdapter<String> adapterDetail, adapterSubscriptionInfo;\n  private HashMap<String, Long> contributionMap = null;\n  private Unbinder unbinder;\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_pseudo_cache, container, false);\n    unbinder = ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @Override\n  public void onDestroyView() {\n    super.onDestroyView();\n    unbinder.unbind();\n  }\n\n  @OnClick(R.id.btn_pseudoCache_concat)\n  public void onConcatBtnClicked() {\n    infoText.setText(R.string.msg_pseudoCache_demoInfo_concat);\n    wireupDemo();\n\n    Observable.concat(getSlowCachedDiskData(), getFreshNetworkData())\n        .subscribeOn(Schedulers.io()) // we want to add a list item at time of subscription\n        .observeOn(AndroidSchedulers.mainThread())\n        .subscribe(\n            new DisposableObserver<Contributor>() {\n              @Override\n              public void onComplete() {\n                Timber.d(\"done loading all data\");\n              }\n\n              @Override\n              public void onError(Throwable e) {\n                Timber.e(e, \"arr something went wrong\");\n              }\n\n              @Override\n              public void onNext(Contributor contributor) {\n                contributionMap.put(contributor.login, contributor.contributions);\n                adapterDetail.clear();\n                adapterDetail.addAll(mapAsList(contributionMap));\n              }\n            });\n  }\n\n  @OnClick(R.id.btn_pseudoCache_concatEager)\n  public void onConcatEagerBtnClicked() {\n    infoText.setText(R.string.msg_pseudoCache_demoInfo_concatEager);\n    wireupDemo();\n\n    List<Observable<Contributor>> observables = new ArrayList<>(2);\n    observables.add(getSlowCachedDiskData());\n    observables.add(getFreshNetworkData());\n\n    Observable.concatEager(observables)\n        .subscribeOn(Schedulers.io()) // we want to add a list item at time of subscription\n        .observeOn(AndroidSchedulers.mainThread())\n        .subscribe(\n            new DisposableObserver<Contributor>() {\n              @Override\n              public void onComplete() {\n                Timber.d(\"done loading all data\");\n              }\n\n              @Override\n              public void onError(Throwable e) {\n                Timber.e(e, \"arr something went wrong\");\n              }\n\n              @Override\n              public void onNext(Contributor contributor) {\n                contributionMap.put(contributor.login, contributor.contributions);\n                adapterDetail.clear();\n                adapterDetail.addAll(mapAsList(contributionMap));\n              }\n            });\n  }\n\n  @OnClick(R.id.btn_pseudoCache_merge)\n  public void onMergeBtnClicked() {\n    infoText.setText(R.string.msg_pseudoCache_demoInfo_merge);\n    wireupDemo();\n\n    Observable.merge(getCachedDiskData(), getFreshNetworkData())\n        .subscribeOn(Schedulers.io()) // we want to add a list item at time of subscription\n        .observeOn(AndroidSchedulers.mainThread())\n        .subscribe(\n            new DisposableObserver<Contributor>() {\n              @Override\n              public void onComplete() {\n                Timber.d(\"done loading all data\");\n              }\n\n              @Override\n              public void onError(Throwable e) {\n                Timber.e(e, \"arr something went wrong\");\n              }\n\n              @Override\n              public void onNext(Contributor contributor) {\n                contributionMap.put(contributor.login, contributor.contributions);\n                adapterDetail.clear();\n                adapterDetail.addAll(mapAsList(contributionMap));\n              }\n            });\n  }\n\n  @OnClick(R.id.btn_pseudoCache_mergeSlowDisk)\n  public void onMergeSlowBtnClicked() {\n    infoText.setText(R.string.msg_pseudoCache_demoInfo_mergeSlowDisk);\n    wireupDemo();\n\n    Observable.merge(getSlowCachedDiskData(), getFreshNetworkData())\n        .subscribeOn(Schedulers.io()) // we want to add a list item at time of subscription\n        .observeOn(AndroidSchedulers.mainThread())\n        .subscribe(\n            new DisposableObserver<Contributor>() {\n              @Override\n              public void onComplete() {\n                Timber.d(\"done loading all data\");\n              }\n\n              @Override\n              public void onError(Throwable e) {\n                Timber.e(e, \"arr something went wrong\");\n              }\n\n              @Override\n              public void onNext(Contributor contributor) {\n                contributionMap.put(contributor.login, contributor.contributions);\n                adapterDetail.clear();\n                adapterDetail.addAll(mapAsList(contributionMap));\n              }\n            });\n  }\n\n  @OnClick(R.id.btn_pseudoCache_mergeOptimized)\n  public void onMergeOptimizedBtnClicked() {\n    infoText.setText(R.string.msg_pseudoCache_demoInfo_mergeOptimized);\n    wireupDemo();\n\n    getFreshNetworkData() //\n        .publish(\n            network -> //\n            Observable.merge(\n                    network, //\n                    getCachedDiskData().takeUntil(network)))\n        .subscribeOn(Schedulers.io()) // we want to add a list item at time of subscription\n        .observeOn(AndroidSchedulers.mainThread())\n        .subscribe(\n            new DisposableObserver<Contributor>() {\n              @Override\n              public void onComplete() {\n                Timber.d(\"done loading all data\");\n              }\n\n              @Override\n              public void onError(Throwable e) {\n                Timber.e(e, \"arr something went wrong\");\n              }\n\n              @Override\n              public void onNext(Contributor contributor) {\n                contributionMap.put(contributor.login, contributor.contributions);\n                adapterDetail.clear();\n                adapterDetail.addAll(mapAsList(contributionMap));\n              }\n            });\n  }\n\n  @OnClick(R.id.btn_pseudoCache_mergeOptimizedSlowDisk)\n  public void onMergeOptimizedWithSlowDiskBtnClicked() {\n    infoText.setText(R.string.msg_pseudoCache_demoInfo_mergeOptimizedSlowDisk);\n    wireupDemo();\n\n    getFreshNetworkData() //\n        .publish(\n            network -> //\n            Observable.merge(\n                    network, //\n                    getSlowCachedDiskData().takeUntil(network)))\n        .subscribeOn(Schedulers.io()) // we want to add a list item at time of subscription\n        .observeOn(AndroidSchedulers.mainThread())\n        .subscribe(\n            new DisposableObserver<Contributor>() {\n              @Override\n              public void onComplete() {\n                Timber.d(\"done loading all data\");\n              }\n\n              @Override\n              public void onError(Throwable e) {\n                Timber.e(e, \"arr something went wrong\");\n              }\n\n              @Override\n              public void onNext(Contributor contributor) {\n                contributionMap.put(contributor.login, contributor.contributions);\n                adapterDetail.clear();\n                adapterDetail.addAll(mapAsList(contributionMap));\n              }\n            });\n  }\n\n  // -----------------------------------------------------------------------------------\n  // WIRING for example\n\n  private void wireupDemo() {\n    contributionMap = new HashMap<>();\n\n    adapterDetail =\n        new ArrayAdapter<>(\n            getActivity(), R.layout.item_log_white, R.id.item_log, new ArrayList<>());\n    listDetail.setAdapter(adapterDetail);\n\n    adapterSubscriptionInfo =\n        new ArrayAdapter<>(\n            getActivity(), R.layout.item_log_white, R.id.item_log, new ArrayList<>());\n    listSubscriptionInfo.setAdapter(adapterSubscriptionInfo);\n  }\n\n  private Observable<Contributor> getSlowCachedDiskData() {\n    return Observable.timer(1, TimeUnit.SECONDS).flatMap(dummy -> getCachedDiskData());\n  }\n\n  private Observable<Contributor> getCachedDiskData() {\n    List<Contributor> list = new ArrayList<>();\n    Map<String, Long> map = dummyDiskData();\n\n    for (String username : map.keySet()) {\n      Contributor c = new Contributor();\n      c.login = username;\n      c.contributions = map.get(username);\n      list.add(c);\n    }\n\n    return Observable.fromIterable(list) //\n        .doOnSubscribe(\n            (data) ->\n                new Handler(Looper.getMainLooper()) //\n                    .post(() -> adapterSubscriptionInfo.add(\"(disk) cache subscribed\"))) //\n        .doOnComplete(\n            () ->\n                new Handler(Looper.getMainLooper()) //\n                    .post(() -> adapterSubscriptionInfo.add(\"(disk) cache completed\")));\n  }\n\n  private Observable<Contributor> getFreshNetworkData() {\n    String githubToken = getResources().getString(R.string.github_oauth_token);\n    GithubApi githubService = GithubService.createGithubService(githubToken);\n\n    return githubService\n        .contributors(\"square\", \"retrofit\")\n        .flatMap(Observable::fromIterable)\n        .doOnSubscribe(\n            (data) ->\n                new Handler(Looper.getMainLooper()) //\n                    .post(() -> adapterSubscriptionInfo.add(\"(network) subscribed\"))) //\n        .doOnComplete(\n            () ->\n                new Handler(Looper.getMainLooper()) //\n                    .post(() -> adapterSubscriptionInfo.add(\"(network) completed\")));\n  }\n\n  private List<String> mapAsList(HashMap<String, Long> map) {\n    List<String> list = new ArrayList<>();\n\n    for (String username : map.keySet()) {\n      String rowLog = String.format(\"%s [%d]\", username, contributionMap.get(username));\n      list.add(rowLog);\n    }\n\n    return list;\n  }\n\n  private Map<String, Long> dummyDiskData() {\n    Map<String, Long> map = new HashMap<>();\n    map.put(\"JakeWharton\", 0L);\n    map.put(\"pforhan\", 0L);\n    map.put(\"edenman\", 0L);\n    map.put(\"swankjesse\", 0L);\n    map.put(\"bruceLee\", 0L);\n    return map;\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/PseudoCacheMergeFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.util.Pair;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ArrayAdapter;\nimport android.widget.ListView;\n\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.retrofit.Contributor;\nimport com.morihacky.android.rxjava.retrofit.GithubApi;\nimport com.morihacky.android.rxjava.retrofit.GithubService;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport butterknife.Unbinder;\nimport io.reactivex.Observable;\nimport io.reactivex.android.schedulers.AndroidSchedulers;\nimport io.reactivex.observers.DisposableObserver;\nimport io.reactivex.schedulers.Schedulers;\nimport timber.log.Timber;\n\npublic class PseudoCacheMergeFragment extends BaseFragment {\n\n  @BindView(R.id.log_list)\n  ListView _resultList;\n\n  private ArrayAdapter<String> _adapter;\n  private HashMap<String, Long> _contributionMap = null;\n  private HashMap<Contributor, Long> _resultAgeMap = new HashMap<>();\n  private Unbinder unbinder;\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_pseudo_cache_concat, container, false);\n    unbinder = ButterKnife.bind(this, layout);\n    _initializeCache();\n    return layout;\n  }\n\n  @Override\n  public void onDestroyView() {\n    super.onDestroyView();\n    unbinder.unbind();\n  }\n\n  @OnClick(R.id.btn_start_pseudo_cache)\n  public void onDemoPseudoCacheClicked() {\n    _adapter =\n        new ArrayAdapter<>(getActivity(), R.layout.item_log, R.id.item_log, new ArrayList<>());\n\n    _resultList.setAdapter(_adapter);\n    _initializeCache();\n\n    Observable.merge(_getCachedData(), _getFreshData())\n        .subscribeOn(Schedulers.io())\n        .observeOn(AndroidSchedulers.mainThread())\n        .subscribe(\n            new DisposableObserver<Pair<Contributor, Long>>() {\n              @Override\n              public void onComplete() {\n                Timber.d(\"done loading all data\");\n              }\n\n              @Override\n              public void onError(Throwable e) {\n                Timber.e(e, \"arr something went wrong\");\n              }\n\n              @Override\n              public void onNext(Pair<Contributor, Long> contributorAgePair) {\n                Contributor contributor = contributorAgePair.first;\n\n                if (_resultAgeMap.containsKey(contributor)\n                    && _resultAgeMap.get(contributor) > contributorAgePair.second) {\n                  return;\n                }\n\n                _contributionMap.put(contributor.login, contributor.contributions);\n                _resultAgeMap.put(contributor, contributorAgePair.second);\n\n                _adapter.clear();\n                _adapter.addAll(getListStringFromMap());\n              }\n            });\n  }\n\n  private List<String> getListStringFromMap() {\n    List<String> list = new ArrayList<>();\n\n    for (String username : _contributionMap.keySet()) {\n      String rowLog = String.format(\"%s [%d]\", username, _contributionMap.get(username));\n      list.add(rowLog);\n    }\n\n    return list;\n  }\n\n  private Observable<Pair<Contributor, Long>> _getCachedData() {\n\n    List<Pair<Contributor, Long>> list = new ArrayList<>();\n\n    Pair<Contributor, Long> dataWithAgePair;\n\n    for (String username : _contributionMap.keySet()) {\n      Contributor c = new Contributor();\n      c.login = username;\n      c.contributions = _contributionMap.get(username);\n\n      dataWithAgePair = new Pair<>(c, System.currentTimeMillis());\n      list.add(dataWithAgePair);\n    }\n\n    return Observable.fromIterable(list);\n  }\n\n  private Observable<Pair<Contributor, Long>> _getFreshData() {\n    String githubToken = getResources().getString(R.string.github_oauth_token);\n    GithubApi githubService = GithubService.createGithubService(githubToken);\n\n    return githubService\n        .contributors(\"square\", \"retrofit\")\n        .flatMap(Observable::fromIterable)\n        .map(contributor -> new Pair<>(contributor, System.currentTimeMillis()));\n  }\n\n  private void _initializeCache() {\n    _contributionMap = new HashMap<>();\n    _contributionMap.put(\"JakeWharton\", 0l);\n    _contributionMap.put(\"pforhan\", 0l);\n    _contributionMap.put(\"edenman\", 0l);\n    _contributionMap.put(\"swankjesse\", 0l);\n    _contributionMap.put(\"bruceLee\", 0l);\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/RetrofitAsyncTaskDeathFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport android.os.AsyncTask;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ArrayAdapter;\nimport android.widget.EditText;\nimport android.widget.ListView;\n\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.retrofit.GithubApi;\nimport com.morihacky.android.rxjava.retrofit.GithubService;\nimport com.morihacky.android.rxjava.retrofit.User;\n\nimport java.util.ArrayList;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport butterknife.Unbinder;\nimport io.reactivex.android.schedulers.AndroidSchedulers;\nimport io.reactivex.observers.DisposableObserver;\nimport io.reactivex.schedulers.Schedulers;\n\nimport static java.lang.String.format;\n\npublic class RetrofitAsyncTaskDeathFragment extends Fragment {\n\n  @BindView(R.id.btn_demo_retrofit_async_death_username)\n  EditText _username;\n\n  @BindView(R.id.log_list)\n  ListView _resultList;\n\n  private GithubApi _githubService;\n  private ArrayAdapter<String> _adapter;\n  private Unbinder unbinder;\n\n  @Override\n  public void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n\n    String githubToken = getResources().getString(R.string.github_oauth_token);\n    _githubService = GithubService.createGithubService(githubToken);\n  }\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n\n    View layout = inflater.inflate(R.layout.fragment_retrofit_async_task_death, container, false);\n    unbinder = ButterKnife.bind(this, layout);\n\n    _adapter =\n        new ArrayAdapter<>(getActivity(), R.layout.item_log, R.id.item_log, new ArrayList<>());\n    //_adapter.setNotifyOnChange(true);\n    _resultList.setAdapter(_adapter);\n\n    return layout;\n  }\n\n  @Override\n  public void onDestroyView() {\n    super.onDestroyView();\n    unbinder.unbind();\n  }\n\n  @OnClick(R.id.btn_demo_retrofit_async_death)\n  public void onGetGithubUserClicked() {\n    _adapter.clear();\n\n    /*new AsyncTask<String, Void, User>() {\n        @Override\n        protected User doInBackground(String... params) {\n            return _githubService.getUser(params[0]);\n        }\n\n        @Override\n        protected void onPostExecute(User user) {\n            _adapter.add(format(\"%s  = [%s: %s]\", _username.getText(), user.name, user.email));\n        }\n    }.execute(_username.getText().toString());*/\n\n    _githubService\n        .user(_username.getText().toString())\n        .subscribeOn(Schedulers.io())\n        .observeOn(AndroidSchedulers.mainThread())\n        .subscribe(\n            new DisposableObserver<User>() {\n              @Override\n              public void onComplete() {}\n\n              @Override\n              public void onError(Throwable e) {}\n\n              @Override\n              public void onNext(User user) {\n                _adapter.add(format(\"%s  = [%s: %s]\", _username.getText(), user.name, user.email));\n              }\n            });\n  }\n\n  // -----------------------------------------------------------------------------------\n\n  private class GetGithubUser extends AsyncTask<String, Void, User> {\n\n    @Override\n    protected User doInBackground(String... params) {\n      return _githubService.getUser(params[0]);\n    }\n\n    @Override\n    protected void onPostExecute(User user) {\n      _adapter.add(format(\"%s  = [%s: %s]\", _username.getText(), user.name, user.email));\n    }\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/RetrofitFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v4.app.Fragment;\nimport android.util.Pair;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ArrayAdapter;\nimport android.widget.EditText;\nimport android.widget.ListView;\n\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.retrofit.Contributor;\nimport com.morihacky.android.rxjava.retrofit.GithubApi;\nimport com.morihacky.android.rxjava.retrofit.GithubService;\nimport com.morihacky.android.rxjava.retrofit.User;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport butterknife.Unbinder;\nimport io.reactivex.Observable;\nimport io.reactivex.android.schedulers.AndroidSchedulers;\nimport io.reactivex.disposables.CompositeDisposable;\nimport io.reactivex.observers.DisposableObserver;\nimport io.reactivex.schedulers.Schedulers;\nimport timber.log.Timber;\n\nimport static android.text.TextUtils.isEmpty;\nimport static java.lang.String.format;\n\npublic class RetrofitFragment extends Fragment {\n\n  @BindView(R.id.demo_retrofit_contributors_username)\n  EditText _username;\n\n  @BindView(R.id.demo_retrofit_contributors_repository)\n  EditText _repo;\n\n  @BindView(R.id.log_list)\n  ListView _resultList;\n\n  private ArrayAdapter<String> _adapter;\n  private GithubApi _githubService;\n  private CompositeDisposable _disposables;\n  private Unbinder unbinder;\n\n  @Override\n  public void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    String githubToken = getResources().getString(R.string.github_oauth_token);\n    _githubService = GithubService.createGithubService(githubToken);\n\n    _disposables = new CompositeDisposable();\n  }\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n\n    View layout = inflater.inflate(R.layout.fragment_retrofit, container, false);\n    unbinder = ButterKnife.bind(this, layout);\n\n    _adapter =\n        new ArrayAdapter<>(getActivity(), R.layout.item_log, R.id.item_log, new ArrayList<>());\n    //_adapter.setNotifyOnChange(true);\n    _resultList.setAdapter(_adapter);\n\n    return layout;\n  }\n\n  @Override\n  public void onDestroyView() {\n    super.onDestroyView();\n    unbinder.unbind();\n  }\n\n  @Override\n  public void onDestroy() {\n    super.onDestroy();\n    _disposables.dispose();\n  }\n\n  @OnClick(R.id.btn_demo_retrofit_contributors)\n  public void onListContributorsClicked() {\n    _adapter.clear();\n\n    _disposables.add( //\n        _githubService\n            .contributors(_username.getText().toString(), _repo.getText().toString())\n            .subscribeOn(Schedulers.io())\n            .observeOn(AndroidSchedulers.mainThread())\n            .subscribeWith(\n                new DisposableObserver<List<Contributor>>() {\n\n                  @Override\n                  public void onComplete() {\n                    Timber.d(\"Retrofit call 1 completed\");\n                  }\n\n                  @Override\n                  public void onError(Throwable e) {\n                    Timber.e(e, \"woops we got an error while getting the list of contributors\");\n                  }\n\n                  @Override\n                  public void onNext(List<Contributor> contributors) {\n                    for (Contributor c : contributors) {\n                      _adapter.add(\n                          format(\n                              \"%s has made %d contributions to %s\",\n                              c.login, c.contributions, _repo.getText().toString()));\n\n                      Timber.d(\n                          \"%s has made %d contributions to %s\",\n                          c.login, c.contributions, _repo.getText().toString());\n                    }\n                  }\n                }));\n  }\n\n  @OnClick(R.id.btn_demo_retrofit_contributors_with_user_info)\n  public void onListContributorsWithFullUserInfoClicked() {\n    _adapter.clear();\n\n    _disposables.add(\n        _githubService\n            .contributors(_username.getText().toString(), _repo.getText().toString())\n            .flatMap(Observable::fromIterable)\n            .flatMap(\n                contributor -> {\n                  Observable<User> _userObservable =\n                      _githubService\n                          .user(contributor.login)\n                          .filter(user -> !isEmpty(user.name) && !isEmpty(user.email));\n\n                  return Observable.zip(_userObservable, Observable.just(contributor), Pair::new);\n                })\n            .subscribeOn(Schedulers.newThread())\n            .observeOn(AndroidSchedulers.mainThread())\n            .subscribeWith(\n                new DisposableObserver<Pair<User, Contributor>>() {\n                  @Override\n                  public void onComplete() {\n                    Timber.d(\"Retrofit call 2 completed \");\n                  }\n\n                  @Override\n                  public void onError(Throwable e) {\n                    Timber.e(\n                        e,\n                        \"error while getting the list of contributors along with full \" + \"names\");\n                  }\n\n                  @Override\n                  public void onNext(Pair<User, Contributor> pair) {\n                    User user = pair.first;\n                    Contributor contributor = pair.second;\n\n                    _adapter.add(\n                        format(\n                            \"%s(%s) has made %d contributions to %s\",\n                            user.name,\n                            user.email,\n                            contributor.contributions,\n                            _repo.getText().toString()));\n\n                    _adapter.notifyDataSetChanged();\n\n                    Timber.d(\n                        \"%s(%s) has made %d contributions to %s\",\n                        user.name,\n                        user.email,\n                        contributor.contributions,\n                        _repo.getText().toString());\n                  }\n                }));\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/RotationPersist1Fragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport static android.os.Looper.getMainLooper;\n\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.support.annotation.Nullable;\nimport android.support.v4.app.FragmentManager;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ListView;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport butterknife.Unbinder;\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.wiring.LogAdapter;\nimport io.reactivex.Flowable;\nimport io.reactivex.disposables.CompositeDisposable;\nimport io.reactivex.subscribers.DisposableSubscriber;\nimport java.util.ArrayList;\nimport java.util.List;\nimport timber.log.Timber;\n\npublic class RotationPersist1Fragment extends BaseFragment\n    implements RotationPersist1WorkerFragment.IAmYourMaster {\n\n  public static final String TAG = RotationPersist1Fragment.class.toString();\n\n  @BindView(R.id.list_threading_log)\n  ListView _logList;\n\n  private LogAdapter _adapter;\n  private List<String> _logs;\n  private Unbinder unbinder;\n\n  private CompositeDisposable _disposables = new CompositeDisposable();\n\n  // -----------------------------------------------------------------------------------\n\n  @OnClick(R.id.btn_rotate_persist)\n  public void startOperationFromWorkerFrag() {\n    _logs = new ArrayList<>();\n    _adapter.clear();\n\n    FragmentManager fm = getActivity().getSupportFragmentManager();\n    RotationPersist1WorkerFragment frag =\n        (RotationPersist1WorkerFragment) fm.findFragmentByTag(RotationPersist1WorkerFragment.TAG);\n\n    if (frag == null) {\n      frag = new RotationPersist1WorkerFragment();\n      fm.beginTransaction().add(frag, RotationPersist1WorkerFragment.TAG).commit();\n    } else {\n      Timber.d(\"Worker frag already spawned\");\n    }\n  }\n\n  @Override\n  public void observeResults(Flowable<Integer> intsFlowable) {\n\n    DisposableSubscriber<Integer> d =\n        new DisposableSubscriber<Integer>() {\n          @Override\n          public void onNext(Integer integer) {\n            _log(String.format(\"Worker frag spits out - %d\", integer));\n          }\n\n          @Override\n          public void onError(Throwable e) {\n            Timber.e(e, \"Error in worker demo frag observable\");\n            _log(\"Dang! something went wrong.\");\n          }\n\n          @Override\n          public void onComplete() {\n            _log(\"Observable is complete\");\n          }\n        };\n\n    intsFlowable\n        .doOnSubscribe(\n            subscription -> {\n              _log(\"Subscribing to intsObservable\");\n            })\n        .subscribe(d);\n\n    _disposables.add(d);\n  }\n\n  // -----------------------------------------------------------------------------------\n  // Boilerplate\n  // -----------------------------------------------------------------------------------\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n    _setupLogger();\n  }\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_rotation_persist, container, false);\n    unbinder = ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @Override\n  public void onPause() {\n    super.onPause();\n    _disposables.clear();\n  }\n\n  @Override\n  public void onDestroyView() {\n    super.onDestroyView();\n    unbinder.unbind();\n  }\n\n  private void _setupLogger() {\n    _logs = new ArrayList<>();\n    _adapter = new LogAdapter(getActivity(), new ArrayList<>());\n    _logList.setAdapter(_adapter);\n  }\n\n  private void _log(String logMsg) {\n    _logs.add(0, logMsg);\n\n    // You can only do below stuff on main thread.\n    new Handler(getMainLooper())\n        .post(\n            () -> {\n              _adapter.clear();\n              _adapter.addAll(_logs);\n            });\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/RotationPersist1WorkerFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport com.morihacky.android.rxjava.MainActivity;\nimport io.reactivex.Flowable;\nimport io.reactivex.disposables.Disposable;\nimport io.reactivex.flowables.ConnectableFlowable;\nimport java.util.concurrent.TimeUnit;\n\npublic class RotationPersist1WorkerFragment extends Fragment {\n\n  public static final String TAG = RotationPersist1WorkerFragment.class.toString();\n\n  private IAmYourMaster _masterFrag;\n  private ConnectableFlowable<Integer> _storedIntsFlowable;\n  private Disposable _storedIntsDisposable;\n\n  /**\n   * Hold a reference to the activity -> caller fragment this way when the worker frag kicks off we\n   * can talk back to the master and send results\n   */\n  @Override\n  public void onAttach(Context context) {\n    super.onAttach(context);\n\n    _masterFrag =\n        (RotationPersist1Fragment)\n            ((MainActivity) context)\n                .getSupportFragmentManager()\n                .findFragmentByTag(RotationPersist1Fragment.TAG);\n\n    if (_masterFrag == null) {\n      throw new ClassCastException(\"We did not find a master who can understand us :(\");\n    }\n  }\n\n  /** This method will only be called once when the retained Fragment is first created. */\n  @Override\n  public void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n\n    // Retain this fragment across configuration changes.\n    setRetainInstance(true);\n\n    if (_storedIntsFlowable != null) {\n      return;\n    }\n\n    Flowable<Integer> intsObservable =\n        Flowable.interval(1, TimeUnit.SECONDS).map(Long::intValue).take(20);\n\n    _storedIntsFlowable = intsObservable.publish();\n    _storedIntsDisposable = _storedIntsFlowable.connect();\n  }\n\n  /** The Worker fragment has started doing it's thing */\n  @Override\n  public void onResume() {\n    super.onResume();\n    _masterFrag.observeResults(_storedIntsFlowable);\n  }\n\n  @Override\n  public void onDestroy() {\n    super.onDestroy();\n    _storedIntsDisposable.dispose();\n  }\n\n  /** Set the callback to null so we don't accidentally leak the Activity instance. */\n  @Override\n  public void onDetach() {\n    super.onDetach();\n    _masterFrag = null;\n  }\n\n  public interface IAmYourMaster {\n    void observeResults(Flowable<Integer> intsObservable);\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/RotationPersist2Fragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport static android.os.Looper.getMainLooper;\n\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.support.annotation.Nullable;\nimport android.support.v4.app.FragmentManager;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ListView;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.wiring.LogAdapter;\nimport io.reactivex.Flowable;\nimport io.reactivex.disposables.CompositeDisposable;\nimport io.reactivex.subscribers.DisposableSubscriber;\nimport java.util.ArrayList;\nimport java.util.List;\nimport timber.log.Timber;\n\npublic class RotationPersist2Fragment extends BaseFragment\n    implements RotationPersist2WorkerFragment.IAmYourMaster {\n\n  public static final String TAG = RotationPersist2Fragment.class.toString();\n\n  @BindView(R.id.list_threading_log)\n  ListView _logList;\n\n  private LogAdapter _adapter;\n  private List<String> _logs;\n\n  private CompositeDisposable _disposables = new CompositeDisposable();\n\n  // -----------------------------------------------------------------------------------\n\n  @OnClick(R.id.btn_rotate_persist)\n  public void startOperationFromWorkerFrag() {\n    _logs = new ArrayList<>();\n    _adapter.clear();\n\n    FragmentManager fm = getActivity().getSupportFragmentManager();\n    RotationPersist2WorkerFragment frag =\n        (RotationPersist2WorkerFragment) fm.findFragmentByTag(RotationPersist2WorkerFragment.TAG);\n\n    if (frag == null) {\n      frag = new RotationPersist2WorkerFragment();\n      fm.beginTransaction().add(frag, RotationPersist2WorkerFragment.TAG).commit();\n    } else {\n      Timber.d(\"Worker frag already spawned\");\n    }\n  }\n\n  @Override\n  public void setStream(Flowable<Integer> intStream) {\n    DisposableSubscriber<Integer> d =\n        new DisposableSubscriber<Integer>() {\n          @Override\n          public void onNext(Integer integer) {\n            _log(String.format(\"Worker frag spits out - %d\", integer));\n          }\n\n          @Override\n          public void onError(Throwable e) {\n            Timber.e(e, \"Error in worker demo frag observable\");\n            _log(\"Dang! something went wrong.\");\n          }\n\n          @Override\n          public void onComplete() {\n            _log(\"Observable is complete\");\n          }\n        };\n\n    intStream.doOnSubscribe(subscription -> _log(\"Subscribing to intsObservable\")).subscribe(d);\n\n    _disposables.add(d);\n  }\n\n  // -----------------------------------------------------------------------------------\n  // Boilerplate\n  // -----------------------------------------------------------------------------------\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n    _setupLogger();\n  }\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_rotation_persist, container, false);\n    ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @Override\n  public void onPause() {\n    super.onPause();\n    _disposables.clear();\n  }\n\n  private void _setupLogger() {\n    _logs = new ArrayList<>();\n    _adapter = new LogAdapter(getActivity(), new ArrayList<>());\n    _logList.setAdapter(_adapter);\n  }\n\n  private void _log(String logMsg) {\n    _logs.add(0, logMsg);\n\n    // You can only do below stuff on main thread.\n    new Handler(getMainLooper())\n        .post(\n            () -> {\n              _adapter.clear();\n              _adapter.addAll(_logs);\n            });\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/RotationPersist2WorkerFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport com.morihacky.android.rxjava.MainActivity;\nimport io.reactivex.Flowable;\nimport io.reactivex.processors.PublishProcessor;\nimport java.util.concurrent.TimeUnit;\n\npublic class RotationPersist2WorkerFragment extends Fragment {\n\n  public static final String TAG = RotationPersist2WorkerFragment.class.toString();\n\n  private PublishProcessor<Integer> _intStream;\n  private PublishProcessor<Boolean> _lifeCycleStream;\n\n  private IAmYourMaster _masterFrag;\n\n  /**\n   * Since we're holding a reference to the Master a.k.a Activity/Master Frag remember to explicitly\n   * remove the worker fragment or you'll have a mem leak in your hands.\n   *\n   * <p>See {@link MainActivity#onBackPressed()}\n   */\n  @Override\n  public void onAttach(Context context) {\n    super.onAttach(context);\n\n    _masterFrag =\n        (RotationPersist2Fragment)\n            ((MainActivity) context)\n                .getSupportFragmentManager()\n                .findFragmentByTag(RotationPersist2Fragment.TAG);\n\n    if (_masterFrag == null) {\n      throw new ClassCastException(\"We did not find a master who can understand us :(\");\n    }\n  }\n\n  /** This method will only be called once when the retained Fragment is first created. */\n  @Override\n  public void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n\n    _intStream = PublishProcessor.create();\n    _lifeCycleStream = PublishProcessor.create();\n\n    // Retain this fragment across configuration changes.\n    setRetainInstance(true);\n\n    _intStream.takeUntil(_lifeCycleStream);\n\n    Flowable.interval(1, TimeUnit.SECONDS).map(Long::intValue).take(20).subscribe(_intStream);\n  }\n\n  /** The Worker fragment has started doing it's thing */\n  @Override\n  public void onResume() {\n    super.onResume();\n    _masterFrag.setStream(_intStream);\n  }\n\n  @Override\n  public void onDestroy() {\n    super.onDestroy();\n    _lifeCycleStream.onComplete();\n  }\n\n  /** Set the callback to null so we don't accidentally leak the Activity instance. */\n  @Override\n  public void onDetach() {\n    super.onDetach();\n    _masterFrag = null;\n  }\n\n  public interface IAmYourMaster {\n    void setStream(Flowable<Integer> intStream);\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/RotationPersist3Fragment.kt",
    "content": "package com.morihacky.android.rxjava.fragments\n\nimport android.arch.lifecycle.ViewModel\nimport android.arch.lifecycle.ViewModelProviders\nimport android.os.Bundle\nimport android.os.Handler\nimport android.os.Looper.getMainLooper\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.ListView\nimport butterknife.BindView\nimport butterknife.ButterKnife\nimport butterknife.OnClick\nimport com.morihacky.android.rxjava.MyApp\nimport com.morihacky.android.rxjava.R\nimport com.morihacky.android.rxjava.ext.plus\nimport com.morihacky.android.rxjava.wiring.LogAdapter\nimport io.reactivex.Flowable\nimport io.reactivex.disposables.CompositeDisposable\nimport io.reactivex.disposables.Disposable\nimport timber.log.Timber\nimport java.util.concurrent.TimeUnit\n\nclass RotationPersist3Fragment : BaseFragment() {\n\n    @BindView(R.id.list_threading_log)\n    lateinit var logList: ListView\n    lateinit var adapter: LogAdapter\n    lateinit var sharedViewModel: SharedViewModel\n\n    private var logs: MutableList<String> = ArrayList()\n    private var disposables = CompositeDisposable()\n\n    // -----------------------------------------------------------------------------------\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        sharedViewModel = ViewModelProviders.of(activity).get(SharedViewModel::class.java)\n    }\n\n    override fun onCreateView(\n            inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {\n        val layout = inflater!!.inflate(R.layout.fragment_rotation_persist, container, false)\n        ButterKnife.bind(this, layout)\n        return layout\n    }\n\n    @OnClick(R.id.btn_rotate_persist)\n    fun startOperationFromWorkerFrag() {\n        logs = ArrayList<String>()\n        adapter.clear()\n\n        disposables +=\n                sharedViewModel\n                        .sourceStream()\n                        .subscribe({ l ->\n                            _log(\"Received element $l\")\n                        })\n    }\n\n    // -----------------------------------------------------------------------------------\n    // Boilerplate\n    // -----------------------------------------------------------------------------------\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n        _setupLogger()\n    }\n\n    override fun onPause() {\n        super.onPause()\n        disposables.clear()\n    }\n\n    private fun _setupLogger() {\n        logs = ArrayList<String>()\n        adapter = LogAdapter(activity, ArrayList<String>())\n        logList.adapter = adapter\n    }\n\n    private fun _log(logMsg: String) {\n        logs.add(0, logMsg)\n\n        // You can only do below stuff on main thread.\n        Handler(getMainLooper())\n                .post {\n                    adapter.clear()\n                    adapter.addAll(logs)\n                }\n    }\n}\n\nclass SharedViewModel : ViewModel() {\n    var disposable: Disposable? = null\n\n    var sharedObservable: Flowable<Long> =\n            Flowable.interval(1, TimeUnit.SECONDS)\n                    .take(20)\n                    .doOnNext { l -> Timber.tag(\"KG\").d(\"onNext $l\") }\n                    // .replayingShare()\n                    .replay(1)\n                    .autoConnect(1) { t -> disposable = t }\n\n    fun sourceStream(): Flowable<Long> {\n        return sharedObservable\n    }\n\n    override fun onCleared() {\n        super.onCleared()\n        Timber.tag(\"KG\").d(\"Clearing ViewModel\")\n        disposable?.dispose()\n        MyApp.getRefWatcher().watch(this)\n    }\n}\n\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/TimeoutDemoFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.support.annotation.Nullable;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ListView;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.wiring.LogAdapter;\nimport io.reactivex.Observable;\nimport io.reactivex.ObservableEmitter;\nimport io.reactivex.ObservableOnSubscribe;\nimport io.reactivex.android.schedulers.AndroidSchedulers;\nimport io.reactivex.observers.DisposableObserver;\nimport io.reactivex.schedulers.Schedulers;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\nimport timber.log.Timber;\n\npublic class TimeoutDemoFragment extends BaseFragment {\n\n  @BindView(R.id.list_threading_log)\n  ListView _logsList;\n\n  private LogAdapter _adapter;\n  private DisposableObserver<String> _disposable;\n  private List<String> _logs;\n\n  @Override\n  public void onDestroy() {\n    super.onDestroy();\n\n    if (_disposable == null) {\n      return;\n    }\n\n    _disposable.dispose();\n  }\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_subject_timeout, container, false);\n    ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n    _setupLogger();\n  }\n\n  @OnClick(R.id.btn_demo_timeout_1_2s)\n  public void onStart2sTask() {\n    _disposable = _getEventCompletionObserver();\n\n    _getObservableTask_2sToComplete()\n        .timeout(3, TimeUnit.SECONDS)\n        .subscribeOn(Schedulers.computation())\n        .observeOn(AndroidSchedulers.mainThread())\n        .subscribe(_disposable);\n  }\n\n  @OnClick(R.id.btn_demo_timeout_1_5s)\n  public void onStart5sTask() {\n    _disposable = _getEventCompletionObserver();\n\n    _getObservableTask_5sToComplete()\n        .timeout(3, TimeUnit.SECONDS, _onTimeoutObservable())\n        .subscribeOn(Schedulers.computation())\n        .observeOn(AndroidSchedulers.mainThread())\n        .subscribe(_disposable);\n  }\n\n  // -----------------------------------------------------------------------------------\n  // Main Rx entities\n\n  private Observable<String> _getObservableTask_5sToComplete() {\n    return Observable.create(\n        new ObservableOnSubscribe<String>() {\n          @Override\n          public void subscribe(ObservableEmitter<String> subscriber) throws Exception {\n            _log(String.format(\"Starting a 5s task\"));\n            subscriber.onNext(\"5 s\");\n            try {\n              Thread.sleep(5_000);\n            } catch (InterruptedException e) {\n              e.printStackTrace();\n            }\n            subscriber.onComplete();\n          }\n        });\n  }\n\n  private Observable<String> _getObservableTask_2sToComplete() {\n    return Observable.create(\n        new ObservableOnSubscribe<String>() {\n          @Override\n          public void subscribe(ObservableEmitter<String> subscriber) throws Exception {\n            _log(String.format(\"Starting a 2s task\"));\n            subscriber.onNext(\"2 s\");\n            try {\n              Thread.sleep(2_000);\n            } catch (InterruptedException e) {\n              e.printStackTrace();\n            }\n            subscriber.onComplete();\n          }\n        });\n  }\n\n  private Observable<? extends String> _onTimeoutObservable() {\n    return Observable.create(\n        new ObservableOnSubscribe<String>() {\n\n          @Override\n          public void subscribe(ObservableEmitter<String> subscriber) throws Exception {\n            _log(\"Timing out this task ...\");\n            subscriber.onError(new Throwable(\"Timeout Error\"));\n          }\n        });\n  }\n\n  private DisposableObserver<String> _getEventCompletionObserver() {\n    return new DisposableObserver<String>() {\n      @Override\n      public void onNext(String taskType) {\n        _log(String.format(\"onNext %s task\", taskType));\n      }\n\n      @Override\n      public void onError(Throwable e) {\n        _log(String.format(\"Dang a task timeout\"));\n        Timber.e(e, \"Timeout Demo exception\");\n      }\n\n      @Override\n      public void onComplete() {\n        _log(String.format(\"task was completed\"));\n      }\n    };\n  }\n\n  // -----------------------------------------------------------------------------------\n  // Method that help wiring up the example (irrelevant to RxJava)\n\n  private void _setupLogger() {\n    _logs = new ArrayList<>();\n    _adapter = new LogAdapter(getActivity(), new ArrayList<>());\n    _logsList.setAdapter(_adapter);\n  }\n\n  private void _log(String logMsg) {\n\n    if (_isCurrentlyOnMainThread()) {\n      _logs.add(0, logMsg + \" (main thread) \");\n      _adapter.clear();\n      _adapter.addAll(_logs);\n    } else {\n      _logs.add(0, logMsg + \" (NOT main thread) \");\n\n      // You can only do below stuff on main thread.\n      new Handler(Looper.getMainLooper())\n          .post(\n              () -> {\n                _adapter.clear();\n                _adapter.addAll(_logs);\n              });\n    }\n  }\n\n  private boolean _isCurrentlyOnMainThread() {\n    return Looper.myLooper() == Looper.getMainLooper();\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/fragments/TimingDemoFragment.java",
    "content": "package com.morihacky.android.rxjava.fragments;\n\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.support.annotation.Nullable;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ListView;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.wiring.LogAdapter;\n\nimport butterknife.Unbinder;\nimport io.reactivex.Flowable;\nimport io.reactivex.subscribers.DefaultSubscriber;\nimport io.reactivex.subscribers.DisposableSubscriber;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.concurrent.TimeUnit;\nimport timber.log.Timber;\n\nimport static android.os.Looper.getMainLooper;\nimport static android.os.Looper.myLooper;\n\npublic class TimingDemoFragment extends BaseFragment {\n\n  @BindView(R.id.list_threading_log)\n  ListView _logsList;\n\n  private LogAdapter _adapter;\n  private List<String> _logs;\n\n  private DisposableSubscriber<Long> _subscriber1;\n  private DisposableSubscriber<Long> _subscriber2;\n  private Unbinder unbinder;\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_demo_timing, container, false);\n    unbinder = ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n    _setupLogger();\n  }\n\n  @Override\n  public void onDestroyView() {\n    super.onDestroyView();\n    unbinder.unbind();\n  }\n  // -----------------------------------------------------------------------------------\n\n  @OnClick(R.id.btn_demo_timing_1)\n  public void btn1_RunSingleTaskAfter2s() {\n    _log(String.format(\"A1 [%s] --- BTN click\", _getCurrentTimestamp()));\n\n    Flowable.timer(2, TimeUnit.SECONDS) //\n        .subscribe(\n            new DefaultSubscriber<Long>() {\n              @Override\n              public void onNext(Long number) {\n                _log(String.format(\"A1 [%s]     NEXT\", _getCurrentTimestamp()));\n              }\n\n              @Override\n              public void onError(Throwable e) {\n                Timber.e(e, \"something went wrong in TimingDemoFragment example\");\n              }\n\n              @Override\n              public void onComplete() {\n                _log(String.format(\"A1 [%s] XXX COMPLETE\", _getCurrentTimestamp()));\n              }\n            });\n  }\n\n  @OnClick(R.id.btn_demo_timing_2)\n  public void btn2_RunTask_IntervalOf1s() {\n    if (_subscriber1 != null && !_subscriber1.isDisposed()) {\n      _subscriber1.dispose();\n      _log(String.format(\"B2 [%s] XXX BTN KILLED\", _getCurrentTimestamp()));\n      return;\n    }\n\n    _log(String.format(\"B2 [%s] --- BTN click\", _getCurrentTimestamp()));\n\n    _subscriber1 =\n        new DisposableSubscriber<Long>() {\n          @Override\n          public void onComplete() {\n            _log(String.format(\"B2 [%s] XXXX COMPLETE\", _getCurrentTimestamp()));\n          }\n\n          @Override\n          public void onError(Throwable e) {\n            Timber.e(e, \"something went wrong in TimingDemoFragment example\");\n          }\n\n          @Override\n          public void onNext(Long number) {\n            _log(String.format(\"B2 [%s]     NEXT\", _getCurrentTimestamp()));\n          }\n        };\n\n    Flowable.interval(1, TimeUnit.SECONDS).subscribe(_subscriber1);\n  }\n\n  @OnClick(R.id.btn_demo_timing_3)\n  public void btn3_RunTask_IntervalOf1s_StartImmediately() {\n    if (_subscriber2 != null && !_subscriber2.isDisposed()) {\n      _subscriber2.dispose();\n      _log(String.format(\"C3 [%s] XXX BTN KILLED\", _getCurrentTimestamp()));\n      return;\n    }\n\n    _log(String.format(\"C3 [%s] --- BTN click\", _getCurrentTimestamp()));\n\n    _subscriber2 =\n        new DisposableSubscriber<Long>() {\n          @Override\n          public void onNext(Long number) {\n            _log(String.format(\"C3 [%s]     NEXT\", _getCurrentTimestamp()));\n          }\n\n          @Override\n          public void onComplete() {\n            _log(String.format(\"C3 [%s] XXXX COMPLETE\", _getCurrentTimestamp()));\n          }\n\n          @Override\n          public void onError(Throwable e) {\n            Timber.e(e, \"something went wrong in TimingDemoFragment example\");\n          }\n        };\n\n    Flowable.interval(0, 1, TimeUnit.SECONDS).subscribe(_subscriber2);\n  }\n\n  @OnClick(R.id.btn_demo_timing_4)\n  public void btn4_RunTask5Times_IntervalOf3s() {\n    _log(String.format(\"D4 [%s] --- BTN click\", _getCurrentTimestamp()));\n\n    Flowable.interval(3, TimeUnit.SECONDS)\n        .take(5)\n        .subscribe(\n            new DefaultSubscriber<Long>() {\n              @Override\n              public void onNext(Long number) {\n                _log(String.format(\"D4 [%s]     NEXT\", _getCurrentTimestamp()));\n              }\n\n              @Override\n              public void onError(Throwable e) {\n                Timber.e(e, \"something went wrong in TimingDemoFragment example\");\n              }\n\n              @Override\n              public void onComplete() {\n                _log(String.format(\"D4 [%s] XXX COMPLETE\", _getCurrentTimestamp()));\n              }\n            });\n  }\n\n  @OnClick(R.id.btn_demo_timing_5)\n  public void btn5_RunTask5Times_IntervalOf3s() {\n    _log(String.format(\"D5 [%s] --- BTN click\", _getCurrentTimestamp()));\n\n    Flowable.just(\"Do task A right away\")\n        .doOnNext(input -> _log(String.format(\"D5 %s [%s]\", input, _getCurrentTimestamp())))\n        .delay(1, TimeUnit.SECONDS)\n        .doOnNext(\n            oldInput ->\n                _log(\n                    String.format(\n                        \"D5 %s [%s]\", \"Doing Task B after a delay\", _getCurrentTimestamp())))\n        .subscribe(\n            new DefaultSubscriber<String>() {\n              @Override\n              public void onComplete() {\n                _log(String.format(\"D5 [%s] XXX COMPLETE\", _getCurrentTimestamp()));\n              }\n\n              @Override\n              public void onError(Throwable e) {\n                Timber.e(e, \"something went wrong in TimingDemoFragment example\");\n              }\n\n              @Override\n              public void onNext(String number) {\n                _log(String.format(\"D5 [%s]     NEXT\", _getCurrentTimestamp()));\n              }\n            });\n  }\n\n  // -----------------------------------------------------------------------------------\n  // Method that help wiring up the example (irrelevant to RxJava)\n\n  @OnClick(R.id.btn_clr)\n  public void OnClearLog() {\n    _logs = new ArrayList<>();\n    _adapter.clear();\n  }\n\n  private void _setupLogger() {\n    _logs = new ArrayList<>();\n    _adapter = new LogAdapter(getActivity(), new ArrayList<>());\n    _logsList.setAdapter(_adapter);\n  }\n\n  private void _log(String logMsg) {\n    _logs.add(0, String.format(logMsg + \" [MainThread: %b]\", getMainLooper() == myLooper()));\n\n    // You can only do below stuff on main thread.\n    new Handler(getMainLooper())\n        .post(\n            () -> {\n              _adapter.clear();\n              _adapter.addAll(_logs);\n            });\n  }\n\n  private String _getCurrentTimestamp() {\n    return new SimpleDateFormat(\"k:m:s:S a\", Locale.getDefault()).format(new Date());\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/pagination/PaginationAdapter.java",
    "content": "package com.morihacky.android.rxjava.pagination;\n\nimport android.support.v7.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Button;\nimport android.widget.TextView;\n\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.rxbus.RxBus;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/** There isn't anything specific to Pagination here. Just wiring for the example */\nclass PaginationAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {\n\n  private static final int ITEM_LOG = 0;\n  private static final int ITEM_BTN = 1;\n\n  private final List<String> _items = new ArrayList<>();\n  private final RxBus _bus;\n\n  PaginationAdapter(RxBus bus) {\n    _bus = bus;\n  }\n\n  void addItems(List<String> items) {\n    _items.addAll(items);\n  }\n\n  @Override\n  public int getItemViewType(int position) {\n    if (position == _items.size()) {\n      return ITEM_BTN;\n    }\n\n    return ITEM_LOG;\n  }\n\n  @Override\n  public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n    switch (viewType) {\n      case ITEM_BTN:\n        return ItemBtnViewHolder.create(parent);\n      default:\n        return ItemLogViewHolder.create(parent);\n    }\n  }\n\n  @Override\n  public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {\n    switch (getItemViewType(position)) {\n      case ITEM_LOG:\n        ((ItemLogViewHolder) holder).bindContent(_items.get(position));\n        return;\n      case ITEM_BTN:\n        ((ItemBtnViewHolder) holder).bindContent(_bus);\n    }\n  }\n\n  @Override\n  public int getItemCount() {\n    return _items.size() + 1; // add 1 for paging button\n  }\n\n  private static class ItemLogViewHolder extends RecyclerView.ViewHolder {\n    ItemLogViewHolder(View itemView) {\n      super(itemView);\n    }\n\n    static ItemLogViewHolder create(ViewGroup parent) {\n      return new ItemLogViewHolder(\n          LayoutInflater.from(parent.getContext()).inflate(R.layout.item_log, parent, false));\n    }\n\n    void bindContent(String content) {\n      ((TextView) itemView).setText(content);\n    }\n  }\n\n  static class ItemBtnViewHolder extends RecyclerView.ViewHolder {\n    ItemBtnViewHolder(View itemView) {\n      super(itemView);\n    }\n\n    static ItemBtnViewHolder create(ViewGroup parent) {\n      return new ItemBtnViewHolder(\n          LayoutInflater.from(parent.getContext()).inflate(R.layout.item_btn, parent, false));\n    }\n\n    void bindContent(RxBus bus) {\n      ((Button) itemView).setText(R.string.btn_demo_pagination_more);\n      itemView.setOnClickListener(v -> bus.send(new ItemBtnViewHolder.PageEvent()));\n    }\n\n    static class PageEvent {}\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/pagination/PaginationAutoAdapter.java",
    "content": "package com.morihacky.android.rxjava.pagination;\n\nimport android.support.v7.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.rxbus.RxBus;\nimport java.util.ArrayList;\nimport java.util.List;\n\nclass PaginationAutoAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {\n\n  private static final int ITEM_LOG = 0;\n\n  private final List<String> _items = new ArrayList<>();\n  private final RxBus _bus;\n\n  PaginationAutoAdapter(RxBus bus) {\n    _bus = bus;\n  }\n\n  @Override\n  public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n    return ItemLogViewHolder.create(parent);\n  }\n\n  @Override\n  public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {\n    ((ItemLogViewHolder) holder).bindContent(_items.get(position));\n\n    boolean lastPositionReached = position == _items.size() - 1;\n    if (lastPositionReached) {\n      _bus.send(new PageEvent());\n    }\n  }\n\n  @Override\n  public int getItemViewType(int position) {\n    return ITEM_LOG;\n  }\n\n  @Override\n  public int getItemCount() {\n    return _items.size();\n  }\n\n  void addItems(List<String> items) {\n    _items.addAll(items);\n  }\n\n  private static class ItemLogViewHolder extends RecyclerView.ViewHolder {\n    ItemLogViewHolder(View itemView) {\n      super(itemView);\n    }\n\n    static ItemLogViewHolder create(ViewGroup parent) {\n      return new ItemLogViewHolder(\n          LayoutInflater.from(parent.getContext()).inflate(R.layout.item_log, parent, false));\n    }\n\n    void bindContent(String content) {\n      ((TextView) itemView).setText(content);\n    }\n  }\n\n  static class PageEvent {}\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/pagination/PaginationAutoFragment.java",
    "content": "package com.morihacky.android.rxjava.pagination;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\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.ProgressBar;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport com.morihacky.android.rxjava.MainActivity;\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.fragments.BaseFragment;\nimport com.morihacky.android.rxjava.rxbus.RxBus;\nimport io.reactivex.Flowable;\nimport io.reactivex.android.schedulers.AndroidSchedulers;\nimport io.reactivex.disposables.CompositeDisposable;\nimport io.reactivex.disposables.Disposable;\nimport io.reactivex.processors.PublishProcessor;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\npublic class PaginationAutoFragment extends BaseFragment {\n\n  @BindView(R.id.list_paging)\n  RecyclerView _pagingList;\n\n  @BindView(R.id.progress_paging)\n  ProgressBar _progressBar;\n\n  private PaginationAutoAdapter _adapter;\n  private RxBus _bus;\n  private CompositeDisposable _disposables;\n  private PublishProcessor<Integer> _paginator;\n  private boolean _requestUnderWay = false;\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_pagination, container, false);\n    ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n\n    _bus = ((MainActivity) getActivity()).getRxBusSingleton();\n\n    LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());\n    layoutManager.setOrientation(LinearLayoutManager.VERTICAL);\n    _pagingList.setLayoutManager(layoutManager);\n\n    _adapter = new PaginationAutoAdapter(_bus);\n    _pagingList.setAdapter(_adapter);\n\n    _paginator = PublishProcessor.create();\n  }\n\n  @Override\n  public void onStart() {\n    super.onStart();\n    _disposables = new CompositeDisposable();\n\n    Disposable d2 =\n        _paginator\n            .onBackpressureDrop()\n            .doOnNext(\n                i -> {\n                  _requestUnderWay = true;\n                  _progressBar.setVisibility(View.VISIBLE);\n                })\n            .concatMap(this::_itemsFromNetworkCall)\n            .observeOn(AndroidSchedulers.mainThread())\n            .map(\n                items -> {\n                  _adapter.addItems(items);\n                  _adapter.notifyDataSetChanged();\n\n                  return items;\n                })\n            .doOnNext(\n                i -> {\n                  _requestUnderWay = false;\n                  _progressBar.setVisibility(View.INVISIBLE);\n                })\n            .subscribe();\n\n    // I'm using an RxBus purely to hear from a nested button click\n    // we don't really need Rx for this part. it's just easy ¯\\_(ツ)_/¯\n\n    Disposable d1 =\n        _bus.asFlowable()\n            .filter(o -> !_requestUnderWay)\n            .subscribe(\n                event -> {\n                  if (event instanceof PaginationAutoAdapter.PageEvent) {\n\n                    // trigger the paginator for the next event\n                    int nextPage = _adapter.getItemCount();\n                    _paginator.onNext(nextPage);\n                  }\n                });\n\n    _disposables.add(d1);\n    _disposables.add(d2);\n\n    _paginator.onNext(0);\n  }\n\n  @Override\n  public void onStop() {\n    super.onStop();\n    _disposables.clear();\n  }\n\n  /** Fake Observable that simulates a network call and then sends down a list of items */\n  private Flowable<List<String>> _itemsFromNetworkCall(int pageStart) {\n    return Flowable.just(true)\n        .observeOn(AndroidSchedulers.mainThread())\n        .delay(2, TimeUnit.SECONDS)\n        .map(\n            dummy -> {\n              List<String> items = new ArrayList<>();\n              for (int i = 0; i < 10; i++) {\n                items.add(\"Item \" + (pageStart + i));\n              }\n              return items;\n            });\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/pagination/PaginationFragment.java",
    "content": "package com.morihacky.android.rxjava.pagination;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\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.ProgressBar;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport com.morihacky.android.rxjava.MainActivity;\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.fragments.BaseFragment;\nimport com.morihacky.android.rxjava.rxbus.RxBus;\nimport io.reactivex.Flowable;\nimport io.reactivex.android.schedulers.AndroidSchedulers;\nimport io.reactivex.disposables.CompositeDisposable;\nimport io.reactivex.disposables.Disposable;\nimport io.reactivex.processors.PublishProcessor;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\npublic class PaginationFragment extends BaseFragment {\n\n  @BindView(R.id.list_paging)\n  RecyclerView _pagingList;\n\n  @BindView(R.id.progress_paging)\n  ProgressBar _progressBar;\n\n  private PaginationAdapter _adapter;\n  private RxBus _bus;\n  private CompositeDisposable _disposables;\n  private PublishProcessor<Integer> _paginator;\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n\n    _bus = ((MainActivity) getActivity()).getRxBusSingleton();\n\n    LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());\n    layoutManager.setOrientation(LinearLayoutManager.VERTICAL);\n    _pagingList.setLayoutManager(layoutManager);\n\n    _adapter = new PaginationAdapter(_bus);\n    _pagingList.setAdapter(_adapter);\n\n    _paginator = PublishProcessor.create();\n  }\n\n  @Override\n  public void onStart() {\n    super.onStart();\n    _disposables = new CompositeDisposable();\n\n    Disposable d2 =\n        _paginator\n            .onBackpressureDrop()\n            .concatMap(nextPage -> _itemsFromNetworkCall(nextPage + 1, 10))\n            .observeOn(AndroidSchedulers.mainThread())\n            .map(\n                items -> {\n                  int start = _adapter.getItemCount() - 1;\n\n                  _adapter.addItems(items);\n                  _adapter.notifyItemRangeInserted(start, 10);\n\n                  _progressBar.setVisibility(View.INVISIBLE);\n\n                  return items;\n                })\n            .subscribe();\n\n    // I'm using an Rxbus purely to hear from a nested button click\n    // we don't really need Rx for this part. it's just easy ¯\\_(ツ)_/¯\n    Disposable d1 =\n        _bus.asFlowable()\n            .subscribe(\n                event -> {\n                  if (event instanceof PaginationAdapter.ItemBtnViewHolder.PageEvent) {\n\n                    // trigger the paginator for the next event\n                    int nextPage = _adapter.getItemCount() - 1;\n                    _paginator.onNext(nextPage);\n                  }\n                });\n\n    _disposables.add(d1);\n    _disposables.add(d2);\n  }\n\n  @Override\n  public void onStop() {\n    super.onStop();\n    _disposables.clear();\n  }\n\n  /** Fake Observable that simulates a network call and then sends down a list of items */\n  private Flowable<List<String>> _itemsFromNetworkCall(int start, int count) {\n    return Flowable.just(true)\n        .observeOn(AndroidSchedulers.mainThread())\n        .doOnNext(dummy -> _progressBar.setVisibility(View.VISIBLE))\n        .delay(2, TimeUnit.SECONDS)\n        .map(\n            dummy -> {\n              List<String> items = new ArrayList<>();\n              for (int i = 0; i < count; i++) {\n                items.add(\"Item \" + (start + i));\n              }\n              return items;\n            });\n  }\n\n  // -----------------------------------------------------------------------------------\n  // WIRING up the views required for this example\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_pagination, container, false);\n    ButterKnife.bind(this, layout);\n    return layout;\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/retrofit/Contributor.java",
    "content": "package com.morihacky.android.rxjava.retrofit;\n\npublic class Contributor {\n  public String login;\n  public long contributions;\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/retrofit/GithubApi.java",
    "content": "package com.morihacky.android.rxjava.retrofit;\n\nimport java.util.List;\n\nimport io.reactivex.Observable;\nimport retrofit2.http.GET;\nimport retrofit2.http.Path;\n\npublic interface GithubApi {\n\n  /** See https://developer.github.com/v3/repos/#list-contributors */\n  @GET(\"/repos/{owner}/{repo}/contributors\")\n  Observable<List<Contributor>> contributors(\n      @Path(\"owner\") String owner, @Path(\"repo\") String repo);\n\n  @GET(\"/repos/{owner}/{repo}/contributors\")\n  List<Contributor> getContributors(@Path(\"owner\") String owner, @Path(\"repo\") String repo);\n\n  /** See https://developer.github.com/v3/users/ */\n  @GET(\"/users/{user}\")\n  Observable<User> user(@Path(\"user\") String user);\n\n  /** See https://developer.github.com/v3/users/ */\n  @GET(\"/users/{user}\")\n  User getUser(@Path(\"user\") String user);\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/retrofit/GithubService.java",
    "content": "package com.morihacky.android.rxjava.retrofit;\n\nimport android.text.TextUtils;\n\nimport com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;\n\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport retrofit2.Retrofit;\nimport retrofit2.converter.gson.GsonConverterFactory;\n\nimport static java.lang.String.format;\n\npublic class GithubService {\n\n  private GithubService() {}\n\n  public static GithubApi createGithubService(final String githubToken) {\n    Retrofit.Builder builder =\n        new Retrofit.Builder()\n            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())\n            .addConverterFactory(GsonConverterFactory.create())\n            .baseUrl(\"https://api.github.com\");\n\n    if (!TextUtils.isEmpty(githubToken)) {\n\n      OkHttpClient client =\n          new OkHttpClient.Builder()\n              .addInterceptor(\n                  chain -> {\n                    Request request = chain.request();\n                    Request newReq =\n                        request\n                            .newBuilder()\n                            .addHeader(\"Authorization\", format(\"token %s\", githubToken))\n                            .build();\n                    return chain.proceed(newReq);\n                  })\n              .build();\n\n      builder.client(client);\n    }\n\n    return builder.build().create(GithubApi.class);\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/retrofit/User.java",
    "content": "package com.morihacky.android.rxjava.retrofit;\n\npublic class User {\n  public String name;\n  public String email;\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/rxbus/RxBus.java",
    "content": "package com.morihacky.android.rxjava.rxbus;\n\nimport com.jakewharton.rxrelay2.PublishRelay;\nimport com.jakewharton.rxrelay2.Relay;\n\nimport io.reactivex.BackpressureStrategy;\nimport io.reactivex.Flowable;\n\n/** courtesy: https://gist.github.com/benjchristensen/04eef9ca0851f3a5d7bf */\npublic class RxBus {\n\n  private final Relay<Object> _bus = PublishRelay.create().toSerialized();\n\n  public void send(Object o) {\n    _bus.accept(o);\n  }\n\n  public Flowable<Object> asFlowable() {\n    return _bus.toFlowable(BackpressureStrategy.LATEST);\n  }\n\n  public boolean hasObservers() {\n    return _bus.hasObservers();\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/rxbus/RxBusDemoFragment.java",
    "content": "package com.morihacky.android.rxjava.rxbus;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport butterknife.ButterKnife;\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.fragments.BaseFragment;\n\npublic class RxBusDemoFragment extends BaseFragment {\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_rxbus_demo, container, false);\n    ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n\n    getActivity()\n        .getSupportFragmentManager()\n        .beginTransaction()\n        .replace(R.id.demo_rxbus_frag_1, new RxBusDemo_TopFragment())\n        .replace(R.id.demo_rxbus_frag_2, new RxBusDemo_Bottom3Fragment())\n        //.replace(R.id.demo_rxbus_frag_2, new RxBusDemo_Bottom2Fragment())\n        //.replace(R.id.demo_rxbus_frag_2, new RxBusDemo_Bottom1Fragment())\n        .commit();\n  }\n\n  public static class TapEvent {}\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/rxbus/RxBusDemo_Bottom1Fragment.java",
    "content": "package com.morihacky.android.rxjava.rxbus;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v4.view.ViewCompat;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport com.morihacky.android.rxjava.MainActivity;\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.fragments.BaseFragment;\nimport io.reactivex.disposables.CompositeDisposable;\n\npublic class RxBusDemo_Bottom1Fragment extends BaseFragment {\n\n  @BindView(R.id.demo_rxbus_tap_txt)\n  TextView _tapEventTxtShow;\n\n  private CompositeDisposable _disposables;\n  private RxBus _rxBus;\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_rxbus_bottom, container, false);\n    ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n    _rxBus = ((MainActivity) getActivity()).getRxBusSingleton();\n  }\n\n  @Override\n  public void onStart() {\n    super.onStart();\n    _disposables = new CompositeDisposable();\n\n    _disposables.add(\n        _rxBus\n            .asFlowable()\n            .subscribe(\n                event -> {\n                  if (event instanceof RxBusDemoFragment.TapEvent) {\n                    _showTapText();\n                  }\n                }));\n  }\n\n  @Override\n  public void onStop() {\n    super.onStop();\n    _disposables.clear();\n  }\n\n  private void _showTapText() {\n    _tapEventTxtShow.setVisibility(View.VISIBLE);\n    _tapEventTxtShow.setAlpha(1f);\n    ViewCompat.animate(_tapEventTxtShow).alphaBy(-1f).setDuration(400);\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/rxbus/RxBusDemo_Bottom2Fragment.java",
    "content": "package com.morihacky.android.rxjava.rxbus;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v4.view.ViewCompat;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport com.morihacky.android.rxjava.MainActivity;\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.fragments.BaseFragment;\nimport io.reactivex.Flowable;\nimport io.reactivex.android.schedulers.AndroidSchedulers;\nimport io.reactivex.disposables.CompositeDisposable;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\npublic class RxBusDemo_Bottom2Fragment extends BaseFragment {\n\n  @BindView(R.id.demo_rxbus_tap_txt)\n  TextView _tapEventTxtShow;\n\n  @BindView(R.id.demo_rxbus_tap_count)\n  TextView _tapEventCountShow;\n\n  private RxBus _rxBus;\n  private CompositeDisposable _disposables;\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_rxbus_bottom, container, false);\n    ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n    _rxBus = ((MainActivity) getActivity()).getRxBusSingleton();\n  }\n\n  @Override\n  public void onStart() {\n    super.onStart();\n    _disposables = new CompositeDisposable();\n\n    Flowable<Object> tapEventEmitter = _rxBus.asFlowable().share();\n\n    _disposables.add(\n        tapEventEmitter.subscribe(\n            event -> {\n              if (event instanceof RxBusDemoFragment.TapEvent) {\n                _showTapText();\n              }\n            }));\n\n    Flowable<Object> debouncedEmitter = tapEventEmitter.debounce(1, TimeUnit.SECONDS);\n    Flowable<List<Object>> debouncedBufferEmitter = tapEventEmitter.buffer(debouncedEmitter);\n\n    _disposables.add(\n        debouncedBufferEmitter\n            .observeOn(AndroidSchedulers.mainThread())\n            .subscribe(\n                taps -> {\n                  _showTapCount(taps.size());\n                }));\n  }\n\n  @Override\n  public void onStop() {\n    super.onStop();\n    _disposables.clear();\n  }\n\n  // -----------------------------------------------------------------------------------\n  // Helper to show the text via an animation\n\n  private void _showTapText() {\n    _tapEventTxtShow.setVisibility(View.VISIBLE);\n    _tapEventTxtShow.setAlpha(1f);\n    ViewCompat.animate(_tapEventTxtShow).alphaBy(-1f).setDuration(400);\n  }\n\n  private void _showTapCount(int size) {\n    _tapEventCountShow.setText(String.valueOf(size));\n    _tapEventCountShow.setVisibility(View.VISIBLE);\n    _tapEventCountShow.setScaleX(1f);\n    _tapEventCountShow.setScaleY(1f);\n    ViewCompat.animate(_tapEventCountShow)\n        .scaleXBy(-1f)\n        .scaleYBy(-1f)\n        .setDuration(800)\n        .setStartDelay(100);\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/rxbus/RxBusDemo_Bottom3Fragment.java",
    "content": "package com.morihacky.android.rxjava.rxbus;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v4.view.ViewCompat;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport com.morihacky.android.rxjava.MainActivity;\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.fragments.BaseFragment;\nimport io.reactivex.android.schedulers.AndroidSchedulers;\nimport io.reactivex.disposables.CompositeDisposable;\nimport io.reactivex.flowables.ConnectableFlowable;\nimport java.util.concurrent.TimeUnit;\n\npublic class RxBusDemo_Bottom3Fragment extends BaseFragment {\n\n  @BindView(R.id.demo_rxbus_tap_txt)\n  TextView _tapEventTxtShow;\n\n  @BindView(R.id.demo_rxbus_tap_count)\n  TextView _tapEventCountShow;\n\n  private RxBus _rxBus;\n  private CompositeDisposable _disposables;\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_rxbus_bottom, container, false);\n    ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n    _rxBus = ((MainActivity) getActivity()).getRxBusSingleton();\n  }\n\n  @Override\n  public void onStart() {\n    super.onStart();\n    _disposables = new CompositeDisposable();\n\n    ConnectableFlowable<Object> tapEventEmitter = _rxBus.asFlowable().publish();\n\n    _disposables //\n        .add(\n        tapEventEmitter.subscribe(\n            event -> {\n              if (event instanceof RxBusDemoFragment.TapEvent) {\n                _showTapText();\n              }\n            }));\n\n    _disposables.add(\n        tapEventEmitter\n            .publish(stream -> stream.buffer(stream.debounce(1, TimeUnit.SECONDS)))\n            .observeOn(AndroidSchedulers.mainThread())\n            .subscribe(\n                taps -> {\n                  _showTapCount(taps.size());\n                }));\n\n    _disposables.add(tapEventEmitter.connect());\n  }\n\n  @Override\n  public void onStop() {\n    super.onStop();\n    _disposables.clear();\n  }\n\n  // -----------------------------------------------------------------------------------\n  // Helper to show the text via an animation\n\n  private void _showTapText() {\n    _tapEventTxtShow.setVisibility(View.VISIBLE);\n    _tapEventTxtShow.setAlpha(1f);\n    ViewCompat.animate(_tapEventTxtShow).alphaBy(-1f).setDuration(400);\n  }\n\n  private void _showTapCount(int size) {\n    _tapEventCountShow.setText(String.valueOf(size));\n    _tapEventCountShow.setVisibility(View.VISIBLE);\n    _tapEventCountShow.setScaleX(1f);\n    _tapEventCountShow.setScaleY(1f);\n    ViewCompat.animate(_tapEventCountShow)\n        .scaleXBy(-1f)\n        .scaleYBy(-1f)\n        .setDuration(800)\n        .setStartDelay(100);\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/rxbus/RxBusDemo_TopFragment.java",
    "content": "package com.morihacky.android.rxjava.rxbus;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport com.morihacky.android.rxjava.MainActivity;\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.fragments.BaseFragment;\n\npublic class RxBusDemo_TopFragment extends BaseFragment {\n\n  private RxBus _rxBus;\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_rxbus_top, container, false);\n    ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n    _rxBus = ((MainActivity) getActivity()).getRxBusSingleton();\n  }\n\n  @OnClick(R.id.btn_demo_rxbus_tap)\n  public void onTapButtonClicked() {\n    if (_rxBus.hasObservers()) {\n      _rxBus.send(new RxBusDemoFragment.TapEvent());\n    }\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/volley/MyVolley.java",
    "content": "package com.morihacky.android.rxjava.volley;\n\nimport android.content.Context;\nimport com.android.volley.RequestQueue;\nimport com.android.volley.toolbox.Volley;\n\n/**\n * Helper class that is used to provide references to initialized RequestQueue(s) and ImageLoader(s)\n */\npublic class MyVolley {\n  private static RequestQueue mRequestQueue;\n\n  private MyVolley() {\n    // no instances\n  }\n\n  public static void init(Context context) {\n    mRequestQueue = Volley.newRequestQueue(context);\n  }\n\n  static RequestQueue getRequestQueue() {\n    if (mRequestQueue != null) {\n      return mRequestQueue;\n    } else {\n      throw new IllegalStateException(\"RequestQueue not initialized\");\n    }\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/volley/VolleyDemoFragment.java",
    "content": "package com.morihacky.android.rxjava.volley;\n\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.support.annotation.Nullable;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ListView;\nimport butterknife.BindView;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport com.android.volley.Request;\nimport com.android.volley.VolleyError;\nimport com.android.volley.toolbox.JsonObjectRequest;\nimport com.android.volley.toolbox.RequestFuture;\nimport com.morihacky.android.rxjava.R;\nimport com.morihacky.android.rxjava.fragments.BaseFragment;\nimport com.morihacky.android.rxjava.wiring.LogAdapter;\n\nimport butterknife.Unbinder;\nimport io.reactivex.Flowable;\nimport io.reactivex.android.schedulers.AndroidSchedulers;\nimport io.reactivex.disposables.CompositeDisposable;\nimport io.reactivex.schedulers.Schedulers;\nimport io.reactivex.subscribers.DisposableSubscriber;\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.ExecutionException;\nimport org.json.JSONObject;\nimport timber.log.Timber;\n\npublic class VolleyDemoFragment extends BaseFragment {\n\n  public static final String TAG = \"VolleyDemoFragment\";\n\n  @BindView(R.id.list_threading_log)\n  ListView _logsList;\n\n  private List<String> _logs;\n  private LogAdapter _adapter;\n  private Unbinder unbinder;\n\n  private CompositeDisposable _disposables = new CompositeDisposable();\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n    View layout = inflater.inflate(R.layout.fragment_volley, container, false);\n    unbinder = ButterKnife.bind(this, layout);\n    return layout;\n  }\n\n  @Override\n  public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n    _setupLogger();\n  }\n\n  @Override\n  public void onPause() {\n    super.onPause();\n    _disposables.clear();\n  }\n\n  @Override\n  public void onDestroyView() {\n    super.onDestroyView();\n    unbinder.unbind();\n  }\n\n  /**\n   * Creates and returns an observable generated from the Future returned from {@code\n   * getRouteData()}. The observable can then be subscribed to as shown in {@code\n   * startVolleyRequest()}\n   *\n   * @return Observable<JSONObject>\n   */\n  public Flowable<JSONObject> newGetRouteData() {\n    return Flowable.defer(\n        () -> {\n          try {\n            return Flowable.just(getRouteData());\n          } catch (InterruptedException | ExecutionException e) {\n            Log.e(\"routes\", e.getMessage());\n            return Flowable.error(e);\n          }\n        });\n  }\n\n  @OnClick(R.id.btn_start_operation)\n  void startRequest() {\n    startVolleyRequest();\n  }\n\n  private void startVolleyRequest() {\n    DisposableSubscriber<JSONObject> d =\n        new DisposableSubscriber<JSONObject>() {\n          @Override\n          public void onNext(JSONObject jsonObject) {\n            Log.e(TAG, \"onNext \" + jsonObject.toString());\n            _log(\"onNext \" + jsonObject.toString());\n          }\n\n          @Override\n          public void onError(Throwable e) {\n            VolleyError cause = (VolleyError) e.getCause();\n            String s = new String(cause.networkResponse.data, Charset.forName(\"UTF-8\"));\n            Log.e(TAG, s);\n            Log.e(TAG, cause.toString());\n            _log(\"onError \" + s);\n          }\n\n          @Override\n          public void onComplete() {\n            Log.e(TAG, \"onCompleted\");\n            Timber.d(\"----- onCompleted\");\n            _log(\"onCompleted \");\n          }\n        };\n\n    newGetRouteData()\n        .subscribeOn(Schedulers.io())\n        .observeOn(AndroidSchedulers.mainThread())\n        .subscribe(d);\n\n    _disposables.add(d);\n  }\n\n  /**\n   * Converts the Asynchronous Request into a Synchronous Future that can be used to block via\n   * {@code Future.get()}. Observables require blocking/synchronous functions\n   *\n   * @return JSONObject\n   * @throws ExecutionException\n   * @throws InterruptedException\n   */\n  private JSONObject getRouteData() throws ExecutionException, InterruptedException {\n    RequestFuture<JSONObject> future = RequestFuture.newFuture();\n    String url = \"http://www.weather.com.cn/adat/sk/101010100.html\";\n    JsonObjectRequest req = new JsonObjectRequest(Request.Method.GET, url, future, future);\n    MyVolley.getRequestQueue().add(req);\n    return future.get();\n  }\n\n  // -----------------------------------------------------------------------------------\n  // Methods that help wiring up the example (irrelevant to RxJava)\n\n  private void _setupLogger() {\n    _logs = new ArrayList<>();\n    _adapter = new LogAdapter(getActivity(), new ArrayList<>());\n    _logsList.setAdapter(_adapter);\n  }\n\n  private void _log(String logMsg) {\n\n    if (_isCurrentlyOnMainThread()) {\n      _logs.add(0, logMsg + \" (main thread) \");\n      _adapter.clear();\n      _adapter.addAll(_logs);\n    } else {\n      _logs.add(0, logMsg + \" (NOT main thread) \");\n\n      // You can only do below stuff on main thread.\n      new Handler(Looper.getMainLooper())\n          .post(\n              () -> {\n                _adapter.clear();\n                _adapter.addAll(_logs);\n              });\n    }\n  }\n\n  private boolean _isCurrentlyOnMainThread() {\n    return Looper.myLooper() == Looper.getMainLooper();\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/morihacky/android/rxjava/wiring/LogAdapter.java",
    "content": "package com.morihacky.android.rxjava.wiring;\n\nimport android.content.Context;\nimport android.widget.ArrayAdapter;\nimport com.morihacky.android.rxjava.R;\nimport java.util.List;\n\npublic class LogAdapter extends ArrayAdapter<String> {\n\n  public LogAdapter(Context context, List<String> logs) {\n    super(context, R.layout.item_log, R.id.item_log, logs);\n  }\n}\n"
  },
  {
    "path": "app/src/main/kotlin/com/morihacky/android/rxjava/ext/RxExt.kt",
    "content": "package com.morihacky.android.rxjava.ext\n\nimport io.reactivex.disposables.CompositeDisposable\nimport io.reactivex.disposables.Disposable\n\noperator fun CompositeDisposable.plus(disposable: Disposable): CompositeDisposable {\n    add(disposable)\n    return this\n}\n\n\n"
  },
  {
    "path": "app/src/main/kotlin/com/morihacky/android/rxjava/fragments/MulticastPlaygroundFragment.kt",
    "content": "package com.morihacky.android.rxjava.fragments\n\nimport android.content.Context\nimport android.os.Bundle\nimport android.os.Handler\nimport android.os.Looper\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.*\nimport butterknife.BindView\nimport butterknife.ButterKnife\nimport butterknife.OnClick\nimport com.jakewharton.rx.replayingShare\nimport com.morihacky.android.rxjava.R\nimport io.reactivex.Observable\nimport io.reactivex.disposables.Disposable\nimport java.util.concurrent.TimeUnit\n\nclass MulticastPlaygroundFragment : BaseFragment() {\n\n    @BindView(R.id.list_threading_log) lateinit var logList: ListView\n    @BindView(R.id.dropdown) lateinit var pickOperatorDD: Spinner\n    @BindView(R.id.msg_text) lateinit var messageText: TextView\n\n    private lateinit var sharedObservable: Observable<Long>\n    private lateinit var adapter: LogAdapter\n\n    private var logs: MutableList<String> = ArrayList()\n    private var disposable1: Disposable? = null\n    private var disposable2: Disposable? = null\n\n    override fun onCreateView(inflater: LayoutInflater?,\n                              container: ViewGroup?,\n                              savedInstanceState: Bundle?): View? {\n        val layout = inflater!!.inflate(R.layout.fragment_multicast_playground, container, false)\n        ButterKnife.bind(this, layout)\n\n        _setupLogger()\n        _setupDropdown()\n\n        return layout\n    }\n\n    @OnClick(R.id.btn_1)\n    fun onBtn1Click() {\n\n        disposable1?.let {\n            it.dispose()\n            _log(\"subscriber 1 disposed\")\n            disposable1 = null\n            return\n        }\n\n        disposable1 =\n                sharedObservable\n                        .doOnSubscribe { _log(\"subscriber 1 (subscribed)\") }\n                        .subscribe({ long -> _log(\"subscriber 1: onNext $long\") })\n\n    }\n\n    @OnClick(R.id.btn_2)\n    fun onBtn2Click() {\n        disposable2?.let {\n            it.dispose()\n            _log(\"subscriber 2 disposed\")\n            disposable2 = null\n            return\n        }\n\n        disposable2 =\n                sharedObservable\n                        .doOnSubscribe { _log(\"subscriber 2 (subscribed)\") }\n                        .subscribe({ long -> _log(\"subscriber 2: onNext $long\") })\n    }\n\n    @OnClick(R.id.btn_3)\n    fun onBtn3Click() {\n        logs = ArrayList<String>()\n        adapter.clear()\n    }\n\n    // -----------------------------------------------------------------------------------\n    // Method that help wiring up the example (irrelevant to RxJava)\n\n    private fun _log(logMsg: String) {\n\n        if (_isCurrentlyOnMainThread()) {\n            logs.add(0, logMsg + \" (main thread) \")\n            adapter.clear()\n            adapter.addAll(logs)\n        } else {\n            logs.add(0, logMsg + \" (NOT main thread) \")\n\n            // You can only do below stuff on main thread.\n            Handler(Looper.getMainLooper()).post {\n                adapter.clear()\n                adapter.addAll(logs)\n            }\n        }\n    }\n\n    private fun _setupLogger() {\n        logs = ArrayList<String>()\n        adapter = LogAdapter(activity, ArrayList<String>())\n        logList.adapter = adapter\n    }\n\n    private fun _setupDropdown() {\n        pickOperatorDD.adapter = ArrayAdapter<String>(context,\n                android.R.layout.simple_spinner_dropdown_item,\n                arrayOf(\".publish().refCount()\",\n                        \".publish().autoConnect(2)\",\n                        \".replay(1).autoConnect(2)\",\n                        \".replay(1).refCount()\",\n                        \".replayingShare()\"))\n\n\n        pickOperatorDD.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {\n\n            override fun onItemSelected(p0: AdapterView<*>?, p1: View?, index: Int, p3: Long) {\n\n                val sourceObservable = Observable.interval(0L, 3, TimeUnit.SECONDS)\n                        .doOnSubscribe { _log(\"observer (subscribed)\") }\n                        .doOnDispose { _log(\"observer (disposed)\") }\n                        .doOnTerminate { _log(\"observer (terminated)\") }\n\n                sharedObservable =\n                        when (index) {\n                            0 -> {\n                                messageText.setText(R.string.msg_demo_multicast_publishRefCount)\n                                sourceObservable.publish().refCount()\n                            }\n                            1 -> {\n                                messageText.setText(R.string.msg_demo_multicast_publishAutoConnect)\n                                sourceObservable.publish().autoConnect(2)\n                            }\n                            2 -> {\n                                messageText.setText(R.string.msg_demo_multicast_replayAutoConnect)\n                                sourceObservable.replay(1).autoConnect(2)\n                            }\n                            3 -> {\n                                messageText.setText(R.string.msg_demo_multicast_replayRefCount)\n                                sourceObservable.replay(1).refCount()\n                            }\n                            4 -> {\n                                messageText.setText(R.string.msg_demo_multicast_replayingShare)\n                                sourceObservable.replayingShare()\n                            }\n                            else -> throw RuntimeException(\"got to pick an op yo!\")\n                        }\n            }\n\n            override fun onNothingSelected(p0: AdapterView<*>?) {}\n        }\n    }\n\n    private fun _isCurrentlyOnMainThread(): Boolean {\n        return Looper.myLooper() == Looper.getMainLooper()\n    }\n\n    private inner class LogAdapter(context: Context, logs: List<String>) :\n            ArrayAdapter<String>(context, R.layout.item_log, R.id.item_log, logs)\n\n}\n\n"
  },
  {
    "path": "app/src/main/kotlin/com/morihacky/android/rxjava/fragments/PlaygroundFragment.kt",
    "content": "package com.morihacky.android.rxjava.fragments\n\nimport android.content.Context\nimport android.os.Bundle\nimport android.os.Handler\nimport android.os.Looper\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.ArrayAdapter\nimport android.widget.ListView\nimport com.morihacky.android.rxjava.R\n\nclass PlaygroundFragment : BaseFragment() {\n\n    private var _logsList: ListView? = null\n    private var _adapter: LogAdapter? = null\n\n    private var _logs: MutableList<String> = ArrayList()\n\n    override fun onCreateView(inflater: LayoutInflater?,\n                              container: ViewGroup?,\n                              savedInstanceState: Bundle?): View? {\n        val view = inflater?.inflate(R.layout.fragment_concurrency_schedulers, container, false)\n\n        _logsList = view?.findViewById(R.id.list_threading_log) as ListView\n        _setupLogger()\n\n        view.findViewById(R.id.btn_start_operation).setOnClickListener { _ ->\n            _log(\"Button clicked\")\n        }\n\n        return view\n    }\n\n    // -----------------------------------------------------------------------------------\n    // Method that help wiring up the example (irrelevant to RxJava)\n\n    private fun _log(logMsg: String) {\n\n        if (_isCurrentlyOnMainThread()) {\n            _logs.add(0, logMsg + \" (main thread) \")\n            _adapter?.clear()\n            _adapter?.addAll(_logs)\n        } else {\n            _logs.add(0, logMsg + \" (NOT main thread) \")\n\n            // You can only do below stuff on main thread.\n            Handler(Looper.getMainLooper()).post {\n                _adapter?.clear()\n                _adapter?.addAll(_logs)\n            }\n        }\n    }\n\n    private fun _setupLogger() {\n        _logs = ArrayList<String>()\n        _adapter = LogAdapter(activity, ArrayList<String>())\n        _logsList?.adapter = _adapter\n    }\n\n    private fun _isCurrentlyOnMainThread(): Boolean {\n        return Looper.myLooper() == Looper.getMainLooper()\n    }\n\n    private inner class LogAdapter(context: Context, logs: List<String>) : ArrayAdapter<String>(context, R.layout.item_log, R.id.item_log, logs)\n}"
  },
  {
    "path": "app/src/main/kotlin/com/morihacky/android/rxjava/fragments/UsingFragment.kt",
    "content": "package com.morihacky.android.rxjava.fragments\n\nimport android.content.Context\nimport android.os.Bundle\nimport android.os.Handler\nimport android.os.Looper\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.ArrayAdapter\nimport android.widget.ListView\nimport android.widget.TextView\nimport com.morihacky.android.rxjava.R\nimport io.reactivex.Flowable\nimport io.reactivex.functions.Consumer\nimport io.reactivex.functions.Function\nimport org.reactivestreams.Publisher\nimport java.util.*\nimport java.util.concurrent.Callable\n\nclass UsingFragment : BaseFragment() {\n\n    private lateinit var _logs: MutableList<String>\n    private lateinit var _logsList: ListView\n    private lateinit var _adapter: UsingFragment.LogAdapter\n\n    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {\n        val view = inflater?.inflate(R.layout.fragment_buffer, container, false)\n        _logsList = view?.findViewById(R.id.list_threading_log) as ListView\n\n        (view.findViewById(R.id.text_description) as TextView).setText(R.string.msg_demo_using)\n\n        _setupLogger()\n        view.findViewById(R.id.btn_start_operation).setOnClickListener { executeUsingOperation() }\n        return view\n    }\n\n    private fun executeUsingOperation() {\n        val resourceSupplier = Callable<Realm> { Realm() }\n        val sourceSupplier = Function<Realm, Publisher<Int>> { realm ->\n            Flowable.just(true)\n                    .map {\n                        realm.doSomething()\n                        // i would use the copyFromRealm and change it to a POJO\n                        Random().nextInt(50)\n                    }\n        }\n        val disposer = Consumer<Realm> { realm ->\n            realm.clear()\n        }\n\n        Flowable.using(resourceSupplier, sourceSupplier, disposer)\n                .subscribe({ i ->\n                    _log(\"got a value $i - (look at the logs)\")\n                })\n    }\n\n    inner class Realm {\n        init {\n            _log(\"initializing Realm instance\")\n        }\n\n        fun doSomething() {\n            _log(\"do something with Realm instance\")\n        }\n\n        fun clear() {\n            // notice how this is called even before you manually \"dispose\"\n            _log(\"cleaning up the resources (happens before a manual 'dispose'\")\n        }\n    }\n\n    // -----------------------------------------------------------------------------------\n    // Method that help wiring up the example (irrelevant to RxJava)\n\n    private fun _log(logMsg: String) {\n        _logs.add(0, logMsg)\n\n        // You can only do below stuff on main thread.\n        Handler(Looper.getMainLooper()).post {\n            _adapter.clear()\n            _adapter.addAll(_logs)\n        }\n    }\n\n    private fun _setupLogger() {\n        _logs = ArrayList<String>()\n        _adapter = LogAdapter(activity, ArrayList<String>())\n        _logsList.adapter = _adapter\n    }\n\n    private class LogAdapter(context: Context, logs: List<String>) : ArrayAdapter<String>(context, R.layout.item_log, R.id.item_log, logs)\n}"
  },
  {
    "path": "app/src/main/res/drawable/btn_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"rectangle\"\n    >\n    <solid android:color=\"@color/red\"/>\n    <corners\n        android:bottomRightRadius=\"100dp\"\n        android:bottomLeftRadius=\"100dp\"\n        android:topRightRadius=\"100dp\"\n        android:topLeftRadius=\"100dp\"\n        />\n</shape>"
  },
  {
    "path": "app/src/main/res/layout/fragment_buffer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    android:orientation=\"vertical\"\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <TextView\n      android:id=\"@+id/text_description\"\n      android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:padding=\"10dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/msg_demo_buffer\"\n        />\n\n    <Button\n        android:id=\"@+id/btn_start_operation\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:layout_marginLeft=\"90dp\"\n        android:layout_marginRight=\"90dp\"\n        android:textSize=\"16sp\"\n        android:text=\"@string/tap_me\"\n        />\n\n    <ListView\n        android:id=\"@+id/list_threading_log\"\n        android:layout_height=\"match_parent\"\n        android:layout_width=\"match_parent\"\n        />\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_concurrency_schedulers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    android:orientation=\"vertical\"\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <TextView\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:padding=\"10dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/msg_demo_concurrency_schedulers\"\n        />\n\n\n    <LinearLayout\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        >\n\n        <Button\n            android:id=\"@+id/btn_start_operation\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"wrap_content\"\n            android:layout_marginLeft=\"20dp\"\n            android:textSize=\"16sp\"\n            android:text=\"@string/start_long_operation\"\n            />\n\n        <ProgressBar\n            android:id=\"@+id/progress_operation_running\"\n            android:visibility=\"invisible\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"wrap_content\"\n            android:layout_marginLeft=\"20dp\"\n            />\n    </LinearLayout>\n\n    <ListView\n        android:id=\"@+id/list_threading_log\"\n        android:layout_height=\"match_parent\"\n        android:layout_width=\"match_parent\"\n        />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_debounce.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    android:orientation=\"vertical\"\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <TextView\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:padding=\"10dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/msg_demo_debounce\"\n        />\n\n    <LinearLayout\n        android:orientation=\"horizontal\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        >\n\n        <EditText\n            android:id=\"@+id/input_txt_debounce\"\n            android:layout_weight=\"7\"\n            android:layout_height=\"match_parent\"\n            android:layout_width=\"0dp\"\n            android:textSize=\"16sp\"\n            android:hint=\"@string/enter_some_search_text\"\n            android:inputType=\"textNoSuggestions\"\n            />\n\n        <ImageButton\n            android:id=\"@+id/clr_debounce\"\n            android:src=\"@android:drawable/ic_menu_close_clear_cancel\"\n            android:layout_weight=\"1\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"0dp\"\n            />\n\n    </LinearLayout>\n\n    <ListView\n        android:id=\"@+id/list_threading_log\"\n        android:layout_height=\"match_parent\"\n        android:layout_width=\"match_parent\"\n        />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_demo_timing.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    android:orientation=\"vertical\"\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <TableLayout\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        >\n\n        <TableRow\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            >\n\n            <TextView\n                android:layout_weight=\"1\"\n                android:layout_height=\"wrap_content\"\n                android:layout_width=\"match_parent\"\n                android:padding=\"@dimen/text_micro\"\n                android:gravity=\"left\"\n                android:text=\"@string/msg_demo_timing\"\n                android:layout_span=\"2\"\n                />\n        </TableRow>\n\n        <TableRow\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            >\n\n            <Button\n                android:id=\"@+id/btn_demo_timing_1\"\n                android:layout_weight=\"1\"\n                android:layout_height=\"wrap_content\"\n                android:layout_width=\"wrap_content\"\n                android:text=\"@string/btn_1\"\n                />\n\n            <Button\n                android:id=\"@+id/btn_demo_timing_2\"\n                android:layout_weight=\"1\"\n                android:layout_height=\"wrap_content\"\n                android:layout_width=\"wrap_content\"\n                android:text=\"@string/btn_2\"\n                />\n        </TableRow>\n\n        <TableRow\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            >\n\n            <Button\n                android:id=\"@+id/btn_demo_timing_3\"\n                android:layout_weight=\"1\"\n                android:layout_height=\"wrap_content\"\n                android:layout_width=\"wrap_content\"\n                android:text=\"@string/btn_3\"\n                />\n\n            <Button\n                android:id=\"@+id/btn_demo_timing_4\"\n                android:layout_weight=\"1\"\n                android:layout_height=\"wrap_content\"\n                android:layout_width=\"wrap_content\"\n                android:text=\"@string/btn_4\"\n                />\n        </TableRow>\n\n        <TableRow\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            >\n\n            <Button\n                android:id=\"@+id/btn_demo_timing_5\"\n                android:layout_weight=\"1\"\n                android:layout_height=\"wrap_content\"\n                android:layout_width=\"wrap_content\"\n                android:text=\"@string/btn_5\"\n                />\n\n            <Button\n                android:id=\"@+id/btn_clr\"\n                android:src=\"@android:drawable/ic_menu_close_clear_cancel\"\n                android:layout_weight=\"1\"\n                android:layout_height=\"wrap_content\"\n                android:layout_width=\"wrap_content\"\n                android:text=\"@string/clear_log\"\n                />\n        </TableRow>\n    </TableLayout>\n\n\n    <LinearLayout\n        android:orientation=\"horizontal\"\n        android:layout_height=\"match_parent\"\n        android:layout_width=\"match_parent\"\n        >\n\n        <ListView\n            android:id=\"@+id/list_threading_log\"\n            android:layout_weight=\"1\"\n            android:layout_height=\"match_parent\"\n            android:layout_width=\"0dp\"\n            />\n    </LinearLayout>\n\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_double_binding_textview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    android:orientation=\"vertical\"\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <TextView\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:padding=\"10dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/msg_demo_doublebinding\"\n        />\n\n    <LinearLayout\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:layout_marginTop=\"10dp\"\n        >\n\n        <EditText\n            android:id=\"@+id/double_binding_num1\"\n            android:layout_weight=\"1\"\n            android:layout_height=\"50dp\"\n            android:layout_width=\"0dp\"\n            android:gravity=\"center\"\n            android:text=\"@string/one_hundred\"\n            android:inputType=\"numberDecimal\"\n            />\n\n        <TextView\n            android:layout_weight=\"1\"\n            android:layout_height=\"50dp\"\n            android:layout_width=\"0dp\"\n            android:gravity=\"center\"\n            android:text=\"@string/plus\"\n            />\n\n        <EditText\n            android:id=\"@+id/double_binding_num2\"\n            android:layout_weight=\"1\"\n            android:layout_height=\"50dp\"\n            android:layout_width=\"0dp\"\n            android:gravity=\"center\"\n            android:text=\"@string/eight\"\n            android:inputType=\"numberDecimal\"\n            />\n    </LinearLayout>\n\n    <TextView\n        android:id=\"@+id/double_binding_result\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:layout_marginTop=\"10dp\"\n        android:gravity=\"center\"\n        android:textSize=\"45sp\"\n        android:text=\"@string/zero\"\n        />\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_exponential_backoff.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    android:orientation=\"vertical\"\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <TextView\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:padding=\"10dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/msg_demo_exponential_backoff\"\n        />\n\n\n    <LinearLayout\n        android:layout_gravity=\"center\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        >\n\n        <Button\n            android:id=\"@+id/btn_eb_retry\"\n            android:layout_weight=\"1\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"wrap_content\"\n            android:text=\"@string/retry\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_eb_delay\"\n            android:layout_weight=\"1\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"wrap_content\"\n            android:text=\"@string/delay\"\n            />\n    </LinearLayout>\n\n    <ListView\n        android:id=\"@+id/list_threading_log\"\n        android:layout_height=\"match_parent\"\n        android:layout_width=\"match_parent\"\n        />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_form_validation_comb_latest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    android:orientation=\"vertical\"\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <TextView\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:padding=\"10dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/msg_demo_form_comb_latest\"\n        />\n\n\n    <LinearLayout\n        android:orientation=\"horizontal\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        >\n\n        <TextView\n            android:layout_weight=\"1\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"0dp\"\n            android:padding=\"10dp\"\n            android:text=\"@string/enter_a_valid_email_below\"\n            />\n\n        <EditText\n            android:id=\"@+id/demo_combl_email\"\n            android:layout_weight=\"1\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"0dp\"\n            android:inputType=\"textEmailAddress\"\n            />\n    </LinearLayout>\n\n    <LinearLayout\n        android:orientation=\"horizontal\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        >\n\n        <TextView\n            android:layout_weight=\"1\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"0dp\"\n            android:padding=\"10dp\"\n            android:text=\"@string/password_than_8_chrs\"\n            />\n\n        <EditText\n            android:id=\"@+id/demo_combl_password\"\n            android:layout_weight=\"1\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"0dp\"\n            />\n    </LinearLayout>\n\n\n    <LinearLayout\n        android:orientation=\"horizontal\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        >\n\n        <TextView\n            android:layout_weight=\"1\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"0dp\"\n            android:padding=\"10dp\"\n            android:text=\"@string/number_between_1_amp_100\"\n            />\n\n        <EditText\n            android:id=\"@+id/demo_combl_num\"\n            android:layout_weight=\"1\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"0dp\"\n            android:inputType=\"number\"\n            />\n    </LinearLayout>\n\n    <TextView\n        android:id=\"@+id/btn_demo_form_valid\"\n        android:background=\"@color/gray\"\n        android:layout_gravity=\"center\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"wrap_content\"\n        android:layout_marginTop=\"10dp\"\n        android:padding=\"10dp\"\n        android:text=\"@string/submit\"\n        />\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><ScrollView\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <LinearLayout\n        android:orientation=\"vertical\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:paddingTop=\"@dimen/activity_vertical_margin\"\n        android:paddingBottom=\"@dimen/activity_vertical_margin\"\n        android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n        android:paddingRight=\"@dimen/activity_horizontal_margin\"\n        xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        xmlns:tools=\"http://schemas.android.com/tools\"\n        tools:context=\".DemoActivity\"\n        >\n\n        <Button\n            android:id=\"@+id/btn_demo_schedulers\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_schedulers\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_demo_buffer\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_buffer\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_demo_debounce\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_debounce\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_demo_retrofit\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_retrofit\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_demo_double_binding_textview\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_double_binding_textview\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_demo_polling\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_polling\" />\n\n        <Button\n            android:id=\"@+id/btn_demo_rxbus\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_rxbus\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_demo_form_validation_combinel\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_form_validation_combinel\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_demo_pseudo_cache\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_pseudocache\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_demo_timing\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_timing\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_demo_timeout\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_timeout\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_demo_exponential_backoff\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_exponential_backoff\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_demo_rotation_persist\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_rotation_persist\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_demo_volley\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_volley\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_demo_pagination\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_pagination\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_demo_networkDetector\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_networkDetector\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_demo_using\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_using\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_demo_multicastPlayground\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\"\n            android:text=\"@string/btn_demo_multicastPlayground\"\n            />\n    </LinearLayout>\n</ScrollView>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_multicast_playground.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  android:layout_width=\"match_parent\"\n  android:layout_height=\"match_parent\"\n  android:orientation=\"vertical\">\n\n\n  <TableLayout\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <TableRow\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:gravity=\"center\">\n\n      <Spinner\n        android:id=\"@+id/dropdown\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_span=\"3\"/>\n\n    </TableRow>\n\n    <TableRow\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\">\n\n      <TextView\n        android:id=\"@+id/msg_text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"1\"\n        android:layout_span=\"3\"\n        android:padding=\"@dimen/text_micro\"\n        android:text=\"@string/msg_demo_multicast_publishRefCount\"/>\n    </TableRow>\n\n    <TableRow\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\">\n\n      <Button\n        android:id=\"@+id/btn_1\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"1\"\n        android:text=\"Subscribe 1\"/>\n\n      <Button\n        android:id=\"@+id/btn_2\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"1\"\n        android:text=\"Subscribe 2\"/>\n\n      <Button\n        android:id=\"@+id/btn_3\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"1\"\n        android:text=\"Clear log\"/>\n    </TableRow>\n\n  </TableLayout>\n\n\n  <ListView\n    android:id=\"@+id/list_threading_log\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"/>\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_network_detector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    >\n\n    <TextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center\"\n        android:padding=\"10dp\"\n        android:text=\"@string/msg_demo_network_detector\"\n        />\n\n    <ListView\n        android:id=\"@+id/list_threading_log\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_pagination.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    >\n\n    <TextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"@dimen/activity_horizontal_margin\"\n        android:text=\"@string/msg_demo_pagination\"\n        />\n\n    <ProgressBar\n        android:id=\"@+id/progress_paging\"\n        style=\"@style/Widget.AppCompat.ProgressBar.Horizontal\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:indeterminate=\"true\"\n        android:visibility=\"invisible\"\n        />\n\n    <android.support.v7.widget.RecyclerView\n        android:id=\"@+id/list_paging\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        />\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_polling.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    >\n\n    <TextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center\"\n        android:padding=\"10dp\"\n        android:text=\"@string/msg_demo_polling\"\n        />\n\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        >\n\n        <Button\n            android:id=\"@+id/btn_start_simple_polling\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/start_simple_polling\"\n            android:textSize=\"16sp\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_start_increasingly_delayed_polling\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/start_increasingly_delayed_polling\"\n            android:textSize=\"16sp\"\n            />\n    </LinearLayout>\n\n    <ListView\n        android:id=\"@+id/list_threading_log\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_pseudo_cache.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:baselineAligned=\"false\"\n    android:orientation=\"horizontal\">\n\n\n    <LinearLayout\n        android:layout_width=\"0dp\"\n        android:layout_height=\"match_parent\"\n        android:layout_gravity=\"center_vertical\"\n        android:layout_weight=\"1\"\n        android:orientation=\"vertical\"\n        >\n\n\n        <TextView\n            android:id=\"@+id/info_pseudoCache_demo\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"0dp\"\n            android:layout_weight=\"2\"\n            android:background=\"@color/colorPrimary\"\n            android:gravity=\"center\"\n            android:padding=\"@dimen/text_micro\"\n            android:text=\"@string/info_about_the_demo_will_show_up_here\"\n            android:textColor=\"@color/white\"\n            />\n\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@color/colorPrimaryDark\"\n            android:gravity=\"center_vertical\"\n            android:orientation=\"vertical\"\n            android:padding=\"@dimen/activity_horizontal_margin\"\n            >\n\n            <Button\n                android:id=\"@+id/btn_pseudoCache_concat\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center\"\n                android:text=\"@string/concat\"\n                />\n\n\n            <Button\n                android:id=\"@+id/btn_pseudoCache_concatEager\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center\"\n                android:text=\"@string/concat_eager\"\n                />\n\n\n            <Button\n                android:id=\"@+id/btn_pseudoCache_merge\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center\"\n                android:text=\"@string/merge\"\n                />\n\n\n            <Button\n                android:id=\"@+id/btn_pseudoCache_mergeSlowDisk\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center\"\n                android:text=\"@string/merge_n_slower_disk\"\n                />\n\n            <Button\n                android:id=\"@+id/btn_pseudoCache_mergeOptimized\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center\"\n                android:text=\"@string/merge_n_optimized\"\n                />\n\n            <Button\n                android:id=\"@+id/btn_pseudoCache_mergeOptimizedSlowDisk\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center\"\n                android:text=\"@string/merge_n_optimized_slow_disk\"\n                />\n\n        </LinearLayout>\n\n\n        <ListView\n            android:id=\"@+id/info_pseudoCache_listSubscription\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"0dp\"\n            android:layout_weight=\"1\"\n            android:background=\"@color/colorPrimary\"\n            />\n\n\n    </LinearLayout>\n\n    <ListView\n        android:id=\"@+id/info_pseudoCache_listDtl\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"match_parent\"\n        android:layout_weight=\"1\"\n        android:background=\"@color/colorPrimaryDark\"\n        />\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_pseudo_cache_concat.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    android:baselineAligned=\"false\"\n    android:orientation=\"vertical\"\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <Button\n        android:id=\"@+id/btn_start_pseudo_cache\"\n        android:layout_gravity=\"center\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"wrap_content\"\n        android:text=\"@string/start_disk_network_call\"\n        android:layout_margin=\"30dp\"\n        />\n\n    <ListView\n        android:id=\"@+id/log_list\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        />\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_retrofit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    android:orientation=\"vertical\"\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <TextView\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:padding=\"10dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/msg_demo_retrofit\"\n        />\n\n    <LinearLayout\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        >\n\n        <Button\n            android:id=\"@+id/btn_demo_retrofit_contributors\"\n            android:layout_weight=\"2\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"0dip\"\n            android:textSize=\"14sp\"\n            android:text=\"@string/log_contributors_of\"\n            />\n\n        <EditText\n            android:id=\"@+id/demo_retrofit_contributors_username\"\n            android:layout_weight=\"1\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"0dip\"\n            android:textSize=\"12sp\"\n            android:text=\"@string/square\"\n            android:hint=\"@string/owner\"\n            />\n\n        <EditText\n            android:id=\"@+id/demo_retrofit_contributors_repository\"\n            android:layout_weight=\"1\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"0dip\"\n            android:textSize=\"12sp\"\n            android:text=\"@string/retrofit\"\n            android:hint=\"@string/reponame\"\n            />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        >\n\n        <Button\n            android:id=\"@+id/btn_demo_retrofit_contributors_with_user_info\"\n            android:layout_weight=\"2\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"0dip\"\n            android:textSize=\"14sp\"\n            android:text=\"@string/log_with_full_user_info\"\n            />\n\n        <EditText\n            android:id=\"@+id/demo_retrofit_contributors_with_user_info_username\"\n            android:layout_weight=\"1\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"0dip\"\n            android:textSize=\"12sp\"\n            android:text=\"@string/square\"\n            android:hint=\"@string/owner\"\n            />\n\n        <EditText\n            android:id=\"@+id/demo_retrofit_contributors_with_user_info_repository\"\n            android:layout_weight=\"1\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"0dip\"\n            android:textSize=\"12sp\"\n            android:text=\"@string/retrofit\"\n            android:hint=\"@string/reponame\"\n            />\n    </LinearLayout>\n\n    <ListView\n        android:id=\"@+id/log_list\"\n        android:layout_height=\"match_parent\"\n        android:layout_width=\"match_parent\"\n        ></ListView>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_retrofit_async_task_death.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    android:orientation=\"vertical\"\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <TextView\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:padding=\"10dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/msg_demo_retrofit_async_task_death\"\n        />\n\n    <LinearLayout\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        >\n\n        <Button\n            android:id=\"@+id/btn_demo_retrofit_async_death\"\n            android:layout_weight=\"2\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"0dip\"\n            android:textSize=\"14sp\"\n            android:text=\"@string/log_user_info\"\n            />\n\n        <EditText\n            android:id=\"@+id/btn_demo_retrofit_async_death_username\"\n            android:layout_weight=\"1\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"0dip\"\n            android:textSize=\"12sp\"\n            android:text=\"@string/kaushikgopal\"\n            android:hint=\"@string/owner\"\n            />\n\n    </LinearLayout>\n\n    <ListView\n        android:id=\"@+id/log_list\"\n        android:layout_height=\"match_parent\"\n        android:layout_width=\"match_parent\"\n        />\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_rotation_persist.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    android:orientation=\"vertical\"\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <TextView\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:padding=\"10dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/msg_demo_rotation_persist\"\n        />\n\n    <Button\n        android:id=\"@+id/btn_rotate_persist\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:text=\"@string/start_operation\"\n        />\n\n    <ListView\n        android:id=\"@+id/list_threading_log\"\n        android:layout_height=\"match_parent\"\n        android:layout_width=\"match_parent\"\n        />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_rxbus_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    android:background=\"@color/blue\"\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <TextView\n        android:id=\"@+id/demo_rxbus_tap_count\"\n        android:visibility=\"invisible\"\n        android:layout_gravity=\"center\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"wrap_content\"\n        android:textSize=\"@dimen/text_xxlarge\"\n        android:textColor=\"@android:color/white\"\n        android:text=\"@string/one\"\n        />\n\n    <TextView\n        android:id=\"@+id/demo_rxbus_tap_txt\"\n        android:visibility=\"invisible\"\n        android:layout_gravity=\"bottom|center_horizontal\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"wrap_content\"\n        android:paddingBottom=\"@dimen/activity_vertical_margin\"\n        android:textSize=\"@dimen/text_large\"\n        android:textColor=\"@android:color/white\"\n        android:text=\"@string/tap\"\n        />\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_rxbus_demo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    android:id=\"@+id/demo_rxbus_frag_12\"\n    android:baselineAligned=\"false\"\n    android:orientation=\"vertical\"\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    android:minHeight=\"220dp\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <FrameLayout\n        android:id=\"@+id/demo_rxbus_frag_1\"\n        android:layout_weight=\"1\"\n        android:layout_height=\"0dp\"\n        android:layout_width=\"match_parent\"\n        />\n\n    <FrameLayout\n        android:id=\"@+id/demo_rxbus_frag_2\"\n        android:layout_weight=\"1\"\n        android:layout_height=\"0dp\"\n        android:layout_width=\"match_parent\"\n        />\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_rxbus_frag3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <ListView\n        android:id=\"@+id/list_threading_log\"\n        android:layout_height=\"match_parent\"\n        android:layout_width=\"match_parent\"\n        />\n\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_rxbus_top.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    android:background=\"@color/green\"\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <TextView\n        android:layout_gravity=\"center_horizontal\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"wrap_content\"\n        android:gravity=\"center\"\n        android:text=\"@string/msg_demo_rxbus_1\"\n        />\n\n    <Button\n        android:id=\"@+id/btn_demo_rxbus_tap\"\n        android:background=\"@drawable/btn_round\"\n        android:layout_gravity=\"center\"\n        android:layout_height=\"90dp\"\n        android:layout_width=\"90dp\"\n        android:textSize=\"@dimen/text_xlarge\"\n        android:textColor=\"@android:color/white\"\n        android:text=\"Tap\"\n        />\n\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_subject_timeout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    android:orientation=\"vertical\"\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <TextView\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:padding=\"10dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/msg_demo_timeout\"\n        />\n\n    <LinearLayout\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        >\n\n        <Button\n            android:id=\"@+id/btn_demo_timeout_1_2s\"\n            android:layout_weight=\"1\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"0dip\"\n            android:text=\"@string/button_1\"\n            />\n\n        <Button\n            android:id=\"@+id/btn_demo_timeout_1_5s\"\n            android:layout_weight=\"1\"\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"0dip\"\n            android:text=\"@string/button_2\"\n            />\n\n    </LinearLayout>\n\n    <ListView\n        android:id=\"@+id/list_threading_log\"\n        android:layout_height=\"match_parent\"\n        android:layout_width=\"match_parent\"\n        />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_timer_demo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <Button\n        android:id=\"@+id/btn_start_demo\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"wrap_content\"\n        android:text=\"@string/start\"\n        />\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_volley.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    android:orientation=\"vertical\"\n    android:layout_height=\"match_parent\"\n    android:layout_width=\"match_parent\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    >\n\n    <TextView\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:padding=\"10dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/msg_demo_volley\"\n        />\n\n    <Button\n        android:id=\"@+id/btn_start_operation\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:layout_marginLeft=\"90dp\"\n        android:layout_marginRight=\"90dp\"\n        android:textSize=\"16sp\"\n        android:text=\"@string/tap_me\"\n        />\n\n    <ListView\n        android:id=\"@+id/list_threading_log\"\n        android:layout_height=\"match_parent\"\n        android:layout_width=\"match_parent\"\n        />\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/item_btn.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Button\n    android:id=\"@+id/item_btn\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_margin=\"@dimen/activity_horizontal_margin\"\n\n    android:text=\"@string/i_amz_btn\"\n    />"
  },
  {
    "path": "app/src/main/res/layout/item_log.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TextView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/item_log\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingEnd=\"@dimen/activity_horizontal_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingStart=\"@dimen/activity_horizontal_margin\"\n    android:textSize=\"@dimen/text_medium\"\n    tools:text=\"Item Log goes here\"/>"
  },
  {
    "path": "app/src/main/res/layout/item_log_white.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TextView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/item_log\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingEnd=\"@dimen/activity_horizontal_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingStart=\"@dimen/activity_horizontal_margin\"\n    android:textColor=\"@color/white\"\n    android:textSize=\"@dimen/text_medium\"\n    tools:text=\"Item Log goes here\"\n    />"
  },
  {
    "path": "app/src/main/res/menu/demo.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n      xmlns:tools=\"http://schemas.android.com/tools\"\n      tools:context=\".DemoActivity\"\n    >\n    <item\n        android:id=\"@+id/action_settings\"\n        android:title=\"@string/action_settings\"\n        android:orderInCategory=\"100\"\n        android:showAsAction=\"never\"\n        />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n\n    <color name=\"red\">#eb676b</color>\n    <color name=\"blue\">#488cef</color>\n    <color name=\"green\">#3bc4a2</color>\n    <color name=\"gray\">#cfcfcf</color>\n    <color name=\"white\">#fff</color>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<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=\"text_micro\">10sp</dimen>\n    <dimen name=\"text_small\">12sp</dimen>\n    <dimen name=\"text_medium\">14sp</dimen>\n    <dimen name=\"text_regular\">16sp</dimen>\n    <dimen name=\"text_large\">20sp</dimen>\n    <dimen name=\"text_xlarge\">24sp</dimen>\n    <dimen name=\"text_xxlarge\">60sp</dimen>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">Android-RxJava</string>\n    <string name=\"hello_world\">Hello world!</string>\n    <string name=\"action_settings\">Settings</string>\n    <string name=\"github_oauth_token\"><!-- InsertYouroAuthTokenHere --></string>\n\n    <string name=\"btn_demo_schedulers\">bg work (schedulers &amp; concurrency)</string>\n    <string name=\"btn_demo_buffer\">accumulate calls (buffer)</string>\n    <string name=\"btn_demo_debounce\">search text listener(debounce)</string>\n    <string name=\"btn_demo_timeout\">Timeout long running jobs</string>\n    <string name=\"btn_demo_retrofit\">Retrofit + RxJava</string>\n    <string name=\"btn_demo_double_binding_textview\">Double binding (PublishSubject)</string>\n    <string name=\"btn_demo_polling\">Polling with RxJava</string>\n    <string name=\"btn_demo_rxbus\">Event Bus with RxJava</string>\n    <string name=\"btn_demo_form_validation_combinel\">Form Validation with CombineLatest</string>\n    <string name=\"btn_demo_pseudocache\">Pseudo cache using concat</string>\n    <string name=\"btn_demo_timing\">Variations of timing/intervals/delays</string>\n    <string name=\"btn_demo_exponential_backoff\">Exponential backoff</string>\n    <string name=\"btn_demo_rotation_persist\">Rotation persist</string>\n    <string name=\"btn_demo_volley\">Volley request demo</string>\n    <string name=\"btn_demo_pagination\">Paging example</string>\n    <string name=\"btn_demo_pagination_more\">MOAR</string>\n    <string name=\"btn_demo_networkDetector\">Network Detector (Subject)</string>\n    <string name=\"btn_demo_using\">Setup &amp; teardown resources (using)</string>\n    <string name=\"btn_demo_multicastPlayground\">MultiConnect operator playground</string>\n\n    <string name=\"msg_demo_pagination\">This is a demo of how you can do a list pagination with Rx. We page 10 items at a time and there are 55 items altogether</string>\n    <string name=\"msg_demo_volley\">This is a Volley request demo</string>\n    <string name=\"msg_demo_concurrency_schedulers\">This is a demo of how long running operations can be offloaded to a background thread. After the operation is done, we resume back on the main thread. All using RxJava! \\n\\n To really see this shine. Hit the button multiple times and see how the button click which is a ui operation is never blocked because the long operation only runs in the background</string>\n    <string name=\"msg_demo_buffer\">This is a demo of how events can be accumulated using the \"buffer\" operation. Tap the button below repetitively and you will notice in the logs that button taps are collected over a span of 2s and printed below.</string>\n    <string name=\"msg_demo_debounce\">As you type in the input box, it will not shoot out log messages at every single input character change, but rather only pick the last one.</string>\n    <string name=\"msg_demo_timeout\">This is a demo of terminating events, that take too long to process. Events in this demo should timeout in 3 seconds. Button 1 is an event that takes 2s to process, and Button 2 is a n event that takes 5s to process. Hit Button 2, and see in the logs that it\\'s been cancelled, while this is not the case for Button 1.</string>\n    <string name=\"msg_demo_retrofit\">Retrofit from Square is a super easy networking helper library. It works really well with RxJava and these are examples taken from Jake Wharton\\'s talk at Netflix (see README). Really the only interesting bits here are in the code and logs.</string>\n    <string name=\"msg_demo_retrofit_async_task_death\">This shows how you can replace an AsyncTask with RxJava. The interesting parts are in the code.</string>\n    <string name=\"msg_demo_doublebinding\">Watch how the result gloriously auto-updates <ba></ba>sed on your changing inputs. Using a technique like this, you could achieve the two-way binding in Angular Js, or more efficiently use a pattern like the Presentation View Model.</string>\n    <string name=\"msg_demo_polling\">Demo polling or making a call repeatedly with RxJava.\\n\\nSimple polling: Notice in the logs how a (simulated) network call is repeatedly made in the background.</string>\n    <string name=\"msg_demo_exponential_backoff\">These two examples demonstrate retrying and executing with a delay using an exponential backoff strategy.</string>\n    <string name=\"msg_demo_rxbus_1\">Tap on the below button and RxBus will listen to the events</string>\n    <string name=\"msg_demo_form_comb_latest\">Monitor the state of multiple observables with the combineLatest operator. Only after all the 3 inputs contain valid entries will the submit button light up</string>\n    <string name=\"msg_demo_timing\">BTN 1: run single task once (after 2s complete)\\nBTN 2: run task every 1s (start delay of 1s) toggle \\nBTN 3: run task every 1s (start immediately) toggle \\nBTN 4: run task 5 times every 3s (then complete) \\nBTN 5: run task A, pause for sometime, then proceed with Task B</string>\n    <string name=\"msg_demo_rotation_persist\">This is an example of starting an Observable and using the result across rotations. There are many ways to do this, we use ViewModels from architecture components</string>\n    <string name=\"msg_demo_network_detector\">This is a demo of how to use Subjects to detect Network connectivity\\nToggle your Wifi/Network on or off and notice the logs</string>\n    <string name=\"msg_demo_using\">This is a demo of the somewhat unknown operator \"using\".\\n\\nYou typically use it for managing setup/teardown of resources. Classic cases are DB connections (like Realm), sockets, locks etc.\\n\\nTap the button and look at the logcat. Particularly notice how the Realm instance is self-contained. That is, it is auto-disposed right after use.</string>\n    <string name=\"msg_demo_multicast_publishRefCount\">RefCount starts the upstream right away and gets disposed off, when all subscribers stop. Hit S1, Hit S2, Hit S1, Hit S2. Hit S1/S2 now and notice the stream starts all over.</string>\n    <string name=\"msg_demo_multicast_publishAutoConnect\">AutoConnect(2) waits for a min. subscriber count, before starting the upstream. Hit S1 (notice events don\\'t start), Hit S2 (notice events now start), Hit S1 (notice that unsubscribing doesn\\'t affect upstream), Hit S2, wait for sometime and hit S1 again (notice source stream doesn\\'t restart)</string>\n    <string name=\"msg_demo_multicast_replayAutoConnect\">Replay caches the last item. Hit S1, Hit S2, event starts, Hit S2, wait a bit, Hit S2 again (notice it starts with the last item that S1 saw - courtesy Replay). Hit S2, Hit S1, wait a bit. Hit S1 again (notice event upstream continues and doesn\\'t restart)</string>\n    <string name=\"msg_demo_multicast_replayRefCount\">Replay caches the last item. Hit S1, wait a bit, then hit S2 (notice S2 starts immediately with last item that S1 saw), Hit S2, Hit S1. Hit S1/S2 again (notice the stream restarts all over. Interestingly cached last item also removed when both subscribers released)</string>\n    <string name=\"msg_demo_multicast_replayingShare\">Courtesy: new #AndroidDev on the block - JakeWharton. exactly like replay(1).refCount(), but caches the last item even when upstream has been disposed off/released. Hit S1, Hit S2, Hit S1, Hit S2 (notice observable is disposed). Hit S1/S2 again (notice we start with last item emitted)</string>\n\n    <string name=\"msg_pseudoCache_demoInfo_concat\">Concat merges the results sequentially. But notice that the latter subscription starts only AFTER the first one completes. Some unnecessary waiting there.</string>\n    <string name=\"msg_pseudoCache_demoInfo_concatEager\">Concat eager is cooler. Both subscriptions start at the same time (parallely) but the order of emission is respected.</string>\n    <string name=\"msg_pseudoCache_demoInfo_merge\">Merge presents the result as they come in which is great if the disk is *always* faster. If not, you have problems.</string>\n    <string name=\"msg_pseudoCache_demoInfo_mergeSlowDisk\">Here\\'s a fake example where the disk is made slower than the network call. You can see the results are whack.</string>\n    <string name=\"msg_pseudoCache_demoInfo_mergeOptimized\">This is an optimized merge and probably want to use. Notice subscriptions happen immediately and network results are respected regardless of when they come in. So if the disk is slower, it is discarded.</string>\n    <string name=\"msg_pseudoCache_demoInfo_mergeOptimizedSlowDisk\">Similar to optimized merge (same code). Notice though that if the disk is slower it\\'s discarded in favor of the \"fresh\" network data which in this case happens to be faster.</string>\n    <string name=\"tap_me\">tap me</string>\n    <string name=\"start_long_operation\">Start long operation</string>\n    <string name=\"enter_some_search_text\">Enter some search text</string>\n    <string name=\"btn_1\">BTN 1</string>\n    <string name=\"btn_2\">BTN 2</string>\n    <string name=\"btn_3\">BTN 3</string>\n    <string name=\"btn_4\">BTN 4</string>\n    <string name=\"btn_5\">BTN 5</string>\n    <string name=\"clear_log\">CLEAR LOG</string>\n    <string name=\"one_hundred\">100</string>\n    <string name=\"plus\">+</string>\n    <string name=\"eight\">8</string>\n    <string name=\"zero\">0</string>\n    <string name=\"retry\">Retry</string>\n    <string name=\"delay\">Delay</string>\n    <string name=\"enter_a_valid_email_below\">Enter a valid email below:</string>\n    <string name=\"password_than_8_chrs\"><![CDATA[password (> than 8 chrs):]]></string>\n    <string name=\"number_between_1_amp_100\"><![CDATA[number (between 1 & 100):]]></string>\n    <string name=\"submit\">Submit</string>\n    <string name=\"start_simple_polling\">Start simple polling</string>\n    <string name=\"start_increasingly_delayed_polling\">Start increasingly delayed polling</string>\n    <string name=\"info_about_the_demo_will_show_up_here\">Info about the demo will show up here</string>\n    <string name=\"concat\">concat</string>\n    <string name=\"concat_eager\">concat (eager)</string>\n    <string name=\"merge\">merge</string>\n    <string name=\"merge_n_slower_disk\">merge (slower disk)</string>\n    <string name=\"merge_n_optimized\">merge (optimized)</string>\n    <string name=\"merge_n_optimized_slow_disk\">merge (optimized) slow disk</string>\n    <string name=\"start_disk_network_call\"><![CDATA[Start disk > network call]]></string>\n    <string name=\"log_contributors_of\">Log contributors of:</string>\n    <string name=\"square\">square</string>\n    <string name=\"owner\">owner</string>\n    <string name=\"retrofit\">retrofit</string>\n    <string name=\"reponame\">reponame</string>\n    <string name=\"log_with_full_user_info\">Log with full User Info:</string>\n    <string name=\"log_user_info\">Log User Info:</string>\n    <string name=\"kaushikgopal\">kaushikgopal</string>\n    <string name=\"start_operation\">Start operation</string>\n    <string name=\"one\">1</string>\n    <string name=\"tap\">tap!</string>\n    <string name=\"button_1\">Button 1</string>\n    <string name=\"button_2\">Button 2</string>\n    <string name=\"start\">Start</string>\n    <string name=\"i_amz_btn\">I AMZ BTN</string>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\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</resources>\n"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        jcenter()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:2.3.3'\n        classpath 'com.f2prateek.javafmt:javafmt:0.1.2'\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n        maven { url 'https://maven.google.com' }\n        maven {\n            url \"https://jitpack.io\"\n        }\n    }\n\n    ext {\n        sdkVersion = 24\n        buildToolsVrs = \"25.0.0\"\n\n        kotlinVersion = \"1.1.3-2\"\n\n        archComponentsVersion = \"1.0.0-alpha3\"\n        butterKnifeVersion = '8.5.1'\n        mockitoKotlinVersion = \"1.4.0\"\n        okhttpVersion = \"3.0.1\"\n        retrofitVersion = \"2.0.0\"\n        supportLibVersion = \"25.3.1\"\n    }\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Wed May 24 09:29:38 PDT 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.0-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Settings specified in this file will override any Gradle settings\n# configured through the IDE.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx10248m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\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\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\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\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\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\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\" -a \"$nonstop\" = \"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    JAVACMD=`cygpath --unix \"$JAVACMD\"`\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# Escape application args\nsave ( ) {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\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\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\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\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 Windows variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_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\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": "settings.gradle",
    "content": "include ':app'\n"
  }
]