[
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\nThank you for contributing to RIBs. Before pressing the \"Create Pull Request\" button, please consider the following points.\nFeel free to remove any irrelevant parts that you know are not related to the issue.\nAny HTML comment like this will be stripped when rendering markdown, no need to delete them.\n-->\n\n<!-- Please give a description about what and why you are contributing, even if it's trivial. -->\n**Description**:\n\n<!-- Please include the issue list number(s) or other PR numbers in the description if you are contributing in response to those. -->\n**Related issue(s)**:\n\n<!-- Please include a reasonable set of unit tests if you contribute new code or change an existing one. -->\n"
  },
  {
    "path": ".github/workflows/android.yml",
    "content": "name: CI Android\n\non: [push, pull_request]\n\njobs:\n  build:\n    name: JDK ${{ matrix.java_version }}\n    runs-on: ubuntu-latest\n    defaults:\n      run:\n        working-directory: ./\n    strategy:\n      matrix:\n        java_version: [17]\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v2\n      - name: Install JDK\n        uses: actions/setup-java@v2\n        with:\n          distribution: 'zulu'\n          java-version: ${{ matrix.java_version }}\n      - name: Install Android SDK\n        uses: malinskiy/action-android/install-sdk@release/0.1.1\n      - name: Gradle Wrapper Validation\n        uses: gradle/wrapper-validation-action@v1\n      - name: Configure Gradle\n        # Gradle configuration install deps\n        run: ./gradlew help\n      - name: Spot check\n        run: ./gradlew spotlessCheck --stacktrace\n      - name: Build Project\n        run: ./gradlew assemble --stacktrace\n      - name : Testing\n        run: ./gradlew test --stacktrace\n      - name: Final Checks\n        run: ./gradlew check --stacktrace\n      - name: Upload snapshot\n        run: ./gradlew publish -PmavenCentralUsername=\"${{ secrets.SonatypeUsername }}\" -PmavenCentralPassword=\"${{ secrets.SonatypePassword }}\"\n        if: success() && github.ref == 'refs/heads/main' && github.event_name != 'pull_request' && matrix.java_version == '11'\n"
  },
  {
    "path": ".gitignore",
    "content": "###OSX###\n\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must ends with two \\r.\nIcon\n\n\n# Thumbnails\n._*\n\n# Files that might appear on external disk\n.Spotlight-V100\n.Trashes\n\n\n###Linux###\n\n*~\n\n# KDE directory preferences\n.directory\n\n\n###Android###\n\n# Built application files\n*.apk\n*.ap_\n\n# Files for ART and Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated files\nbin/\ngen/\n\n# Gradle files\n.gradle/\n.gradletasknamecache\nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Proguard folder generated by Eclipse\nproguard/\n\n# Lint\nlint-report.html\nlint-report_files/\nlint_result.txt\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Package Files #\n*.war\n*.ear\n\n# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml\nhs_err_pid*\n\n\n###IntelliJ###\n\n*.iml\n*.ipr\n*.iws\n.idea/\n.intellijPlatform/\n\n###Eclipse###\n\n*.pydevproject\n.metadata\ntmp/\n*.tmp\n*.bak\n*.swp\n*~.nib\n.settings/\n.loadpath\n\n\n### Xcode ###\n# Build generated\nbuild/\nDerivedData/\n\n# Various settings\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\nxcuserdata/\n\n## Other\n*.moved-aside\n*.xccheckout\n*.xcscmblueprint\n\n## Obj-C/Swift specific\n*.hmap\n*.ipa\n*.dSYM.zip\n*.dSYM\n\n# Swift Package Manager\n.build/\n\n# CocoaPods\nPods/\nPodfile.lock\n*.xcworkspace/\n\n# Carthage\nCartfile.resolved\nCarthage/\n\n\n### Other ###\n\n# External tool builders\n.externalToolBuilders/\n\n# Locally stored \"Eclipse launch configurations\"\n*.launch\n\n# CDT-specific\n.cproject\n\n# PDT-specific\n.buildpath\n\n# sbteclipse plugin\n.target\n\n# TeXlipse plugin\n.texlipse\n\n# kotlin\nannotations/\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n### Version 0.1.0\n\n* Initial release\n\n### Version 0.9.2\n\n* Fix forked Workflow invoking didComplete multiple times\n\n### Version 0.9.3\n\n* Upgraded iOS library to Swift 5\n\n### Version 0.10.0\n\n* Updates from the internal fork of RIBs (see Releases section)\n\n### Version 0.10.1\n\n* Added REPLACE_TOP `RouterNavigator` flag\n\n### Version 0.11.0\n\n* Migrate library modules to Kotlin but keep the same APIs\n\n### Version 0.11.1\n\n* Bugfixes for Kotlin migration\n\n### Version 0.11.2\n\n* One more bugfix and log message for Kotlin migration edge case\n\n### Version 0.11.3\n\n* Added NEW_TASK_REPLACE `RouterNavigator` flag\n\n### Version 0.12.0\n\n* Added Jetpack Compose RIB classes\n\n### Version 0.12.1\n\n* `BasicComposeRouter` now auto-attaches child composable content\n\n### Version 0.12.2\n\n* Work around Bazel desugar issues\n\n### Version 0.13.0\n\n* [Android] Adds rib-coroutines and rib-coroutines-test to enable corotouines interop\n\n### Version 0.13.1\n\n* [Android] Upgrade to Kotlin 1.7\n* [Android] Add Window Focus Event API\n* [Android] Add open modifier to doOnRemoved()8\n* [Android] Deprecate mockitokotlin2\n\n### Version 0.13.2\n* [Android] Reverting binary breaking change from 0.13.1 on Basic Interactor\n\n### Version 0.13.3\n* [Intellij] Plugin 0.1.5 \n* [Android] Clear cached CoroutineScope instance once its job completes \n* [Android] Make all TestDispatchers in TestRibDispatchers use the same TestCoroutineScheduler\n\n### Version 0.14.0\n* [Android] Bump Kotlin, Gradle, and other dependencies versions.\n* [Android] Provide option to bind multiple Workers at once on specific RibDispatchers  AndroidAndroid related tickets\n* [Android] Use Kotlin contracts to remove var and !! usage in RibCoroutineWorker\n* [Android] [Draft] Add capability for binding multiple Workers in specified CoroutineDispatcher  AndroidAndroid related tickets\n* [Android] Enable explicit api mode for Kotlin libraries  AndroidAndroid related tickets\n* [Android] Provide a more idiomatic Java API for RibDispatchers\n* [Android] Upgrade code formatters versions  AndroidAndroid related tickets\n* [Android] Create README for Compose Demo  AndroidAndroid related tickets\n* [Android] [Rib Worker] Specify CoroutineDispatcher for onStart/onStop and provide WorkerBinder monitoring option  AndroidAndroid related tickets\n* [Android] Reduce Rx <-> Coroutines interop and allow unconfined coroutines to run eagerly inside Workers onStart\n* [Android] Redesign RouterAndState to avoid router caching\n* [Android] Fix router navigator events source compatibility\n* [Android] Enable strict explicit API mode on rib-base\n* [Android] Introduce RibCoroutineWorker  AndroidAndroid related tickets\n* [Android] Replacing some Behavior/Publish Relay usage in core artifacts with coroutines\n\n### Version 0.14.1\n* [Android] Open lifecycleFlow, thus enabling it for mocking \n* [Android] [WorkerBinder] Guard against potential Worker.coroutineContext being null while using Mockito\n\n### Version 0.14.2\n* [Android] Fix potential for deadlocks in `Worker` binding. by @psteiger in https://github.com/uber/RIBs/pull/582\n* [Android] Add  Rib Worker demo app by @FranAguilera in https://github.com/uber/RIBs/pull/575\n\n### Version 0.15.0\n* Only complete the worker's scope after calling `Worker.onStop` by @psteiger in https://github.com/uber/RIBs/pull/585\n* Improve KDoc on `ActivityLifecycleEvent` by explaining ordering semantics. by @psteiger in https://github.com/uber/RIBs/pull/586\n* Make use of `jvmToolchain` for building the project. by @psteiger in https://github.com/uber/RIBs/pull/583\n* Revamp Gradle scripts by @psteiger in https://github.com/uber/RIBs/pull/588\n* Deprecate old worker by @FranAguilera in https://github.com/uber/RIBs/pull/597\n* Allow overriding default CoroutineDispatcher for WorkerBinder calls by @FranAguilera in https://github.com/uber/RIBs/pull/596\n* Update README.md by @FranAguilera in https://github.com/uber/RIBs/pull/600\n* Deprecate WorkerUnbinder by @FranAguilera in https://github.com/uber/RIBs/pull/601\n* Expose ribActionEvents stream by @FranAguilera in https://github.com/uber/RIBs/pull/599\n\n### Version 0.15.1\n* [Android] Remove use of @JvmDefault in favor of using -Xjvm-default=all by @psteiger in https://github.com/uber/RIBs/pull/576\n\n### Version 0.15.2\n* [Android] Set view tree owners for RibActivity\n\n### Version 0.15.3\n* Add RibCoroutineWorker.bind that receives multiple workers by @FranAguilera in https://github.com/uber/RIBs/pull/607\n* Change default CoroutineContext from empty to default for the RibCoroutineWorker<>Worker conversion by @FranAguilera in https://github.com/uber/RIBs/pull/608\n* Add `RibCoroutineWorker` factory method with `CoroutineScope` as receiver by @psteiger in https://github.com/uber/RIBs/pull/610\n* Update coroutines 1.7.3 by @tyvsmith in https://github.com/uber/RIBs/pull/609\n* Bump kotlinx.coroutines.test to 1.7.3 by @psteiger in https://github.com/uber/RIBs/pull/611\n\n### Version 0.15.4\n* Set JvmVersion to 1.8\n\n### Version 0.16.0\n* Get rid of suppressions for \"invisible_reference\" and \"invisible_member\" by @psteiger in https://github.com/uber/RIBs/pull/618\n* Introduce `TestScope.test(RibCoroutineWorker)` test helper utility. by @psteiger in https://github.com/uber/RIBs/pull/620\n\n### Version 0.16.1\n* [Android] Remove duplicate method by @jbarr in https://github.com/uber/RIBs/pull/621\n\n### Version 0.16.2\n* Make suspend functions callable inside `test(worker) { }` by @psteiger in https://github.com/uber/RIBs/pull/624\n* [RibCoroutineWorker] In `asWorker()`, keep scope alive until lifecycl… by @psteiger in https://github.com/uber/RIBs/pull/625\n\n### Version 0.16.3\n* Fix Flipper Ribtree Plugin memory leak by @mamykin-andrey in https://github.com/uber/RIBs/pull/630\n* Support classes that are both `Worker` and `RibCoroutineWorker` in co… by @psteiger in https://github.com/uber/RIBs/pull/629\n* Increase buffer capacity for mutableRouterEvents flow within RibEvents by @RahulDMello in https://github.com/uber/RIBs/pull/635\n* Add test asserting Rx subscription is disposed after `RibCoroutineWor… by @psteiger in https://github.com/uber/RIBs/pull/628\n\n### Version 0.16.4\n* Remove intrinsics usage in `RibCoroutineWorker` by @psteiger in https://github.com/uber/RIBs/pull/627\n* Remove ios to prep for separate repos by @tyvsmith in https://github.com/uber/RIBs/pull/646\n* fix url typo for ribs-ios by @tyvsmith in https://github.com/uber/RIBs/pull/647\n* Replace the SharedFlow with a StateFlow in the Interactor class by @rysh88 in https://github.com/uber/RIBs/pull/651\n* Remove Java 8 usage from repository. by @psteiger in https://github.com/uber/RIBs/pull/652\n* Bump Kotlin, Compose, Coroutines, AGP. by @psteiger in https://github.com/uber/RIBs/pull/653\n* Add tests for RibEvents buffer size configuration by @psteiger in https://github.com/uber/RIBs/pull/642\n* `ScopeProvider.coroutineScope`: Fail silently when accessing outside of RIB scope. by @psteiger in https://github.com/uber/RIBs/pull/632\n\n### Version 0.16.5\n* Remove unneeded deps\n* Restore source compatibility for Interactor\n* Add mavenCentral publishing by @psteiger in https://github.com/uber/RIBs/pull/655\n\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mobile-open-source@uber.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to RIBs\n\nUber welcomes contributions of all kinds and sizes. This includes everything from from simple bug reports to large features.\n\nBefore we can accept your contributions, we kindly ask you to sign our [Contributor License Agreement](https://cla-assistant.io/uber/RIBs).\n\nWorkflow\n--------\n\nWe love GitHub issues!\n\nFor small feature requests, an issue first proposing it for discussion or demo implementation in a PR suffice.\n\nFor big features, please open an issue so that we can agree on the direction, and hopefully avoid investing a lot of time on a feature that might need reworking.\n\nSmall pull requests for things like typos, bug fixes, etc are always welcome.\n\n### Code style\n\nThis project uses [ktfmt](https://github.com/facebookincubator/ktfmt), [ktlint](https://github.com/pinterest/ktlint), and [GJF](https://github.com/google/google-java-format),\nprovided via the [spotless](https://github.com/diffplug/spotless) gradle plugin.\n\nIf you find that one of your pull reviews does not pass the CI server check due to a code style\nconflict, you can easily fix it by running: `./gradlew spotlessApply`.\n\nGenerally speaking - we use ktfmt, vanilla ktlint + 2space indents, and vanilla GJF. You can integrate both of\nthese in IntelliJ code style via either [GJF's official plugin](https://plugins.jetbrains.com/plugin/8527-google-java-format) or applying code style from Jetbrains' official style.\n\nNo star imports please!\n\nDOs and DON'Ts\n--------------\n\n* DO follow our [coding style](https://github.com/uber/java-code-styles)\n* DO include tests when adding new features. When fixing bugs, start with adding a test that highlights how the current behavior is broken.\n* DO keep the discussions focused. When a new or related topic comes up it's often better to create new issue than to side track the discussion.\n\n* DON'T submit PRs that alter licensing related files or headers. If you believe there's a problem with them, file an issue and we'll be happy to discuss it.\n\nGuiding Principles\n------------------\n\n* We allow anyone to participate in our projects. Tasks can be carried out by anyone that demonstrates the capability to complete them\n* Always be respectful of one another. Assume the best in others and act with empathy at all times\n* Collaborate closely with individuals maintaining the project or experienced users. Getting ideas out in the open and seeing a proposal before it's a pull request helps reduce redundancy and ensures we're all connected to the decision making process\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n  RIBs depends on the following libraries:\n\n  Copyright (C) 2017 The Guava Authors\n   \n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n<img src=\"https://github.com/uber/ribs/blob/assets/rib_horizontal_black.png\" width=\"60%\" height=\"60%\" alt=\"RIBs\"/>\n</p>\n\n[![Android CI](https://github.com/uber/RIBs/actions/workflows/android.yml/badge.svg?branch=main)](https://github.com/uber/RIBs/actions/workflows/android.yml)\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![Maven Central](https://img.shields.io/maven-central/v/com.uber.rib/rib-android.svg)](https://search.maven.org/artifact/com.uber.rib/rib-android)\n\n> ⚠️ **Alert:** RIBs for IOS has been has been moved to a [separate repo](https://github.com/uber/ribs-ios)\n\nRIBs is the cross-platform architecture framework behind many mobile apps at Uber. The name RIBs is short for Router, Interactor and Builder, which are core components of this architecture. This framework is designed for mobile apps with a large number of engineers and nested states.\n\nThe RIBs architecture provides:\n* **Shared architecture across iOS and Android.** Build cross-platform apps that have similar architecture, enabling iOS and Android teams to cross-review business logic code.\n* **Testability and Isolation.** Classes must be easy to unit test and reason about in isolation. Individual RIB classes have distinct responsibilities like: routing, business, view logic, creation. Plus, most RIB logic is decoupled from child RIB logic. This makes RIB classes easy to test and reason about independently.\n* **Tooling for developer productivity.** RIBs come with IDE tooling around code generation, memory leak detection, static analysis and runtime integrations - all which improve developer productivity for large teams or small.\n* **An architecture that scales.** This architecture has proven to scale to hundreds of engineers working on the same codebase and apps with hundreds of RIBs.\n\n\n## Documentation\nTo get started with RIBs, please refer to the [RIBs documentation](https://github.com/uber/RIBs/wiki). This describes key concepts on RIBs, from what they are for, their structure and common use cases.\n\nTo get more hands on with RIBs, we have written a [series of tutorials](https://github.com/uber/RIBs/wiki) that run you through the main aspects of the architecture with hands-on examples.\n\nTo read about the backstory on why we created RIBs, see [this blog post](https://www.uber.com/blog/new-rider-app-architecture/) we wrote when releasing RIBs in production the first time and see [this short video](https://www.youtube.com/watch?v=Q5cTT0M0YXg) where we discussed how the RIBs architecture works.\n\n#### What is the difference between RIBs and MV*/VIPER?\n\nMVC, MVP, MVI, MVVM and VIPER are architecture patterns. RIBs is a framework. What differentiates RIBs from frameworks based on MV*/VIPER is:\n\n- **Business logic drives the app, not the view tree**. Unlike with MV*/VIPER, a RIB does not have to have a view. This means that the app hierarchy is driven by the business logic, not the view tree.\n- **Independent business logic and view trees**. RIBs decouple how the business logic scopes are structured from view hierarchies. This allows the application to have a deep business logic tree, isolating business logic nodes, while maintaining a shallow view hierarchy making layouts, animations and transitions easy.\n\nThere are some other novel things about RIBs. However, these could also be implemented with other MV*/VIPER frameworks. These are:\n- **Cross-platform approach**, allowing iOS and Android architecture to stay in sync.\n- **Tooling for easier adoption** on larger apps or teams. Tooling we are open sourcing includes IDE plugins for code generation and static code analysis.\n- **Strong opinions about how state should be communicated**, using DI and Rx. Each RIB defines its dependencies and what dependencies it needs from its parent. Parent components that fulfill a child’s parent dependencies are provided to child Builders as a constructor dependency to allow for hierarchical DI scoping. This means that information is communicated via these dependencies up and down the tree.\n\n## Usage\n\n1. Clone this repository\n2. Integrate using your preferred installation mechanism\n\nFor usage of the tooling built around RIBs, please see the [Tooling section](https://github.com/uber/RIBs/wiki#rib-tooling) in our documentation.\n\n## Installation for Android\n\nTo integrate the recommended minimum setup for RIBs add the following to your `build.gradle`:\n\n```gradle\ndependencies {\n  annotationProcessor 'com.uber.rib:rib-compiler-test:0.16.5'\n  implementation 'com.uber.rib:rib-android:0.16.5'\n  testImplementation 'com.uber.rib:rib-test:0.16.5'\n}\n```\nThere are a number of extension packages available as well including Kotlin extensions, Jetpack Compose support, Coroutines support\n\n## Related projects\n\nIf you like RIBs, check out other related open source projects from our team:\n- [RIBs-iOS](https://github.com/uber/ribs-ios): The iOS version of RIBs\n- [Needle](https://github.com/uber/needle): a compile-time safe Swift dependency injection framework.\n- [Motif](https://github.com/uber/motif): An abstract on top of Dagger offering simpler APIs for nested scopes.\n- [Swift Concurrency](https://github.com/uber/swift-concurrency): a set of concurrency utility classes used by Uber, inspired by the equivalent [java.util.concurrent](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/package-summary.html) package classes.\n- [Swift Abstract Class](https://github.com/uber/swift-abstract-class): a light-weight library along with an executable that enables compile-time safe abstract class development for Swift projects.\n- [Swift Common](https://github.com/uber/swift-common): common libraries used by this set of Swift open source projects.\n\n## License\n\n    Copyright (C) 2017 Uber Technologies\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n"
  },
  {
    "path": "RELEASING.md",
    "content": "Releasing\n=========\n\nAndroid\n-------\n\n 1. Change the version in `gradle.properties` to a non-SNAPSHOT version.\n 2. Update the `README.md` with the new version.\n 3. Update the `CHANGELOG.md` for the impending release.\n 4. `git commit -am \"Prepare for release X.Y.Z.\"` (where X.Y.Z is the new version)\n 5. `git tag -a X.Y.Z -m \"Version X.Y.Z\"` (where X.Y.Z is the new version)\n 6. `./gradlew clean publish --no-daemon --no-parallel && ./gradlew closeAndReleaseRepository`\n 7. Update the `gradle.properties` to the next SNAPSHOT version.\n 8. `git commit -am \"Prepare next development version.\"`\n 9. `git push && git push --tags`\n"
  },
  {
    "path": "build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    alias(libs.plugins.android.application) apply false\n    alias(libs.plugins.android.library) apply false\n    alias(libs.plugins.kotlin.android) apply false\n    alias(libs.plugins.kotlin.kapt) apply false\n    alias(libs.plugins.kotlin.ksp) apply false\n    alias(libs.plugins.compose.compiler) apply false\n    alias(libs.plugins.maven.publish) apply false\n    alias(libs.plugins.errorprone) apply false\n    alias(libs.plugins.nullaway) apply false\n    alias(libs.plugins.intellij.platform) apply false\n    alias(libs.plugins.spotless) apply false\n}\n"
  },
  {
    "path": "config/lint/lint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<lint>\n    <issue id=\"GoogleAppIndexingWarning\" severity=\"ignore\" />\n</lint>\n"
  },
  {
    "path": "config/spotless/copyright.java",
    "content": "/*\n * Copyright (C) $YEAR. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */"
  },
  {
    "path": "config/spotless/copyright.kt",
    "content": "/*\n * Copyright (C) $YEAR. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */"
  },
  {
    "path": "conventions/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    `kotlin-dsl`\n    `kotlin-dsl-precompiled-script-plugins`\n}\n\nrepositories {\n    google()\n    mavenCentral()\n    gradlePluginPortal()\n}\n\ndependencies {\n    // Workaround for using version catalog on precompiled scripts.\n    // https://github.com/gradle/gradle/issues/15383#issuecomment-779893192\n    implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))\n    implementation(files(appLibs.javaClass.superclass.protectionDomain.codeSource.location))\n    implementation(gradleApi())\n    implementation(libs.gradle.android.plugin)\n    implementation(libs.gradle.kotlin.plugin)\n    implementation(plugin(libs.plugins.errorprone))\n    implementation(plugin(libs.plugins.nullaway))\n    implementation(plugin(libs.plugins.spotless))\n    implementation(plugin(libs.plugins.kotlin.ksp))\n}\n\n// Helper function that transforms a Gradle Plugin alias from a\n// Version Catalog into a valid dependency notation for buildSrc\n// See https://docs.gradle.org/current/userguide/version_catalogs.html\n@Suppress(\"UnusedReceiverParameter\")\nfun DependencyHandlerScope.plugin(plugin: Provider<PluginDependency>) =\n    plugin.map { \"${it.pluginId}:${it.pluginId}.gradle.plugin:${it.version}\" }"
  },
  {
    "path": "conventions/settings.gradle.kts",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\ndependencyResolutionManagement {\n  versionCatalogs {\n    create(\"libs\") {\n      from(files(\"../gradle/libs.versions.toml\"))\n    }\n    create(\"appLibs\") {\n      from(files(\"../gradle/app-libs.versions.toml\"))\n    }\n  }\n}\n\nrootProject.name = \"conventions\"\n"
  },
  {
    "path": "conventions/src/main/kotlin/Extensions.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport com.android.build.gradle.AbstractAppExtension\nimport com.android.build.gradle.LibraryExtension\nimport com.android.build.gradle.TestedExtension\nimport com.android.build.gradle.api.BaseVariant\nimport net.ltgt.gradle.errorprone.CheckSeverity\nimport net.ltgt.gradle.errorprone.errorprone\nimport net.ltgt.gradle.nullaway.nullaway\n\nfun AbstractAppExtension.errorprone() {\n  applicationVariants.configureEach { errorprone() }\n  testErrorprone()\n}\n\nfun LibraryExtension.errorprone() {\n  libraryVariants.configureEach { errorprone() }\n  testErrorprone()\n}\n\nfun TestedExtension.testErrorprone() {\n  testVariants.configureEach { errorprone() }\n  unitTestVariants.configureEach { errorprone() }\n}\n\nfun BaseVariant.errorprone() {\n  javaCompileProvider.configure {\n    options.errorprone.nullaway {\n      severity.set(CheckSeverity.ERROR)\n      annotatedPackages.add(\"com.uber\")\n    }\n    options.errorprone.excludedPaths.set(\".*/build/generated/.*\")\n  }\n}\n"
  },
  {
    "path": "conventions/src/main/kotlin/ribs.android.application.errorprone.gradle.kts",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nval libs = the<org.gradle.accessors.dm.LibrariesForLibs>()\nval appLibs = the<VersionCatalogsExtension>().named(\"appLibs\")\n\nplugins {\n    id(\"ribs.android.application\")\n    id(\"com.google.devtools.ksp\")\n    id(\"net.ltgt.errorprone\")\n    id(\"net.ltgt.nullaway\")\n    id(\"ribs.spotless\")\n}\n\nandroid {\n    errorprone()\n}\n\ndependencies {\n    ksp(appLibs.findLibrary(\"autodispose-errorprone\").get())\n    ksp(appLibs.findLibrary(\"uber-nullaway\").get())\n    errorprone(appLibs.findLibrary(\"errorprone-core\").get())\n    errorprone(libs.guava.jre)\n    errorproneJavac(appLibs.findLibrary(\"errorprone-javac\").get())\n    errorprone(appLibs.findLibrary(\"uber-nullaway\").get())\n}\n"
  },
  {
    "path": "conventions/src/main/kotlin/ribs.android.application.gradle.kts",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport org.jetbrains.kotlin.gradle.dsl.JvmTarget\n\nval libs = the<org.gradle.accessors.dm.LibrariesForLibs>()\n\nplugins {\n    kotlin(\"android\")\n    id(\"com.android.application\")\n    id(\"ribs.spotless\")\n}\n\nkotlin {\n    jvmToolchain(17)\n\n    compilerOptions {\n        jvmTarget = JvmTarget.JVM_1_8\n        optIn.add(\"kotlin.RequiresOptIn\")\n        freeCompilerArgs.add(\"-Xjvm-default=all\")\n        // TODO: For Kotlin 2.2, delete the line above and uncomment the line below.\n        // jvmDefault = JvmDefaultMode.NO_COMPATIBILITY\n    }\n}\n\nandroid {\n    compileSdk = 36\n\n    defaultConfig {\n        minSdk = 21\n        targetSdk = 35\n        versionCode = 1\n        versionName = \"1.0\"\n    }\n\n    compileOptions {\n        sourceCompatibility = JavaVersion.VERSION_1_8\n        targetCompatibility = JavaVersion.VERSION_1_8\n    }\n\n    lint {\n        // Those are just tutorials/demos. Don't fail the build because of them.\n        abortOnError = false\n        quiet = true\n        // FlowOperatorInvokedInComposition Lint detector crashes on lintAnalyzeDebug task\n        // due to version mismatch in kotlinx-metadata-jvm dependency. After bumping compose,\n        // verify if this can be removed and pass CI.\n        disable.add(\"FlowOperatorInvokedInComposition\")\n    }\n\n    buildTypes {\n        debug {\n            matchingFallbacks.add(\"release\")\n        }\n    }\n}\n\nandroidComponents {\n    beforeVariants { variantBuilder ->\n        if (variantBuilder.buildType == \"release\") {\n            variantBuilder.enable = false\n        }\n    }\n}\n"
  },
  {
    "path": "conventions/src/main/kotlin/ribs.android.library.gradle.kts",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport org.jetbrains.kotlin.gradle.dsl.JvmTarget\n\nplugins {\n    kotlin(\"android\")\n    id(\"com.android.library\")\n    id(\"ribs.spotless\")\n}\n\nkotlin {\n    jvmToolchain(17)\n    explicitApi()\n\n    compilerOptions {\n        jvmTarget = JvmTarget.JVM_1_8\n        optIn.add(\"kotlin.RequiresOptIn\")\n        freeCompilerArgs.add(\"-Xjvm-default=all\")\n        // TODO: For Kotlin 2.2, delete the line above and uncomment the line below.\n        // jvmDefault = JvmDefaultMode.NO_COMPATIBILITY\n    }\n}\n\njava {\n    sourceCompatibility = JavaVersion.VERSION_1_8\n    targetCompatibility = JavaVersion.VERSION_1_8\n}\n\nandroid {\n    compileSdk = 36\n\n    defaultConfig {\n        minSdk = 21\n    }\n\n    compileOptions {\n        sourceCompatibility = JavaVersion.VERSION_1_8\n        targetCompatibility = JavaVersion.VERSION_1_8\n    }\n\n    testOptions {\n        targetSdk = 35\n\n        unitTests {\n            isIncludeAndroidResources = true\n        }\n    }\n}\n\nandroidComponents {\n    beforeVariants { variantBuilder ->\n        if (variantBuilder.buildType == \"debug\") {\n            variantBuilder.enable = false\n        }\n    }\n}\n"
  },
  {
    "path": "conventions/src/main/kotlin/ribs.kotlin.library.gradle.kts",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport org.jetbrains.kotlin.gradle.dsl.JvmTarget\n\nplugins {\n    kotlin(\"jvm\")\n    id(\"ribs.spotless\")\n}\n\nkotlin {\n    jvmToolchain(17)\n    explicitApi()\n\n    compilerOptions {\n        freeCompilerArgs.add(\"-Xjvm-default=all\")\n        jvmTarget = JvmTarget.JVM_1_8\n    }\n}\n\njava {\n    sourceCompatibility = JavaVersion.VERSION_1_8\n    targetCompatibility = JavaVersion.VERSION_1_8\n}\n"
  },
  {
    "path": "conventions/src/main/kotlin/ribs.spotless.gradle.kts",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n  id(\"com.diffplug.spotless\")\n}\n\nval libs = the<org.gradle.accessors.dm.LibrariesForLibs>()\n\nconfigure<com.diffplug.gradle.spotless.SpotlessExtension> {\n  format(\"misc\") {\n    target(\"**/*.md\", \"**/.gitignore\")\n    trimTrailingWhitespace()\n    endWithNewline()\n  }\n  kotlin {\n    target(\"**/*.kt\")\n    ktlint(libs.versions.ktlint.get()).editorConfigOverride(\n        mapOf(\n            \"indent_size\" to \"2\",\n            \"continuation_indent_size\" to \"4\",\n        )\n    )\n    ktfmt(libs.versions.ktfmt.get()).googleStyle()\n    licenseHeaderFile(rootProject.file(\"config/spotless/copyright.kt\"))\n    trimTrailingWhitespace()\n    endWithNewline()\n  }\n  java {\n    target(\"src/*/java/**/*.java\")\n    googleJavaFormat(libs.versions.google.java.format.get())\n    licenseHeaderFile(rootProject.file(\"config/spotless/copyright.java\"))\n    removeUnusedImports()\n    trimTrailingWhitespace()\n    endWithNewline()\n  }\n  kotlinGradle {\n    target(\"**/*.gradle.kts\")\n    trimTrailingWhitespace()\n    endWithNewline()\n  }\n  groovyGradle {\n    target(\"**/*.gradle\")\n    trimTrailingWhitespace()\n    endWithNewline()\n  }\n}\n"
  },
  {
    "path": "demos/compose/README.md",
    "content": "The Uber RIBs Compose Demo showcases the integration of RIBs with Jetpack Compose, an Android UI toolkit for building native interfaces using a declarative approach. It demonstrates how to combine RIBs concepts such as Routers, Interactors, and Scopes with Jetpack Compose components like Composable functions and ComposeView.\n\nHere's a summary of the key differences this demo offers:\n\n- RIBs: RIBs (Router, Interactor, Builder) is a mobile app architecture developed by Uber, designed to manage complex navigation flows and business logic in a modular and scalable manner. RIBs have three core components: Router, Interactor, and Builder (now replaced with Motif Scope).\n\n- Scope: In traditional RIBs architecture, the Builder creates and connects the components of a RIB, including the Router, Interactor, and View, while handling dependency injection. The Compose Demo replaces the Builder with Motif Scope, a lightweight dependency injection (DI) library. A Scope provides the necessary dependencies for a RIB, which can be shared across multiple RIBs, enhancing modularity and scalability.\n\n- ComposePresenter: Traditional RIBs architecture employs a Presenter for communication between the Interactor and View. The Compose Demo introduces the ComposePresenter, containing a composable function that describes the UI, allowing RIBs to utilize Jetpack Compose for UI creation.\n\n- ComposeView: While traditional RIBs architecture uses Android ViewGroup components for the View, the Compose Demo employs a ComposeView to host Jetpack Compose content within the RIB's view hierarchy.\n\n- UI: The demo app leverages Jetpack Compose for UI definition in each RIB, with the MainView composable function displaying the Main RIB UI and hosting the content of the active child RIB.\n\n- Coroutines: The Compose Demo uses coroutines to handle asynchronous tasks. Integrated into the RIBs architecture by extending the BasicInteractor with a coroutineScope property, coroutines enable non-blocking, concurrent, and readable asynchronous code while automatically managing their lifecycle.\n\n- Flow: Flow, a Kotlin library for reactive data streams, is used in the Compose Demo to observe and manage state changes. For example, AuthStream employs MutableStateFlow and StateFlow to handle authentication state changes. Integrated with coroutines, Flow allows launching and consuming flows within a coroutine scope.\n  - For example, The MainInteractor observes authStream with the onEach and launchIn functions, reacting to state changes and updating the UI.\n\nIn conclusion, the demo offers an innovative way to build mobile apps using RIBs architecture, effectively creating a SIRs (Scope, Interactor, Router) Architecture.\n"
  },
  {
    "path": "demos/compose/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2025. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.android.application\")\n    alias(libs.plugins.kotlin.ksp)\n    alias(libs.plugins.compose.compiler)\n}\n\nandroid {\n    namespace = \"com.uber.rib.compose\"\n\n    defaultConfig {\n        applicationId = \"com.uber.rib.compose\"\n    }\n    buildFeatures {\n        compose = true\n        buildConfig = true\n    }\n}\n\ndependencies {\n    ksp(appLibs.motif.compiler)\n    implementation(project(\":libraries:rib-android\"))\n    implementation(project(\":libraries:rib-android-compose\"))\n    implementation(project(\":libraries:rib-coroutines\"))\n    implementation(appLibs.activity.compose)\n    implementation(libs.compose.foundation)\n    implementation(appLibs.compose.material)\n    implementation(libs.compose.runtime)\n    implementation(libs.compose.ui)\n    implementation(libs.compose.uitooling)\n    implementation(libs.savedstate)\n    implementation(libs.rxandroid2)\n    implementation(libs.kotlinx.coroutines.android)\n    implementation(libs.kotlinx.coroutines.rx2)\n    implementation(libs.autodispose.coroutines)\n    implementation(appLibs.motif.library)\n\n\n    // Flipper Debug tool integration\n    debugImplementation(libs.flipper)\n    debugImplementation(appLibs.soloader)\n    releaseImplementation(libs.flipper.noop)\n\n    // Flipper RIBs plugin\n    implementation(project(\":tooling:rib-flipper-plugin\"))\n\n    testImplementation(testLibs.junit)\n}\n"
  },
  {
    "path": "demos/compose/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:name=\".ComposeApplication\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/Theme.Android\">\n\n        <activity\n            android:name=\".root.RootActivity\"\n            android:label=\"RIB Compose\"\n            android:windowSoftInputMode=\"adjustResize\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n\n    </application>\n\n</manifest>"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/ComposeApplication.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose\n\nimport android.app.Application\nimport com.facebook.flipper.android.AndroidFlipperClient\nimport com.facebook.flipper.android.utils.FlipperUtils\nimport com.facebook.flipper.core.FlipperClient\nimport com.facebook.flipper.plugins.inspector.DescriptorMapping\nimport com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin\nimport com.facebook.soloader.SoLoader\nimport com.uber.rib.flipper.RibTreePlugin\n\nclass ComposeApplication : Application() {\n\n  override fun onCreate() {\n    super.onCreate()\n    SoLoader.init(this, false)\n\n    if (BuildConfig.DEBUG && FlipperUtils.shouldEnableFlipper(this)) {\n      val client: FlipperClient = AndroidFlipperClient.getInstance(this)\n      client.addPlugin(RibTreePlugin())\n      client.addPlugin(InspectorFlipperPlugin(this, DescriptorMapping.withDefaults()))\n      client.start()\n    }\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/RootActivity.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root\n\nimport android.view.ViewGroup\nimport com.uber.rib.core.RibActivity\nimport com.uber.rib.core.ViewRouter\nimport motif.Creatable\nimport motif.Expose\nimport motif.NoDependencies\nimport motif.ScopeFactory\n\nclass RootActivity : RibActivity() {\n\n  override fun createRouter(parentViewGroup: ViewGroup): ViewRouter<*, *> {\n    return ScopeFactory.create(Parent::class.java)\n      .rootScope(this, findViewById(android.R.id.content))\n      .router()\n  }\n\n  @motif.Scope\n  interface Parent : Creatable<NoDependencies> {\n    fun rootScope(@Expose activity: RibActivity, parentViewGroup: ViewGroup): RootScope\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/RootInteractor.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root\n\nimport com.uber.rib.core.BasicInteractor\nimport com.uber.rib.core.EmptyPresenter\n\nclass RootInteractor(presenter: EmptyPresenter) :\n  BasicInteractor<EmptyPresenter, RootRouter>(presenter)\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/RootRouter.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root\n\nimport com.uber.rib.compose.root.main.MainRouter\nimport com.uber.rib.core.BasicViewRouter\n\nclass RootRouter(\n  view: RootView,\n  interactor: RootInteractor,\n  private val scope: RootScope,\n) : BasicViewRouter<RootView, RootInteractor>(view, interactor) {\n\n  private var mainRouter: MainRouter? = null\n\n  override fun willAttach() {\n    attachMain()\n  }\n\n  override fun willDetach() {\n    detachMain()\n  }\n\n  private fun attachMain() {\n    if (mainRouter == null) {\n      mainRouter = scope.mainScope(view).router().also { attachChild(it) }\n    }\n  }\n\n  private fun detachMain() {\n    mainRouter?.let { detachChild(it) }\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/RootScope.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root\n\nimport android.view.ViewGroup\nimport com.uber.rib.compose.root.main.MainScope\nimport com.uber.rib.compose.util.AnalyticsClient\nimport com.uber.rib.compose.util.AnalyticsClientImpl\nimport com.uber.rib.compose.util.ExperimentClient\nimport com.uber.rib.compose.util.ExperimentClientImpl\nimport com.uber.rib.compose.util.LoggerClient\nimport com.uber.rib.compose.util.LoggerClientImpl\nimport com.uber.rib.core.EmptyPresenter\nimport com.uber.rib.core.RibActivity\nimport motif.Expose\n\n@motif.Scope\ninterface RootScope {\n  fun router(): RootRouter\n\n  fun mainScope(parentViewGroup: ViewGroup): MainScope\n\n  @motif.Objects\n  abstract class Objects {\n    abstract fun router(): RootRouter\n\n    abstract fun interactor(): RootInteractor\n\n    abstract fun presenter(): EmptyPresenter\n\n    fun view(parentViewGroup: ViewGroup): RootView {\n      return RootView(parentViewGroup.context)\n    }\n\n    @Expose\n    fun analyticsClient(activity: RibActivity): AnalyticsClient {\n      return AnalyticsClientImpl(activity.application)\n    }\n\n    @Expose\n    fun experimentClient(activity: RibActivity): ExperimentClient {\n      return ExperimentClientImpl(activity.application)\n    }\n\n    @Expose\n    fun loggerClient(activity: RibActivity): LoggerClient {\n      return LoggerClientImpl(activity.application)\n    }\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/RootView.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root\n\nimport android.content.Context\nimport android.graphics.Color\nimport android.util.AttributeSet\nimport android.widget.FrameLayout\nimport android.widget.TextView\n\nclass RootView\n@JvmOverloads\nconstructor(\n  context: Context,\n  attrs: AttributeSet? = null,\n  defStyle: Int = 0,\n) : FrameLayout(context, attrs, defStyle) {\n\n  init {\n    setBackgroundColor(Color.RED)\n    addView(\n      TextView(context).apply {\n        text = \"root (view)\"\n        setTextColor(Color.WHITE)\n      },\n    )\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/AuthStream.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main\n\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.flow.update\n\nclass AuthStream {\n  private val _authFlow = MutableStateFlow(AuthInfo(false, \"\", \"\"))\n  private val authFlow = _authFlow.asStateFlow()\n\n  fun observe() = authFlow\n\n  fun accept(value: AuthInfo) {\n    _authFlow.update { value }\n  }\n}\n\ndata class AuthInfo(\n  val isLoggedIn: Boolean,\n  val playerOne: String = \"\",\n  val playerTwo: String = \"\",\n)\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/MainInteractor.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main\n\nimport com.uber.rib.core.BasicInteractor\nimport com.uber.rib.core.Bundle\nimport com.uber.rib.core.ComposePresenter\nimport com.uber.rib.core.coroutineScope\nimport kotlinx.coroutines.flow.launchIn\nimport kotlinx.coroutines.flow.onEach\n\nclass MainInteractor(\n  presenter: ComposePresenter,\n  private val authStream: AuthStream,\n  private val childContent: MainRouter.ChildContent,\n) : BasicInteractor<ComposePresenter, MainRouter>(presenter) {\n\n  override fun didBecomeActive(savedInstanceState: Bundle?) {\n    super.didBecomeActive(savedInstanceState)\n\n    router.view.setContent { MainView(childContent = childContent) }\n    authStream\n      .observe()\n      .onEach {\n        if (it.isLoggedIn) {\n          router.detachLoggedOut()\n          router.attachLoggedIn(it)\n        } else {\n          router.detachLoggedIn()\n          router.attachLoggedOut()\n        }\n      }\n      .launchIn(coroutineScope)\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/MainRouter.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main\n\nimport android.view.ViewGroup\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.MutableState\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.ui.platform.ComposeView\nimport com.uber.rib.compose.root.main.loggedin.LoggedInRouter\nimport com.uber.rib.compose.root.main.loggedout.LoggedOutRouter\nimport com.uber.rib.core.BasicViewRouter\n\nclass MainRouter(\n  view: ComposeView,\n  interactor: MainInteractor,\n  private val parentView: ViewGroup,\n  private val scope: MainScope,\n  private val childContent: ChildContent,\n) : BasicViewRouter<ComposeView, MainInteractor>(view, interactor) {\n\n  private var loggedOutRouter: LoggedOutRouter? = null\n  private var loggedInRouter: LoggedInRouter? = null\n\n  override fun willAttach() {\n    super.willAttach()\n    parentView.addView(view)\n  }\n\n  override fun willDetach() {\n    parentView.removeView(view)\n    super.willDetach()\n  }\n\n  internal fun attachLoggedOut() {\n    if (loggedOutRouter == null) {\n      loggedOutRouter =\n        scope.loggedOutScope(childContent.fullScreenSlot).router().also { attachChild(it) }\n    }\n  }\n\n  internal fun attachLoggedIn(authInfo: AuthInfo) {\n    if (loggedInRouter == null) {\n      loggedInRouter =\n        scope.loggedInScope(childContent.fullScreenSlot, authInfo).router().also { attachChild(it) }\n    }\n  }\n\n  internal fun detachLoggedOut() {\n    loggedOutRouter?.let { detachChild(it) }\n    loggedOutRouter = null\n  }\n\n  internal fun detachLoggedIn() {\n    loggedInRouter?.let { detachChild(it) }\n    loggedInRouter = null\n  }\n\n  class ChildContent {\n    internal var fullScreenSlot: MutableState<@Composable () -> Unit> = mutableStateOf({})\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/MainScope.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main\n\nimport android.view.ViewGroup\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.MutableState\nimport androidx.compose.ui.platform.ComposeView\nimport com.uber.rib.compose.root.main.loggedin.LoggedInScope\nimport com.uber.rib.compose.root.main.loggedout.LoggedOutScope\nimport com.uber.rib.core.ComposePresenter\nimport motif.Expose\n\n@motif.Scope\ninterface MainScope {\n  fun router(): MainRouter\n\n  fun loggedOutScope(slot: MutableState<@Composable () -> Unit>): LoggedOutScope\n\n  fun loggedInScope(slot: MutableState<@Composable () -> Unit>, authInfo: AuthInfo): LoggedInScope\n\n  @motif.Objects\n  abstract class Objects {\n    abstract fun router(): MainRouter\n\n    abstract fun interactor(): MainInteractor\n\n    fun presenter(childContent: MainRouter.ChildContent): ComposePresenter {\n      return object : ComposePresenter() {\n        override val composable = @Composable { MainView(childContent) }\n      }\n    }\n\n    fun view(parentViewGroup: ViewGroup): ComposeView {\n      return ComposeView(parentViewGroup.context)\n    }\n\n    abstract fun childContent(): MainRouter.ChildContent\n\n    @Expose abstract fun authStream(): AuthStream\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/MainView.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main\n\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.tooling.preview.Preview\nimport androidx.compose.ui.unit.dp\n\n@Composable\nfun MainView(childContent: MainRouter.ChildContent) {\n  Column(\n    horizontalAlignment = Alignment.CenterHorizontally,\n    verticalArrangement = Arrangement.Top,\n    modifier =\n      Modifier.fillMaxSize().padding(all = 4.dp).padding(top = 14.dp).background(Color(0xFFFFA500)),\n  ) {\n    Text(\"Main RIB (Compose w/ CompView)\")\n    Box(\n      modifier = Modifier.fillMaxWidth().weight(1.0f).padding(4.dp).background(Color.Yellow),\n    ) {\n      childContent.fullScreenSlot.value.invoke()\n    }\n  }\n}\n\n@Preview\n@Composable\nfun MainViewPreview() {\n  MainView(MainRouter.ChildContent())\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/LoggedInEvent.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin\n\nsealed class LoggedInEvent {\n  object LogOutClick : LoggedInEvent()\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/LoggedInInteractor.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin\n\nimport com.uber.rib.compose.root.main.AuthInfo\nimport com.uber.rib.compose.root.main.AuthStream\nimport com.uber.rib.compose.root.main.loggedin.offgame.OffGameInteractor\nimport com.uber.rib.compose.root.main.loggedin.tictactoe.TicTacToeInteractor\nimport com.uber.rib.compose.util.EventStream\nimport com.uber.rib.core.BasicInteractor\nimport com.uber.rib.core.Bundle\nimport com.uber.rib.core.ComposePresenter\nimport com.uber.rib.core.coroutineScope\nimport kotlinx.coroutines.flow.launchIn\nimport kotlinx.coroutines.flow.onEach\nimport kotlinx.coroutines.launch\n\nclass LoggedInInteractor(\n  presenter: ComposePresenter,\n  private val authInfo: AuthInfo,\n  private val authStream: AuthStream,\n  private val eventStream: EventStream<LoggedInEvent>,\n  private val scoreStream: ScoreStream,\n) :\n  BasicInteractor<ComposePresenter, LoggedInRouter>(presenter),\n  OffGameInteractor.Listener,\n  TicTacToeInteractor.Listener {\n\n  override fun didBecomeActive(savedInstanceState: Bundle?) {\n    super.didBecomeActive(savedInstanceState)\n\n    eventStream\n      .observe()\n      .onEach {\n        when (it) {\n          is LoggedInEvent.LogOutClick -> authStream.accept(AuthInfo(false))\n        }\n      }\n      .launchIn(coroutineScope)\n\n    router.attachOffGame(authInfo)\n  }\n\n  override fun onStartGame() {\n    router.detachOffGame()\n    router.attachTicTacToe(authInfo)\n  }\n\n  override fun onGameWon(winner: String?) {\n    if (winner != null) {\n      coroutineScope.launch { scoreStream.addVictory(winner) }\n    }\n\n    router.detachTicTacToe()\n    router.attachOffGame(authInfo)\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/LoggedInRouter.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.MutableState\nimport androidx.compose.runtime.mutableStateOf\nimport com.uber.rib.compose.root.main.AuthInfo\nimport com.uber.rib.compose.root.main.loggedin.offgame.OffGameRouter\nimport com.uber.rib.compose.root.main.loggedin.tictactoe.TicTacToeRouter\nimport com.uber.rib.core.BasicComposeRouter\nimport com.uber.rib.core.ComposePresenter\n\nclass LoggedInRouter(\n  presenter: ComposePresenter,\n  interactor: LoggedInInteractor,\n  slot: MutableState<@Composable () -> Unit>,\n  private val scope: LoggedInScope,\n  private val childContent: ChildContent,\n) : BasicComposeRouter<LoggedInInteractor>(presenter, interactor, slot) {\n\n  private var offGameRouter: OffGameRouter? = null\n  private var ticTacToeRouter: TicTacToeRouter? = null\n\n  internal fun attachOffGame(authInfo: AuthInfo) {\n    if (offGameRouter == null) {\n      offGameRouter =\n        scope.offGameScope(childContent.fullScreenSlot, authInfo).router().also { attachChild(it) }\n    }\n  }\n\n  internal fun attachTicTacToe(authInfo: AuthInfo) {\n    if (ticTacToeRouter == null) {\n      ticTacToeRouter =\n        scope.ticTacToeScope(childContent.fullScreenSlot, authInfo).router().also {\n          attachChild(it)\n        }\n    }\n  }\n\n  internal fun detachOffGame() {\n    offGameRouter?.let { detachChild(it) }\n    offGameRouter = null\n  }\n\n  internal fun detachTicTacToe() {\n    ticTacToeRouter?.let { detachChild(it) }\n    ticTacToeRouter = null\n  }\n\n  class ChildContent {\n    internal var fullScreenSlot: MutableState<@Composable () -> Unit> = mutableStateOf({})\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/LoggedInScope.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.MutableState\nimport com.uber.rib.compose.root.main.AuthInfo\nimport com.uber.rib.compose.root.main.loggedin.offgame.OffGameInteractor\nimport com.uber.rib.compose.root.main.loggedin.offgame.OffGameScope\nimport com.uber.rib.compose.root.main.loggedin.tictactoe.TicTacToeInteractor\nimport com.uber.rib.compose.root.main.loggedin.tictactoe.TicTacToeScope\nimport com.uber.rib.compose.util.EventStream\nimport com.uber.rib.core.ComposePresenter\nimport motif.Expose\n\n@motif.Scope\ninterface LoggedInScope {\n  fun router(): LoggedInRouter\n\n  fun offGameScope(slot: MutableState<@Composable () -> Unit>, authInfo: AuthInfo): OffGameScope\n\n  fun ticTacToeScope(\n    slot: MutableState<@Composable () -> Unit>,\n    authInfo: AuthInfo,\n  ): TicTacToeScope\n\n  @motif.Objects\n  abstract class Objects {\n    abstract fun router(): LoggedInRouter\n\n    abstract fun interactor(): LoggedInInteractor\n\n    abstract fun childContent(): LoggedInRouter.ChildContent\n\n    fun presenter(\n      eventStream: EventStream<LoggedInEvent>,\n      childContent: LoggedInRouter.ChildContent,\n    ): ComposePresenter {\n      return object : ComposePresenter() {\n        override val composable = @Composable { LoggedInView(eventStream, childContent) }\n      }\n    }\n\n    fun eventStream() = EventStream<LoggedInEvent>()\n\n    @Expose\n    fun scoreSteam(authInfo: AuthInfo): ScoreStream {\n      return ScoreStream(authInfo.playerOne, authInfo.playerTwo)\n    }\n\n    @Expose\n    abstract fun startGameListener(interactor: LoggedInInteractor): OffGameInteractor.Listener\n\n    @Expose\n    abstract fun gameWonListener(interactor: LoggedInInteractor): TicTacToeInteractor.Listener\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/LoggedInView.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin\n\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.tooling.preview.Preview\nimport androidx.compose.ui.unit.dp\nimport com.uber.rib.compose.util.CustomButton\nimport com.uber.rib.compose.util.EventStream\n\n@Composable\nfun LoggedInView(\n  eventStream: EventStream<LoggedInEvent>,\n  childContent: LoggedInRouter.ChildContent,\n) {\n  Column(\n    horizontalAlignment = Alignment.CenterHorizontally,\n    verticalArrangement = Arrangement.Top,\n    modifier = Modifier.fillMaxSize().background(Color.Green),\n  ) {\n    Text(\"Logged In! (Compose RIB)\")\n    Spacer(Modifier.height(16.dp))\n    Box(\n      modifier = Modifier.fillMaxWidth().weight(1.0f).padding(4.dp).background(Color.LightGray),\n    ) {\n      childContent.fullScreenSlot.value.invoke()\n    }\n    CustomButton(\n      analyticsId = \"8a570808-07a4\",\n      onClick = { eventStream.notify(LoggedInEvent.LogOutClick) },\n      modifier = Modifier.fillMaxWidth().padding(16.dp),\n    ) {\n      Text(text = \"LOGOUT\")\n    }\n  }\n}\n\n@Preview\n@Composable\nfun LoggedInViewPreview() {\n  LoggedInView(EventStream(), LoggedInRouter.ChildContent())\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/ScoreStream.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin\n\nimport com.uber.rib.core.RibDispatchers\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.flow.update\nimport kotlinx.coroutines.withContext\n\nclass ScoreStream(playerOne: String, playerTwo: String) {\n\n  private val _scoresFlow =\n    MutableStateFlow(\n      mapOf(\n        playerOne to 0,\n        playerTwo to 0,\n      ),\n    )\n  private val scoresFlow = _scoresFlow.asStateFlow()\n\n  suspend fun addVictory(userName: String) =\n    withContext(RibDispatchers.Default) {\n      _scoresFlow.update { scores ->\n        scores\n          .toMutableMap()\n          .apply {\n            if (userName in scores) {\n              set(userName, getValue(userName) + 1)\n            }\n          }\n          .toMap()\n      }\n    }\n\n  fun scores() = scoresFlow\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/offgame/OffGameEvent.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin.offgame\n\nsealed class OffGameEvent {\n  object StartGame : OffGameEvent()\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/offgame/OffGameInteractor.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin.offgame\n\nimport com.uber.rib.compose.root.main.loggedin.ScoreStream\nimport com.uber.rib.compose.util.EventStream\nimport com.uber.rib.compose.util.StateStream\nimport com.uber.rib.core.BasicInteractor\nimport com.uber.rib.core.Bundle\nimport com.uber.rib.core.ComposePresenter\nimport com.uber.rib.core.coroutineScope\nimport kotlinx.coroutines.flow.launchIn\nimport kotlinx.coroutines.flow.onEach\n\nclass OffGameInteractor(\n  presenter: ComposePresenter,\n  private val eventStream: EventStream<OffGameEvent>,\n  private val stateStream: StateStream<OffGameViewModel>,\n  private val scoreStream: ScoreStream,\n  private val listener: Listener,\n) : BasicInteractor<ComposePresenter, OffGameRouter>(presenter) {\n\n  override fun didBecomeActive(savedInstanceState: Bundle?) {\n    super.didBecomeActive(savedInstanceState)\n\n    eventStream\n      .observe()\n      .onEach {\n        when (it) {\n          is OffGameEvent.StartGame -> {\n            listener.onStartGame()\n          }\n        }\n      }\n      .launchIn(coroutineScope)\n\n    scoreStream\n      .scores()\n      .onEach {\n        val currentState = stateStream.current()\n        stateStream.dispatch(\n          currentState.copy(\n            playerOneWins = it[currentState.playerOne] ?: 0,\n            playerTwoWins = it[currentState.playerTwo] ?: 0,\n          ),\n        )\n      }\n      .launchIn(coroutineScope)\n  }\n\n  interface Listener {\n    fun onStartGame()\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/offgame/OffGameRouter.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin.offgame\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.MutableState\nimport com.uber.rib.core.BasicComposeRouter\nimport com.uber.rib.core.ComposePresenter\n\nclass OffGameRouter(\n  presenter: ComposePresenter,\n  interactor: OffGameInteractor,\n  slot: MutableState<@Composable () -> Unit>,\n) : BasicComposeRouter<OffGameInteractor>(presenter, interactor, slot)\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/offgame/OffGameScope.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin.offgame\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.collectAsState\nimport com.uber.rib.compose.root.main.AuthInfo\nimport com.uber.rib.compose.util.EventStream\nimport com.uber.rib.compose.util.StateStream\nimport com.uber.rib.core.ComposePresenter\n\n@motif.Scope\ninterface OffGameScope {\n  fun router(): OffGameRouter\n\n  @motif.Objects\n  abstract class Objects {\n    abstract fun router(): OffGameRouter\n\n    abstract fun interactor(): OffGameInteractor\n\n    fun presenter(\n      stateStream: StateStream<OffGameViewModel>,\n      eventStream: EventStream<OffGameEvent>,\n    ): ComposePresenter {\n      return object : ComposePresenter() {\n        override val composable =\n          @Composable {\n            OffGameView(\n              stateStream.observe().collectAsState(initial = stateStream.current()),\n              eventStream,\n            )\n          }\n      }\n    }\n\n    fun eventStream() = EventStream<OffGameEvent>()\n\n    fun stateStream(authInfo: AuthInfo) =\n      StateStream(\n        OffGameViewModel(\n          playerOne = authInfo.playerOne,\n          playerTwo = authInfo.playerTwo,\n        ),\n      )\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/offgame/OffGameView.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin.offgame\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.State\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.tooling.preview.Preview\nimport androidx.compose.ui.unit.dp\nimport com.uber.rib.compose.util.CustomButton\nimport com.uber.rib.compose.util.EventStream\n\n@Composable\nfun OffGameView(viewModel: State<OffGameViewModel>, eventStream: EventStream<OffGameEvent>) {\n  Column(\n    modifier = Modifier.fillMaxSize().padding(16.dp),\n    verticalArrangement = Arrangement.spacedBy(16.dp, alignment = Alignment.Bottom),\n  ) {\n    Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {\n      Text(text = viewModel.value.playerOne)\n      Text(text = \"Win Count: ${viewModel.value.playerOneWins}\")\n    }\n    Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {\n      Text(text = viewModel.value.playerTwo)\n      Text(text = \"Win Count: ${viewModel.value.playerTwoWins}\")\n    }\n    CustomButton(\n      analyticsId = \"26882559-fc45\",\n      onClick = { eventStream.notify(OffGameEvent.StartGame) },\n      modifier = Modifier.fillMaxWidth(),\n    ) {\n      Text(text = \"START GAME\")\n    }\n  }\n}\n\n@Preview\n@Composable\nfun OffGameViewPreview() {\n  val viewModel = remember { mutableStateOf(OffGameViewModel(\"James\", \"Alejandro\", 3, 0)) }\n  OffGameView(viewModel, EventStream())\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/offgame/OffGameViewModel.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin.offgame\n\ndata class OffGameViewModel(\n  val playerOne: String = \"\",\n  val playerTwo: String = \"\",\n  val playerOneWins: Int = 0,\n  val playerTwoWins: Int = 0,\n)\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/tictactoe/Board.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin.tictactoe\n\nimport javax.inject.Inject\n\nclass Board @Inject constructor() {\n  var cells: Array<Array<MarkerType?>>\n  var currentRow = 0\n  var currentCol = 0\n\n  /** Return true if it is a draw (i.e., no more EMPTY cell) */\n  fun isDraw(): Boolean {\n    for (row in 0 until ROWS) {\n      for (col in 0 until COLS) {\n        if (cells[row][col] == null) {\n          return false\n        }\n      }\n    }\n    return !hasWon(MarkerType.CROSS) && !hasWon(MarkerType.NOUGHT)\n  }\n\n  /** Return true if the player with \"theSeed\" has won after placing at (currentRow, currentCol) */\n  fun hasWon(theSeed: MarkerType): Boolean {\n    return (cells[currentRow][0] == theSeed &&\n      cells[currentRow][1] == theSeed &&\n      cells[currentRow][2] == theSeed ||\n      cells[0][currentCol] == theSeed &&\n        cells[1][currentCol] == theSeed &&\n        cells[2][currentCol] == theSeed ||\n      currentRow == currentCol &&\n        cells[0][0] == theSeed &&\n        cells[1][1] == theSeed &&\n        cells[2][2] == theSeed ||\n      currentRow + currentCol == 2 &&\n        cells[0][2] == theSeed &&\n        cells[1][1] == theSeed &&\n        cells[2][0] == theSeed)\n  }\n\n  enum class MarkerType {\n    CROSS,\n    NOUGHT,\n  }\n\n  companion object {\n    const val ROWS = 3\n    const val COLS = 3\n  }\n\n  init {\n    cells = Array(ROWS) { arrayOfNulls(COLS) }\n    for (row in 0 until ROWS) {\n      for (col in 0 until COLS) {\n        cells[row][col] = null\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/tictactoe/BoardCoordinate.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin.tictactoe\n\ndata class BoardCoordinate(val x: Int, val y: Int)\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/tictactoe/TicTacToeEvent.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin.tictactoe\n\nsealed class TicTacToeEvent {\n  object XpButtonClick : TicTacToeEvent()\n  class BoardClick(val coordinate: BoardCoordinate) : TicTacToeEvent()\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/tictactoe/TicTacToeInteractor.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin.tictactoe\n\nimport com.uber.rib.compose.root.main.AuthInfo\nimport com.uber.rib.compose.util.EventStream\nimport com.uber.rib.compose.util.StateStream\nimport com.uber.rib.core.BasicInteractor\nimport com.uber.rib.core.Bundle\nimport com.uber.rib.core.ComposePresenter\nimport com.uber.rib.core.coroutineScope\nimport kotlinx.coroutines.flow.launchIn\nimport kotlinx.coroutines.flow.onEach\n\nclass TicTacToeInteractor(\n  presenter: ComposePresenter,\n  private val authInfo: AuthInfo,\n  private val eventStream: EventStream<TicTacToeEvent>,\n  private val stateStream: StateStream<TicTacToeViewModel>,\n  private val listener: Listener,\n) : BasicInteractor<ComposePresenter, TicTacToeRouter>(presenter) {\n\n  var currentPlayer: Board.MarkerType = Board.MarkerType.CROSS\n\n  override fun didBecomeActive(savedInstanceState: Bundle?) {\n    super.didBecomeActive(savedInstanceState)\n\n    eventStream\n      .observe()\n      .onEach {\n        when (it) {\n          is TicTacToeEvent.BoardClick -> {\n            val board: Board = stateStream.current().board\n            val coord = it.coordinate\n\n            if (board.cells[coord.x][coord.y] == null) {\n              if (currentPlayer == Board.MarkerType.CROSS) {\n                board.cells[coord.x][coord.y] = Board.MarkerType.CROSS\n                board.currentRow = coord.x\n                board.currentCol = coord.y\n                currentPlayer = Board.MarkerType.NOUGHT\n              } else {\n                board.cells[coord.x][coord.y] = Board.MarkerType.NOUGHT\n                board.currentRow = coord.x\n                board.currentCol = coord.y\n                currentPlayer = Board.MarkerType.CROSS\n              }\n            }\n\n            if (board.hasWon(Board.MarkerType.CROSS)) {\n              listener.onGameWon(authInfo.playerOne)\n            } else if (board.hasWon(Board.MarkerType.NOUGHT)) {\n              listener.onGameWon(authInfo.playerTwo)\n            } else if (board.isDraw()) {\n              listener.onGameWon(null)\n            }\n\n            val newPlayerName =\n              if (currentPlayer == Board.MarkerType.CROSS) {\n                authInfo.playerOne\n              } else {\n                authInfo.playerTwo\n              }\n\n            stateStream.dispatch(\n              stateStream\n                .current()\n                .copy(\n                  board = board,\n                  currentPlayer = newPlayerName,\n                ),\n            )\n          }\n          TicTacToeEvent.XpButtonClick -> TODO(\"Go somewhere\")\n        }\n      }\n      .launchIn(coroutineScope)\n  }\n\n  interface Listener {\n    fun onGameWon(winnerName: String?)\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/tictactoe/TicTacToeRouter.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin.tictactoe\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.MutableState\nimport com.uber.rib.core.BasicComposeRouter\nimport com.uber.rib.core.ComposePresenter\n\nclass TicTacToeRouter(\n  presenter: ComposePresenter,\n  interactor: TicTacToeInteractor,\n  slot: MutableState<@Composable () -> Unit>,\n) : BasicComposeRouter<TicTacToeInteractor>(presenter, interactor, slot)\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/tictactoe/TicTacToeScope.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin.tictactoe\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.collectAsState\nimport com.uber.rib.compose.root.main.AuthInfo\nimport com.uber.rib.compose.util.EventStream\nimport com.uber.rib.compose.util.StateStream\nimport com.uber.rib.core.ComposePresenter\n\n@motif.Scope\ninterface TicTacToeScope {\n  fun router(): TicTacToeRouter\n\n  @motif.Objects\n  abstract class Objects {\n    abstract fun router(): TicTacToeRouter\n\n    abstract fun interactor(): TicTacToeInteractor\n\n    fun presenter(\n      stateStream: StateStream<TicTacToeViewModel>,\n      eventStream: EventStream<TicTacToeEvent>,\n    ): ComposePresenter {\n      return object : ComposePresenter() {\n        override val composable =\n          @Composable {\n            TicTacToeView(\n              stateStream.observe().collectAsState(initial = stateStream.current()),\n              eventStream,\n            )\n          }\n      }\n    }\n\n    fun eventStream() = EventStream<TicTacToeEvent>()\n\n    fun stateStream(authInfo: AuthInfo) =\n      StateStream(TicTacToeViewModel(authInfo.playerOne, Board()))\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/tictactoe/TicTacToeView.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin.tictactoe\n\nimport androidx.compose.foundation.ExperimentalFoundationApi\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.aspectRatio\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.lazy.grid.GridCells\nimport androidx.compose.foundation.lazy.grid.LazyVerticalGrid\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.State\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.text.style.TextAlign\nimport androidx.compose.ui.tooling.preview.Preview\nimport androidx.compose.ui.unit.dp\nimport com.uber.rib.compose.util.EventStream\n\n@OptIn(ExperimentalFoundationApi::class)\n@Composable\nfun TicTacToeView(viewModel: State<TicTacToeViewModel>, eventStream: EventStream<TicTacToeEvent>) {\n  Column(\n    horizontalAlignment = Alignment.CenterHorizontally,\n    verticalArrangement = Arrangement.Top,\n    modifier = Modifier.fillMaxSize().background(Color.Blue),\n  ) {\n    Text(\"Current Player: ${viewModel.value.currentPlayer}\", color = Color.White)\n    Box(\n      modifier = Modifier.aspectRatio(1f).fillMaxSize(),\n    ) {\n      LazyVerticalGrid(columns = GridCells.Fixed(3), modifier = Modifier.fillMaxSize()) {\n        val board = viewModel.value.board\n        items(9) { i ->\n          val row = i / 3\n          val col = i % 3\n          Text(\n            text =\n              when (board.cells[row][col]) {\n                Board.MarkerType.CROSS -> \"X\"\n                Board.MarkerType.NOUGHT -> \"O\"\n                else -> \" \"\n              },\n            textAlign = TextAlign.Center,\n            modifier =\n              Modifier.fillMaxWidth()\n                .aspectRatio(1f)\n                .padding(16.dp)\n                .background(Color.LightGray)\n                .clickable(\n                  enabled = board.cells[row][col] == null,\n                  onClick = {\n                    eventStream.notify(TicTacToeEvent.BoardClick(BoardCoordinate(row, col)))\n                  },\n                )\n                .padding(32.dp),\n          )\n        }\n      }\n    }\n  }\n}\n\n@Preview\n@Composable\nfun ProductSelectionViewPreview() {\n  val board = Board()\n  board.cells[0][2] = Board.MarkerType.CROSS\n  board.cells[1][0] = Board.MarkerType.NOUGHT\n  board.cells[2][1] = Board.MarkerType.CROSS\n  val viewModel = remember { mutableStateOf(TicTacToeViewModel(\"James\", board)) }\n  TicTacToeView(viewModel, EventStream())\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedin/tictactoe/TicTacToeViewModel.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedin.tictactoe\n\ndata class TicTacToeViewModel(\n  val currentPlayer: String,\n  val board: Board,\n)\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedout/LoggedOutEvent.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedout\n\nsealed class LoggedOutEvent {\n  class PlayerNameChanged(val name: String, val num: Int) : LoggedOutEvent()\n  object LogInClick : LoggedOutEvent()\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedout/LoggedOutInteractor.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedout\n\nimport com.uber.rib.compose.root.main.AuthInfo\nimport com.uber.rib.compose.root.main.AuthStream\nimport com.uber.rib.compose.util.EventStream\nimport com.uber.rib.compose.util.StateStream\nimport com.uber.rib.core.BasicInteractor\nimport com.uber.rib.core.Bundle\nimport com.uber.rib.core.ComposePresenter\nimport com.uber.rib.core.coroutineScope\nimport kotlinx.coroutines.flow.launchIn\nimport kotlinx.coroutines.flow.onEach\n\nclass LoggedOutInteractor(\n  presenter: ComposePresenter,\n  private val authStream: AuthStream,\n  private val eventStream: EventStream<LoggedOutEvent>,\n  private val stateStream: StateStream<LoggedOutViewModel>,\n) : BasicInteractor<ComposePresenter, LoggedOutRouter>(presenter) {\n  override fun didBecomeActive(savedInstanceState: Bundle?) {\n    super.didBecomeActive(savedInstanceState)\n    eventStream\n      .observe()\n      .onEach {\n        when (it) {\n          is LoggedOutEvent.PlayerNameChanged -> {\n            with(stateStream) {\n              dispatch(\n                current()\n                  .copy(\n                    playerOne = if (it.num == 1) it.name else current().playerOne,\n                    playerTwo = if (it.num == 2) it.name else current().playerTwo,\n                  ),\n              )\n            }\n          }\n          LoggedOutEvent.LogInClick -> {\n            val currentState = stateStream.current()\n            authStream.accept(AuthInfo(true, currentState.playerOne, currentState.playerTwo))\n          }\n        }\n      }\n      .launchIn(coroutineScope)\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedout/LoggedOutRouter.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedout\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.MutableState\nimport com.uber.rib.core.BasicComposeRouter\nimport com.uber.rib.core.ComposePresenter\n\nclass LoggedOutRouter(\n  presenter: ComposePresenter,\n  interactor: LoggedOutInteractor,\n  slot: MutableState<@Composable () -> Unit>,\n) : BasicComposeRouter<LoggedOutInteractor>(presenter, interactor, slot)\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedout/LoggedOutScope.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedout\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.collectAsState\nimport com.uber.rib.compose.util.EventStream\nimport com.uber.rib.compose.util.StateStream\nimport com.uber.rib.core.ComposePresenter\n\n@motif.Scope\ninterface LoggedOutScope {\n  fun router(): LoggedOutRouter\n\n  @motif.Objects\n  abstract class Objects {\n    abstract fun router(): LoggedOutRouter\n\n    abstract fun interactor(): LoggedOutInteractor\n\n    fun presenter(\n      stateStream: StateStream<LoggedOutViewModel>,\n      eventStream: EventStream<LoggedOutEvent>,\n    ): ComposePresenter {\n      return object : ComposePresenter() {\n        override val composable =\n          @Composable {\n            LoggedOutView(\n              stateStream.observe().collectAsState(initial = stateStream.current()),\n              eventStream,\n            )\n          }\n      }\n    }\n\n    fun eventStream() = EventStream<LoggedOutEvent>()\n\n    fun stateStream() = StateStream(LoggedOutViewModel())\n  }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedout/LoggedOutView.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedout\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material.Button\nimport androidx.compose.material.ButtonDefaults\nimport androidx.compose.material.Text\nimport androidx.compose.material.TextField\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.State\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.tooling.preview.Preview\nimport androidx.compose.ui.unit.dp\nimport com.uber.rib.compose.util.EventStream\n\n@Composable\nfun LoggedOutView(viewModel: State<LoggedOutViewModel>, eventStream: EventStream<LoggedOutEvent>) {\n  Column(\n    modifier = Modifier.fillMaxSize().padding(16.dp),\n    verticalArrangement = Arrangement.spacedBy(16.dp, alignment = Alignment.Bottom),\n  ) {\n    TextField(\n      value = viewModel.value.playerOne,\n      onValueChange = { eventStream.notify(LoggedOutEvent.PlayerNameChanged(it, 1)) },\n      placeholder = { Text(text = \"Player One Name\") },\n      modifier = Modifier.fillMaxWidth(),\n    )\n    TextField(\n      value = viewModel.value.playerTwo,\n      onValueChange = { eventStream.notify(LoggedOutEvent.PlayerNameChanged(it, 2)) },\n      placeholder = { Text(text = \"Player Two Name\") },\n      modifier = Modifier.fillMaxWidth(),\n    )\n    Button(\n      colors =\n        ButtonDefaults.buttonColors(\n          backgroundColor = Color.Black,\n          contentColor = Color.White,\n        ),\n      onClick = { eventStream.notify(LoggedOutEvent.LogInClick) },\n      modifier = Modifier.fillMaxWidth(),\n    ) {\n      Text(text = \"LOGIN\")\n    }\n  }\n}\n\n@Preview\n@Composable\nfun LoggedOutViewPreview() {\n  val viewModel = remember { mutableStateOf(LoggedOutViewModel(\"James\", \"Alejandro\")) }\n  LoggedOutView(viewModel, EventStream())\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/loggedout/LoggedOutViewModel.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.root.main.loggedout\n\ndata class LoggedOutViewModel(\n  val playerOne: String = \"\",\n  val playerTwo: String = \"\",\n)\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/util/AnalyticsClient.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.util\n\nimport android.app.Application\nimport android.util.Log\nimport android.widget.Toast\n\nclass AnalyticsClientImpl(private val application: Application) : AnalyticsClient {\n  override fun trackClick(id: String) {\n    track(id, EventType.CLICK)\n  }\n\n  override fun trackImpression(id: String) {\n    track(id, EventType.IMPRESSION)\n  }\n\n  private fun track(id: String, type: EventType) {\n    val message = \"$type for $id @ ${System.currentTimeMillis()}\"\n    Toast.makeText(application.applicationContext, message, Toast.LENGTH_SHORT).show()\n    Log.d(this::class.java.simpleName, message)\n  }\n\n  enum class EventType {\n    CLICK,\n    IMPRESSION,\n  }\n}\n\nobject NoOpAnalyticsClient : AnalyticsClient {\n  override fun trackClick(id: String) = Unit\n  override fun trackImpression(id: String) = Unit\n}\n\ninterface AnalyticsClient {\n  fun trackClick(id: String)\n  fun trackImpression(id: String)\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/util/CustomButton.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.util\n\nimport androidx.compose.foundation.layout.RowScope\nimport androidx.compose.material.Button\nimport androidx.compose.material.ButtonDefaults\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.LaunchedEffect\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\n\n@Composable\nfun CustomButton(\n  onClick: () -> Unit,\n  modifier: Modifier = Modifier,\n  analyticsId: String? = null,\n  content: @Composable RowScope.() -> Unit,\n) {\n  val analyticsClient = AnalyticsLocal.current\n  val onClickWrapper: () -> Unit = {\n    analyticsId?.let { analyticsClient.trackClick(it) }\n    onClick()\n  }\n  Button(\n    onClick = onClickWrapper,\n    modifier = modifier,\n    colors = ButtonDefaults.buttonColors(backgroundColor = Color.Black, contentColor = Color.White),\n    content = content,\n  )\n  LaunchedEffect(null) { analyticsId?.let { analyticsClient.trackImpression(it) } }\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/util/CustomClientProvider.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.util\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.CompositionLocalProvider\nimport androidx.compose.runtime.staticCompositionLocalOf\n\nval AnalyticsLocal = staticCompositionLocalOf<AnalyticsClient> { NoOpAnalyticsClient }\nval ExperimentsLocal = staticCompositionLocalOf<ExperimentClient> { NoOpExperimentClient }\nval LoggerLocal = staticCompositionLocalOf<LoggerClient> { NoOpLoggerClient }\n\n@Composable\nfun CustomClientProvider(\n  analyticsClient: AnalyticsClient,\n  experimentClient: ExperimentClient,\n  loggerClient: LoggerClient,\n  content: @Composable () -> Unit,\n) {\n  CompositionLocalProvider(\n    AnalyticsLocal provides analyticsClient,\n    ExperimentsLocal provides experimentClient,\n    LoggerLocal provides loggerClient,\n    content = content,\n  )\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/util/EventStream.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.util\n\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.flow.asSharedFlow\n\nclass EventStream<T> {\n  private val _sharedFlow = MutableSharedFlow<T>(extraBufferCapacity = 1)\n  private val sharedFlow = _sharedFlow.asSharedFlow()\n\n  fun notify(event: T) = _sharedFlow.tryEmit(event)\n\n  fun observe() = sharedFlow\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/util/ExperimentClient.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.util\n\nimport android.app.Application\nimport android.util.Log\nimport android.widget.Toast\nimport java.lang.Math.random\n\nclass ExperimentClientImpl(private val application: Application) : ExperimentClient {\n  override fun isTreated(id: String): Boolean {\n    val result = random() > 0.5\n    val message = \"isTreated($id) = $result\"\n    Toast.makeText(application.applicationContext, message, Toast.LENGTH_SHORT).show()\n    Log.d(this::class.java.simpleName, message)\n    return result\n  }\n}\n\nobject NoOpExperimentClient : ExperimentClient {\n  override fun isTreated(id: String) = false\n}\n\ninterface ExperimentClient {\n  fun isTreated(id: String): Boolean\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/util/LoggerClient.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.util\n\nimport android.app.Application\nimport android.util.Log\nimport android.widget.Toast\n\nclass LoggerClientImpl(private val application: Application) : LoggerClient {\n  override fun log(message: String) {\n    Toast.makeText(application.applicationContext, message, Toast.LENGTH_SHORT).show()\n    Log.d(this::class.java.simpleName, message)\n  }\n}\n\nobject NoOpLoggerClient : LoggerClient {\n  override fun log(message: String) = Unit\n}\n\ninterface LoggerClient {\n  fun log(message: String)\n}\n"
  },
  {
    "path": "demos/compose/src/main/kotlin/com/uber/rib/compose/util/StateStream.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compose.util\n\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.flow.update\n\nclass StateStream<T : Any>(default: T) {\n  private val _stateFlow = MutableStateFlow(default)\n  private val stateFlow = _stateFlow.asStateFlow()\n\n  fun dispatch(viewModel: T) = _stateFlow.update { viewModel }\n\n  fun observe() = stateFlow\n\n  fun current() = stateFlow.value\n}\n"
  },
  {
    "path": "demos/compose/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path\n        android:fillColor=\"#3DDC84\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n</vector>\n"
  },
  {
    "path": "demos/compose/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path android:pathData=\"M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"85.84757\"\n                android:endY=\"92.4963\"\n                android:startX=\"42.9492\"\n                android:startY=\"49.59793\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>"
  },
  {
    "path": "demos/compose/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "demos/compose/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "demos/compose/src/main/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n</resources>"
  },
  {
    "path": "demos/compose/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">Compose RIBs</string>\n</resources>"
  },
  {
    "path": "demos/compose/src/main/res/values/themes.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <style name=\"Theme.Android\" parent=\"Theme.AppCompat.NoActionBar\">\n         <!-- Customize your theme here. -->\n        <item name=\"android:windowActionBar\">false</item>\n        <item name=\"android:windowNoTitle\">true</item>\n    </style>\n</resources>"
  },
  {
    "path": "demos/flipper/README.md",
    "content": "\n# RIB Demo: Flipper Debugging Tool\n\n## Goal\n\nThis demo shows off RIBs plugin for Flipper Debugging Tool. It re-uses one of our tutorial application, and integrates\nit with Flipper tool and our Flipper RIBs plugin.\n\n## Integration\n\nTo integrate Flipper to your app, please refer to [Flipper official documentation](https://fbflipper.com/docs/getting-started/android-native)\n\nThen, to enable RIBs plugin, simply add it to the list of used plugin during Flipper initialization.\n\nIn summary, your application ```onCreate()``` method should look similar to the one below:\n\n```\npublic class SampleApplication extends Application {\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        SoLoader.init(this, false);\n\n        if (BuildConfig.DEBUG && FlipperUtils.shouldEnableFlipper(this)) {\n            final FlipperClient client = AndroidFlipperClient.getInstance(this);\n            client.addPlugin(new RibTreePlugin());\n            client.start();\n        }\n    }\n}\n```\n"
  },
  {
    "path": "demos/flipper/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.android.application\")\n    alias(libs.plugins.kotlin.kapt)\n    alias(libs.plugins.kotlin.ksp)\n}\n\nandroid {\n    namespace = \"com.uber.rib.flipper\"\n\n    defaultConfig {\n        applicationId = \"com.uber.rib.flipper\"\n    }\n\n    buildFeatures {\n        buildConfig = true\n    }\n}\n\ndependencies {\n    ksp(libs.dagger.compiler)\n    kapt(project(\":libraries:rib-compiler-test\"))\n    implementation(project(\":libraries:rib-android\"))\n    implementation(libs.androidx.appcompat)\n    implementation(libs.dagger.library)\n    implementation(libs.rxbinding)\n    implementation(appLibs.percent)\n    compileOnly(appLibs.jsr250)\n    testImplementation(project(\":libraries:rib-test\"))\n\n    // Flipper Debug tool integration\n    debugImplementation(libs.flipper)\n    debugImplementation(appLibs.soloader)\n    releaseImplementation(libs.flipper.noop)\n\n    // Flipper RIBs plugin\n    implementation(project(\":tooling:rib-flipper-plugin\"))\n}\n"
  },
  {
    "path": "demos/flipper/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <uses-feature android:required=\"true\" android:glEsVersion='0x00020000'/>\n\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>\n\n    <application\n        android:name=\"com.uber.rib.SampleApplication\"\n        android:allowBackup=\"false\"\n        android:icon=\"@drawable/ub__ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:screenOrientation=\"portrait\">\n        <activity\n            android:name=\"com.uber.rib.RootActivity\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n    </application>\n</manifest>\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/RootActivity.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib;\n\nimport android.view.ViewGroup;\nimport com.uber.rib.core.RibActivity;\nimport com.uber.rib.core.ViewRouter;\nimport com.uber.rib.root.RootBuilder;\n\n/** The sample app's single activity. */\npublic class RootActivity extends RibActivity {\n\n  @SuppressWarnings(\"unchecked\")\n  @Override\n  protected ViewRouter<?, ?> createRouter(ViewGroup parentViewGroup) {\n    RootBuilder rootBuilder = new RootBuilder(new RootBuilder.ParentComponent() {});\n    return rootBuilder.build(parentViewGroup);\n  }\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/SampleApplication.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib;\n\nimport android.app.Application;\nimport com.facebook.flipper.android.AndroidFlipperClient;\nimport com.facebook.flipper.android.utils.FlipperUtils;\nimport com.facebook.flipper.core.FlipperClient;\nimport com.facebook.soloader.SoLoader;\nimport com.uber.rib.flipper.BuildConfig;\nimport com.uber.rib.flipper.RibTreePlugin;\n\npublic class SampleApplication extends Application {\n\n  @Override\n  public void onCreate() {\n    super.onCreate();\n    SoLoader.init(this, false);\n\n    if (BuildConfig.DEBUG && FlipperUtils.shouldEnableFlipper(this)) {\n      final FlipperClient client = AndroidFlipperClient.getInstance(this);\n      client.addPlugin(new RibTreePlugin());\n      client.start();\n    }\n  }\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/RootBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.flipper.R;\nimport com.uber.rib.root.loggedin.LoggedInBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutInteractor;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Scope;\n\n/** Builder for the {@link RootScope}. */\npublic class RootBuilder extends ViewBuilder<RootView, RootRouter, RootBuilder.ParentComponent> {\n\n  public RootBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link RootRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link RootRouter}.\n   */\n  public RootRouter build(ViewGroup parentViewGroup) {\n    RootView view = createView(parentViewGroup);\n    RootInteractor interactor = new RootInteractor();\n    Component component =\n        DaggerRootBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.rootRouter();\n  }\n\n  @Override\n  protected RootView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (RootView) inflater.inflate(R.layout.root_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n    // Define dependencies required from your parent interactor here.\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @RootScope\n    @Provides\n    static LoggedOutInteractor.Listener loggedOutListener(RootInteractor rootInteractor) {\n      return rootInteractor.new LoggedOutListener();\n    }\n\n    @RootScope\n    @Binds\n    abstract RootInteractor.RootPresenter presenter(RootView view);\n\n    @RootScope\n    @Provides\n    static RootRouter router(Component component, RootView view, RootInteractor interactor) {\n      return new RootRouter(\n          view,\n          interactor,\n          component,\n          new LoggedOutBuilder(component),\n          new LoggedInBuilder(component));\n    }\n  }\n\n  @RootScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component\n      extends InteractorBaseComponent<RootInteractor>,\n          LoggedOutBuilder.ParentComponent,\n          LoggedInBuilder.ParentComponent,\n          BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(RootInteractor interactor);\n\n      @BindsInstance\n      Builder view(RootView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    RootRouter rootRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface RootScope {}\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/RootInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedout.LoggedOutInteractor;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link RootBuilder.RootScope}. */\n@RibInteractor\npublic class RootInteractor extends Interactor<RootInteractor.RootPresenter, RootRouter> {\n\n  @Inject RootPresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n    getRouter().attachLoggedOut();\n  }\n\n  class LoggedOutListener implements LoggedOutInteractor.Listener {\n\n    @Override\n    public void login(String userNameA, String userNameB) {\n      // Switch to logged in. Let’s just ignore userName for now.\n      getRouter().detachLoggedOut();\n      getRouter().attachLoggedIn();\n    }\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface RootPresenter {}\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/RootRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.ViewRouter;\nimport com.uber.rib.root.loggedin.LoggedInBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutRouter;\n\n/** Adds and removes children of {@link RootBuilder.RootScope}. */\npublic class RootRouter extends ViewRouter<RootView, RootInteractor> {\n\n  private final LoggedOutBuilder loggedOutBuilder;\n  private final LoggedInBuilder loggedInBuilder;\n  @Nullable private LoggedOutRouter loggedOutRouter;\n\n  RootRouter(\n      RootView view,\n      RootInteractor interactor,\n      RootBuilder.Component component,\n      LoggedOutBuilder loggedOutBuilder,\n      LoggedInBuilder loggedInBuilder) {\n    super(view, interactor, component);\n    this.loggedOutBuilder = loggedOutBuilder;\n    this.loggedInBuilder = loggedInBuilder;\n  }\n\n  void attachLoggedOut() {\n    loggedOutRouter = loggedOutBuilder.build(getView());\n    attachChild(loggedOutRouter);\n    getView().addView(loggedOutRouter.getView());\n  }\n\n  void detachLoggedOut() {\n    if (loggedOutRouter != null) {\n      detachChild(loggedOutRouter);\n      getView().removeView(loggedOutRouter.getView());\n      loggedOutRouter = null;\n    }\n  }\n\n  void attachLoggedIn() {\n    attachChild(loggedInBuilder.build());\n  }\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/RootView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.FrameLayout;\nimport androidx.annotation.Nullable;\n\n/** Top level view for {@link RootBuilder.RootScope}. */\npublic class RootView extends FrameLayout implements RootInteractor.RootPresenter {\n\n  public RootView(Context context) {\n    this(context, null);\n  }\n\n  public RootView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public RootView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/loggedin/LoggedInBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport com.uber.rib.core.Builder;\nimport com.uber.rib.core.EmptyPresenter;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.root.RootView;\nimport com.uber.rib.root.loggedin.offgame.OffGameBuilder;\nimport com.uber.rib.root.loggedin.offgame.OffGameInteractor;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeBuilder;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\npublic class LoggedInBuilder extends Builder<LoggedInRouter, LoggedInBuilder.ParentComponent> {\n\n  public LoggedInBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link LoggedInRouter}.\n   *\n   * @return a new {@link LoggedInRouter}.\n   */\n  public LoggedInRouter build() {\n    LoggedInInteractor interactor = new LoggedInInteractor();\n    Component component =\n        DaggerLoggedInBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .interactor(interactor)\n            .build();\n\n    return component.loggedinRouter();\n  }\n\n  public interface ParentComponent {\n\n    RootView rootView();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @LoggedInScope\n    @Provides\n    static EmptyPresenter presenter() {\n      return new EmptyPresenter();\n    }\n\n    @LoggedInScope\n    @Provides\n    static LoggedInRouter router(\n        Component component, LoggedInInteractor interactor, RootView rootView) {\n      return new LoggedInRouter(\n          interactor,\n          component,\n          rootView,\n          new OffGameBuilder(component),\n          new TicTacToeBuilder(component));\n    }\n\n    @LoggedInScope\n    @Provides\n    static OffGameInteractor.Listener listener(LoggedInInteractor interactor) {\n      return interactor.new OffGameListener();\n    }\n  }\n\n  @LoggedInScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  public interface Component\n      extends InteractorBaseComponent<LoggedInInteractor>,\n          BuilderComponent,\n          OffGameBuilder.ParentComponent,\n          TicTacToeBuilder.ParentComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(LoggedInInteractor interactor);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    LoggedInRouter loggedinRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface LoggedInScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface LoggedInInternal {}\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/loggedin/LoggedInInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.EmptyPresenter;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedin.offgame.OffGameInteractor;\n\n/** Coordinates Business Logic for {@link LoggedInScope}. */\n@RibInteractor\npublic class LoggedInInteractor extends Interactor<EmptyPresenter, LoggedInRouter> {\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    // when first logging in we should be in the OffGame state\n    getRouter().attachOffGame();\n  }\n\n  class OffGameListener implements OffGameInteractor.Listener {\n\n    @Override\n    public void onStartGame() {\n      getRouter().detachOffGame();\n      getRouter().attachTicTacToe();\n    }\n  }\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/loggedin/LoggedInRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport android.view.ViewGroup;\nimport com.uber.rib.core.Router;\nimport com.uber.rib.root.loggedin.offgame.OffGameBuilder;\nimport com.uber.rib.root.loggedin.offgame.OffGameRouter;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeBuilder;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeRouter;\n\n/** Adds and removes children of {@link LoggedInBuilder.LoggedInScope}. */\npublic class LoggedInRouter extends Router<LoggedInInteractor> {\n\n  private final ViewGroup parentView;\n  private final OffGameBuilder offGameBuilder;\n  private final TicTacToeBuilder ticTacToeBuilder;\n  private OffGameRouter offGameRouter;\n  private TicTacToeRouter ticTacToeRouter;\n\n  LoggedInRouter(\n      LoggedInInteractor interactor,\n      LoggedInBuilder.Component component,\n      ViewGroup parentView,\n      OffGameBuilder offGameBuilder,\n      TicTacToeBuilder ticTacToeBuilder) {\n    super(interactor, component);\n    this.parentView = parentView;\n    this.offGameBuilder = offGameBuilder;\n    this.ticTacToeBuilder = ticTacToeBuilder;\n  }\n\n  @Override\n  protected void willDetach() {\n    super.willDetach();\n    detachOffGame();\n    detachTicTacToe();\n  }\n\n  void attachOffGame() {\n    offGameRouter = offGameBuilder.build(parentView);\n    attachChild(offGameRouter);\n    parentView.addView(offGameRouter.getView());\n  }\n\n  void detachOffGame() {\n    if (offGameRouter != null) {\n      detachChild(offGameRouter);\n      parentView.removeView(offGameRouter.getView());\n      offGameRouter = null;\n    }\n  }\n\n  void attachTicTacToe() {\n    ticTacToeRouter = ticTacToeBuilder.build(parentView);\n    attachChild(ticTacToeRouter);\n    parentView.addView(ticTacToeRouter.getView());\n  }\n\n  void detachTicTacToe() {\n    if (ticTacToeRouter != null) {\n      detachChild(ticTacToeRouter);\n      parentView.removeView(ticTacToeRouter.getView());\n      ticTacToeRouter = null;\n    }\n  }\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.flipper.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link OffGameScope}. */\npublic class OffGameBuilder\n    extends ViewBuilder<OffGameView, OffGameRouter, OffGameBuilder.ParentComponent> {\n\n  public OffGameBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link OffGameRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link OffGameRouter}.\n   */\n  public OffGameRouter build(ViewGroup parentViewGroup) {\n    OffGameView view = createView(parentViewGroup);\n    OffGameInteractor interactor = new OffGameInteractor();\n    Component component =\n        DaggerOffGameBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.offgameRouter();\n  }\n\n  @Override\n  protected OffGameView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (OffGameView) inflater.inflate(R.layout.off_game_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n\n    OffGameInteractor.Listener listener();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @OffGameScope\n    @Binds\n    abstract OffGameInteractor.OffGamePresenter presenter(OffGameView view);\n\n    @OffGameScope\n    @Provides\n    static OffGameRouter router(\n        Component component, OffGameView view, OffGameInteractor interactor) {\n      return new OffGameRouter(view, interactor, component);\n    }\n  }\n\n  @OffGameScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<OffGameInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(OffGameInteractor interactor);\n\n      @BindsInstance\n      Builder view(OffGameView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    OffGameRouter offgameRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface OffGameScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface OffGameInternal {}\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link OffGameScope}. */\n@RibInteractor\npublic class OffGameInteractor\n    extends Interactor<OffGameInteractor.OffGamePresenter, OffGameRouter> {\n\n  @Inject Listener listener;\n  @Inject OffGamePresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    presenter\n        .startGameRequest()\n        .subscribe(\n            new Consumer<Object>() {\n              @Override\n              public void accept(Object object) throws Exception {\n                listener.onStartGame();\n              }\n            });\n  }\n\n  public interface Listener {\n\n    void onStartGame();\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface OffGamePresenter {\n\n    Observable<Object> startGameRequest();\n  }\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link OffGameBuilder.OffGameScope}. */\npublic class OffGameRouter extends ViewRouter<OffGameView, OffGameInteractor> {\n\n  public OffGameRouter(\n      OffGameView view, OffGameInteractor interactor, OffGameBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.Button;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.core.Initializer;\nimport com.uber.rib.flipper.R;\nimport io.reactivex.Observable;\n\n/** Top level view for {@link OffGameBuilder.OffGameScope}. */\npublic class OffGameView extends LinearLayout implements OffGameInteractor.OffGamePresenter {\n\n  private Button button;\n  private TextView playerOneName;\n  private TextView playerTwoName;\n  private TextView playerOneScore;\n  private TextView playerTwoScore;\n\n  public OffGameView(Context context) {\n    this(context, null);\n  }\n\n  public OffGameView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public OffGameView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Initializer\n  @Override\n  protected void onFinishInflate() {\n    super.onFinishInflate();\n    button = (Button) findViewById(R.id.start_game_button);\n    playerOneName = (TextView) findViewById(R.id.player_one_name);\n    playerTwoName = (TextView) findViewById(R.id.player_two_name);\n    playerOneScore = (TextView) findViewById(R.id.player_one_win_count);\n    playerTwoScore = (TextView) findViewById(R.id.player_two_win_count);\n  }\n\n  @Override\n  public Observable<Object> startGameRequest() {\n    return RxView.clicks(button);\n  }\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/loggedin/tictactoe/Board.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport javax.inject.Inject;\n\nclass Board {\n\n  static final int ROWS = 3;\n  static final int COLS = 3;\n\n  MarkerType[][] cells;\n  int currentRow;\n  int currentCol;\n\n  @Inject\n  Board() {\n    cells = new MarkerType[ROWS][COLS];\n    for (int row = 0; row < ROWS; ++row) {\n      for (int col = 0; col < COLS; ++col) {\n        cells[row][col] = null;\n      }\n    }\n  }\n\n  /** Return true if it is a draw (i.e., no more EMPTY cell) */\n  boolean isDraw() {\n    for (int row = 0; row < ROWS; ++row) {\n      for (int col = 0; col < COLS; ++col) {\n        if (cells[row][col] == null) {\n          return false;\n        }\n      }\n    }\n    return !hasWon(MarkerType.CROSS) && !hasWon(MarkerType.NOUGHT);\n  }\n\n  /** Return true if the player with \"theSeed\" has won after placing at (currentRow, currentCol) */\n  boolean hasWon(MarkerType theSeed) {\n    return ((cells[currentRow][0] == theSeed\n            && cells[currentRow][1] == theSeed\n            && cells[currentRow][2] == theSeed)\n        || (cells[0][currentCol] == theSeed\n            && cells[1][currentCol] == theSeed\n            && cells[2][currentCol] == theSeed)\n        || (currentRow == currentCol\n            && cells[0][0] == theSeed\n            && cells[1][1] == theSeed\n            && cells[2][2] == theSeed)\n        || (currentRow + currentCol == 2\n            && cells[0][2] == theSeed\n            && cells[1][1] == theSeed\n            && cells[2][0] == theSeed));\n  }\n\n  enum MarkerType {\n    CROSS,\n    NOUGHT\n  }\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/loggedin/tictactoe/BoardCoordinate.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nclass BoardCoordinate {\n\n  private final int x;\n  private final int y;\n\n  BoardCoordinate(int x, int y) {\n    this.x = x;\n    this.y = y;\n  }\n\n  int getX() {\n    return x;\n  }\n\n  int getY() {\n    return y;\n  }\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.flipper.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link TicTacToeScope}. */\npublic class TicTacToeBuilder\n    extends ViewBuilder<TicTacToeView, TicTacToeRouter, TicTacToeBuilder.ParentComponent> {\n\n  public TicTacToeBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link TicTacToeRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link TicTacToeRouter}.\n   */\n  public TicTacToeRouter build(ViewGroup parentViewGroup) {\n    TicTacToeView view = createView(parentViewGroup);\n    TicTacToeInteractor interactor = new TicTacToeInteractor();\n    Component component =\n        DaggerTicTacToeBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.tictactoeRouter();\n  }\n\n  @Override\n  protected TicTacToeView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (TicTacToeView) inflater.inflate(R.layout.tic_tac_toe_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n    // TODO: Define dependencies required from your parent interactor here.\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @TicTacToeScope\n    @Binds\n    abstract TicTacToeInteractor.TicTacToePresenter presenter(TicTacToeView view);\n\n    @TicTacToeScope\n    @Provides\n    static TicTacToeRouter router(\n        Component component, TicTacToeView view, TicTacToeInteractor interactor) {\n      return new TicTacToeRouter(view, interactor, component);\n    }\n  }\n\n  @TicTacToeScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<TicTacToeInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(TicTacToeInteractor interactor);\n\n      @BindsInstance\n      Builder view(TicTacToeView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    TicTacToeRouter tictactoeRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface TicTacToeScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface TicTacToeInternal {}\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedin.tictactoe.Board.MarkerType;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link TicTacToeScope}. */\n@RibInteractor\npublic class TicTacToeInteractor\n    extends Interactor<TicTacToeInteractor.TicTacToePresenter, TicTacToeRouter> {\n\n  @Inject Board board;\n  @Inject TicTacToePresenter presenter;\n\n  private final String playerOne = \"Fake name 1\";\n  private final String playerTwo = \"Fake name 2\";\n\n  private MarkerType currentPlayer = MarkerType.CROSS;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    presenter\n        .squareClicks()\n        .subscribe(\n            new Consumer<BoardCoordinate>() {\n              @Override\n              public void accept(BoardCoordinate xy) throws Exception {\n                if (board.cells[xy.getX()][xy.getY()] == null) {\n                  if (currentPlayer == MarkerType.CROSS) {\n                    board.cells[xy.getX()][xy.getY()] = MarkerType.CROSS;\n                    board.currentRow = xy.getX();\n                    board.currentCol = xy.getY();\n                    presenter.addCross(xy);\n                    currentPlayer = MarkerType.NOUGHT;\n                  } else {\n                    board.cells[xy.getX()][xy.getY()] = MarkerType.NOUGHT;\n                    board.currentRow = xy.getX();\n                    board.currentCol = xy.getY();\n                    presenter.addNought(xy);\n                    currentPlayer = MarkerType.CROSS;\n                  }\n                }\n                if (board.hasWon(MarkerType.CROSS)) {\n                  presenter.setPlayerWon(playerOne);\n                } else if (board.hasWon(MarkerType.NOUGHT)) {\n                  presenter.setPlayerWon(playerTwo);\n                } else if (board.isDraw()) {\n                  presenter.setPlayerTie();\n                } else {\n                  updateCurrentPlayer();\n                }\n              }\n            });\n    updateCurrentPlayer();\n  }\n\n  private void updateCurrentPlayer() {\n    if (currentPlayer == MarkerType.CROSS) {\n      presenter.setCurrentPlayerName(playerOne);\n    } else {\n      presenter.setCurrentPlayerName(playerTwo);\n    }\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface TicTacToePresenter {\n    Observable<BoardCoordinate> squareClicks();\n\n    void setCurrentPlayerName(String currentPlayer);\n\n    void setPlayerWon(String playerName);\n\n    void setPlayerTie();\n\n    void addCross(BoardCoordinate xy);\n\n    void addNought(BoardCoordinate xy);\n  }\n\n  public interface Listener {}\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link TicTacToeBuilder.TicTacToeScope}. */\npublic class TicTacToeRouter extends ViewRouter<TicTacToeView, TicTacToeInteractor> {\n\n  public TicTacToeRouter(\n      TicTacToeView view, TicTacToeInteractor interactor, TicTacToeBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport androidx.percentlayout.widget.PercentRelativeLayout;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.core.Initializer;\nimport com.uber.rib.flipper.R;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Function;\nimport java.util.ArrayList;\n\n/** Top level view for {@link TicTacToeBuilder.TicTacToeScope}. */\npublic class TicTacToeView extends PercentRelativeLayout\n    implements TicTacToeInteractor.TicTacToePresenter {\n\n  private TextView[][] imageButtons;\n  private TextView titleView;\n\n  public TicTacToeView(Context context) {\n    this(context, null);\n  }\n\n  public TicTacToeView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public TicTacToeView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Initializer\n  @Override\n  protected void onFinishInflate() {\n    super.onFinishInflate();\n    imageButtons = new TextView[3][];\n    imageButtons[0] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button11),\n          (TextView) findViewById(R.id.button12),\n          (TextView) findViewById(R.id.button13)\n        };\n    imageButtons[1] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button21),\n          (TextView) findViewById(R.id.button22),\n          (TextView) findViewById(R.id.button23)\n        };\n    imageButtons[2] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button31),\n          (TextView) findViewById(R.id.button32),\n          (TextView) findViewById(R.id.button33)\n        };\n    titleView = (TextView) findViewById(R.id.title);\n  }\n\n  @Override\n  public Observable<BoardCoordinate> squareClicks() {\n    ArrayList<Observable<BoardCoordinate>> observables = new ArrayList<>();\n    for (int i = 0; i < 3; i++) {\n      for (int j = 0; j < 3; j++) {\n        final int finalI = i;\n        final int finalJ = j;\n        observables.add(\n            RxView.clicks(imageButtons[i][j])\n                .map(\n                    new Function<Object, BoardCoordinate>() {\n                      @Override\n                      public BoardCoordinate apply(Object irrelevant) throws Exception {\n                        return new BoardCoordinate(finalI, finalJ);\n                      }\n                    }));\n      }\n    }\n    return Observable.merge(observables);\n  }\n\n  @Override\n  public void addCross(BoardCoordinate xy) {\n    TextView textView = imageButtons[xy.getX()][xy.getY()];\n    textView.setText(\"x\");\n    textView.setClickable(false);\n  }\n\n  @Override\n  public void addNought(BoardCoordinate xy) {\n    TextView textView = imageButtons[xy.getX()][xy.getY()];\n    textView.setText(\"O\");\n    textView.setClickable(false);\n  }\n\n  @Override\n  public void setCurrentPlayerName(String currentPlayer) {\n    titleView.setText(\"Current Player: \" + currentPlayer);\n  }\n\n  @Override\n  public void setPlayerWon(String playerName) {\n    titleView.setText(\"Player won: \" + playerName + \"!!!\");\n  }\n\n  @Override\n  public void setPlayerTie() {\n    titleView.setText(\"Tie game!\");\n  }\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/loggedout/LoggedOutBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.flipper.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link LoggedOutScope}. */\npublic class LoggedOutBuilder\n    extends ViewBuilder<LoggedOutView, LoggedOutRouter, LoggedOutBuilder.ParentComponent> {\n\n  public LoggedOutBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link LoggedOutRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link LoggedOutRouter}.\n   */\n  public LoggedOutRouter build(ViewGroup parentViewGroup) {\n    LoggedOutView view = createView(parentViewGroup);\n    LoggedOutInteractor interactor = new LoggedOutInteractor();\n    Component component =\n        DaggerLoggedOutBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.loggedoutRouter();\n  }\n\n  @Override\n  protected LoggedOutView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (LoggedOutView) inflater.inflate(R.layout.logged_out_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n\n    LoggedOutInteractor.Listener listener();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @LoggedOutScope\n    @Binds\n    abstract LoggedOutInteractor.LoggedOutPresenter presenter(LoggedOutView view);\n\n    @LoggedOutScope\n    @Provides\n    static LoggedOutRouter router(\n        Component component, LoggedOutView view, LoggedOutInteractor interactor) {\n      return new LoggedOutRouter(view, interactor, component);\n    }\n\n    // TODO: Create provider methods for dependencies created by this Rib. These should be static.\n  }\n\n  @LoggedOutScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<LoggedOutInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(LoggedOutInteractor interactor);\n\n      @BindsInstance\n      Builder view(LoggedOutView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    LoggedOutRouter loggedoutRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface LoggedOutScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface LoggedOutInternal {}\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/loggedout/LoggedOutInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport androidx.annotation.Nullable;\nimport androidx.core.util.Pair;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder.LoggedOutScope;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link LoggedOutScope}. */\n@RibInteractor\npublic class LoggedOutInteractor\n    extends Interactor<LoggedOutInteractor.LoggedOutPresenter, LoggedOutRouter> {\n\n  @Inject Listener listener;\n  @Inject LoggedOutPresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n    presenter\n        .loginName()\n        .subscribe(\n            new Consumer<Pair<String, String>>() {\n              @Override\n              public void accept(Pair<String, String> names) throws Exception {\n                if (!isEmpty(names.first) && !isEmpty(names.second)) {\n                  listener.login(names.first, names.second);\n                }\n              }\n            });\n  }\n\n  private boolean isEmpty(@Nullable String string) {\n    return string == null || string.length() == 0;\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface LoggedOutPresenter {\n\n    Observable<Pair<String, String>> loginName();\n  }\n\n  public interface Listener {\n    void login(String userNameA, String userNameB);\n  }\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/loggedout/LoggedOutRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link LoggedOutBuilder.LoggedOutScope}. */\npublic class LoggedOutRouter extends ViewRouter<LoggedOutView, LoggedOutInteractor> {\n\n  public LoggedOutRouter(\n      LoggedOutView view, LoggedOutInteractor interactor, LoggedOutBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "demos/flipper/src/main/java/com/uber/rib/root/loggedout/LoggedOutView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport androidx.core.util.Pair;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.flipper.R;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Function;\n\n/** Top level view for {@link LoggedOutBuilder.LoggedOutScope}. */\npublic class LoggedOutView extends LinearLayout implements LoggedOutInteractor.LoggedOutPresenter {\n\n  public LoggedOutView(Context context) {\n    this(context, null);\n  }\n\n  public LoggedOutView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public LoggedOutView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Override\n  public Observable<Pair<String, String>> loginName() {\n    return RxView.clicks(findViewById(R.id.login_button))\n        .map(\n            new Function<Object, Pair<String, String>>() {\n              @Override\n              public Pair<String, String> apply(Object o) throws Exception {\n                TextView playerNameOne = (TextView) findViewById(R.id.player_name_1);\n                TextView playerNameTwo = (TextView) findViewById(R.id.player_name_2);\n                return Pair.create(\n                    playerNameOne.getText().toString(), playerNameTwo.getText().toString());\n              }\n            });\n  }\n}\n"
  },
  {
    "path": "demos/flipper/src/main/res/layout/logged_out_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedout.LoggedOutView\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    <Space\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"/>\n    <EditText\n        android:id=\"@+id/player_name_1\"\n        android:hint=\"Player Name 1\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"/>\n    <EditText\n        android:id=\"@+id/player_name_2\"\n        android:hint=\"Player Name 2\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"/>\n    <Button\n        android:id=\"@+id/login_button\"\n        android:text=\"Login\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"/>\n</com.uber.rib.root.loggedout.LoggedOutView>"
  },
  {
    "path": "demos/flipper/src/main/res/layout/off_game_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedin.offgame.OffGameView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:gravity=\"bottom\"\n    android:orientation=\"vertical\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:layout_margin=\"16dp\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/player_one_name\"\n            android:text=\"@string/player_one_name\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"3\"/>\n\n        <TextView\n            android:id=\"@+id/player_one_win_count\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"@string/win_count\"/>\n\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\"\n        android:layout_gravity=\"center_horizontal\"\n        android:layout_margin=\"16dp\">\n\n        <TextView\n            android:id=\"@+id/player_two_name\"\n            android:text=\"@string/player_two_name\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"3\"/>\n\n        <TextView\n            android:id=\"@+id/player_two_win_count\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"@string/win_count\"/>\n\n    </LinearLayout>\n    <Button\n        android:id=\"@+id/start_game_button\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"\n        android:layout_gravity=\"center_horizontal\"\n        android:text=\"@string/start_game\"/>\n\n</com.uber.rib.root.loggedin.offgame.OffGameView>\n"
  },
  {
    "path": "demos/flipper/src/main/res/layout/root_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.RootView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"/>\n"
  },
  {
    "path": "demos/flipper/src/main/res/layout/tic_tac_toe_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedin.tictactoe.TicTacToeView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/tic_tac_toe\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <TextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"40dp\"\n        android:gravity=\"center_horizontal\"/>\n\n    <TextView\n        android:id=\"@+id/button11\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button12\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        android:layout_toRightOf=\"@id/button11\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button13\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        android:layout_toRightOf=\"@id/button12\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button21\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button11\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button22\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button11\"\n        android:layout_toRightOf=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button23\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button13\"\n        android:layout_toRightOf=\"@id/button22\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button31\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button32\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button22\"\n        android:layout_toRightOf=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button33\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button23\"\n        android:layout_toRightOf=\"@id/button22\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n</com.uber.rib.root.loggedin.tictactoe.TicTacToeView>\n"
  },
  {
    "path": "demos/flipper/src/main/res/values/ub__strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">Flipper Demo</string>\n    <string name=\"player_one_name\">Player One Name</string>\n    <string name=\"player_two_name\">Player Two Name</string>\n    <string name=\"login\">Login</string>\n    <string name=\"start_game\">Start game</string>\n    <string name=\"welcome_to_off_game\">Welcome FakeName!</string>\n    <string name=\"win_count\">win count</string>\n</resources>\n"
  },
  {
    "path": "demos/intellij/README.md",
    "content": "\n# RIB Demo: RIBs Tree IntelliJ plugin.\n\n## Goal\n\nThis demo shows off integration for the RIBs tree IntelliJ plugin.\n\n## Integration\n\nTo integrate the RIBs tree extension required by IntelliJ plugin in your app, simply initialize with the code below :\n\n```\npublic class SampleApplication extends Application {\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n\n        // Enable IntelliJ RIB tree plugin extension\n        if (BuildConfig.DEBUG) {\n            DebugBroadcastReceiver.initWithDefaults(\n                    this,\n                    Arrays.asList(\n                            new RibHierarchyDebugBroadcastHandler(\n                                    getApplicationContext(), RibEvents.getRouterEvents())));\n        }\n    }\n}\n```\n"
  },
  {
    "path": "demos/intellij/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.android.application\")\n    alias(libs.plugins.kotlin.kapt)\n    alias(libs.plugins.kotlin.ksp)\n}\n\nandroid {\n    namespace = \"com.uber.rib.intellij\"\n\n    defaultConfig {\n        applicationId = \"com.uber.rib.intellij\"\n    }\n\n    buildFeatures {\n        buildConfig = true\n    }\n}\n\ndependencies {\n    ksp(libs.dagger.compiler)\n    kapt(project(\":libraries:rib-compiler-test\"))\n    implementation(project(\":libraries:rib-android\"))\n    implementation(libs.androidx.appcompat)\n    implementation(libs.dagger.library)\n    implementation(libs.rxbinding)\n    implementation(appLibs.percent)\n    compileOnly(appLibs.jsr250)\n    testImplementation(project(\":libraries:rib-test\"))\n\n    // IntelliJ debugging integration\n    implementation(project(\":tooling:rib-intellij-plugin:native:intellij-broadcast-rib\"))\n}\n"
  },
  {
    "path": "demos/intellij/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <uses-feature android:required=\"true\" android:glEsVersion='0x00020000'/>\n\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>\n\n    <application\n        android:name=\"com.uber.rib.SampleApplication\"\n        android:allowBackup=\"false\"\n        android:icon=\"@drawable/ub__ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:screenOrientation=\"portrait\">\n        <activity\n            android:name=\"com.uber.rib.RootActivity\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n    </application>\n</manifest>\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/RootActivity.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib;\n\nimport android.view.ViewGroup;\nimport com.uber.rib.core.RibActivity;\nimport com.uber.rib.core.ViewRouter;\nimport com.uber.rib.root.RootBuilder;\n\n/** The sample app's single activity. */\npublic class RootActivity extends RibActivity {\n\n  @SuppressWarnings(\"unchecked\")\n  @Override\n  protected ViewRouter<?, ?> createRouter(ViewGroup parentViewGroup) {\n    RootBuilder rootBuilder = new RootBuilder(new RootBuilder.ParentComponent() {});\n    return rootBuilder.build(parentViewGroup);\n  }\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/SampleApplication.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib;\n\nimport android.app.Application;\nimport com.uber.debug.broadcast.core.DebugBroadcastReceiver;\nimport com.uber.debug.broadcast.rib.RibHierarchyDebugBroadcastHandler;\nimport com.uber.rib.core.RibEvents;\nimport com.uber.rib.intellij.BuildConfig;\nimport java.util.Arrays;\n\npublic class SampleApplication extends Application {\n\n  @Override\n  public void onCreate() {\n    super.onCreate();\n\n    // Enable IntelliJ RIB tree plugin extension\n    if (BuildConfig.DEBUG) {\n      DebugBroadcastReceiver.initWithDefaults(\n          this,\n          Arrays.asList(\n              new RibHierarchyDebugBroadcastHandler(\n                  getApplicationContext(), RibEvents.getRouterEvents())));\n    }\n  }\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/RootBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.intellij.R;\nimport com.uber.rib.root.loggedin.LoggedInBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutInteractor;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Scope;\n\n/** Builder for the {@link RootScope}. */\npublic class RootBuilder extends ViewBuilder<RootView, RootRouter, RootBuilder.ParentComponent> {\n\n  public RootBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link RootRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link RootRouter}.\n   */\n  public RootRouter build(ViewGroup parentViewGroup) {\n    RootView view = createView(parentViewGroup);\n    RootInteractor interactor = new RootInteractor();\n    Component component =\n        DaggerRootBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.rootRouter();\n  }\n\n  @Override\n  protected RootView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (RootView) inflater.inflate(R.layout.root_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n    // Define dependencies required from your parent interactor here.\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @RootScope\n    @Provides\n    static LoggedOutInteractor.Listener loggedOutListener(RootInteractor rootInteractor) {\n      return rootInteractor.new LoggedOutListener();\n    }\n\n    @RootScope\n    @Binds\n    abstract RootInteractor.RootPresenter presenter(RootView view);\n\n    @RootScope\n    @Provides\n    static RootRouter router(Component component, RootView view, RootInteractor interactor) {\n      return new RootRouter(\n          view,\n          interactor,\n          component,\n          new LoggedOutBuilder(component),\n          new LoggedInBuilder(component));\n    }\n  }\n\n  @RootScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component\n      extends InteractorBaseComponent<RootInteractor>,\n          LoggedOutBuilder.ParentComponent,\n          LoggedInBuilder.ParentComponent,\n          BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(RootInteractor interactor);\n\n      @BindsInstance\n      Builder view(RootView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    RootRouter rootRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface RootScope {}\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/RootInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedout.LoggedOutInteractor;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link RootBuilder.RootScope}. */\n@RibInteractor\npublic class RootInteractor extends Interactor<RootInteractor.RootPresenter, RootRouter> {\n\n  @Inject RootPresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n    getRouter().attachLoggedOut();\n  }\n\n  class LoggedOutListener implements LoggedOutInteractor.Listener {\n\n    @Override\n    public void login(String userNameA, String userNameB) {\n      // Switch to logged in. Let’s just ignore userName for now.\n      getRouter().detachLoggedOut();\n      getRouter().attachLoggedIn();\n    }\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface RootPresenter {}\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/RootRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.ViewRouter;\nimport com.uber.rib.root.loggedin.LoggedInBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutRouter;\n\n/** Adds and removes children of {@link RootBuilder.RootScope}. */\npublic class RootRouter extends ViewRouter<RootView, RootInteractor> {\n\n  private final LoggedOutBuilder loggedOutBuilder;\n  private final LoggedInBuilder loggedInBuilder;\n  @Nullable private LoggedOutRouter loggedOutRouter;\n\n  RootRouter(\n      RootView view,\n      RootInteractor interactor,\n      RootBuilder.Component component,\n      LoggedOutBuilder loggedOutBuilder,\n      LoggedInBuilder loggedInBuilder) {\n    super(view, interactor, component);\n    this.loggedOutBuilder = loggedOutBuilder;\n    this.loggedInBuilder = loggedInBuilder;\n  }\n\n  void attachLoggedOut() {\n    loggedOutRouter = loggedOutBuilder.build(getView());\n    attachChild(loggedOutRouter);\n    getView().addView(loggedOutRouter.getView());\n  }\n\n  void detachLoggedOut() {\n    if (loggedOutRouter != null) {\n      detachChild(loggedOutRouter);\n      getView().removeView(loggedOutRouter.getView());\n      loggedOutRouter = null;\n    }\n  }\n\n  void attachLoggedIn() {\n    attachChild(loggedInBuilder.build());\n  }\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/RootView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.FrameLayout;\nimport androidx.annotation.Nullable;\n\n/** Top level view for {@link RootBuilder.RootScope}. */\npublic class RootView extends FrameLayout implements RootInteractor.RootPresenter {\n\n  public RootView(Context context) {\n    this(context, null);\n  }\n\n  public RootView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public RootView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/loggedin/LoggedInBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport com.uber.rib.core.Builder;\nimport com.uber.rib.core.EmptyPresenter;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.root.RootView;\nimport com.uber.rib.root.loggedin.offgame.OffGameBuilder;\nimport com.uber.rib.root.loggedin.offgame.OffGameInteractor;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeBuilder;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\npublic class LoggedInBuilder extends Builder<LoggedInRouter, LoggedInBuilder.ParentComponent> {\n\n  public LoggedInBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link LoggedInRouter}.\n   *\n   * @return a new {@link LoggedInRouter}.\n   */\n  public LoggedInRouter build() {\n    LoggedInInteractor interactor = new LoggedInInteractor();\n    Component component =\n        DaggerLoggedInBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .interactor(interactor)\n            .build();\n\n    return component.loggedinRouter();\n  }\n\n  public interface ParentComponent {\n\n    RootView rootView();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @LoggedInScope\n    @Provides\n    static EmptyPresenter presenter() {\n      return new EmptyPresenter();\n    }\n\n    @LoggedInScope\n    @Provides\n    static LoggedInRouter router(\n        Component component, LoggedInInteractor interactor, RootView rootView) {\n      return new LoggedInRouter(\n          interactor,\n          component,\n          rootView,\n          new OffGameBuilder(component),\n          new TicTacToeBuilder(component));\n    }\n\n    @LoggedInScope\n    @Provides\n    static OffGameInteractor.Listener listener(LoggedInInteractor interactor) {\n      return interactor.new OffGameListener();\n    }\n  }\n\n  @LoggedInScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  public interface Component\n      extends InteractorBaseComponent<LoggedInInteractor>,\n          BuilderComponent,\n          OffGameBuilder.ParentComponent,\n          TicTacToeBuilder.ParentComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(LoggedInInteractor interactor);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    LoggedInRouter loggedinRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface LoggedInScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface LoggedInInternal {}\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/loggedin/LoggedInInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.EmptyPresenter;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedin.offgame.OffGameInteractor;\n\n/** Coordinates Business Logic for {@link LoggedInScope}. */\n@RibInteractor\npublic class LoggedInInteractor extends Interactor<EmptyPresenter, LoggedInRouter> {\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    // when first logging in we should be in the OffGame state\n    getRouter().attachOffGame();\n  }\n\n  class OffGameListener implements OffGameInteractor.Listener {\n\n    @Override\n    public void onStartGame() {\n      getRouter().detachOffGame();\n      getRouter().attachTicTacToe();\n    }\n  }\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/loggedin/LoggedInRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport android.view.ViewGroup;\nimport com.uber.rib.core.Router;\nimport com.uber.rib.root.loggedin.offgame.OffGameBuilder;\nimport com.uber.rib.root.loggedin.offgame.OffGameRouter;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeBuilder;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeRouter;\n\n/** Adds and removes children of {@link LoggedInBuilder.LoggedInScope}. */\npublic class LoggedInRouter extends Router<LoggedInInteractor> {\n\n  private final ViewGroup parentView;\n  private final OffGameBuilder offGameBuilder;\n  private final TicTacToeBuilder ticTacToeBuilder;\n  private OffGameRouter offGameRouter;\n  private TicTacToeRouter ticTacToeRouter;\n\n  LoggedInRouter(\n      LoggedInInteractor interactor,\n      LoggedInBuilder.Component component,\n      ViewGroup parentView,\n      OffGameBuilder offGameBuilder,\n      TicTacToeBuilder ticTacToeBuilder) {\n    super(interactor, component);\n    this.parentView = parentView;\n    this.offGameBuilder = offGameBuilder;\n    this.ticTacToeBuilder = ticTacToeBuilder;\n  }\n\n  @Override\n  protected void willDetach() {\n    super.willDetach();\n    detachOffGame();\n    detachTicTacToe();\n  }\n\n  void attachOffGame() {\n    offGameRouter = offGameBuilder.build(parentView);\n    attachChild(offGameRouter);\n    parentView.addView(offGameRouter.getView());\n  }\n\n  void detachOffGame() {\n    if (offGameRouter != null) {\n      detachChild(offGameRouter);\n      parentView.removeView(offGameRouter.getView());\n      offGameRouter = null;\n    }\n  }\n\n  void attachTicTacToe() {\n    ticTacToeRouter = ticTacToeBuilder.build(parentView);\n    attachChild(ticTacToeRouter);\n    parentView.addView(ticTacToeRouter.getView());\n  }\n\n  void detachTicTacToe() {\n    if (ticTacToeRouter != null) {\n      detachChild(ticTacToeRouter);\n      parentView.removeView(ticTacToeRouter.getView());\n      ticTacToeRouter = null;\n    }\n  }\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.intellij.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link OffGameScope}. */\npublic class OffGameBuilder\n    extends ViewBuilder<OffGameView, OffGameRouter, OffGameBuilder.ParentComponent> {\n\n  public OffGameBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link OffGameRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link OffGameRouter}.\n   */\n  public OffGameRouter build(ViewGroup parentViewGroup) {\n    OffGameView view = createView(parentViewGroup);\n    OffGameInteractor interactor = new OffGameInteractor();\n    Component component =\n        DaggerOffGameBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.offgameRouter();\n  }\n\n  @Override\n  protected OffGameView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (OffGameView) inflater.inflate(R.layout.off_game_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n\n    OffGameInteractor.Listener listener();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @OffGameScope\n    @Binds\n    abstract OffGameInteractor.OffGamePresenter presenter(OffGameView view);\n\n    @OffGameScope\n    @Provides\n    static OffGameRouter router(\n        Component component, OffGameView view, OffGameInteractor interactor) {\n      return new OffGameRouter(view, interactor, component);\n    }\n  }\n\n  @OffGameScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<OffGameInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(OffGameInteractor interactor);\n\n      @BindsInstance\n      Builder view(OffGameView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    OffGameRouter offgameRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface OffGameScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface OffGameInternal {}\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link OffGameScope}. */\n@RibInteractor\npublic class OffGameInteractor\n    extends Interactor<OffGameInteractor.OffGamePresenter, OffGameRouter> {\n\n  @Inject Listener listener;\n  @Inject OffGamePresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    presenter\n        .startGameRequest()\n        .subscribe(\n            new Consumer<Object>() {\n              @Override\n              public void accept(Object object) throws Exception {\n                listener.onStartGame();\n              }\n            });\n  }\n\n  public interface Listener {\n\n    void onStartGame();\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface OffGamePresenter {\n\n    Observable<Object> startGameRequest();\n  }\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link OffGameBuilder.OffGameScope}. */\npublic class OffGameRouter extends ViewRouter<OffGameView, OffGameInteractor> {\n\n  public OffGameRouter(\n      OffGameView view, OffGameInteractor interactor, OffGameBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.Button;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.core.Initializer;\nimport com.uber.rib.intellij.R;\nimport io.reactivex.Observable;\n\n/** Top level view for {@link OffGameBuilder.OffGameScope}. */\npublic class OffGameView extends LinearLayout implements OffGameInteractor.OffGamePresenter {\n\n  private Button button;\n  private TextView playerOneName;\n  private TextView playerTwoName;\n  private TextView playerOneScore;\n  private TextView playerTwoScore;\n\n  public OffGameView(Context context) {\n    this(context, null);\n  }\n\n  public OffGameView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public OffGameView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Initializer\n  @Override\n  protected void onFinishInflate() {\n    super.onFinishInflate();\n    button = (Button) findViewById(R.id.start_game_button);\n    playerOneName = (TextView) findViewById(R.id.player_one_name);\n    playerTwoName = (TextView) findViewById(R.id.player_two_name);\n    playerOneScore = (TextView) findViewById(R.id.player_one_win_count);\n    playerTwoScore = (TextView) findViewById(R.id.player_two_win_count);\n  }\n\n  @Override\n  public Observable<Object> startGameRequest() {\n    return RxView.clicks(button);\n  }\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/loggedin/tictactoe/Board.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport javax.inject.Inject;\n\nclass Board {\n\n  static final int ROWS = 3;\n  static final int COLS = 3;\n\n  MarkerType[][] cells;\n  int currentRow;\n  int currentCol;\n\n  @Inject\n  Board() {\n    cells = new MarkerType[ROWS][COLS];\n    for (int row = 0; row < ROWS; ++row) {\n      for (int col = 0; col < COLS; ++col) {\n        cells[row][col] = null;\n      }\n    }\n  }\n\n  /** Return true if it is a draw (i.e., no more EMPTY cell) */\n  boolean isDraw() {\n    for (int row = 0; row < ROWS; ++row) {\n      for (int col = 0; col < COLS; ++col) {\n        if (cells[row][col] == null) {\n          return false;\n        }\n      }\n    }\n    return !hasWon(MarkerType.CROSS) && !hasWon(MarkerType.NOUGHT);\n  }\n\n  /** Return true if the player with \"theSeed\" has won after placing at (currentRow, currentCol) */\n  boolean hasWon(MarkerType theSeed) {\n    return ((cells[currentRow][0] == theSeed\n            && cells[currentRow][1] == theSeed\n            && cells[currentRow][2] == theSeed)\n        || (cells[0][currentCol] == theSeed\n            && cells[1][currentCol] == theSeed\n            && cells[2][currentCol] == theSeed)\n        || (currentRow == currentCol\n            && cells[0][0] == theSeed\n            && cells[1][1] == theSeed\n            && cells[2][2] == theSeed)\n        || (currentRow + currentCol == 2\n            && cells[0][2] == theSeed\n            && cells[1][1] == theSeed\n            && cells[2][0] == theSeed));\n  }\n\n  enum MarkerType {\n    CROSS,\n    NOUGHT\n  }\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/loggedin/tictactoe/BoardCoordinate.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nclass BoardCoordinate {\n\n  private final int x;\n  private final int y;\n\n  BoardCoordinate(int x, int y) {\n    this.x = x;\n    this.y = y;\n  }\n\n  int getX() {\n    return x;\n  }\n\n  int getY() {\n    return y;\n  }\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.intellij.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link TicTacToeScope}. */\npublic class TicTacToeBuilder\n    extends ViewBuilder<TicTacToeView, TicTacToeRouter, TicTacToeBuilder.ParentComponent> {\n\n  public TicTacToeBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link TicTacToeRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link TicTacToeRouter}.\n   */\n  public TicTacToeRouter build(ViewGroup parentViewGroup) {\n    TicTacToeView view = createView(parentViewGroup);\n    TicTacToeInteractor interactor = new TicTacToeInteractor();\n    Component component =\n        DaggerTicTacToeBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.tictactoeRouter();\n  }\n\n  @Override\n  protected TicTacToeView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (TicTacToeView) inflater.inflate(R.layout.tic_tac_toe_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n    // TODO: Define dependencies required from your parent interactor here.\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @TicTacToeScope\n    @Binds\n    abstract TicTacToeInteractor.TicTacToePresenter presenter(TicTacToeView view);\n\n    @TicTacToeScope\n    @Provides\n    static TicTacToeRouter router(\n        Component component, TicTacToeView view, TicTacToeInteractor interactor) {\n      return new TicTacToeRouter(view, interactor, component);\n    }\n  }\n\n  @TicTacToeScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<TicTacToeInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(TicTacToeInteractor interactor);\n\n      @BindsInstance\n      Builder view(TicTacToeView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    TicTacToeRouter tictactoeRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface TicTacToeScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface TicTacToeInternal {}\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedin.tictactoe.Board.MarkerType;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link TicTacToeScope}. */\n@RibInteractor\npublic class TicTacToeInteractor\n    extends Interactor<TicTacToeInteractor.TicTacToePresenter, TicTacToeRouter> {\n\n  @Inject Board board;\n  @Inject TicTacToePresenter presenter;\n\n  private final String playerOne = \"Fake name 1\";\n  private final String playerTwo = \"Fake name 2\";\n\n  private MarkerType currentPlayer = MarkerType.CROSS;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    presenter\n        .squareClicks()\n        .subscribe(\n            new Consumer<BoardCoordinate>() {\n              @Override\n              public void accept(BoardCoordinate xy) throws Exception {\n                if (board.cells[xy.getX()][xy.getY()] == null) {\n                  if (currentPlayer == MarkerType.CROSS) {\n                    board.cells[xy.getX()][xy.getY()] = MarkerType.CROSS;\n                    board.currentRow = xy.getX();\n                    board.currentCol = xy.getY();\n                    presenter.addCross(xy);\n                    currentPlayer = MarkerType.NOUGHT;\n                  } else {\n                    board.cells[xy.getX()][xy.getY()] = MarkerType.NOUGHT;\n                    board.currentRow = xy.getX();\n                    board.currentCol = xy.getY();\n                    presenter.addNought(xy);\n                    currentPlayer = MarkerType.CROSS;\n                  }\n                }\n                if (board.hasWon(MarkerType.CROSS)) {\n                  presenter.setPlayerWon(playerOne);\n                } else if (board.hasWon(MarkerType.NOUGHT)) {\n                  presenter.setPlayerWon(playerTwo);\n                } else if (board.isDraw()) {\n                  presenter.setPlayerTie();\n                } else {\n                  updateCurrentPlayer();\n                }\n              }\n            });\n    updateCurrentPlayer();\n  }\n\n  private void updateCurrentPlayer() {\n    if (currentPlayer == MarkerType.CROSS) {\n      presenter.setCurrentPlayerName(playerOne);\n    } else {\n      presenter.setCurrentPlayerName(playerTwo);\n    }\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface TicTacToePresenter {\n    Observable<BoardCoordinate> squareClicks();\n\n    void setCurrentPlayerName(String currentPlayer);\n\n    void setPlayerWon(String playerName);\n\n    void setPlayerTie();\n\n    void addCross(BoardCoordinate xy);\n\n    void addNought(BoardCoordinate xy);\n  }\n\n  public interface Listener {}\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link TicTacToeBuilder.TicTacToeScope}. */\npublic class TicTacToeRouter extends ViewRouter<TicTacToeView, TicTacToeInteractor> {\n\n  public TicTacToeRouter(\n      TicTacToeView view, TicTacToeInteractor interactor, TicTacToeBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport androidx.percentlayout.widget.PercentRelativeLayout;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.core.Initializer;\nimport com.uber.rib.intellij.R;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Function;\nimport java.util.ArrayList;\n\n/** Top level view for {@link TicTacToeBuilder.TicTacToeScope}. */\npublic class TicTacToeView extends PercentRelativeLayout\n    implements TicTacToeInteractor.TicTacToePresenter {\n\n  private TextView[][] imageButtons;\n  private TextView titleView;\n\n  public TicTacToeView(Context context) {\n    this(context, null);\n  }\n\n  public TicTacToeView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public TicTacToeView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Initializer\n  @Override\n  protected void onFinishInflate() {\n    super.onFinishInflate();\n    imageButtons = new TextView[3][];\n    imageButtons[0] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button11),\n          (TextView) findViewById(R.id.button12),\n          (TextView) findViewById(R.id.button13)\n        };\n    imageButtons[1] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button21),\n          (TextView) findViewById(R.id.button22),\n          (TextView) findViewById(R.id.button23)\n        };\n    imageButtons[2] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button31),\n          (TextView) findViewById(R.id.button32),\n          (TextView) findViewById(R.id.button33)\n        };\n    titleView = (TextView) findViewById(R.id.title);\n  }\n\n  @Override\n  public Observable<BoardCoordinate> squareClicks() {\n    ArrayList<Observable<BoardCoordinate>> observables = new ArrayList<>();\n    for (int i = 0; i < 3; i++) {\n      for (int j = 0; j < 3; j++) {\n        final int finalI = i;\n        final int finalJ = j;\n        observables.add(\n            RxView.clicks(imageButtons[i][j])\n                .map(\n                    new Function<Object, BoardCoordinate>() {\n                      @Override\n                      public BoardCoordinate apply(Object irrelevant) throws Exception {\n                        return new BoardCoordinate(finalI, finalJ);\n                      }\n                    }));\n      }\n    }\n    return Observable.merge(observables);\n  }\n\n  @Override\n  public void addCross(BoardCoordinate xy) {\n    TextView textView = imageButtons[xy.getX()][xy.getY()];\n    textView.setText(\"x\");\n    textView.setClickable(false);\n  }\n\n  @Override\n  public void addNought(BoardCoordinate xy) {\n    TextView textView = imageButtons[xy.getX()][xy.getY()];\n    textView.setText(\"O\");\n    textView.setClickable(false);\n  }\n\n  @Override\n  public void setCurrentPlayerName(String currentPlayer) {\n    titleView.setText(\"Current Player: \" + currentPlayer);\n  }\n\n  @Override\n  public void setPlayerWon(String playerName) {\n    titleView.setText(\"Player won: \" + playerName + \"!!!\");\n  }\n\n  @Override\n  public void setPlayerTie() {\n    titleView.setText(\"Tie game!\");\n  }\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/loggedout/LoggedOutBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.intellij.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link LoggedOutScope}. */\npublic class LoggedOutBuilder\n    extends ViewBuilder<LoggedOutView, LoggedOutRouter, LoggedOutBuilder.ParentComponent> {\n\n  public LoggedOutBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link LoggedOutRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link LoggedOutRouter}.\n   */\n  public LoggedOutRouter build(ViewGroup parentViewGroup) {\n    LoggedOutView view = createView(parentViewGroup);\n    LoggedOutInteractor interactor = new LoggedOutInteractor();\n    Component component =\n        DaggerLoggedOutBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.loggedoutRouter();\n  }\n\n  @Override\n  protected LoggedOutView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (LoggedOutView) inflater.inflate(R.layout.logged_out_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n\n    LoggedOutInteractor.Listener listener();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @LoggedOutScope\n    @Binds\n    abstract LoggedOutInteractor.LoggedOutPresenter presenter(LoggedOutView view);\n\n    @LoggedOutScope\n    @Provides\n    static LoggedOutRouter router(\n        Component component, LoggedOutView view, LoggedOutInteractor interactor) {\n      return new LoggedOutRouter(view, interactor, component);\n    }\n\n    // TODO: Create provider methods for dependencies created by this Rib. These should be static.\n  }\n\n  @LoggedOutScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<LoggedOutInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(LoggedOutInteractor interactor);\n\n      @BindsInstance\n      Builder view(LoggedOutView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    LoggedOutRouter loggedoutRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface LoggedOutScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface LoggedOutInternal {}\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/loggedout/LoggedOutInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport androidx.annotation.Nullable;\nimport androidx.core.util.Pair;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder.LoggedOutScope;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link LoggedOutScope}. */\n@RibInteractor\npublic class LoggedOutInteractor\n    extends Interactor<LoggedOutInteractor.LoggedOutPresenter, LoggedOutRouter> {\n\n  @Inject Listener listener;\n  @Inject LoggedOutPresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n    presenter\n        .loginName()\n        .subscribe(\n            new Consumer<Pair<String, String>>() {\n              @Override\n              public void accept(Pair<String, String> names) throws Exception {\n                if (!isEmpty(names.first) && !isEmpty(names.second)) {\n                  listener.login(names.first, names.second);\n                }\n              }\n            });\n  }\n\n  private boolean isEmpty(@Nullable String string) {\n    return string == null || string.length() == 0;\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface LoggedOutPresenter {\n\n    Observable<Pair<String, String>> loginName();\n  }\n\n  public interface Listener {\n    void login(String userNameA, String userNameB);\n  }\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/loggedout/LoggedOutRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link LoggedOutBuilder.LoggedOutScope}. */\npublic class LoggedOutRouter extends ViewRouter<LoggedOutView, LoggedOutInteractor> {\n\n  public LoggedOutRouter(\n      LoggedOutView view, LoggedOutInteractor interactor, LoggedOutBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "demos/intellij/src/main/java/com/uber/rib/root/loggedout/LoggedOutView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport androidx.core.util.Pair;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.intellij.R;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Function;\n\n/** Top level view for {@link LoggedOutBuilder.LoggedOutScope}. */\npublic class LoggedOutView extends LinearLayout implements LoggedOutInteractor.LoggedOutPresenter {\n\n  public LoggedOutView(Context context) {\n    this(context, null);\n  }\n\n  public LoggedOutView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public LoggedOutView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Override\n  public Observable<Pair<String, String>> loginName() {\n    return RxView.clicks(findViewById(R.id.login_button))\n        .map(\n            new Function<Object, Pair<String, String>>() {\n              @Override\n              public Pair<String, String> apply(Object o) throws Exception {\n                TextView playerNameOne = (TextView) findViewById(R.id.player_name_1);\n                TextView playerNameTwo = (TextView) findViewById(R.id.player_name_2);\n                return Pair.create(\n                    playerNameOne.getText().toString(), playerNameTwo.getText().toString());\n              }\n            });\n  }\n}\n"
  },
  {
    "path": "demos/intellij/src/main/res/layout/logged_out_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedout.LoggedOutView\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    <Space\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"/>\n    <EditText\n        android:id=\"@+id/player_name_1\"\n        android:hint=\"Player Name 1\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"/>\n    <EditText\n        android:id=\"@+id/player_name_2\"\n        android:hint=\"Player Name 2\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"/>\n    <Button\n        android:id=\"@+id/login_button\"\n        android:text=\"Login\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"/>\n</com.uber.rib.root.loggedout.LoggedOutView>"
  },
  {
    "path": "demos/intellij/src/main/res/layout/off_game_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedin.offgame.OffGameView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:gravity=\"bottom\"\n    android:orientation=\"vertical\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:layout_margin=\"16dp\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/player_one_name\"\n            android:text=\"@string/player_one_name\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"3\"/>\n\n        <TextView\n            android:id=\"@+id/player_one_win_count\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"@string/win_count\"/>\n\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\"\n        android:layout_gravity=\"center_horizontal\"\n        android:layout_margin=\"16dp\">\n\n        <TextView\n            android:id=\"@+id/player_two_name\"\n            android:text=\"@string/player_two_name\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"3\"/>\n\n        <TextView\n            android:id=\"@+id/player_two_win_count\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"@string/win_count\"/>\n\n    </LinearLayout>\n    <Button\n        android:id=\"@+id/start_game_button\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"\n        android:layout_gravity=\"center_horizontal\"\n        android:text=\"@string/start_game\"/>\n\n</com.uber.rib.root.loggedin.offgame.OffGameView>\n"
  },
  {
    "path": "demos/intellij/src/main/res/layout/root_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.RootView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"/>\n"
  },
  {
    "path": "demos/intellij/src/main/res/layout/tic_tac_toe_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedin.tictactoe.TicTacToeView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/tic_tac_toe\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <TextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"40dp\"\n        android:gravity=\"center_horizontal\"/>\n\n    <TextView\n        android:id=\"@+id/button11\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button12\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        android:layout_toRightOf=\"@id/button11\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button13\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        android:layout_toRightOf=\"@id/button12\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button21\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button11\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button22\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button11\"\n        android:layout_toRightOf=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button23\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button13\"\n        android:layout_toRightOf=\"@id/button22\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button31\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button32\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button22\"\n        android:layout_toRightOf=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button33\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button23\"\n        android:layout_toRightOf=\"@id/button22\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n</com.uber.rib.root.loggedin.tictactoe.TicTacToeView>\n"
  },
  {
    "path": "demos/intellij/src/main/res/values/ub__strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">Intellij Demo</string>\n    <string name=\"player_one_name\">Player One Name</string>\n    <string name=\"player_two_name\">Player Two Name</string>\n    <string name=\"login\">Login</string>\n    <string name=\"start_game\">Start game</string>\n    <string name=\"welcome_to_off_game\">Welcome FakeName!</string>\n    <string name=\"win_count\">win count</string>\n</resources>\n"
  },
  {
    "path": "demos/memory-leaks/README.md",
    "content": "\n# RIB Demo: Memory Leaks\n\n## Goal\n\nThis demo shows off some memory leak tooling for RIBs.\n\n## Leak Canary\n\nThe `LoggedInInteractor` contains a bug that causes it to be retained in memory indefinately. This will cause memory leaks for both the `LoggedInInteractor` as well as the `RootActivity`. Normal usage of `LeakCanary` is sufficient to detect the `RootActivity` leak. However, we want to know when RIBs leak as well since RIB apps have a lot more RIBs than Activities.\n\nWe do this by adding the following code in `SampleApplication`. The RIB base classes don't have any compile dependencies on LeakCanary. Therefore in order to integrate LeakCanary into RIBs you need to inject a `RibRefWatcher` interface into the RIBs library.\n\n```java\nfinal RefWatcher refWatcher = LeakCanary\n        .refWatcher(this)\n        .watchDelay(2, TimeUnit.SECONDS)\n        .buildAndInstall();\nLeakCanary.install(this);\nRibRefWatcher.getInstance().setReferenceWatcher(new RibRefWatcher.ReferenceWatcher() {\n  @Override\n  public void watch(Object object) {\n    refWatcher.watch(object);\n  }\n\n  @Override\n  public void logBreadcrumb(String eventType, String data, String parent) {\n    // Ignore for now. Useful for collecting production analytics.\n  }\n});\nRibRefWatcher.getInstance().enableLeakCanary();\n```\n\nIf you run the memory-leak demo app and enter a username you'll see the following message caused by the `LoggedOutInteractor` leak:\n\n<img src=\"https://github.com/uber/RIBs/blob/assets/tutorial_assets/android/leak_canary_small.png?raw=true\" width=\"400\">\n\n## Static Leak Detection\n\nWhy did we even need to rely on LeakCanary in this case? Wouldn't it have been better if we had prevented this at build time? If you look at the offending code and remove `SuppressWarnings(\"AutoDispose\")` then you'll be unable to build with this memory leak. You'll see the following error:\n\n```\nerror: [AutoDispose] Missing Disposable handling: Apply AutoDispose or cache the Disposable instance manually and enable lenient mode\n        .subscribe(new Consumer<String>() {\n                  ^\n    (see https://github.com/uber/RIBs/blob/memory_leaks_module/android/demos/memory-leaks/README.md)\n```\n\nThis check is written as an ErrorProne check. It is extremely fast and integrated directly into the compiler. Unfortunately this mean it doesn't support Kotlin :(\n\nWe'd be happy to take a pull request for a PSI lint  :)\n\n## Additional Tools\n\nAdditional leak detection checkers have been written for RIBs. They'll be open sourced in the future if there is interest.\n"
  },
  {
    "path": "demos/memory-leaks/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport net.ltgt.gradle.errorprone.errorprone\n\nplugins {\n    id(\"ribs.android.application.errorprone\")\n    alias(libs.plugins.kotlin.kapt)\n}\n\nandroid {\n    namespace = \"com.uber.rib.memory_leak\"\n\n    defaultConfig {\n        applicationId = \"com.uber.tutorial3\"\n    }\n}\n\ndependencies {\n    implementation(project(\":libraries:rib-android\"))\n    ksp(project(\":libraries:rib-compiler-app\"))\n    kapt(project(\":libraries:rib-compiler-test\"))\n    ksp(appLibs.autodispose.errorprone)\n    ksp(libs.dagger.compiler)\n    implementation(libs.dagger.library)\n    implementation(libs.rxbinding)\n    implementation(appLibs.leakcanary)\n    implementation(appLibs.percent)\n    implementation(libs.androidx.appcompat)\n    compileOnly(appLibs.jsr250)\n\n    testImplementation(project(\":libraries:rib-test\"))\n}\n\ntasks.withType<JavaCompile>().configureEach {\n    // Disable error prone checks I don't want.\n    options.errorprone {\n        disable(\"ReferenceEquality\", \"ShortCircuitBoolean\", \"MissingCasesInEnumSwitch\",\n                \"CheckReturnValue\", \"InvalidPatternSyntax\", \"OperatorPrecedence\", \"DefaultCharset\")\n    }\n}\n"
  },
  {
    "path": "demos/memory-leaks/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <uses-feature android:required=\"true\" android:glEsVersion='0x00020000'/>\n\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>\n\n    <application\n        android:name=\"com.uber.rib.SampleApplication\"\n        android:allowBackup=\"false\"\n        android:icon=\"@drawable/ub__ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:screenOrientation=\"portrait\">\n        <activity\n            android:name=\"com.uber.rib.RootActivity\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n    </application>\n</manifest>\n"
  },
  {
    "path": "demos/memory-leaks/src/main/java/com/uber/rib/RootActivity.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib;\n\nimport android.view.ViewGroup;\nimport com.uber.rib.core.RibActivity;\nimport com.uber.rib.core.ViewRouter;\nimport com.uber.rib.root.RootBuilder;\n\n/** The sample app's single activity. */\npublic class RootActivity extends RibActivity {\n\n  // public Object object;\n\n  @SuppressWarnings(\"unchecked\")\n  @Override\n  protected ViewRouter<?, ?> createRouter(ViewGroup parentViewGroup) {\n    RootBuilder rootBuilder = new RootBuilder(new RootBuilder.ParentComponent() {});\n    return rootBuilder.build(parentViewGroup);\n  }\n}\n"
  },
  {
    "path": "demos/memory-leaks/src/main/java/com/uber/rib/SampleApplication.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib;\n\nimport android.app.Application;\nimport com.uber.rib.core.ActivityDelegate;\nimport com.uber.rib.core.HasActivityDelegate;\nimport com.uber.rib.core.RibRefWatcher;\nimport leakcanary.AppWatcher;\nimport leakcanary.ObjectWatcher;\n\npublic class SampleApplication extends Application implements HasActivityDelegate {\n\n  private SampleActivityDelegate activityDelegate;\n\n  @Override\n  public void onCreate() {\n    activityDelegate = new SampleActivityDelegate();\n    super.onCreate();\n    installLeakCanary();\n  }\n\n  /** Install leak canary for both activities and RIBs. */\n  private static void installLeakCanary() {\n    ObjectWatcher objectWatcher = AppWatcher.INSTANCE.getObjectWatcher();\n    RibRefWatcher.getInstance()\n        .setReferenceWatcher(\n            new RibRefWatcher.ReferenceWatcher() {\n              @Override\n              public void watch(Object objectToWatch) {\n                String description = objectToWatch.getClass().getSimpleName() + \" is detached\";\n                objectWatcher.expectWeaklyReachable(objectToWatch, description);\n              }\n\n              @Override\n              public void logBreadcrumb(String eventType, String data, String parent) {\n                // Ignore for now. Useful for collecting production analytics.\n              }\n            });\n    RibRefWatcher.getInstance().enableLeakCanary();\n  }\n\n  @Override\n  public ActivityDelegate activityDelegate() {\n    return activityDelegate;\n  }\n\n  private static final class SampleActivityDelegate implements ActivityDelegate {}\n}\n"
  },
  {
    "path": "demos/memory-leaks/src/main/java/com/uber/rib/root/RootBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.memory_leak.R;\nimport com.uber.rib.root.loggedin.LoggedInBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutInteractor;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Scope;\n\n/** Builder for the {@link RootScope}. */\npublic class RootBuilder extends ViewBuilder<RootView, RootRouter, RootBuilder.ParentComponent> {\n\n  public RootBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link RootRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link RootRouter}.\n   */\n  public RootRouter build(ViewGroup parentViewGroup) {\n    RootView view = createView(parentViewGroup);\n    RootInteractor interactor = new RootInteractor();\n    Component component =\n        DaggerRootBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.rootRouter();\n  }\n\n  @Override\n  protected RootView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (RootView) inflater.inflate(R.layout.root_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n    // Define dependencies required from your parent interactor here.\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @RootScope\n    @Provides\n    static LoggedOutInteractor.Listener loggedOutListener(RootInteractor rootInteractor) {\n      return rootInteractor.new LoggedOutListener();\n    }\n\n    @RootScope\n    @Binds\n    abstract RootInteractor.RootPresenter presenter(RootView view);\n\n    @RootScope\n    @Provides\n    static RootRouter router(Component component, RootView view, RootInteractor interactor) {\n      return new RootRouter(\n          view,\n          interactor,\n          component,\n          new LoggedOutBuilder(component),\n          new LoggedInBuilder(component));\n    }\n  }\n\n  @RootScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component\n      extends InteractorBaseComponent<RootInteractor>,\n          LoggedOutBuilder.ParentComponent,\n          LoggedInBuilder.ParentComponent,\n          BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(RootInteractor interactor);\n\n      @BindsInstance\n      Builder view(RootView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    RootRouter rootRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface RootScope {}\n}\n"
  },
  {
    "path": "demos/memory-leaks/src/main/java/com/uber/rib/root/RootInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedout.LoggedOutInteractor;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link RootBuilder.RootScope}. */\n@RibInteractor\npublic class RootInteractor extends Interactor<RootInteractor.RootPresenter, RootRouter> {\n\n  @Inject RootPresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n    getRouter().attachLoggedOut();\n  }\n\n  class LoggedOutListener implements LoggedOutInteractor.Listener {\n\n    @Override\n    public void login(String userName) {\n      // Switch to logged in. Let’s just ignore userName for now.\n      getRouter().detachLoggedOut();\n      getRouter().attachLoggedIn();\n    }\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface RootPresenter {}\n}\n"
  },
  {
    "path": "demos/memory-leaks/src/main/java/com/uber/rib/root/RootRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.ViewRouter;\nimport com.uber.rib.root.loggedin.LoggedInBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutRouter;\n\n/** Adds and removes children of {@link RootBuilder.RootScope}. */\npublic class RootRouter extends ViewRouter<RootView, RootInteractor> {\n\n  private final LoggedOutBuilder loggedOutBuilder;\n  private final LoggedInBuilder loggedInBuilder;\n  @Nullable private LoggedOutRouter loggedOutRouter;\n\n  RootRouter(\n      RootView view,\n      RootInteractor interactor,\n      RootBuilder.Component component,\n      LoggedOutBuilder loggedOutBuilder,\n      LoggedInBuilder loggedInBuilder) {\n    super(view, interactor, component);\n    this.loggedOutBuilder = loggedOutBuilder;\n    this.loggedInBuilder = loggedInBuilder;\n  }\n\n  void attachLoggedOut() {\n    loggedOutRouter = loggedOutBuilder.build(getView());\n    attachChild(loggedOutRouter);\n    getView().addView(loggedOutRouter.getView());\n  }\n\n  void detachLoggedOut() {\n    if (loggedOutRouter != null) {\n      detachChild(loggedOutRouter);\n      getView().removeView(loggedOutRouter.getView());\n      loggedOutRouter = null;\n    }\n  }\n\n  void attachLoggedIn() {\n    attachChild(loggedInBuilder.build());\n  }\n}\n"
  },
  {
    "path": "demos/memory-leaks/src/main/java/com/uber/rib/root/RootView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.FrameLayout;\nimport androidx.annotation.Nullable;\n\n/** Top level view for {@link RootBuilder.RootScope}. */\npublic class RootView extends FrameLayout implements RootInteractor.RootPresenter {\n\n  public RootView(Context context) {\n    this(context, null);\n  }\n\n  public RootView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public RootView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n}\n"
  },
  {
    "path": "demos/memory-leaks/src/main/java/com/uber/rib/root/loggedin/LoggedInBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport com.uber.rib.core.Builder;\nimport com.uber.rib.core.EmptyPresenter;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.root.RootView;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\npublic class LoggedInBuilder extends Builder<LoggedInRouter, LoggedInBuilder.ParentComponent> {\n\n  public LoggedInBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link LoggedInRouter}.\n   *\n   * @return a new {@link LoggedInRouter}.\n   */\n  public LoggedInRouter build() {\n    LoggedInInteractor interactor = new LoggedInInteractor();\n    Component component =\n        DaggerLoggedInBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .interactor(interactor)\n            .build();\n\n    return component.loggedinRouter();\n  }\n\n  public interface ParentComponent {\n    RootView rootView();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @LoggedInScope\n    @Provides\n    static EmptyPresenter presenter() {\n      return new EmptyPresenter();\n    }\n\n    @LoggedInScope\n    @Provides\n    static LoggedInRouter router(Component component, LoggedInInteractor interactor) {\n      return new LoggedInRouter(interactor, component);\n    }\n  }\n\n  @LoggedInScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  public interface Component extends InteractorBaseComponent<LoggedInInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(LoggedInInteractor interactor);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n    LoggedInRouter loggedinRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface LoggedInScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface LoggedInInternal {}\n}\n"
  },
  {
    "path": "demos/memory-leaks/src/main/java/com/uber/rib/root/loggedin/LoggedInInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport com.uber.rib.core.EmptyPresenter;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedin.LoggedInBuilder.LoggedInScope;\n\n/** Coordinates Business Logic for {@link LoggedInScope}. */\n@RibInteractor\npublic class LoggedInInteractor extends Interactor<EmptyPresenter, LoggedInRouter> {}\n"
  },
  {
    "path": "demos/memory-leaks/src/main/java/com/uber/rib/root/loggedin/LoggedInRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport com.uber.rib.core.Router;\n\n/** Adds and removes children of {@link LoggedInBuilder.LoggedInScope}. */\npublic class LoggedInRouter extends Router<LoggedInInteractor> {\n  LoggedInRouter(LoggedInInteractor interactor, LoggedInBuilder.Component component) {\n    super(interactor, component);\n  }\n}\n"
  },
  {
    "path": "demos/memory-leaks/src/main/java/com/uber/rib/root/loggedout/LoggedOutBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.memory_leak.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link LoggedOutScope}. */\npublic class LoggedOutBuilder\n    extends ViewBuilder<LoggedOutView, LoggedOutRouter, LoggedOutBuilder.ParentComponent> {\n\n  public LoggedOutBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link LoggedOutRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link LoggedOutRouter}.\n   */\n  public LoggedOutRouter build(ViewGroup parentViewGroup) {\n    LoggedOutView view = createView(parentViewGroup);\n    LoggedOutInteractor interactor = new LoggedOutInteractor();\n    Component component =\n        DaggerLoggedOutBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.loggedoutRouter();\n  }\n\n  @Override\n  protected LoggedOutView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (LoggedOutView) inflater.inflate(R.layout.logged_out_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n\n    LoggedOutInteractor.Listener listener();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @LoggedOutScope\n    @Binds\n    abstract LoggedOutInteractor.LoggedOutPresenter presenter(LoggedOutView view);\n\n    @LoggedOutScope\n    @Provides\n    static LoggedOutRouter router(\n        Component component, LoggedOutView view, LoggedOutInteractor interactor) {\n      return new LoggedOutRouter(view, interactor, component);\n    }\n\n    // TODO: Create provider methods for dependencies created by this Rib. These should be static.\n  }\n\n  @LoggedOutScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<LoggedOutInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(LoggedOutInteractor interactor);\n\n      @BindsInstance\n      Builder view(LoggedOutView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    LoggedOutRouter loggedoutRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface LoggedOutScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface LoggedOutInternal {}\n}\n"
  },
  {
    "path": "demos/memory-leaks/src/main/java/com/uber/rib/root/loggedout/LoggedOutInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder.LoggedOutScope;\nimport io.reactivex.Observable;\nimport io.reactivex.android.schedulers.AndroidSchedulers;\nimport io.reactivex.functions.Consumer;\nimport java.util.concurrent.TimeUnit;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link LoggedOutScope}. */\n@RibInteractor\npublic class LoggedOutInteractor\n    extends Interactor<LoggedOutInteractor.LoggedOutPresenter, LoggedOutRouter> {\n\n  @Inject Listener listener;\n  @Inject LoggedOutPresenter presenter;\n\n  @SuppressWarnings(\"AutoDispose\")\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n    presenter\n        .loginName()\n        .subscribe(\n            new Consumer<String>() {\n              @Override\n              public void accept(String name) throws Exception {\n                if (!isEmpty(name)) {\n                  listener.login(name);\n                }\n              }\n            });\n\n    // BUG: Subscribe to some long running operation and forget to unsubscribe.\n    Observable.timer(2000, TimeUnit.DAYS)\n        .observeOn(AndroidSchedulers.mainThread())\n        .subscribe(\n            new Consumer<Long>() {\n              @Override\n              public void accept(Long aLong) throws Exception {\n                listener.login(\"Really slow person.\");\n              }\n            });\n  }\n\n  private boolean isEmpty(@Nullable String string) {\n    return string == null || string.length() == 0;\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface LoggedOutPresenter {\n\n    Observable<String> loginName();\n  }\n\n  public interface Listener {\n\n    void login(String userName);\n  }\n}\n"
  },
  {
    "path": "demos/memory-leaks/src/main/java/com/uber/rib/root/loggedout/LoggedOutRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link LoggedOutBuilder.LoggedOutScope}. */\npublic class LoggedOutRouter extends ViewRouter<LoggedOutView, LoggedOutInteractor> {\n\n  public LoggedOutRouter(\n      LoggedOutView view, LoggedOutInteractor interactor, LoggedOutBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "demos/memory-leaks/src/main/java/com/uber/rib/root/loggedout/LoggedOutView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.memory_leak.R;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Function;\n\n/** Top level view for {@link LoggedOutBuilder.LoggedOutScope}. */\npublic class LoggedOutView extends LinearLayout implements LoggedOutInteractor.LoggedOutPresenter {\n\n  public LoggedOutView(Context context) {\n    this(context, null);\n  }\n\n  public LoggedOutView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public LoggedOutView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Override\n  public Observable<String> loginName() {\n    return RxView.clicks(findViewById(R.id.login_button))\n        .map(\n            new Function<Object, String>() {\n              @Override\n              public String apply(Object o) throws Exception {\n                TextView textView = (TextView) findViewById(R.id.edit_text);\n                return textView.getText().toString();\n              }\n            });\n  }\n}\n"
  },
  {
    "path": "demos/memory-leaks/src/main/res/layout/logged_out_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedout.LoggedOutView\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    <Space\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"/>\n    <EditText\n        android:id=\"@+id/edit_text\"\n        android:hint=\"Enter your name\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"/>\n    <Button\n        android:id=\"@+id/login_button\"\n        android:text=\"Login\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"/>\n</com.uber.rib.root.loggedout.LoggedOutView>"
  },
  {
    "path": "demos/memory-leaks/src/main/res/layout/root_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.RootView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"/>\n"
  },
  {
    "path": "demos/memory-leaks/src/main/res/values/ub__strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">Memory Leak Demo</string>\n    <string name=\"login\">Login</string>\n    <string name=\"start_game\">Start game</string>\n    <string name=\"welcome_to_off_game\">Welcome FakeName!</string>\n    <string name=\"win_count\">win count</string>\n</resources>\n"
  },
  {
    "path": "demos/rib-workers/README.md",
    "content": "The RIBs Workers Demo app showcases:\n\n1. Binding of [com.uber.rib.core.Worker] with different [CoroutineContext]\n2. Binding of [com.uber.rib.core.RibCoroutineWorker]\n3. Binding multiple [com.uber.rib.core.Worker] in specific [CoroutineContext]\n4. Convert existing [com.uber.rib.core.Worker] to [com.uber.rib.core.RibCoroutineWorker] and viceversa\n"
  },
  {
    "path": "demos/rib-workers/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2025. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.android.application.errorprone\")\n    alias(libs.plugins.compose.compiler)\n}\n\nandroid {\n    namespace = \"com.uber.rib.workers\"\n\n    defaultConfig {\n        applicationId = \"com.uber.ribs.workers\"\n    }\n    buildFeatures {\n        compose = true\n        buildConfig = true\n    }\n}\n\ndependencies {\n    ksp(appLibs.motif.compiler)\n    implementation(project(\":libraries:rib-android\"))\n    implementation(project(\":libraries:rib-android-compose\"))\n    implementation(project(\":libraries:rib-coroutines\"))\n    implementation(appLibs.activity.compose)\n    implementation(libs.androidx.annotation)\n    implementation(libs.compose.foundation)\n    implementation(appLibs.compose.material)\n    implementation(libs.compose.runtime)\n    implementation(libs.compose.ui)\n    implementation(libs.compose.uitooling)\n    implementation(libs.rxandroid2)\n    implementation(libs.kotlinx.coroutines.core)\n    implementation(libs.kotlinx.coroutines.android)\n    implementation(libs.kotlinx.coroutines.rx2)\n    implementation(libs.autodispose.coroutines)\n    implementation(appLibs.motif.library)\n\n    debugImplementation(libs.flipper)\n    debugImplementation(appLibs.soloader)\n    releaseImplementation(libs.flipper.noop)\n\n    implementation(project(\":tooling:rib-flipper-plugin\"))\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:name=\".ComposeApplication\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/Theme.Android\">\n\n        <activity\n            android:name=\".root.RootActivity\"\n            android:windowSoftInputMode=\"adjustResize\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n\n    </application>\n\n</manifest>"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/ComposeApplication.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers\n\nimport android.app.Application\nimport com.facebook.flipper.android.AndroidFlipperClient\nimport com.facebook.flipper.android.utils.FlipperUtils\nimport com.facebook.flipper.core.FlipperClient\nimport com.facebook.flipper.plugins.inspector.DescriptorMapping\nimport com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin\nimport com.facebook.soloader.SoLoader\nimport com.uber.rib.flipper.RibTreePlugin\nimport com.uber.rib.workers.root.logger.ApplicationLevelWorkerLogger\n\nclass ComposeApplication : Application() {\n\n  override fun onCreate() {\n    super.onCreate()\n    SoLoader.init(this, false)\n\n    if (BuildConfig.DEBUG && FlipperUtils.shouldEnableFlipper(this)) {\n      val client: FlipperClient = AndroidFlipperClient.getInstance(this)\n      client.addPlugin(RibTreePlugin())\n      client.addPlugin(InspectorFlipperPlugin(this, DescriptorMapping.withDefaults()))\n      client.start()\n    }\n\n    ApplicationLevelWorkerLogger.start()\n  }\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/RootActivity.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root\n\nimport android.app.Application\nimport android.view.ViewGroup\nimport com.uber.rib.core.RibActivity\nimport com.uber.rib.core.ViewRouter\nimport motif.Creatable\nimport motif.Expose\nimport motif.NoDependencies\nimport motif.ScopeFactory\n\nclass RootActivity : RibActivity() {\n\n  override fun createRouter(parentViewGroup: ViewGroup): ViewRouter<*, *> {\n    return ScopeFactory.create(Parent::class.java)\n      .rootScope(application, this, findViewById(android.R.id.content))\n      .router()\n  }\n\n  @motif.Scope\n  interface Parent : Creatable<NoDependencies> {\n    fun rootScope(\n      @Expose application: Application,\n      @Expose activity: RibActivity,\n      parentViewGroup: ViewGroup,\n    ): RootScope\n  }\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/RootInteractor.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root\n\nimport com.uber.rib.core.BasicInteractor\nimport com.uber.rib.core.EmptyPresenter\n\nclass RootInteractor(\n  presenter: EmptyPresenter,\n) : BasicInteractor<EmptyPresenter, RootRouter>(presenter)\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/RootRouter.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root\n\nimport com.uber.rib.core.BasicViewRouter\nimport com.uber.rib.workers.root.main.MainRouter\n\nclass RootRouter(\n  view: RootView,\n  interactor: RootInteractor,\n  private val scope: RootScope,\n) : BasicViewRouter<RootView, RootInteractor>(view, interactor) {\n\n  private var mainRouter: MainRouter? = null\n\n  override fun willAttach() {\n    attachMain()\n  }\n\n  override fun willDetach() {\n    detachMain()\n  }\n\n  private fun attachMain() {\n    if (mainRouter == null) {\n      mainRouter = scope.mainScope(view).router().also { attachChild(it) }\n    }\n  }\n\n  private fun detachMain() {\n    mainRouter?.let { detachChild(it) }\n  }\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/RootScope.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root\n\nimport android.view.ViewGroup\nimport com.uber.rib.core.EmptyPresenter\nimport com.uber.rib.workers.root.main.MainScope\n\n@motif.Scope\ninterface RootScope {\n  fun router(): RootRouter\n\n  fun mainScope(parentViewGroup: ViewGroup): MainScope\n\n  @motif.Objects\n  abstract class Objects {\n    abstract fun router(): RootRouter\n\n    abstract fun interactor(): RootInteractor\n\n    abstract fun presenter(): EmptyPresenter\n\n    fun view(parentViewGroup: ViewGroup): RootView {\n      return RootView(parentViewGroup.context)\n    }\n  }\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/RootView.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.widget.FrameLayout\n\nclass RootView\n@JvmOverloads\nconstructor(\n  context: Context,\n  attrs: AttributeSet? = null,\n  defStyle: Int = 0,\n) : FrameLayout(context, attrs, defStyle)\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root.logger\n\nimport android.util.Log\nimport com.uber.rib.core.RibActionEmitterType\nimport com.uber.rib.core.RibActionInfo\nimport com.uber.rib.core.RibActionState\nimport com.uber.rib.core.RibEvents\nimport java.lang.System.currentTimeMillis\nimport java.util.concurrent.ConcurrentHashMap\nimport kotlinx.coroutines.DelicateCoroutinesApi\nimport kotlinx.coroutines.GlobalScope\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.rx2.asFlow\n\n/**\n * Sample of consuming [ribActionEvents] possibilities.\n * 1. Can pipe Interactor/Router/Presenter/Worker information to backend\n * 2. Could report expensive workers on Ui thread and crash on Debug builds for early detection\n * 3. More tailored aggregation if needed.\n *\n * IMPORTANT: Given logic at [logWorkerDuration] will be running upon\n * Interactor/Router/Presenter/Worker ATTACH/DETACH, the added logic should guaranteed that we are\n * not impacting app performance\n */\nobject ApplicationLevelWorkerLogger {\n  private const val LOG_TAG = \"WorkerLogger\"\n\n  private val workerTimeStampMap = ConcurrentHashMap<String, Long>()\n\n  @OptIn(DelicateCoroutinesApi::class)\n  fun start() {\n    RibEvents.enableRibActionEmissions()\n\n    GlobalScope.launch {\n      RibEvents.ribActionEvents\n        .filter { it.ribActionEmitterType == RibActionEmitterType.DEPRECATED_WORKER }\n        .asFlow()\n        .collect { it.logWorkerDuration() }\n    }\n  }\n\n  private fun RibActionInfo.logWorkerDuration() {\n    if (ribActionState == RibActionState.STARTED && !ribActionEmitterName.isClassNameInMap()) {\n      workerTimeStampMap[this.ribActionEmitterName] = currentTimeMillis()\n    } else if (\n      ribActionState == RibActionState.COMPLETED && ribActionEmitterName.isClassNameInMap()\n    ) {\n      val startedTimeStamp = workerTimeStampMap[ribActionEmitterName]\n      startedTimeStamp?.let {\n        val totalDuration = getTotalDuration(it)\n        this.logDuration(totalDuration)\n      }\n      workerTimeStampMap.remove(ribActionEmitterName)\n    }\n  }\n\n  private fun String.isClassNameInMap(): Boolean = workerTimeStampMap.containsKey(this)\n\n  private fun getTotalDuration(preOnStartDuration: Long): Long =\n    currentTimeMillis() - preOnStartDuration\n\n  private fun RibActionInfo.logDuration(totalDuration: Long) {\n    Log.d(\n      LOG_TAG,\n      \"${this.ribActionEmitterName} ${this.ribEventType} took $totalDuration ms on $originalCallerThreadName thread\",\n    )\n  }\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/MainInteractor.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root.main\n\nimport com.uber.rib.core.BasicInteractor\nimport com.uber.rib.core.Bundle\nimport com.uber.rib.core.ComposePresenter\n\nclass MainInteractor(\n  presenter: ComposePresenter,\n  private val childContent: MainRouter.ChildContent,\n) : BasicInteractor<ComposePresenter, MainRouter>(presenter) {\n\n  override fun didBecomeActive(savedInstanceState: Bundle?) {\n    super.didBecomeActive(savedInstanceState)\n\n    router.view.setContent { MainView(childContent = childContent) }\n    router.attachRibWorkersSelection()\n  }\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/MainRouter.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root.main\n\nimport android.view.ViewGroup\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.MutableState\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.ui.platform.ComposeView\nimport com.uber.rib.core.BasicViewRouter\nimport com.uber.rib.workers.root.main.ribworkerselection.RibWorkerSelectionRouter\n\nclass MainRouter(\n  view: ComposeView,\n  interactor: MainInteractor,\n  private val parentView: ViewGroup,\n  private val scope: MainScope,\n  private val childContent: ChildContent,\n) : BasicViewRouter<ComposeView, MainInteractor>(view, interactor) {\n\n  private var ribWorkerSelectionRouter: RibWorkerSelectionRouter? = null\n\n  override fun willAttach() {\n    super.willAttach()\n    parentView.addView(view)\n  }\n\n  override fun willDetach() {\n    parentView.removeView(view)\n    super.willDetach()\n  }\n\n  internal fun attachRibWorkersSelection() {\n    if (ribWorkerSelectionRouter == null) {\n      ribWorkerSelectionRouter =\n        scope.ribWorkerSelectionScope(childContent.fullScreenSlot).router().also { attachChild(it) }\n    }\n  }\n\n  class ChildContent {\n    internal var fullScreenSlot: MutableState<@Composable () -> Unit> = mutableStateOf({})\n  }\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/MainScope.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root.main\n\nimport android.view.ViewGroup\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.MutableState\nimport androidx.compose.ui.platform.ComposeView\nimport com.uber.rib.core.ComposePresenter\nimport com.uber.rib.workers.root.main.ribworkerselection.RibWorkerSelectionScope\n\n@motif.Scope\ninterface MainScope {\n  fun router(): MainRouter\n\n  fun ribWorkerSelectionScope(slot: MutableState<@Composable () -> Unit>): RibWorkerSelectionScope\n\n  @motif.Objects\n  abstract class Objects {\n    abstract fun router(): MainRouter\n\n    abstract fun interactor(): MainInteractor\n\n    fun presenter(\n      childContent: MainRouter.ChildContent,\n    ): ComposePresenter {\n      return object : ComposePresenter() {\n        override val composable = @Composable { MainView(childContent) }\n      }\n    }\n\n    fun view(parentViewGroup: ViewGroup): ComposeView {\n      return ComposeView(parentViewGroup.context)\n    }\n\n    abstract fun childContent(): MainRouter.ChildContent\n  }\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/MainView.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root.main\n\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.tooling.preview.Preview\nimport androidx.compose.ui.unit.dp\n\n@Composable\nfun MainView(childContent: MainRouter.ChildContent) {\n  Column(\n    horizontalAlignment = Alignment.CenterHorizontally,\n    verticalArrangement = Arrangement.Top,\n    modifier = Modifier.fillMaxSize().padding(all = 4.dp).padding(top = 14.dp),\n  ) {\n    Box(\n      modifier = Modifier.fillMaxWidth().weight(1.0f).padding(4.dp).background(Color.White),\n    ) {\n      childContent.fullScreenSlot.value.invoke()\n    }\n  }\n}\n\n@Preview\n@Composable\nfun MainViewPreview() {\n  MainView(MainRouter.ChildContent())\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerBindTypeClickType.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root.main.ribworkerselection\n\nenum class RibWorkerBindTypeClickType {\n  SINGLE_WORKER_BIND_CALLER_THREAD,\n  SINGLE_WORKER_BIND_BACKGROUND_THREAD,\n  BIND_MULTIPLE_DEPRECATED_WORKERS,\n  BIND_MULTIPLE_RIB_COROUTINE_WORKERS,\n  BIND_RIB_COROUTINE_WORKER,\n  WORKER_TO_RIB_COROUTINE_WORKER,\n  RIB_COROUTINE_WORKER_TO_WORKER,\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionInteractor.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root.main.ribworkerselection\n\nimport android.util.Log\nimport com.uber.rib.core.BasicInteractor\nimport com.uber.rib.core.Bundle\nimport com.uber.rib.core.ComposePresenter\nimport com.uber.rib.core.RibEvents\nimport com.uber.rib.core.WorkerBinder\nimport com.uber.rib.core.asRibCoroutineWorker\nimport com.uber.rib.core.asWorker\nimport com.uber.rib.core.bind\nimport com.uber.rib.core.coroutineScope\nimport com.uber.rib.workers.root.main.workers.BackgroundWorker\nimport com.uber.rib.workers.root.main.workers.DefaultRibCoroutineWorker\nimport com.uber.rib.workers.root.main.workers.DefaultWorker\nimport com.uber.rib.workers.root.main.workers.IoWorker\nimport com.uber.rib.workers.root.main.workers.UiWorker\nimport com.uber.rib.workers.util.EventStream\nimport com.uber.rib.workers.util.StateStream\nimport kotlinx.coroutines.flow.launchIn\nimport kotlinx.coroutines.flow.onEach\n\nclass RibWorkerSelectionInteractor(\n  presenter: ComposePresenter,\n  private val eventStream: EventStream<RibWorkerBindTypeClickType>,\n  private val stateStream: StateStream<RibWorkerSelectionViewModel>,\n  private val backgroundWorker: BackgroundWorker,\n  private val defaultWorker: DefaultWorker,\n  private val ioWorker: IoWorker,\n  private val uiWorker: UiWorker,\n  private val defaultRibCoroutineWorker: DefaultRibCoroutineWorker,\n) : BasicInteractor<ComposePresenter, RibWorkerSelectionRouter>(presenter) {\n\n  override fun didBecomeActive(savedInstanceState: Bundle?) {\n    super.didBecomeActive(savedInstanceState)\n\n    Log.d(this.javaClass.name, \"WorkerLogger enabled? ${RibEvents.areRibActionEmissionsAllowed}\")\n\n    eventStream\n      .observe()\n      .onEach {\n        when (it) {\n          RibWorkerBindTypeClickType.SINGLE_WORKER_BIND_CALLER_THREAD -> {\n            updateViewModel(uiWorker::class.simpleName)\n            WorkerBinder.bind(this, uiWorker)\n          }\n          RibWorkerBindTypeClickType.SINGLE_WORKER_BIND_BACKGROUND_THREAD -> {\n            updateViewModel(ioWorker::class.simpleName)\n            WorkerBinder.bind(this, ioWorker)\n          }\n          RibWorkerBindTypeClickType.BIND_MULTIPLE_DEPRECATED_WORKERS -> {\n            val workers = listOf(backgroundWorker, defaultWorker, ioWorker, uiWorker)\n            updateViewModel(\"Multiple deprecated workers \")\n            WorkerBinder.bind(this, workers)\n          }\n          RibWorkerBindTypeClickType.BIND_MULTIPLE_RIB_COROUTINE_WORKERS -> {\n            val workers = listOf(defaultRibCoroutineWorker, defaultRibCoroutineWorker)\n            updateViewModel(\"Multiple RibCoroutineWorkers \")\n            coroutineScope.bind(workers)\n          }\n          RibWorkerBindTypeClickType.BIND_RIB_COROUTINE_WORKER -> {\n            updateViewModel(defaultRibCoroutineWorker::class.simpleName)\n            coroutineScope.bind(defaultRibCoroutineWorker)\n          }\n          RibWorkerBindTypeClickType.WORKER_TO_RIB_COROUTINE_WORKER -> {\n            val ribCoroutineWorker = uiWorker.asRibCoroutineWorker()\n            updateViewModel(ribCoroutineWorker::class.simpleName)\n            coroutineScope.bind(ribCoroutineWorker)\n          }\n          RibWorkerBindTypeClickType.RIB_COROUTINE_WORKER_TO_WORKER -> {\n            val worker = defaultRibCoroutineWorker.asWorker()\n            updateViewModel(worker::class.simpleName)\n            WorkerBinder.bind(this, worker)\n          }\n        }\n      }\n      .launchIn(coroutineScope)\n  }\n\n  private fun updateViewModel(workerBound: String?) {\n    with(stateStream) {\n      dispatch(\n        current()\n          .copy(\n            workerInfo = workerBound + WORKER_BOUND_MESSAGE,\n          ),\n      )\n    }\n  }\n\n  private companion object {\n    const val WORKER_BOUND_MESSAGE =\n      \" being bound. Please check Logcat with [WorkerBinderInfo] tag for full binding info\"\n  }\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionRouter.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root.main.ribworkerselection\n\nimport androidx.annotation.VisibleForTesting\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.MutableState\nimport com.uber.rib.core.BasicComposeRouter\nimport com.uber.rib.core.ComposePresenter\n\n@VisibleForTesting\nopen class RibWorkerSelectionRouter(\n  presenter: ComposePresenter,\n  interactor: RibWorkerSelectionInteractor,\n  slot: MutableState<@Composable () -> Unit>,\n) : BasicComposeRouter<RibWorkerSelectionInteractor>(presenter, interactor, slot)\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionScope.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root.main.ribworkerselection\n\nimport android.app.Application\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.collectAsState\nimport com.uber.rib.core.ComposePresenter\nimport com.uber.rib.workers.root.main.workers.BackgroundWorker\nimport com.uber.rib.workers.root.main.workers.DefaultRibCoroutineWorker\nimport com.uber.rib.workers.root.main.workers.DefaultWorker\nimport com.uber.rib.workers.root.main.workers.IoWorker\nimport com.uber.rib.workers.root.main.workers.UiWorker\nimport com.uber.rib.workers.util.EventStream\nimport com.uber.rib.workers.util.StateStream\n\n@motif.Scope\ninterface RibWorkerSelectionScope {\n  fun router(): RibWorkerSelectionRouter\n\n  @motif.Objects\n  abstract class Objects {\n    abstract fun router(): RibWorkerSelectionRouter\n\n    abstract fun interactor(): RibWorkerSelectionInteractor\n\n    fun presenter(\n      stateStream: StateStream<RibWorkerSelectionViewModel>,\n      eventStream: EventStream<RibWorkerBindTypeClickType>,\n    ): ComposePresenter {\n      return object : ComposePresenter() {\n        override val composable =\n          @Composable {\n            RibWorkerSelectionView(\n              stateStream.observe().collectAsState(initial = stateStream.current()),\n              eventStream,\n            )\n          }\n      }\n    }\n\n    fun eventStream() = EventStream<RibWorkerBindTypeClickType>()\n\n    fun stateStream() = StateStream(RibWorkerSelectionViewModel())\n\n    fun defaultWorker() = DefaultWorker()\n\n    fun backgroundWorker() = BackgroundWorker()\n\n    fun ioWorker(application: Application) = IoWorker(application)\n\n    fun uiWorker(application: Application) = UiWorker(application)\n\n    fun defaultRibCoroutineWorker() = DefaultRibCoroutineWorker()\n  }\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionView.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root.main.ribworkerselection\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material.Button\nimport androidx.compose.material.ButtonDefaults\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.State\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.tooling.preview.Preview\nimport androidx.compose.ui.unit.dp\nimport com.uber.rib.workers.util.EventStream\n\n@Composable\nfun RibWorkerSelectionView(\n  viewModel: State<RibWorkerSelectionViewModel>,\n  eventStream: EventStream<RibWorkerBindTypeClickType>,\n) {\n  Column(\n    modifier = Modifier.fillMaxSize().padding(16.dp),\n    verticalArrangement = Arrangement.spacedBy(16.dp, alignment = Alignment.CenterVertically),\n  ) {\n    AddButton(\n      eventStream,\n      RibWorkerBindTypeClickType.SINGLE_WORKER_BIND_CALLER_THREAD,\n      \"Bind Single Worker on current caller thread\",\n    )\n\n    AddButton(\n      eventStream,\n      RibWorkerBindTypeClickType.SINGLE_WORKER_BIND_BACKGROUND_THREAD,\n      \"Bind single Worker off main thread\",\n    )\n\n    AddButton(\n      eventStream,\n      RibWorkerBindTypeClickType.BIND_MULTIPLE_DEPRECATED_WORKERS,\n      \"Bind multiple Deprecated Workers\",\n    )\n\n    AddButton(\n      eventStream,\n      RibWorkerBindTypeClickType.BIND_MULTIPLE_RIB_COROUTINE_WORKERS,\n      \"Bind multiple RibCoroutineWorkers\",\n    )\n\n    AddButton(\n      eventStream,\n      RibWorkerBindTypeClickType.BIND_RIB_COROUTINE_WORKER,\n      \"Bind RibCoroutineWorker\",\n    )\n\n    AddButton(\n      eventStream,\n      RibWorkerBindTypeClickType.WORKER_TO_RIB_COROUTINE_WORKER,\n      \"Worker <> RibCoroutineWorker\",\n    )\n\n    AddButton(\n      eventStream,\n      RibWorkerBindTypeClickType.RIB_COROUTINE_WORKER_TO_WORKER,\n      \"RibCoroutineWorker <> Worker\",\n    )\n\n    Text(\n      viewModel.value.workerInfo,\n      modifier = Modifier.fillMaxSize(),\n    )\n  }\n}\n\n@Composable\nfun AddButton(\n  eventStream: EventStream<RibWorkerBindTypeClickType>,\n  ribWorkerBindTypeClickType: RibWorkerBindTypeClickType,\n  buttonText: String,\n) {\n  Button(\n    colors =\n      ButtonDefaults.buttonColors(\n        backgroundColor = Color.Black,\n        contentColor = Color.White,\n      ),\n    onClick = { eventStream.notify(ribWorkerBindTypeClickType) },\n    modifier = Modifier.fillMaxWidth(),\n  ) {\n    Text(text = buttonText)\n  }\n}\n\n@Preview\n@Composable\nfun RibWorkerSelectionViewDemo() {\n  val demoText =\n    \"WorkerBinderInfo(workerName=com.uber.rib.workers.root.main.workers.IoWorker, workerEvent=START, coroutineContext=Dispatchers.IO, threadName=DefaultDispatcher-worker-4, totalBindingDurationMilli=107)\\n\"\n  val viewModel = remember { mutableStateOf(RibWorkerSelectionViewModel(demoText)) }\n  RibWorkerSelectionView(viewModel, EventStream())\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionViewModel.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root.main.ribworkerselection\n\ndata class RibWorkerSelectionViewModel(\n  val workerInfo: String = \"\",\n)\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/workers/BackgroundWorker.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root.main.workers\n\nimport com.uber.rib.core.Worker\nimport java.util.concurrent.Executors\nimport kotlin.coroutines.CoroutineContext\nimport kotlinx.coroutines.asCoroutineDispatcher\n\n/** A Rib Worker to be guaranteed on a background thread with a limited pool size of 1 */\nclass BackgroundWorker() : Worker {\n\n  override val coroutineContext: CoroutineContext =\n    Executors.newFixedThreadPool(1).asCoroutineDispatcher()\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/workers/DefaultRibCoroutineWorker.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root.main.workers\n\nimport com.uber.rib.core.RibCoroutineWorker\nimport kotlinx.coroutines.CoroutineScope\n\nclass DefaultRibCoroutineWorker : RibCoroutineWorker {\n  override suspend fun onStart(scope: CoroutineScope) {\n    println(\"WorkerBinderInfo. DefaultRibCoroutineWorker bound on ${Thread.currentThread().name}\")\n  }\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/workers/DefaultWorker.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root.main.workers\n\nimport com.uber.rib.core.Worker\n\n/**\n * This worker will be bound by default on the caller thread where WorkerBinder.bind ( unless an\n * specific Dispatcher is passed.\n *\n * e.g. WorkerBinder(interactor, defaultWorker, RibDispatchers.IO)\n */\nclass DefaultWorker : Worker\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/workers/IoWorker.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root.main.workers\n\nimport android.app.Application\nimport com.uber.rib.core.RibDispatchers\nimport com.uber.rib.core.Worker\nimport com.uber.rib.core.WorkerScopeProvider\nimport java.io.File\nimport java.io.FileOutputStream\nimport java.io.OutputStream\nimport kotlin.coroutines.CoroutineContext\n\nclass IoWorker(private val application: Application) : Worker {\n\n  /**\n   * This worker is guaranteed to always run on IO. (Independently of Dispatchers passed at\n   * WorkerBinder.bind)\n   */\n  override val coroutineContext: CoroutineContext = RibDispatchers.IO\n\n  private var file: File? = null\n\n  override fun onStart(lifecycle: WorkerScopeProvider) {\n    val filePath = application.getExternalFilesDir(null)?.absolutePath + \"IoWorker.txt\"\n    file = File(filePath)\n    file?.createFile()\n  }\n\n  override fun onStop() {\n    file?.delete()\n  }\n\n  private fun File.createFile() {\n    createNewFile()\n    if (exists()) {\n      val outputStream: OutputStream = FileOutputStream(file)\n      // Simulate big file creation\n      for (i in 1..500000) {\n        outputStream.write(i)\n      }\n      outputStream.close()\n    }\n  }\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/workers/UiWorker.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.root.main.workers\n\nimport android.app.Application\nimport android.widget.Toast\nimport com.uber.rib.core.RibDispatchers\nimport com.uber.rib.core.Worker\nimport com.uber.rib.core.WorkerScopeProvider\nimport kotlin.coroutines.CoroutineContext\n\n/**\n * This worker is guaranteed to have onStart/onStop to always run on Main thread (independently of\n * any other default Dispatchers specified via WorkerBinder.bind)\n */\nclass UiWorker(private val activityContext: Application) : Worker {\n\n  override val coroutineContext: CoroutineContext = RibDispatchers.Main\n\n  override fun onStart(lifecycle: WorkerScopeProvider) {\n    Toast.makeText(\n        activityContext,\n        \"Ui Worker (onStart) call\",\n        Toast.LENGTH_LONG,\n      )\n      .show()\n  }\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/util/EventStream.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.util\n\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.flow.asSharedFlow\n\nclass EventStream<T> {\n  private val _sharedFlow = MutableSharedFlow<T>(extraBufferCapacity = 1)\n  private val sharedFlow = _sharedFlow.asSharedFlow()\n\n  fun notify(event: T) = _sharedFlow.tryEmit(event)\n\n  fun observe() = sharedFlow\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/kotlin/com/uber/rib/workers/util/StateStream.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workers.util\n\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.flow.update\n\nclass StateStream<T : Any>(default: T) {\n  private val _stateFlow = MutableStateFlow(default)\n  private val stateFlow = _stateFlow.asStateFlow()\n\n  fun dispatch(viewModel: T) = _stateFlow.update { viewModel }\n\n  fun observe() = stateFlow\n\n  fun current() = stateFlow.value\n}\n"
  },
  {
    "path": "demos/rib-workers/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path\n        android:fillColor=\"#3DDC84\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n</vector>\n"
  },
  {
    "path": "demos/rib-workers/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path android:pathData=\"M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"85.84757\"\n                android:endY=\"92.4963\"\n                android:startX=\"42.9492\"\n                android:startY=\"49.59793\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>"
  },
  {
    "path": "demos/rib-workers/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "demos/rib-workers/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "demos/rib-workers/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">Rib Workers Demo</string>\n</resources>"
  },
  {
    "path": "demos/rib-workers/src/main/res/values/themes.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <style name=\"Theme.Android\" parent=\"Theme.AppCompat.NoActionBar\">\n         <!-- Customize your theme here. -->\n        <item name=\"android:windowActionBar\">false</item>\n        <item name=\"android:windowNoTitle\">true</item>\n    </style>\n</resources>"
  },
  {
    "path": "gradle/app-libs.versions.toml",
    "content": "[versions]\nandroidx-activity = \"1.8.2\"\nautodispose = \"1.4.1\"\ncompose-libraries = \"1.6.8\" # If changing this, change also in libs.versions.toml.\nerrorprone = \"2.41.0\"\nerrorprone-javac = \"9+181-r4173-1\"\njsr250 = \"1.0\"\nleakcanary = \"2.14\"\nmotif = \"0.4.0-alpha06\"\npercent = \"1.0.0\"\nsoloader = \"0.12.1\"\nuber-nullaway = \"0.12.9\"\n\n[libraries]\nactivity-compose = { group = \"androidx.activity\", name = \"activity-compose\", version.ref = \"androidx-activity\" }\nautodispose-errorprone = { group = \"com.uber.autodispose\", name = \"autodispose-error-prone\", version.ref = \"autodispose\" }\ncompose-material = { group = \"androidx.compose.material\", name = \"material\", version.ref = \"compose-libraries\" }\nerrorprone-core = { group = \"com.google.errorprone\", name = \"error_prone_core\", version.ref = \"errorprone\" }\nerrorprone-javac = { group = \"com.google.errorprone\", name = \"javac\", version.ref = \"errorprone-javac\" }\njsr250 = { group = \"javax.annotation\", name = \"jsr250-api\", version.ref = \"jsr250\" }\nleakcanary = { group = \"com.squareup.leakcanary\", name = \"leakcanary-android\", version.ref = \"leakcanary\" }\nmotif-compiler = { group = \"com.uber.motif\", name = \"motif-compiler\", version.ref = \"motif\" }\nmotif-library = { group = \"com.uber.motif\", name = \"motif\", version.ref = \"motif\" }\npercent = { group = \"androidx.percentlayout\", name = \"percentlayout\", version.ref = \"percent\" }\nsoloader = { group = \"com.facebook.soloader\", name = \"soloader\", version.ref = \"soloader\" }\nuber-nullaway = { group = \"com.uber.nullaway\", name = \"nullaway\", version.ref = \"uber-nullaway\" }\n"
  },
  {
    "path": "gradle/japicmp.gradle",
    "content": "import me.champeau.gradle.japicmp.JapicmpTask\n\next.javaBaselineVersion = \"0.10.1\"\n\nbuildscript {\n    repositories {\n        gradlePluginPortal()\n    }\n    dependencies {\n        classpath \"me.champeau.gradle:japicmp-gradle-plugin:0.2.8\"\n        classpath libs.guava.jre // https://github.com/melix/japicmp-gradle-plugin/issues/36\n    }\n}\n\ntask checkBinaryCompatibility {\n    description = 'Generates binary compatibility reports'\n}\n\ndef isAndroidLibrary(Project project) {\n    return project.plugins.hasPlugin(\"com.android.library\")\n}\n\ndef unpackAarDir(Project project) {\n    return \"$buildDir/tmp/japicmp/$project.name\"\n}\n\ndef baselineArtifact(Project project) {\n    def group = project.property(\"GROUP\")\n    def artifactId = project.property(\"POM_ARTIFACT_ID\")\n    def artifactExtension = isAndroidLibrary(project) ? \"aar\" : \"jar\"\n    try {\n        String artifactName = \"$artifactId-${javaBaselineVersion}.$artifactExtension\"\n        project.group = 'virtual_group_for_japicmp' // Prevent it from resolving the current version.\n        def dependency = project.dependencies.create(\"$group:$artifactId:$javaBaselineVersion@$artifactExtension\")\n        return project.configurations\n                .detachedConfiguration(dependency).files\n                .find { it.name == artifactName }\n    } finally {\n        project.group = group\n    }\n}\n\ndef outputJar(Project project) {\n    if (isAndroidLibrary(project)) {\n        return \"${project.buildDir}/intermediates/aar_main_jar/release/classes.jar\"\n    } else {\n        return \"${project.buildDir}/libs/${project.name}-${project.property(\"VERSION_NAME\")}.jar\"\n    }\n}\n\ndef createUnpackAarTask(Project project, String baselineArtifact) {\n    return project.tasks.create(name: 'unpackBaselineAar', type: Copy) {\n        from zipTree(file(baselineArtifact))\n        into file(unpackAarDir(project))\n    }\n}\n\ndef createJapicmpTask(Project project, String baselineJar, String outputJar) {\n    return project.tasks.create(name: 'japicmp', type: JapicmpTask) {\n        oldClasspath = files(baselineJar)\n        newClasspath = files(outputJar)\n        classExcludes = [\n                'com.uber.assert.BuildConfig',\n                'com.uber.rib.android.BuildConfig',\n                'com.uber.rib.android.core.BuildConfig',\n                'com.uber.rib.compiler.InteractorAnnotatedClass',\n                'com.uber.rib.core.BasicInteractor',\n                'com.uber.rib.core.BasicRouter',\n                'com.uber.rib.core.Interactor_MembersInjector',\n                'com.uber.rib.core.Router',\n                'com.uber.rib.core.RouterNavigatorState',\n                'com.uber.rib.compiler.VerificationFailedException',\n                'com.uber.rib.workflow.BuildConfig',\n                'com.uber.rib.workflow.test.BuildConfig',\n                'com.ubercab.core.screenstack.base.BuildConfig'\n        ]\n        onlyBinaryIncompatibleModified = true\n        failOnModification = true\n        txtOutputFile = file(\"$buildDir/reports/japi.txt\")\n        ignoreMissingClasses = true\n        includeSynthetic = true\n    }\n}\n\ndef isProjectBaselined(Project project) {\n    !project.path.startsWith(':libraries:rib-android-compose')\n            && !project.path.startsWith(\":libraries:rib-coroutines\")\n}\n\nallprojects { project ->\n    afterEvaluate {\n        if (project.path.startsWith(':libraries:') && isProjectBaselined(project)) {\n            String baselineArtifact = baselineArtifact(project)\n            String baselineJar = baselineArtifact.endsWith('.aar')\n                    ? \"${unpackAarDir(project)}/classes.jar\"\n                    : baselineArtifact\n\n            def japicmpTask = createJapicmpTask(project, baselineJar, outputJar(project))\n            if (baselineArtifact.endsWith('.aar')) {\n                def unpackAarTask = createUnpackAarTask(project, baselineArtifact)\n                japicmpTask.dependsOn(unpackAarTask)\n            }\n            japicmpTask.dependsOn(\"$project.path:assemble\")\n            checkBinaryCompatibility.dependsOn(japicmpTask)\n        }\n    }\n}"
  },
  {
    "path": "gradle/libs.versions.toml",
    "content": "[versions]\nandroid-api = \"4.1.1.4\"\nandroid-studio = \"2023.3.1.20\"\nandroidx-annotation = \"1.9.1\"\nandroidx-appcompat = \"1.6.1\"\nandroidx-lifecycle = \"2.6.2\"\nautocommon = \"0.8\"\nautodispose = \"1.4.1\"\nautoservice = \"1.1.1\"\ncheckerqual = \"2.5.1\"\ncompose-libraries = \"1.6.8\" # If changing this, change also in apps-libs.versions.toml.\ndagger = \"2.53.1\"\nflipper = \"0.182.0\"\ngoogle-java-format = \"1.28.0\"\ngradle-android-plugin = \"8.13.0\"\ngradle-errorprone-plugin = \"4.3.0\"\ngradle-intellij-platform-plugin = \"2.9.0\"\ngradle-maven-publish-plugin = \"0.34.0\"\ngradle-nullaway-plugin = \"2.3.0\"\ngradle-spotless-plugin = \"7.2.1\"\ngson = \"2.8.7\"\nguava-android = \"27.1-android\"\nguava-jre = \"27.1-jre\"\njavapoet = \"1.11.1\"\nkotlin = \"2.1.10\"\nkotlin-ksp = \"2.1.10-1.0.31\"\nkotlinx-coroutines = \"1.10.2\"\nktfmt = \"0.43\"\nktlint = \"0.48.2\"\nreactivestreams = \"1.0.0\"\nrxandroid2 = \"2.1.1\"\nrxbinding = \"2.2.0\"\nrxjava2 = \"2.2.8\"\nrxkotlin = \"2.2.0\"\nrxrelay2 = \"2.1.0\"\nsavedstate = \"1.2.1\"\n\n[libraries]\nandroid-api = { group = \"com.google.android\", name = \"android\", version.ref = \"android-api\" }\nandroidx-annotation = { group = \"androidx.annotation\", name = \"annotation\", version.ref = \"androidx-annotation\" }\nandroidx-appcompat = { group = \"androidx.appcompat\", name = \"appcompat\", version.ref = \"androidx-appcompat\" }\nautocommon = { group = \"com.google.auto\", name = \"auto-common\", version.ref = \"autocommon\" }\nautodispose-coroutines = { group = \"com.uber.autodispose\", name = \"autodispose-coroutines-interop\", version.ref = \"autodispose\" }\nautodispose-library = { group = \"com.uber.autodispose\", name = \"autodispose\", version.ref = \"autodispose\" }\nautodispose-lifecycle = { group = \"com.uber.autodispose\", name = \"autodispose-lifecycle\", version.ref = \"autodispose\" }\nautoservice = { group = \"com.google.auto.service\", name = \"auto-service\", version.ref = \"autoservice\" }\ncheckerqual = { group = \"org.checkerframework\", name = \"checker-qual\", version.ref = \"checkerqual\" }\ncompose-foundation = { group = \"androidx.compose.foundation\", name = \"foundation\", version.ref = \"compose-libraries\" }\ncompose-runtime = { group = \"androidx.compose.runtime\", name = \"runtime\", version.ref = \"compose-libraries\" }\ncompose-ui = { group = \"androidx.compose.ui\", name = \"ui\", version.ref = \"compose-libraries\" }\ncompose-uitooling = { group = \"androidx.compose.ui\", name = \"ui-tooling\", version.ref = \"compose-libraries\" }\ndagger-compiler = { group = \"com.google.dagger\", name = \"dagger-compiler\", version.ref = \"dagger\" }\ndagger-library = { group = \"com.google.dagger\", name = \"dagger\", version.ref = \"dagger\" }\nflipper = { group = \"com.facebook.flipper\", name = \"flipper\", version.ref = \"flipper\" }\nflipper-noop = { group = \"com.facebook.flipper\", name = \"flipper-noop\", version.ref = \"flipper\" }\ngradle-android-plugin = { module = \"com.android.tools.build:gradle\", version.ref = \"gradle-android-plugin\" }\ngradle-kotlin-plugin = { module = \"org.jetbrains.kotlin:kotlin-gradle-plugin\", version.ref = \"kotlin\" }\ngson = { group = \"com.google.code.gson\", name = \"gson\", version.ref = \"gson\" }\nguava-android = { group = \"com.google.guava\", name = \"guava\", version.ref = \"guava-android\" }\nguava-jre = { group = \"com.google.guava\", name = \"guava\", version.ref = \"guava-jre\" }\njavapoet = { group = \"com.squareup\", name = \"javapoet\", version.ref = \"javapoet\" }\njavax-inject = { group = \"javax.inject\", name = \"javax.inject\", version = \"1\" }\nkotlinx-coroutines-android = { group = \"org.jetbrains.kotlinx\", name = \"kotlinx-coroutines-android\", version.ref = \"kotlinx-coroutines\" }\nkotlinx-coroutines-core = { group = \"org.jetbrains.kotlinx\", name = \"kotlinx-coroutines-core\", version.ref = \"kotlinx-coroutines\" }\nkotlinx-coroutines-rx2 = { group = \"org.jetbrains.kotlinx\", name = \"kotlinx-coroutines-rx2\", version.ref = \"kotlinx-coroutines\" }\nlifecycle-runtime = { group = \"androidx.lifecycle\", name = \"lifecycle-runtime\", version.ref = \"androidx-lifecycle\" }\nreactivestreams = { group = \"org.reactivestreams\", name = \"reactive-streams\", version.ref = \"reactivestreams\" }\nrxandroid2 = { group = \"io.reactivex.rxjava2\", name = \"rxandroid\", version.ref = \"rxandroid2\" }\nrxbinding = { group = \"com.jakewharton.rxbinding2\", name = \"rxbinding\", version.ref = \"rxbinding\" }\nrxjava2 = { group = \"io.reactivex.rxjava2\", name = \"rxjava\", version.ref = \"rxjava2\" }\nrxkotlin = { group = \"io.reactivex.rxjava2\", name = \"rxkotlin\", version.ref = \"rxkotlin\" }\nrxrelay2 = { group = \"com.jakewharton.rxrelay2\", name = \"rxrelay\", version.ref = \"rxrelay2\" }\nsavedstate = { group = \"androidx.savedstate\", name = \"savedstate-ktx\", version.ref = \"savedstate\" }\n\n[plugins]\nandroid-application = { id = \"com.android.application\", version.ref = \"gradle-android-plugin\" }\nandroid-library = { id = \"com.android.library\", version.ref = \"gradle-android-plugin\" }\nerrorprone = { id = \"net.ltgt.errorprone\", version.ref = \"gradle-errorprone-plugin\" }\nintellij-platform = { id = \"org.jetbrains.intellij.platform\", version.ref = \"gradle-intellij-platform-plugin\" }\nkotlin-android = { id = \"org.jetbrains.kotlin.android\", version.ref = \"kotlin\" }\nkotlin-kapt = { id = \"org.jetbrains.kotlin.kapt\", version.ref = \"kotlin\" }\nkotlin-ksp = { id = \"com.google.devtools.ksp\", version.ref = \"kotlin-ksp\" }\ncompose-compiler = { id = \"org.jetbrains.kotlin.plugin.compose\", version.ref = \"kotlin\" }\nmaven-publish = { id = \"com.vanniktech.maven.publish\", version.ref = \"gradle-maven-publish-plugin\" }\nnullaway = { id = \"net.ltgt.nullaway\", version.ref = \"gradle-nullaway-plugin\" }\nspotless = { id = \"com.diffplug.spotless\", version.ref = \"gradle-spotless-plugin\" }\n"
  },
  {
    "path": "gradle/test-libs.versions.toml",
    "content": "[versions]\ncompile-testing = \"0.23.0\"\njunit = \"4.13.2\"\nkotlinx-coroutines = \"1.10.2\"\nmockito = \"3.12.4\"\nmockito-kotlin = \"4.0.0\"\nrobolectric = \"4.16\"\ntruth = \"1.1.3\"\n\n[libraries]\ncompile-testing = { group = \"com.google.testing.compile\", name = \"compile-testing\", version.ref = \"compile-testing\" }\njunit = { group = \"junit\", name = \"junit\", version.ref = \"junit\" }\nkotlinx-coroutines-test = { group = \"org.jetbrains.kotlinx\", name = \"kotlinx-coroutines-test\", version.ref = \"kotlinx-coroutines\" }\nmockito = { group = \"org.mockito\", name = \"mockito-core\", version.ref = \"mockito\" }\nmockito-kotlin = { group = \"org.mockito.kotlin\", name = \"mockito-kotlin\", version.ref = \"mockito-kotlin\" }\nrobolectric = { group = \"org.robolectric\", name = \"robolectric\", version.ref = \"robolectric\" }\ntruth = { group = \"com.google.truth\", name = \"truth\", version.ref = \"truth\" }\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Wed Apr 12 10:45:00 PDT 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.3-bin.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\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# 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\n\nGROUP=com.uber.rib\nVERSION_NAME=0.16.6-SNAPSHOT\nPOM_DESCRIPTION=RIBs is the cross-platform architecture behind many mobile apps at Uber. This framework is designed for mobile apps with a large number of engineers and nested states.\nPOM_URL=https://github.com/uber/RIBs/\nPOM_SCM_URL=https://github.com/uber/RIBs/\nPOM_SCM_CONNECTION=scm:git:git://github.com/uber/RIBs.git\nPOM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/uber/RIBs.git\nPOM_LICENCE_NAME=The Apache Software License, Version 2.0\nPOM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt\nPOM_LICENCE_DIST=repo\nPOM_DEVELOPER_ID=uber\nPOM_DEVELOPER_NAME=Uber Technologies\nSONATYPE_STAGING_PROFILE=com.uber\nSONATYPE_HOST=DEFAULT\nRELEASE_SIGNING_ENABLED=true\nmavenCentralPublishing=true\nsignAllPublications=true\n\nandroid.useAndroidX=true\nandroid.enableJetifier=true\norg.gradle.jvmargs=-Xmx4096m\nandroid.enableR8.fullMode=false\nandroid.nonTransitiveRClass=false\nandroid.nonFinalResIds=false\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# 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\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    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# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windowz variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\ngoto execute\n\n:4NT_args\n@rem Get arguments from the 4NT Shell from JP Software\nset CMD_LINE_ARGS=%$\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\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%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "libraries/rib-android/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.android.library\")\n    alias(libs.plugins.maven.publish)\n}\n\nandroid {\n    namespace = \"com.uber.rib.android\"\n}\n\nkotlin.compilerOptions {\n    optIn.add(\"com.uber.rib.core.internal.CoreFriendModuleApi\")\n}\n\ndependencies {\n    api(project(\":libraries:rib-android-core\"))\n    api(project(\":libraries:rib-base\"))\n    api(libs.rxkotlin)\n    api(libs.rxrelay2)\n    api(libs.rxjava2)\n    api(libs.androidx.annotation)\n    implementation(libs.javax.inject)\n    implementation(libs.autodispose.coroutines)\n    implementation(libs.kotlinx.coroutines.android)\n    implementation(libs.kotlinx.coroutines.rx2)\n    testImplementation(testLibs.robolectric)\n    testImplementation(libs.lifecycle.runtime)\n    testImplementation(testLibs.mockito.kotlin)\n    testImplementation(project(\":libraries:rib-test\"))\n}\n"
  },
  {
    "path": "libraries/rib-android/gradle.properties",
    "content": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPOM_NAME=RIBs (Android)\nPOM_ARTIFACT_ID=rib-android\nPOM_PACKAGING=android\n"
  },
  {
    "path": "libraries/rib-android/src/main/kotlin/com/uber/rib/core/ActivityContext.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport javax.inject.Qualifier\n\n/** Injection qualifier for an Activity Context. */\n@Qualifier @Retention(AnnotationRetention.RUNTIME) public annotation class ActivityContext\n"
  },
  {
    "path": "libraries/rib-android/src/main/kotlin/com/uber/rib/core/ActivityStarter.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.content.Intent\n\n/**\n * Start activities. A much cleaner dependency than an entire activity or context, and easier to\n * inject and mock in tests.\n */\npublic interface ActivityStarter {\n  /**\n   * Start an activity with the given intent.\n   *\n   * @param intent The intent to open a new activity.\n   */\n  public fun startActivity(intent: Intent)\n\n  /**\n   * Start an activity with the given intent, to be notified when that activity finishes.\n   *\n   * @param intent The intent to open a new activity.\n   * @param requestCode The code unique to your current activity to know which activity result is\n   *   from this request.\n   */\n  @Deprecated(\"\"\"use plain Activity instead\"\"\")\n  public fun startActivityForResult(intent: Intent, requestCode: Int)\n}\n"
  },
  {
    "path": "libraries/rib-android/src/main/kotlin/com/uber/rib/core/BasicViewRouter.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.view.View\n\n/**\n * [ViewRouter] that does not require an [InteractorBaseComponent].\n *\n * @param <I> type of interactor.\n */\npublic abstract class BasicViewRouter<V : View, I : Interactor<*, *>>(\n  view: V,\n  interactor: I,\n) : ViewRouter<V, I>(view, interactor)\n"
  },
  {
    "path": "libraries/rib-android/src/main/kotlin/com/uber/rib/core/IntentCreator.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.content.Intent\n\n/** Creates intent objects. */\npublic interface IntentCreator {\n  /**\n   * Create an explicit intent targeted at a particular class, which is guaranteed to be limited to\n   * your app's package.\n   *\n   * @param cls The class that you intend to receive this intent.\n   * @return The intent.\n   */\n  public fun create(cls: Class<*>): Intent\n\n  /**\n   * Create an implicit intent targeted at an action, which may end up resolving to your app or to\n   * any other app on the device which decides to look for this intent action. If you use this and\n   * the intent is meant only for your app, it's wise to take additional precautions like setting\n   * the package on the intent to your own app's package.\n   *\n   * @param action The intent action, which any app may register to receive.\n   * @return The intent.\n   */\n  public fun create(action: String): Intent\n}\n"
  },
  {
    "path": "libraries/rib-android/src/main/kotlin/com/uber/rib/core/IntentCreatorImpl.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.content.Context\nimport android.content.Intent\n\n/** A default implementation of [IntentCreator]. */\npublic open class IntentCreatorImpl(private val context: Context) : IntentCreator {\n  override fun create(cls: Class<*>): Intent = Intent(context, cls)\n\n  override fun create(action: String): Intent = Intent(action)\n}\n"
  },
  {
    "path": "libraries/rib-android/src/main/kotlin/com/uber/rib/core/IntentFactory.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.content.Intent\n\n/** Factory for an [Intent] that opens an activity. */\npublic interface IntentFactory {\n  /**\n   * Create a view router to be displayed for an [Intent].\n   *\n   * @param intentCreator to create the [Intent].\n   * @return the activity [Intent].\n   */\n  public fun create(intentCreator: IntentCreator): Intent\n}\n"
  },
  {
    "path": "libraries/rib-android/src/main/kotlin/com/uber/rib/core/RibActivity.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.content.Intent\nimport android.content.res.Configuration\nimport android.os.Build\nimport android.view.ViewGroup\nimport androidx.annotation.CallSuper\nimport com.uber.autodispose.lifecycle.CorrespondingEventsFunction\nimport com.uber.autodispose.lifecycle.LifecycleEndedException\nimport com.uber.autodispose.lifecycle.LifecycleNotStartedException\nimport com.uber.autodispose.lifecycle.LifecycleScopeProvider\nimport com.uber.rib.core.lifecycle.ActivityCallbackEvent\nimport com.uber.rib.core.lifecycle.ActivityCallbackEvent.Companion.create\nimport com.uber.rib.core.lifecycle.ActivityCallbackEvent.Companion.createNewIntent\nimport com.uber.rib.core.lifecycle.ActivityCallbackEvent.Companion.createOnActivityResultEvent\nimport com.uber.rib.core.lifecycle.ActivityCallbackEvent.Companion.createOnSaveInstanceStateEvent\nimport com.uber.rib.core.lifecycle.ActivityCallbackEvent.Companion.createPictureInPictureMode\nimport com.uber.rib.core.lifecycle.ActivityCallbackEvent.Companion.createTrimMemoryEvent\nimport com.uber.rib.core.lifecycle.ActivityCallbackEvent.Companion.createWindowFocusEvent\nimport com.uber.rib.core.lifecycle.ActivityLifecycleEvent\nimport com.uber.rib.core.lifecycle.ActivityLifecycleEvent.Companion.create\nimport com.uber.rib.core.lifecycle.ActivityLifecycleEvent.Companion.createOnCreateEvent\nimport io.reactivex.CompletableSource\nimport io.reactivex.Observable\nimport kotlinx.coroutines.channels.BufferOverflow\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.flow.SharedFlow\nimport kotlinx.coroutines.rx2.asObservable\n\n/** Base implementation for all VIP [android.app.Activity]s. */\npublic abstract class RibActivity :\n  CoreAppCompatActivity(),\n  ActivityStarter,\n  LifecycleScopeProvider<ActivityLifecycleEvent>,\n  RxActivityEvents {\n  private var router: ViewRouter<*, *>? = null\n\n  private val _lifecycleFlow =\n    MutableSharedFlow<ActivityLifecycleEvent>(1, 0, BufferOverflow.DROP_OLDEST)\n\n  public open val lifecycleFlow: SharedFlow<ActivityLifecycleEvent>\n    get() = _lifecycleFlow\n\n  @Volatile private var _lifecycleObservable: Observable<ActivityLifecycleEvent>? = null\n  private val lifecycleObservable\n    get() = ::_lifecycleObservable.setIfNullAndGet { lifecycleFlow.asObservable() }\n\n  private val _callbacksFlow =\n    MutableSharedFlow<ActivityCallbackEvent>(0, 1, BufferOverflow.DROP_OLDEST)\n  public open val callbacksFlow: SharedFlow<ActivityCallbackEvent>\n    get() = _callbacksFlow\n\n  @Volatile private var _callbacksObservable: Observable<ActivityCallbackEvent>? = null\n  private val callbacksObservable\n    get() = ::_callbacksObservable.setIfNullAndGet { callbacksFlow.asObservable() }\n\n  /** @return an observable of this activity's lifecycle events. */\n  final override fun lifecycle(): Observable<ActivityLifecycleEvent> = lifecycleObservable\n\n  /** @return an observable of this activity's lifecycle events. */\n  override fun callbacks(): Observable<ActivityCallbackEvent> = callbacksObservable\n\n  final override fun correspondingEvents(): CorrespondingEventsFunction<ActivityLifecycleEvent> =\n    ACTIVITY_LIFECYCLE\n\n  final override fun peekLifecycle(): ActivityLifecycleEvent? =\n    lifecycleFlow.replayCache.lastOrNull()\n\n  final override fun requestScope(): CompletableSource =\n    lifecycleFlow.asScopeCompletable(lifecycleRange)\n\n  @Initializer\n  @CallSuper\n  override fun onCreate(savedInstanceState: android.os.Bundle?) {\n    super.onCreate(savedInstanceState)\n    val rootViewGroup = findViewById<ViewGroup>(android.R.id.content)\n    _lifecycleFlow.tryEmit(createOnCreateEvent(savedInstanceState))\n    val wrappedBundle: Bundle? =\n      if (savedInstanceState != null) Bundle(savedInstanceState) else null\n    router = createRouter(rootViewGroup)\n    router?.let {\n      it.dispatchAttach(wrappedBundle)\n      rootViewGroup.addView(it.view)\n      RibEvents.emitRouterEvent(RibEventType.ATTACHED, it, null)\n    }\n  }\n\n  @CallSuper\n  override fun onSaveInstanceState(outState: android.os.Bundle) {\n    super.onSaveInstanceState(outState)\n    _callbacksFlow.tryEmit(createOnSaveInstanceStateEvent(outState))\n    router?.saveInstanceStateInternal(Bundle(outState))\n      ?: throw NullPointerException(\"Router should not be null\")\n  }\n\n  @CallSuper\n  override fun onStart() {\n    super.onStart()\n    _lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.START))\n  }\n\n  @CallSuper\n  override fun onResume() {\n    super.onResume()\n    _lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.RESUME))\n  }\n\n  @CallSuper\n  override fun onNewIntent(intent: Intent) {\n    super.onNewIntent(intent)\n    _callbacksFlow.tryEmit(createNewIntent(intent))\n  }\n\n  @CallSuper\n  override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {\n    super.onActivityResult(requestCode, resultCode, data)\n    _callbacksFlow.tryEmit(createOnActivityResultEvent(requestCode, resultCode, data))\n  }\n\n  @CallSuper\n  override fun onPause() {\n    _lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.PAUSE))\n    super.onPause()\n  }\n\n  @CallSuper\n  override fun onStop() {\n    _lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.STOP))\n    super.onStop()\n  }\n\n  @CallSuper\n  override fun onDestroy() {\n    _lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.DESTROY))\n    router?.let {\n      it.dispatchDetach()\n      RibEvents.emitRouterEvent(RibEventType.DETACHED, it, null)\n    }\n    router = null\n    super.onDestroy()\n  }\n\n  @CallSuper\n  override fun onLowMemory() {\n    super.onLowMemory()\n    _callbacksFlow.tryEmit(create(ActivityCallbackEvent.Type.LOW_MEMORY))\n  }\n\n  @CallSuper\n  override fun onTrimMemory(level: Int) {\n    super.onTrimMemory(level)\n    _callbacksFlow.tryEmit(createTrimMemoryEvent(level))\n  }\n\n  @Suppress(\"MissingSuperCall\") // TODO: check if calling super is safe.\n  override fun onPictureInPictureModeChanged(\n    isInPictureInPictureMode: Boolean,\n    newConfig: Configuration,\n  ) {\n    _callbacksFlow.tryEmit(\n      createPictureInPictureMode(isInPictureInPictureMode),\n    )\n  }\n\n  override fun onBackPressed() {\n    if (router?.handleBackPress() != true) {\n      onUnhandledBackPressed()\n\n      // https://issuetracker.google.com/issues/139738913\n      if (\n        Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&\n          isTaskRoot &&\n          supportFragmentManager.backStackEntryCount == 0\n      ) {\n        super.finishAfterTransition()\n      } else {\n        super.onBackPressed()\n      }\n    }\n  }\n\n  override fun onUserLeaveHint() {\n    _lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.USER_LEAVING))\n    super.onUserLeaveHint()\n  }\n\n  override fun onWindowFocusChanged(hasFocus: Boolean) {\n    super.onWindowFocusChanged(hasFocus)\n    _callbacksFlow.tryEmit(createWindowFocusEvent(hasFocus))\n  }\n\n  /**\n   * Invoked when none of the ribs handle back press. In this case, default activity back press\n   * behavior occurs.\n   */\n  protected open fun onUnhandledBackPressed() {}\n\n  /**\n   * @return the [Interactor] when the activity has alive.\n   * @throws IllegalStateException if the activity has not been created or has been destroyed.\n   */\n  public open val interactor: Interactor<*, *>\n    get() =\n      if (router != null) {\n        router?.interactor as Interactor<*, *>\n      } else {\n        throw IllegalStateException(\n          \"Attempting to get a router when activity is not created or has been destroyed.\",\n        )\n      }\n\n  /**\n   * Creates the [Interactor].\n   *\n   * @return the [Interactor].\n   */\n  protected abstract fun createRouter(parentViewGroup: ViewGroup): ViewRouter<*, *>\n\n  public companion object {\n    /**\n     * Figures out which corresponding next lifecycle event in which to unsubscribe, for Activities.\n     */\n    private val ACTIVITY_LIFECYCLE =\n      CorrespondingEventsFunction { lastEvent: ActivityLifecycleEvent ->\n        return@CorrespondingEventsFunction when (lastEvent.type) {\n          ActivityLifecycleEvent.Type.CREATE -> create(ActivityLifecycleEvent.Type.DESTROY)\n          ActivityLifecycleEvent.Type.START -> create(ActivityLifecycleEvent.Type.STOP)\n          ActivityLifecycleEvent.Type.RESUME -> create(ActivityLifecycleEvent.Type.PAUSE)\n          ActivityLifecycleEvent.Type.USER_LEAVING -> create(ActivityLifecycleEvent.Type.DESTROY)\n          ActivityLifecycleEvent.Type.PAUSE -> create(ActivityLifecycleEvent.Type.STOP)\n          ActivityLifecycleEvent.Type.STOP -> create(ActivityLifecycleEvent.Type.DESTROY)\n          ActivityLifecycleEvent.Type.DESTROY ->\n            throw LifecycleEndedException(\n              \"Cannot bind to Activity lifecycle when outside of it.\",\n            )\n        }\n      }\n  }\n}\n\nprivate val <T : Comparable<T>> LifecycleScopeProvider<T>.lifecycleRange: ClosedRange<T>\n  get() {\n    val lastEmittedEvent = peekLifecycle() ?: throw LifecycleNotStartedException()\n    val finishingEvent = correspondingEvents().apply(lastEmittedEvent)\n    return lastEmittedEvent..finishingEvent\n  }\n"
  },
  {
    "path": "libraries/rib-android/src/main/kotlin/com/uber/rib/core/RibDebugOverlay.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.graphics.Canvas\nimport android.graphics.Color\nimport android.graphics.ColorFilter\nimport android.graphics.Paint\nimport android.graphics.PixelFormat\nimport android.graphics.drawable.Drawable\n\npublic open class RibDebugOverlay : Drawable() {\n  private var enabled = true\n\n  public open fun setEnabled(enabled: Boolean) {\n    this.enabled = enabled\n  }\n\n  override fun draw(canvas: Canvas) {\n    if (enabled) {\n      val p = Paint()\n      p.color = OVERLAY_COLOR\n      p.alpha = OVERLAY_ALPHA\n      p.style = Paint.Style.FILL\n      canvas.drawPaint(p)\n    }\n  }\n\n  override fun setAlpha(i: Int) {}\n\n  override fun setColorFilter(colorFilter: ColorFilter?) {}\n\n  @Deprecated(\"Deprecated in Java\") override fun getOpacity(): Int = PixelFormat.TRANSLUCENT\n\n  public companion object {\n    private const val OVERLAY_COLOR = Color.RED\n    private const val OVERLAY_ALPHA = 35\n  }\n}\n"
  },
  {
    "path": "libraries/rib-android/src/main/kotlin/com/uber/rib/core/RxActivityEvents.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.uber.rib.core.lifecycle.ActivityCallbackEvent\nimport com.uber.rib.core.lifecycle.ActivityLifecycleEvent\nimport io.reactivex.Observable\n\n/** Interface for reactive activities. */\npublic interface RxActivityEvents {\n  /** @return an observable of this activity's lifecycle events. */\n  public fun lifecycle(): Observable<ActivityLifecycleEvent>\n\n  /** @return an observable of this activity's lifecycle events. */\n  public fun callbacks(): Observable<ActivityCallbackEvent>\n\n  /**\n   * @param <T> The type of [ActivityLifecycleEvent] subclass you want.\n   * @param clazz The [ActivityLifecycleEvent] subclass you want.\n   * @return an observable of this activity's lifecycle events.\n   */\n  public fun <T : ActivityLifecycleEvent> lifecycle(clazz: Class<T>): Observable<T> {\n    return lifecycle()\n      .filter { activityEvent -> clazz.isAssignableFrom(activityEvent.javaClass) }\n      .cast(clazz)\n  }\n\n  /**\n   * @param <T> The type of [ActivityCallbackEvent] subclass you want.\n   * @param clazz The [ActivityCallbackEvent] subclass you want.\n   * @return an observable of this activity's callbacks events.\n   */\n  public fun <T : ActivityCallbackEvent> callbacks(clazz: Class<T>): Observable<T> {\n    return callbacks()\n      .filter { activityEvent -> clazz.isAssignableFrom(activityEvent.javaClass) }\n      .cast(clazz)\n  }\n}\n"
  },
  {
    "path": "libraries/rib-android/src/main/kotlin/com/uber/rib/core/ViewBuilder.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.content.Context\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\n\n/**\n * Router builder for routers that own a view.\n *\n * @param <ViewType> type of view owned by router.\n * @param <RouterT> type of router built by this builder.\n * @param <DependencyT> dependency required to create this router.\n */\npublic abstract class ViewBuilder<ViewType : View, RouterT : Router<*>, DependencyT>(\n  dependency: DependencyT,\n) : Builder<RouterT, DependencyT>(dependency) {\n  /**\n   * Utility method to create the view for this router.\n   *\n   * @param parentViewGroup to inflate view with.\n   * @return the view for a new router.\n   */\n  public fun createView(parentViewGroup: ViewGroup): ViewType {\n    val context = parentViewGroup.context\n    return inflateView(LayoutInflater.from(onThemeContext(context)), parentViewGroup)\n  }\n\n  /**\n   * Inflates the router's view with knowledge of its parent. This should never be called directly,\n   * instead use [ViewBuilder.createView] which will automatically pass the correct context.\n   *\n   * @param inflater to inflate view with.\n   * @param parentViewGroup to use for layout parameters.\n   * @return the new view, not attached to its parent.\n   */\n  protected abstract fun inflateView(inflater: LayoutInflater, parentViewGroup: ViewGroup): ViewType\n\n  /**\n   * Optional override that allows the implementation to hook into the context to theme it with a\n   * potentially different one before view creation, such as with [ ].\n   *\n   * @param parentContext the original parent context, and default used if this method isn't\n   *   overridden.\n   * @return the possibly themed context.\n   */\n  protected open fun onThemeContext(parentContext: Context): Context {\n    return parentContext\n  }\n}\n"
  },
  {
    "path": "libraries/rib-android/src/main/kotlin/com/uber/rib/core/ViewPresenter.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.view.View\n\n/**\n * This represents the portion of UI controlled by [Interactor].\n *\n * @param <V> the view type.\n */\npublic abstract class ViewPresenter<V : View>(\n  /** @return the view fronted by the page. */\n  public val view: V,\n) : Presenter()\n"
  },
  {
    "path": "libraries/rib-android/src/main/kotlin/com/uber/rib/core/ViewRouter.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.view.View\n\n/**\n * Router subclass that has a view.\n *\n * @param <V> type of view owned by the router.\n * @param <I> type of interactor owned by the router.\n */\npublic abstract class ViewRouter<V : View, I : Interactor<*, *>> : Router<I> {\n  private val _view: V\n\n  /** @return the router's view. */\n  public open val view: V\n    get() = _view\n\n  public constructor(\n    view: V,\n    interactor: I,\n    component: InteractorBaseComponent<*>,\n  ) : super(interactor, component) {\n    _view = view\n    if (XRay.isEnabled()) {\n      XRay.apply(this, view)\n    }\n  }\n\n  protected constructor(\n    view: V,\n    interactor: I,\n  ) : super(null, interactor, RibRefWatcher.getInstance(), getMainThread()) {\n    _view = view\n    if (XRay.isEnabled()) {\n      XRay.apply(this, view)\n    }\n  }\n}\n"
  },
  {
    "path": "libraries/rib-android/src/main/kotlin/com/uber/rib/core/XRay.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.graphics.Bitmap\nimport android.graphics.Canvas\nimport android.graphics.Color\nimport android.graphics.Paint\nimport android.graphics.drawable.BitmapDrawable\nimport android.graphics.drawable.Drawable\nimport android.view.Gravity\nimport android.view.View\n\n/** Utility class that shows riblets name in its background. */\npublic class XRay private constructor() {\n  private var isEnabled = false\n  private var textPaint: Paint? = null\n  private fun writeOnBitmap(bitmap: Bitmap, text: String) {\n    val canvas = Canvas(bitmap)\n    val textPaint = getTextPaint()\n    val xStartPoint = (bitmap.width - textPaint.measureText(text)) / 2f\n    val yStartPoint = bitmap.height / 2f\n    canvas.drawText(text, xStartPoint, yStartPoint, textPaint)\n  }\n\n  private fun getTextPaint(): Paint {\n    if (textPaint == null) {\n      textPaint =\n        Paint().apply {\n          textSize = TEXT_SIZE.toFloat()\n          color = TEXT_COLOR\n        }\n    }\n    return textPaint!!\n  }\n\n  public companion object {\n    private val INSTANCE = XRay()\n    private const val FRAME_WIDTH = 500\n    private const val FRAME_HEIGHT = 150\n    private const val XRAY_ALFA = 0.9f\n    private const val TEXT_SIZE = 30\n    private const val TEXT_COLOR = Color.RED\n\n    /** Toggles state of XRay. */\n    @JvmStatic\n    public fun toggle() {\n      INSTANCE.isEnabled = !INSTANCE.isEnabled\n    }\n\n    /** @return `true` if XRay is enabled, `false` otherwise. */\n    @JvmStatic\n    public fun isEnabled(): Boolean {\n      return INSTANCE.isEnabled\n    }\n\n    /**\n     * Puts [ViewBuilder]s riblet name in the background of the [View]\n     *\n     * @param viewRouter a [ViewRouter] which riblets name should be written.\n     * @param view a [View] to put the name behind.\n     */\n    @JvmStatic\n    public fun apply(viewRouter: ViewRouter<*, *>, view: View) {\n      val oldBackground = view.background\n      val bitmap: Bitmap =\n        if (oldBackground != null) {\n          drawableToBitmap(oldBackground)\n        } else {\n          Bitmap.createBitmap(FRAME_WIDTH, FRAME_HEIGHT, Bitmap.Config.ARGB_8888)\n        }\n      INSTANCE.writeOnBitmap(bitmap, getRibletName(viewRouter))\n      val newBackground = BitmapDrawable(view.context.resources, bitmap)\n      newBackground.gravity = Gravity.CENTER\n      view.background = newBackground\n      view.alpha = XRAY_ALFA\n    }\n\n    private fun drawableToBitmap(drawable: Drawable): Bitmap {\n      if (drawable is BitmapDrawable) {\n        if (drawable.bitmap != null) {\n          return drawable.bitmap\n        }\n      }\n      val bitmap: Bitmap =\n        if (drawable.intrinsicWidth <= 0 || drawable.intrinsicHeight <= 0) {\n          Bitmap.createBitmap(FRAME_WIDTH, FRAME_HEIGHT, Bitmap.Config.ARGB_8888)\n        } else {\n          Bitmap.createBitmap(\n            drawable.intrinsicWidth,\n            drawable.intrinsicHeight,\n            Bitmap.Config.ARGB_8888,\n          )\n        }\n      val canvas = Canvas(bitmap)\n      drawable.draw(canvas)\n      return bitmap\n    }\n\n    private fun getRibletName(viewRouter: ViewRouter<*, *>): String {\n      return viewRouter.javaClass.simpleName.replace(\"Router\", \"\")\n    }\n  }\n}\n"
  },
  {
    "path": "libraries/rib-android/src/main/kotlin/com/uber/rib/core/lifecycle/ActivityCallbackEvent.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core.lifecycle\n\nimport android.content.Intent\nimport android.os.Bundle\nimport java.util.Locale\n\n/** Callback events that can be emitted by Activities. */\npublic open class ActivityCallbackEvent\nprivate constructor(\n  /** @return this event's type. */\n  override val type: Type,\n) : ActivityEvent {\n\n  /** Types of activity events that can occur. */\n  public enum class Type : ActivityEvent.BaseType {\n    LOW_MEMORY,\n    ACTIVITY_RESULT,\n    SAVE_INSTANCE_STATE,\n    TRIM_MEMORY,\n    PICTURE_IN_PICTURE_MODE,\n    NEW_INTENT,\n    WINDOW_FOCUS,\n  }\n\n  /** An [ActivityCallbackEvent] that represents [android.app.Activity.onNewIntent] event */\n  public open class NewIntent(public open val intent: Intent) :\n    ActivityCallbackEvent(Type.NEW_INTENT)\n\n  public open class PictureInPictureMode(public open val isInPictureInPictureMode: Boolean) :\n    ActivityCallbackEvent(Type.PICTURE_IN_PICTURE_MODE)\n\n  /**\n   * An [ActivityCallbackEvent] that represents [android.app.Activity.onWindowFocusChanged] event\n   */\n  public open class WindowFocus(public open val hasFocus: Boolean) :\n    ActivityCallbackEvent(Type.WINDOW_FOCUS)\n\n  /** An [ActivityCallbackEvent] that represents [android.app.Activity.onTrimMemory] event */\n  public open class TrimMemory internal constructor(public open val trimMemoryType: Int) :\n    ActivityCallbackEvent(Type.TRIM_MEMORY)\n\n  /**\n   * An [ActivityCallbackEvent] that encapsulates information from\n   * [android.app.Activity.onActivityResult].\n   */\n  public open class ActivityResult(\n    /** @return this event's activity result data intent. */\n    public open val data: Intent?,\n    /** @return this event's request code. */\n    public open val requestCode: Int,\n    /** @return this event's result code. */\n    public open val resultCode: Int,\n  ) : ActivityCallbackEvent(Type.ACTIVITY_RESULT)\n\n  /**\n   * An [ActivityCallbackEvent] that encapsulates information from\n   * [android.app.Activity.onSaveInstanceState].\n   */\n  public open class SaveInstanceState(\n    /** @return this event's outState data. */\n    public open val outState: Bundle?,\n  ) : ActivityCallbackEvent(Type.SAVE_INSTANCE_STATE)\n\n  public companion object {\n    private val LOW_MEMORY_EVENT = ActivityCallbackEvent(Type.LOW_MEMORY)\n\n    /**\n     * Creates an event for activity results.\n     *\n     * @param requestCode the request code\n     * @param resultCode the result code\n     * @param resultData the result data intent\n     * @return the created ActivityEvent.\n     */\n    @JvmStatic\n    public fun createOnActivityResultEvent(\n      requestCode: Int,\n      resultCode: Int,\n      resultData: Intent?,\n    ): ActivityResult {\n      return ActivityResult(resultData, requestCode, resultCode)\n    }\n\n    /**\n     * Creates an activity event for a given type.\n     *\n     * @param type The type of event to get.\n     * @return The corresponding ActivityEvent.\n     */\n    @JvmStatic\n    public fun create(type: Type): ActivityCallbackEvent {\n      return when (type) {\n        Type.LOW_MEMORY -> LOW_MEMORY_EVENT\n        else -> {\n          val locale = Locale.getDefault()\n          val name =\n            type.name.lowercase(locale).replaceFirstChar {\n              if (it.isLowerCase()) it.titlecase(locale) else it.toString()\n            }\n          throw IllegalArgumentException(\"Use the createOn${name}Event() method for this type!\")\n        }\n      }\n    }\n\n    /**\n     * Creates an event for onSaveInstanceState.\n     *\n     * @param outState the outState bundle.\n     * @return the created ActivityEvent.\n     */\n    @JvmStatic\n    public fun createOnSaveInstanceStateEvent(outState: Bundle?): ActivityCallbackEvent {\n      return SaveInstanceState(outState)\n    }\n\n    /**\n     * Creates an event for [android.app.Activity.onTrimMemory]\n     *\n     * @param trimMemoryType that is passed by the activity callback\n     * @return the created [TrimMemory]\n     */\n    @JvmStatic\n    public fun createTrimMemoryEvent(trimMemoryType: Int): TrimMemory {\n      return TrimMemory(trimMemoryType)\n    }\n\n    @JvmStatic\n    public fun createPictureInPictureMode(isInPictureInPictureMode: Boolean): PictureInPictureMode {\n      return PictureInPictureMode(isInPictureInPictureMode)\n    }\n\n    /**\n     * Creates an event for onNewIntent.\n     *\n     * @param intent is the new intent received\n     * @return the created [NewIntent].\n     */\n    @JvmStatic\n    public fun createNewIntent(intent: Intent): NewIntent {\n      return NewIntent(intent)\n    }\n\n    /**\n     * Creates an event for onWindowFocusChanged\n     *\n     * @param hasFocus determines whether the window of this activity got focus or not\n     * @return the newly created [WindowFocus]\n     */\n    @JvmStatic\n    public fun createWindowFocusEvent(hasFocus: Boolean): WindowFocus {\n      return WindowFocus(hasFocus)\n    }\n  }\n}\n"
  },
  {
    "path": "libraries/rib-android/src/main/kotlin/com/uber/rib/core/lifecycle/ActivityEvent.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core.lifecycle\n\n/**\n * Base class for Activity events, useful for when you want a stream of both lifecycle and callback\n * events.\n */\npublic interface ActivityEvent {\n  /** @return This activity event type. */\n  public val type: BaseType\n\n  /** Base interface of Activity event types. */\n  public interface BaseType\n}\n"
  },
  {
    "path": "libraries/rib-android/src/main/kotlin/com/uber/rib/core/lifecycle/ActivityLifecycleEvent.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core.lifecycle\n\nimport android.os.Bundle\n\n/**\n * Lifecycle events that can be emitted by Activities.\n *\n * ### Ordering semantics\n *\n * This class implements [Comparable], but it does **not** override `equals`, so even though the\n * following holds:\n * ```\n * val a = createOnCreateEvent(null)\n * val b = createOnCreateEvent(Bundle())\n * assertThat(a <= b).isTrue()\n * assertThat(b <= a).isTrue()\n * ```\n *\n * The equality does not hold:\n * ```\n * assertThat(a == b).isFalse()\n * ```\n *\n * This happens because events of type [CREATE][ActivityLifecycleEvent.Type.CREATE] hold a [Bundle],\n * and even though any two `CREATE` events are equal in ordering ([compareTo]), they are never equal\n * in [equals] comparison.\n *\n * In mathematical terms, the activity events set form a\n * [total preorder](https://en.wikipedia.org/wiki/Weak_ordering#Total_preorders), but *not* a\n * [total order](https://en.wikipedia.org/wiki/Total_order): it is reflexive, transitive, strongly\n * connected, but **not** antisymmetric.\n */\npublic open class ActivityLifecycleEvent\nprivate constructor(\n  /** @return this event's type. */\n  override val type: Type,\n) : ActivityEvent, Comparable<ActivityLifecycleEvent> {\n\n  /** Types of activity events that can occur. */\n  public enum class Type : ActivityEvent.BaseType {\n    CREATE,\n    START,\n    RESUME,\n    USER_LEAVING,\n    PAUSE,\n    STOP,\n    DESTROY,\n  }\n\n  override fun compareTo(other: ActivityLifecycleEvent): Int = type.compareTo(other.type)\n\n  /**\n   * An [ActivityLifecycleEvent] that encapsulates information from\n   * [Activity.onCreate][android.app.Activity.onCreate].\n   */\n  public open class Create(\n    /** @return this event's savedInstanceState data. */\n    public open val savedInstanceState: Bundle?,\n  ) : ActivityLifecycleEvent(Type.CREATE)\n\n  public companion object {\n    private val START_EVENT = ActivityLifecycleEvent(Type.START)\n    private val RESUME_EVENT = ActivityLifecycleEvent(Type.RESUME)\n    private val USER_LEAVING_EVENT = ActivityLifecycleEvent(Type.USER_LEAVING)\n    private val PAUSE_EVENT = ActivityLifecycleEvent(Type.PAUSE)\n    private val STOP_EVENT = ActivityLifecycleEvent(Type.STOP)\n    private val DESTROY_EVENT = ActivityLifecycleEvent(Type.DESTROY)\n\n    /**\n     * Creates an event for onCreate.\n     *\n     * @param stateData the instate bundle.\n     * @return the created ActivityEvent.\n     */\n    @JvmStatic\n    public fun createOnCreateEvent(stateData: Bundle?): Create {\n      return Create(stateData)\n    }\n\n    /**\n     * Creates an activity event for a given type.\n     *\n     * @param type The type of event to get.\n     * @return The corresponding ActivityEvent.\n     */\n    @JvmStatic\n    public fun create(type: Type): ActivityLifecycleEvent {\n      return when (type) {\n        Type.START -> START_EVENT\n        Type.RESUME -> RESUME_EVENT\n        Type.USER_LEAVING -> USER_LEAVING_EVENT\n        Type.PAUSE -> PAUSE_EVENT\n        Type.STOP -> STOP_EVENT\n        Type.DESTROY -> DESTROY_EVENT\n        else ->\n          throw IllegalArgumentException(\n            \"Use the createOn${type.name.lowercase().replaceFirstChar(Char::titlecase)}Event() method for this type!\",\n          )\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "libraries/rib-android/src/test/kotlin/com/uber/rib/core/BundleTest.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.robolectric.RobolectricTestRunner\n\n@RunWith(RobolectricTestRunner::class)\nclass BundleTest {\n\n  private lateinit var androidBundle: android.os.Bundle\n  private lateinit var bundle: Bundle\n\n  @Before\n  fun setup() {\n    androidBundle = android.os.Bundle()\n    androidBundle.putString(TEST_KEY_STRING, TEST_VALUE_STRING)\n    bundle = Bundle(androidBundle)\n  }\n\n  @Test\n  fun string_shouldReturnValueFromAndroidBundle() {\n    assertThat(bundle.getString(TEST_KEY_STRING)).isEqualTo(TEST_VALUE_STRING)\n  }\n\n  @Test\n  fun putString_shouldSetValueOnAndroidBundle() {\n    val newValue = \"test\"\n    bundle.putString(TEST_KEY_STRING, newValue)\n    assertThat(androidBundle.getString(TEST_KEY_STRING)).isEqualTo(newValue)\n  }\n\n  companion object {\n    private const val TEST_KEY_STRING = \"test_string_key\"\n    private const val TEST_VALUE_STRING = \"test_string_value\"\n  }\n}\n"
  },
  {
    "path": "libraries/rib-android/src/test/kotlin/com/uber/rib/core/RibActivityTest.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.FrameLayout\nimport androidx.lifecycle.findViewTreeLifecycleOwner\nimport androidx.lifecycle.findViewTreeViewModelStoreOwner\nimport androidx.savedstate.findViewTreeSavedStateRegistryOwner\nimport com.google.common.truth.Truth.assertThat\nimport com.uber.autodispose.AutoDispose\nimport com.uber.autodispose.lifecycle.LifecycleEndedException\nimport com.uber.rib.android.R\nimport com.uber.rib.core.lifecycle.ActivityCallbackEvent\nimport com.uber.rib.core.lifecycle.ActivityCallbackEvent.Companion.create\nimport com.uber.rib.core.lifecycle.ActivityCallbackEvent.SaveInstanceState\nimport com.uber.rib.core.lifecycle.ActivityLifecycleEvent\nimport com.uber.rib.core.lifecycle.ActivityLifecycleEvent.Companion.create\nimport io.reactivex.Observable\nimport io.reactivex.observers.TestObserver\nimport io.reactivex.subjects.PublishSubject\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.kotlin.doReturn\nimport org.mockito.kotlin.mock\nimport org.robolectric.Robolectric\nimport org.robolectric.RobolectricTestRunner\nimport org.robolectric.RuntimeEnvironment\nimport org.robolectric.android.controller.ActivityController\n\n@RunWith(RobolectricTestRunner::class)\nclass RibActivityTest {\n  @Test\n  fun onCreate_withSaveInstanceState_shouldForwardToRootRiblet() {\n    val interactorBundle = android.os.Bundle()\n    interactorBundle.putString(TEST_BUNDLE_KEY, TEST_BUNDLE_VALUE)\n    val testBundle = android.os.Bundle()\n    testBundle.putBundle(Router.KEY_INTERACTOR, interactorBundle)\n    val activityController: ActivityController<EmptyActivity> =\n      Robolectric.buildActivity(EmptyActivity::class.java)\n    activityController.create(testBundle)\n    assertThat(\n        activityController.get().testInteractor.savedInstanceState?.getString(TEST_BUNDLE_KEY),\n      )\n      .isEqualTo(TEST_BUNDLE_VALUE)\n  }\n\n  @Test\n  fun onCreate_withNullSaveInstanceState_shouldForwardNullToRootRiblet() {\n    val activityController = Robolectric.buildActivity(EmptyActivity::class.java)\n    activityController.create()\n    assertThat(activityController.get().testInteractor.savedInstanceState).isNull()\n  }\n\n  @Test\n  fun rxActivity_shouldCallback_onLowMemory() {\n    val activityController = Robolectric.buildActivity(EmptyActivity::class.java)\n    val activity: RibActivity = activityController.setup().get()\n    val testSub = TestObserver<ActivityCallbackEvent>()\n    activity\n      .callbacks()\n      .filter { activityEvent -> activityEvent.type === ActivityCallbackEvent.Type.LOW_MEMORY }\n      .subscribe(testSub)\n    activity.onLowMemory()\n    testSub.assertValue(create(ActivityCallbackEvent.Type.LOW_MEMORY))\n  }\n\n  @Test\n  fun ribActivity_onSaveInstanceStateAndCallbackFlagEnabled_shouldEmitToCallbacks() {\n    val activityController = Robolectric.buildActivity(EmptyActivity::class.java)\n    val activity: RibActivity = activityController.setup().get()\n    val testSub = TestObserver<SaveInstanceState>()\n    activity.callbacks(SaveInstanceState::class.java).subscribe(testSub)\n    val state = android.os.Bundle()\n    state.putString(\"hello\", \"seattle\")\n    activityController.saveInstanceState(state)\n    testSub.assertValueCount(1)\n    val receivedEvent = testSub.values()[0]\n    assertThat(receivedEvent.type).isEqualTo(ActivityCallbackEvent.Type.SAVE_INSTANCE_STATE)\n    assertThat(receivedEvent.outState).isNotNull()\n    assertThat(receivedEvent.outState!!.getString(\"hello\")).isEqualTo(\"seattle\")\n  }\n\n  @Test\n  fun rxActivity_shouldCallback_onActivityResult() {\n    val activityController = Robolectric.buildActivity(EmptyActivity::class.java)\n    val activity: EmptyActivity = activityController.setup().get()\n    val testSub = TestObserver<ActivityCallbackEvent.ActivityResult>()\n    activity.callbacks(ActivityCallbackEvent.ActivityResult::class.java).subscribe(testSub)\n    val data = android.os.Bundle()\n    data.putString(\"hello\", \"seattle\")\n    val intent = Intent(Intent.ACTION_VIEW)\n    intent.putExtras(data)\n    val requestCode = 2\n    val resultCode = Activity.RESULT_OK\n    activity.onActivityResult(requestCode, resultCode, intent)\n    testSub.assertValueCount(1)\n    val receivedEvent = testSub.values()[0]\n    assertThat(receivedEvent.type).isEqualTo(ActivityCallbackEvent.Type.ACTIVITY_RESULT)\n    assertThat(receivedEvent.requestCode).isEqualTo(requestCode)\n    assertThat(receivedEvent.resultCode).isEqualTo(resultCode)\n    assertThat(receivedEvent.data).isNotNull()\n    assertThat(receivedEvent.data!!.extras).isNotNull()\n    assertThat(receivedEvent.data!!.extras!!.getString(\"hello\")).isEqualTo(\"seattle\")\n  }\n\n  @Test\n  fun rxActivity_delaySubscription_shouldIgnoreOtherEvents() {\n    val activityController = Robolectric.buildActivity(EmptyActivity::class.java)\n    val activity: RibActivity = activityController.get()\n    val subject = PublishSubject.create<Any>()\n    val o = AndroidRecordingRx2Observer<Any>()\n    subject\n      .hide()\n      .delaySubscription(\n        activity.lifecycle().filter { activityEvent ->\n          activityEvent.type === ActivityLifecycleEvent.Type.RESUME\n        },\n      )\n      .subscribe(o)\n    subject.onNext(Any())\n    activityController.create()\n    subject.onNext(Any())\n    o.assertNoMoreEvents()\n    activityController.start()\n    subject.onNext(Any())\n    o.assertNoMoreEvents()\n    activityController.postCreate(null)\n    subject.onNext(Any())\n    o.assertNoMoreEvents()\n    activityController.resume()\n    subject.onNext(Any())\n    assertThat(o.takeNext()).isNotNull()\n    o.assertNoMoreEvents()\n  }\n\n  @Test\n  fun onSaveInstanceState_shouldPropagate() {\n    val activityController = Robolectric.buildActivity(EmptyActivity::class.java)\n    val activity = activityController.setup().get()\n    val bundle = android.os.Bundle()\n    activityController.saveInstanceState(bundle)\n    val interactorBundle = bundle.getBundle(Router.KEY_INTERACTOR)\n    assertThat(interactorBundle).isNotNull()\n  }\n\n  @Test\n  fun bind_afterDestroy_shouldError() {\n    val activityController = Robolectric.buildActivity(EmptyActivity::class.java)\n    val activity = activityController.setup().pause().stop().destroy().get()\n    val o = AndroidRecordingRx2Observer<Any>()\n    Observable.just(Any()).`as`(AutoDispose.autoDisposable(activity)).subscribe(o)\n    assertThat(o.takeError()).isInstanceOf(LifecycleEndedException::class.java)\n  }\n\n  @Test\n  fun rxActivity_shouldCallback_onWindowFocusChanged() {\n    val activityController = Robolectric.buildActivity(EmptyActivity::class.java)\n    val activity: EmptyActivity = activityController.setup().get()\n    val testSub = TestObserver<ActivityCallbackEvent.WindowFocus>()\n    activity.callbacks(ActivityCallbackEvent.WindowFocus::class.java).subscribe(testSub)\n    activity.onWindowFocusChanged(true)\n    activity.onWindowFocusChanged(false)\n    testSub.assertValueCount(2)\n    val receivedEvent1 = testSub.values()[0]\n    assertThat(receivedEvent1.type).isEqualTo(ActivityCallbackEvent.Type.WINDOW_FOCUS)\n    assertThat(receivedEvent1.hasFocus).isTrue()\n    val receivedEvent2 = testSub.values()[1]\n    assertThat(receivedEvent2.type).isEqualTo(ActivityCallbackEvent.Type.WINDOW_FOCUS)\n    assertThat(receivedEvent2.hasFocus).isFalse()\n  }\n\n  @Test\n  fun getController() {\n    val activity: RibActivity = Robolectric.setupActivity(EmptyActivity::class.java)\n    assertThat(activity.interactor).isNotNull()\n  }\n\n  @Test\n  fun onCreate_setsViewTreeOwners_forDecorView() {\n    val activity = Robolectric.buildActivity(EmptyActivity::class.java).create(null).get()\n    val decorView = activity.window.decorView\n    assertThat(decorView.findViewTreeLifecycleOwner()).isNotNull()\n    assertThat(decorView.findViewTreeSavedStateRegistryOwner()).isNotNull()\n    assertThat(decorView.findViewTreeViewModelStoreOwner()).isNotNull()\n  }\n\n  @Test\n  fun onCreate_setsViewTreeOwners_forViewAddedToDecorView() {\n    val activity = Robolectric.buildActivity(EmptyActivity::class.java).create(null).get()\n    val contentView = activity.findViewById<FrameLayout>(android.R.id.content)\n    val rootView = View(RuntimeEnvironment.application)\n    contentView.addView(rootView)\n    assertThat(rootView.findViewTreeLifecycleOwner()).isNotNull()\n    assertThat(rootView.findViewTreeSavedStateRegistryOwner()).isNotNull()\n    assertThat(rootView.findViewTreeViewModelStoreOwner()).isNotNull()\n  }\n\n  @Test(expected = IllegalArgumentException::class)\n  fun createEvent_withIllegalType_shouldFail() {\n    create(ActivityLifecycleEvent.Type.CREATE)\n  }\n\n  private class EmptyActivity : RibActivity() {\n    override fun onCreate(savedInstanceState: android.os.Bundle?) {\n      setTheme(R.style.Theme_AppCompat)\n      super.onCreate(savedInstanceState)\n    }\n\n    override fun createRouter(parentViewGroup: ViewGroup): ViewRouter<*, *> {\n      val view = FrameLayout(RuntimeEnvironment.application)\n      val presenter = object : ViewPresenter<View>(view) {}\n      val component: InteractorComponent<ViewPresenter<*>, *> = mock {\n        on { presenter() } doReturn (presenter)\n      }\n      return EmptyRouter(view, TestInteractor(presenter), component)\n    }\n\n    public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {\n      super.onActivityResult(requestCode, resultCode, data)\n    }\n\n    val testInteractor: TestInteractor\n      get() = interactor as TestInteractor\n  }\n\n  private class EmptyRouter(\n    view: FrameLayout,\n    interactor: Interactor<ViewPresenter<*>, *>,\n    component: InteractorComponent<ViewPresenter<*>, *>,\n  ) : ViewRouter<FrameLayout, Interactor<ViewPresenter<*>, *>>(view, interactor, component)\n\n  private class TestInteractor(\n    presenter: ViewPresenter<*>,\n  ) : Interactor<ViewPresenter<*>, FakeRouter<*>>(presenter) {\n    var savedInstanceState: Bundle? = null\n      private set\n\n    override fun didBecomeActive(savedInstanceState: Bundle?) {\n      super.didBecomeActive(savedInstanceState)\n      this.savedInstanceState = savedInstanceState\n    }\n\n    override fun onSaveInstanceState(outState: Bundle) {\n      super.onSaveInstanceState(outState)\n    }\n  }\n\n  companion object {\n    private const val TEST_BUNDLE_KEY = \"test_bundle_key\"\n    private const val TEST_BUNDLE_VALUE = \"test_bundle_value\"\n  }\n}\n"
  },
  {
    "path": "libraries/rib-android/src/test/kotlin/com/uber/rib/core/ViewBuilderTest.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.content.Context\nimport android.view.ContextThemeWrapper\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.FrameLayout\nimport com.google.common.truth.Truth\nimport com.uber.rib.android.R\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.kotlin.mock\nimport org.robolectric.RobolectricTestRunner\nimport org.robolectric.RuntimeEnvironment\n\n@RunWith(RobolectricTestRunner::class)\nclass ViewBuilderTest {\n  @Test\n  fun createView_shouldUseInflateViewToCreateView() {\n    val parentViewGroup: ViewGroup = FrameLayout(RuntimeEnvironment.application)\n    val holder = Holder()\n    val viewBuilder: ViewBuilder<*, *, *> =\n      object : ViewBuilder<View, Router<*>, Any>(Any()) {\n        override fun inflateView(inflater: LayoutInflater, parentViewGroup: ViewGroup): View {\n          holder.inflaterContext = inflater.context\n          holder.inflaterViewGroup = parentViewGroup\n          return mock()\n        }\n      }\n    viewBuilder.createView(parentViewGroup)\n    Truth.assertThat(holder.inflaterContext).isEqualTo(parentViewGroup.context)\n    Truth.assertThat(holder.inflaterViewGroup).isEqualTo(parentViewGroup)\n  }\n\n  @Test\n  fun createView_useCustomContext() {\n    val parentViewGroup: ViewGroup = FrameLayout(RuntimeEnvironment.application)\n    val customContext = ContextThemeWrapper(RuntimeEnvironment.application, R.style.Theme_AppCompat)\n    val holder = Holder()\n    val viewBuilder: ViewBuilder<*, *, *> =\n      object : ViewBuilder<View, Router<*>, Any>(Any()) {\n        override fun inflateView(inflater: LayoutInflater, parentViewGroup: ViewGroup): View {\n          holder.inflaterContext = inflater.context\n          holder.inflaterViewGroup = parentViewGroup\n          return mock()\n        }\n\n        override fun onThemeContext(parentContext: Context): Context {\n          return customContext\n        }\n      }\n    viewBuilder.createView(parentViewGroup)\n    Truth.assertThat(holder.inflaterContext).isEqualTo(customContext)\n    Truth.assertThat(holder.inflaterViewGroup).isEqualTo(parentViewGroup)\n  }\n\n  private class Holder {\n    var inflaterContext: Context? = null\n    var inflaterViewGroup: ViewGroup? = null\n  }\n}\n"
  },
  {
    "path": "libraries/rib-android/src/test/kotlin/com/uber/rib/core/XRayTest.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.view.View\nimport com.uber.rib.core.XRay.Companion.apply\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.kotlin.any\nimport org.mockito.kotlin.doReturn\nimport org.mockito.kotlin.mock\nimport org.mockito.kotlin.verify\nimport org.mockito.kotlin.verifyNoMoreInteractions\nimport org.robolectric.RobolectricTestRunner\nimport org.robolectric.RuntimeEnvironment\n\n@RunWith(RobolectricTestRunner::class)\nclass XRayTest {\n\n  @Test\n  fun apply_changesViewBackground() {\n    XRay.toggle()\n    val viewRouter: ViewRouter<*, *> = mock()\n    val view: View = mock { on { context } doReturn (RuntimeEnvironment.application.baseContext) }\n    apply(viewRouter, view)\n    verify(view).background = any()\n    verifyNoMoreInteractions(viewRouter)\n  }\n}\n"
  },
  {
    "path": "libraries/rib-android/src/test/resources/robolectric.properties",
    "content": "constants=com.uber.rib.android.test.BuildConfig\n"
  },
  {
    "path": "libraries/rib-android-compose/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.android.library\")\n    alias(libs.plugins.compose.compiler)\n    alias(libs.plugins.maven.publish)\n}\n\nandroid {\n    namespace = \"com.uber.rib.android.compose\"\n\n    buildFeatures {\n        compose = true\n    }\n}\n\ndependencies {\n    api(project(\":libraries:rib-android\"))\n    api(libs.compose.runtime)\n    testImplementation(testLibs.robolectric)\n    testImplementation(testLibs.mockito.kotlin)\n    testImplementation(project(\":libraries:rib-test\"))\n}\n"
  },
  {
    "path": "libraries/rib-android-compose/gradle.properties",
    "content": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPOM_NAME=RIBs (Android Compose)\nPOM_ARTIFACT_ID=rib-android-compose\nPOM_PACKAGING=android\n"
  },
  {
    "path": "libraries/rib-android-compose/src/main/kotlin/com/uber/rib/core/BasicComposeRouter.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.MutableState\n\npublic open class BasicComposeRouter<I : BasicInteractor<*, *>>(\n  public val presenter: ComposePresenter,\n  interactor: I,\n  public val slot: MutableState<@Composable () -> Unit>,\n) : BasicRouter<I>(interactor) {\n  override fun willAttach() {\n    slot.value = presenter.composable\n    super.willAttach()\n  }\n\n  override fun willDetach() {\n    slot.value = {}\n    super.willDetach()\n  }\n}\n"
  },
  {
    "path": "libraries/rib-android-compose/src/main/kotlin/com/uber/rib/core/ComposePresenter.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport androidx.compose.runtime.Composable\n\npublic abstract class ComposePresenter : Presenter() {\n  public abstract val composable: @Composable () -> Unit\n}\n"
  },
  {
    "path": "libraries/rib-android-core/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.android.library\")\n    alias(libs.plugins.maven.publish)\n}\n\nandroid {\n    namespace = \"com.uber.rib.android.core\"\n}\n\ndependencies {\n    api(libs.androidx.appcompat)\n    implementation(libs.androidx.annotation)\n    testImplementation(testLibs.robolectric)\n}\n"
  },
  {
    "path": "libraries/rib-android-core/gradle.properties",
    "content": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPOM_NAME=RIBs (Android Core)\nPOM_ARTIFACT_ID=rib-android-core\nPOM_PACKAGING=android\n"
  },
  {
    "path": "libraries/rib-android-core/src/main/kotlin/com/uber/rib/core/ActivityDelegate.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.os.Bundle\nimport androidx.annotation.IntRange\n\n/**\n * This class represents a delegate which you can use to extend [CoreAppCompatActivity]'s\n * functionality. This allows [RibActivity] and any other type of [Activity] that you need to\n * support to share [CoreAppCompatActivity] as a common parent.\n */\npublic interface ActivityDelegate {\n  /** @see [Activity.onCreate] */\n  public fun onCreate(savedInstanceState: Bundle?) {}\n\n  /** @see [Activity.onStart] */\n  public fun onStart() {}\n\n  /** @see [Activity.onResume] */\n  public fun onResume() {}\n\n  /** @see [Activity.onPause] */\n  public fun onPause() {}\n\n  /** @see [Activity.onStop] */\n  public fun onStop() {}\n\n  /** @see [Activity.onDestroy] */\n  public fun onDestroy() {}\n\n  /** @see [Activity.onActivityResult] */\n  public fun onActivityResult(\n    activity: Activity,\n    requestCode: Int,\n    resultCode: Int,\n    data: Intent?,\n  ) {}\n\n  /** @see [Activity.onRequestPermissionsResult] */\n  public fun onRequestPermissionsResult(\n    activity: Activity,\n    @IntRange(from = 0, to = 255) requestCode: Int,\n    permissions: Array<String>,\n    grantResults: IntArray,\n  ) {}\n}\n"
  },
  {
    "path": "libraries/rib-android-core/src/main/kotlin/com/uber/rib/core/CoreAppCompatActivity.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.content.Intent\nimport android.os.Bundle\nimport androidx.annotation.CallSuper\nimport androidx.annotation.IntRange\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.lifecycle.ViewTreeLifecycleOwner\nimport androidx.lifecycle.ViewTreeViewModelStoreOwner\nimport androidx.savedstate.setViewTreeSavedStateRegistryOwner\n\n/** Core Support v7 AppCompat Activity. */\npublic abstract class CoreAppCompatActivity : AppCompatActivity() {\n\n  private var activityDelegate: ActivityDelegate? = null\n\n  @CallSuper\n  override fun onCreate(savedInstanceState: Bundle?) {\n    if (applicationContext is HasActivityDelegate) {\n      activityDelegate = (applicationContext as HasActivityDelegate).activityDelegate()\n    }\n    super.onCreate(savedInstanceState)\n    initViewTreeOwners()\n    activityDelegate?.onCreate(savedInstanceState)\n  }\n\n  @CallSuper\n  override fun onStart() {\n    super.onStart()\n    activityDelegate?.onStart()\n  }\n\n  @CallSuper\n  override fun onResume() {\n    super.onResume()\n    activityDelegate?.onResume()\n  }\n\n  @CallSuper\n  override fun onPause() {\n    activityDelegate?.onPause()\n    super.onPause()\n  }\n\n  @CallSuper\n  override fun onStop() {\n    activityDelegate?.onStop()\n    super.onStop()\n  }\n\n  @CallSuper\n  override fun onDestroy() {\n    activityDelegate?.onDestroy()\n    activityDelegate = null\n    super.onDestroy()\n  }\n\n  @CallSuper\n  override fun onRequestPermissionsResult(\n    @IntRange(from = 0, to = 255) requestCode: Int,\n    permissions: Array<String>,\n    grantResults: IntArray,\n  ) {\n    super.onRequestPermissionsResult(requestCode, permissions, grantResults)\n    activityDelegate?.onRequestPermissionsResult(this, requestCode, permissions, grantResults)\n  }\n\n  @CallSuper\n  override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {\n    super.onActivityResult(requestCode, resultCode, data)\n    activityDelegate?.onActivityResult(this, requestCode, resultCode, data)\n  }\n\n  /**\n   * [RibActivity] must call this since it does not use [ComponentActivity.setContentView] which\n   * already handles this.\n   */\n  private fun initViewTreeOwners() {\n    // Set the view tree owners before setting the content view so that the inflation process\n    // and attach listeners will see them already present\n    ViewTreeLifecycleOwner.set(window.decorView, this)\n    ViewTreeViewModelStoreOwner.set(window.decorView, this)\n    window.decorView.setViewTreeSavedStateRegistryOwner(this)\n  }\n}\n"
  },
  {
    "path": "libraries/rib-android-core/src/main/kotlin/com/uber/rib/core/HasActivityDelegate.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/** Interface to indicate an object has an [ActivityDelegate]. */\npublic interface HasActivityDelegate {\n  /**\n   * Get the delegate.\n   *\n   * @return The delegate.\n   */\n  public fun activityDelegate(): ActivityDelegate\n}\n"
  },
  {
    "path": "libraries/rib-android-core/src/test/resources/robolectric.properties",
    "content": "constants=com.uber.rib.android.test.BuildConfig\n"
  },
  {
    "path": "libraries/rib-base/README.md",
    "content": "# rib-base\n\nThis module is responsible for defining base rib components.\n"
  },
  {
    "path": "libraries/rib-base/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.kotlin.library\")\n    alias(libs.plugins.kotlin.ksp)\n    alias(libs.plugins.maven.publish)\n}\n\ndependencies {\n    // RIBs themselves don't need to use dagger. But the base library does use dagger\n    // in order to invert a dependency. With a bit of work this could be removed.\n    ksp(libs.dagger.compiler)\n    ksp(libs.android.api)\n\n    implementation(libs.guava.android)\n    implementation(libs.reactivestreams)\n    implementation(libs.rxrelay2)\n    implementation(libs.rxjava2)\n    implementation(libs.autodispose.library)\n    api(libs.autodispose.lifecycle)\n\n    implementation(libs.autodispose.coroutines)\n    implementation(libs.kotlinx.coroutines.rx2)\n    api(libs.kotlinx.coroutines.core)\n    api(project(\":libraries:rib-coroutines\"))\n\n    compileOnly(libs.dagger.compiler)\n    compileOnly(libs.androidx.annotation)\n    compileOnly(libs.android.api)\n    compileOnly(libs.checkerqual)\n\n    testImplementation(project(\":libraries:rib-coroutines-test\"))\n    testImplementation(libs.androidx.annotation)\n    testImplementation(libs.android.api)\n    testImplementation(testLibs.junit)\n    testImplementation(testLibs.mockito)\n    testImplementation(testLibs.mockito.kotlin)\n    testImplementation(testLibs.truth)\n    testImplementation(project(\":libraries:rib-test\")) {\n        isTransitive = false\n    }\n}\n"
  },
  {
    "path": "libraries/rib-base/gradle.properties",
    "content": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPOM_NAME=RIBs (Base)\nPOM_ARTIFACT_ID=rib-base\nPOM_PACKAGING=jar\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/BasicInteractor.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/**\n * [Interactor] that doesn't rely on field injection.\n *\n * @param <P> the type of [Presenter].\n * @param <R> the type of [Router].\n */\npublic abstract class BasicInteractor<P : Any, R : Router<*>>\nprotected constructor(\n  @JvmField protected var presenter: P,\n) : Interactor<P, R>(presenter)\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/BasicRouter.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/**\n * [Router] that does not require an [InteractorBaseComponent].\n *\n * @param <I> type of interactor.\n */\npublic abstract class BasicRouter<I : Interactor<*, *>>(interactor: I) : Router<I>(interactor)\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/Builder.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/**\n * Responsible for building a router. Parent routers should pass in static dependencies via the\n * dependency passed in via the constructor. For dynamic dependencies (things that are fetched\n * asynchronously - or created dynamically in the parent), they should be passed in via a build\n * method that vends a router.\n *\n * @param <T> type of interactor to build.\n * @param <D> type of dependency required to build the interactor.\n * @param dependency required to build the router.\n */\npublic abstract class Builder<T : Router<*>, D>(protected val dependency: D)\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/Bundle.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.os.Parcelable\n\n/** Uber wrapper around Android Bundle to avoid Android and Robolectric dependencies. */\npublic open class Bundle\n@JvmOverloads\nconstructor(\n  private val androidBundle: android.os.Bundle = android.os.Bundle(),\n) {\n\n  /**\n   * Returns the value associated with the given key, or defaultValue if no mapping of the desired\n   * type exists for the given key or if a null value is explicitly associated with the given key.\n   *\n   * @param key to fetch.\n   * @param defaultValue if no value is present.\n   * @return the boolean value associated with the given key or null if there is no string value in\n   *   the bundle.\n   */\n  public open fun getBoolean(key: String, defaultValue: Boolean): Boolean {\n    return androidBundle.getBoolean(key, defaultValue)\n  }\n\n  /**\n   * Inserts a boolean value into the mapping of this Bundle, replacing any existing value for the\n   * given key.\n   *\n   * @param key to insert.\n   * @param value to insert.\n   */\n  public open fun putBoolean(key: String, value: Boolean) {\n    androidBundle.putBoolean(key, value)\n  }\n\n  /**\n   * Returns a [Bundle] for a given key, or `null`.\n   *\n   * @param key to fetch.\n   * @return a [Bundle] or `null`\n   */\n  public open fun getBundleExtra(key: String): Bundle? {\n    val value = androidBundle.getParcelable<Parcelable>(key)\n    return if (value != null) {\n      Bundle(value as android.os.Bundle)\n    } else {\n      null\n    }\n  }\n\n  /**\n   * Inserts a wrapped Bundle value into the mapping of this Bundle, replacing any existing value\n   * for the given key.\n   *\n   * @param key to insert.\n   * @param bundle to insert.\n   */\n  public open fun putBundleExtra(key: String, bundle: Bundle?) {\n    if (bundle != null) {\n      androidBundle.putParcelable(key, bundle.androidBundle)\n    } else {\n      androidBundle.putParcelable(key, null)\n    }\n  }\n\n  /**\n   * Returns the value associated with the given key, or null if no mapping of the desired type\n   * exists for the given key or a null value is explicitly associated with the key.\n   *\n   * @param key to get.\n   * @return the value, or `null`.\n   */\n  public open fun getParcelable(key: String): Parcelable? {\n    return androidBundle.getParcelable(key)\n  }\n\n  /**\n   * Inserts a Parcelable value into the mapping of this Bundle, replacing any existing value for\n   * the given key. Either key or value may be null.\n   *\n   * @param key to insert.\n   * @param value to insert.\n   */\n  public open fun putParcelable(key: String, value: Parcelable?) {\n    androidBundle.putParcelable(key, value)\n  }\n\n  /**\n   * Returns the value associated with the given key, or defaultValue if no mapping of the desired\n   * type exists for the given key or if a null value is explicitly associated with the given key.\n   *\n   * @param key to fetch.\n   * @return the String value associated with the given key or null if there is no string value in\n   *   the bundle.\n   */\n  public open fun getString(key: String): String? {\n    return androidBundle.getString(key)\n  }\n\n  /**\n   * Inserts a String value into the mapping of this Bundle, replacing any existing value for the\n   * given key.\n   *\n   * @param key to insert.\n   * @param value to insert.\n   */\n  public open fun putString(key: String, value: String?) {\n    androidBundle.putString(key, value)\n  }\n\n  /**\n   * Inserts an Int value into the mapping of this Bundle, replacing any existing value for the\n   * given key.\n   *\n   * @param key to insert.\n   * @param value to insert.\n   */\n  public open fun putInt(key: String, value: Int) {\n    androidBundle.putInt(key, value)\n  }\n\n  /**\n   * Returns the value associated with the given key, or defaultValue if no mapping of the desired\n   * type exists for the given key or if a null value is explicitly associated with the given key.\n   *\n   * @param key to fetch.\n   * @return the int value associated with the given key or defaultValue if there is no int value in\n   *   the bundle.\n   */\n  public open fun getInt(key: String, defaultValue: Int): Int {\n    return androidBundle.getInt(key, defaultValue)\n  }\n}\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/EmptyPresenter.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/** No-op [Presenter] */\npublic open class EmptyPresenter : Presenter()\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/FlowAsScope.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:JvmSynthetic\n\npackage com.uber.rib.core\n\nimport com.uber.autodispose.lifecycle.LifecycleEndedException\nimport com.uber.autodispose.lifecycle.LifecycleNotStartedException\nimport com.uber.rib.core.internal.CoreFriendModuleApi\nimport io.reactivex.CompletableSource\nimport kotlin.coroutines.CoroutineContext\nimport kotlin.coroutines.EmptyCoroutineContext\nimport kotlinx.coroutines.flow.SharedFlow\nimport kotlinx.coroutines.flow.collect\nimport kotlinx.coroutines.flow.takeWhile\nimport kotlinx.coroutines.rx2.rxCompletable\n\n/**\n * Converts a [SharedFlow] of lifecycle events into a [CompletableSource] that completes once the\n * flow emits the ending event.\n *\n * The lifecycle start and end events are defined by [range], and this function will throw either:\n * 1. [LifecycleNotStartedException], if the last emitted event is not in range, or\n * 2. [LifecycleEndedException], if the last emitted event is in the end (inclusive) or beyond\n *    [range].\n */\n@CoreFriendModuleApi\npublic fun <T : Comparable<T>> SharedFlow<T>.asScopeCompletable(\n  range: ClosedRange<T>,\n  context: CoroutineContext = EmptyCoroutineContext,\n): CompletableSource {\n  ensureAlive(range)\n  return rxCompletable(RibDispatchers.Unconfined + context) {\n    takeWhile { it < range.endInclusive }.collect()\n  }\n}\n\nprivate fun <T : Comparable<T>> SharedFlow<T>.ensureAlive(range: ClosedRange<T>) {\n  val lastEmitted = replayCache.lastOrNull()\n  when {\n    lastEmitted == null || lastEmitted < range.start -> throw LifecycleNotStartedException()\n    lastEmitted >= range.endInclusive -> throw LifecycleEndedException()\n  }\n}\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/Initializer.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/**\n * Drop-in replacement for com.facebook.infer.annotation.Initializer.\n *\n * Method, annotated as @Initializer, should be call before object is used externally. It can be\n * used as a marker for different late-init methods of Android lifecycle, methods called indirectly\n * by the constructor or builder methods necessary to complete before build.\n */\n@Retention(AnnotationRetention.BINARY)\n@Target(\n  AnnotationTarget.ANNOTATION_CLASS,\n  AnnotationTarget.CLASS,\n  AnnotationTarget.FIELD,\n  AnnotationTarget.CONSTRUCTOR,\n  AnnotationTarget.FUNCTION,\n  AnnotationTarget.PROPERTY_GETTER,\n  AnnotationTarget.PROPERTY_SETTER,\n)\npublic annotation class Initializer\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/Interactor.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport androidx.annotation.CallSuper\nimport androidx.annotation.VisibleForTesting\nimport com.uber.autodispose.lifecycle.CorrespondingEventsFunction\nimport com.uber.autodispose.lifecycle.LifecycleEndedException\nimport com.uber.rib.core.RibEvents.triggerRibActionAndEmitEvents\nimport com.uber.rib.core.internal.CoreFriendModuleApi\nimport com.uber.rib.core.lifecycle.InteractorEvent\nimport io.reactivex.CompletableSource\nimport io.reactivex.Observable\nimport javax.inject.Inject\nimport kotlin.properties.ReadWriteProperty\nimport kotlin.reflect.KProperty\nimport kotlinx.coroutines.ExperimentalForInheritanceCoroutinesApi\nimport kotlinx.coroutines.channels.BufferOverflow\nimport kotlinx.coroutines.flow.FlowCollector\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.SharedFlow\nimport kotlinx.coroutines.rx2.asObservable\n\n/**\n * The base implementation for all [Interactor]s.\n *\n * @param <P> the type of [Presenter].\n * @param <R> the type of [Router].\n */\npublic abstract class Interactor<P : Any, R : Router<*>>() : InteractorType, RibActionEmitter {\n  @Inject public lateinit var injectedPresenter: P\n\n  @CoreFriendModuleApi public var actualPresenter: P? = null\n\n  private val useStateFlow\n    get() = RibEvents.useStateFlowInteractorEvent\n\n  private val _lifecycleFlow: MutableSharedFlow<InteractorEvent?> =\n    if (useStateFlow) {\n      MutableStateFlow(null)\n    } else {\n      MutableSharedFlow(\n        1,\n        0,\n        BufferOverflow.DROP_OLDEST,\n      )\n    }\n  public open val lifecycleFlow: SharedFlow<InteractorEvent> = NonNullSharedFlow(_lifecycleFlow)\n\n  @Volatile private var _lifecycleObservable: Observable<InteractorEvent>? = null\n\n  @OptIn(CoreFriendModuleApi::class)\n  private val lifecycleObservable\n    get() = ::_lifecycleObservable.setIfNullAndGet { lifecycleFlow.asObservable() }\n\n  private val routerDelegate = InitOnceProperty<R>()\n\n  /** @return the router for this interactor. */\n  public open var router: R by routerDelegate\n    protected set\n\n  @OptIn(CoreFriendModuleApi::class)\n  protected constructor(presenter: P) : this() {\n    this.actualPresenter = presenter\n  }\n\n  // ---- LifecycleScopeProvider overrides ---- //\n\n  final override fun lifecycle(): Observable<InteractorEvent> = lifecycleObservable\n\n  final override fun correspondingEvents(): CorrespondingEventsFunction<InteractorEvent> =\n    LIFECYCLE_MAP_FUNCTION\n\n  final override fun peekLifecycle(): InteractorEvent? = lifecycleFlow.replayCache.lastOrNull()\n\n  @OptIn(CoreFriendModuleApi::class)\n  final override fun requestScope(): CompletableSource =\n    lifecycleFlow.asScopeCompletable(lifecycleRange)\n\n  // ---- InteractorType overrides ---- //\n\n  override fun isAttached(): Boolean =\n    lifecycleFlow.replayCache.lastOrNull() == InteractorEvent.ACTIVE\n\n  override fun handleBackPress(): Boolean = false\n\n  /**\n   * Called when attached. The presenter will automatically be added when this happens.\n   *\n   * @param savedInstanceState the saved [Bundle].\n   */\n  @CallSuper protected open fun didBecomeActive(savedInstanceState: Bundle?) {}\n\n  /**\n   * Called when detached. The [Interactor] should do its cleanup here. Note: View will be removed\n   * automatically so [Interactor] doesn't have to remove its view here.\n   */\n  protected open fun willResignActive() {}\n\n  internal fun onSaveInstanceStateInternal(outState: Bundle) {\n    onSaveInstanceState(outState)\n  }\n\n  /**\n   * Called when saving state.\n   *\n   * @param outState the saved [Bundle].\n   */\n  protected open fun onSaveInstanceState(outState: Bundle) {}\n\n  public open fun dispatchAttach(savedInstanceState: Bundle?) {\n    _lifecycleFlow.tryEmit(InteractorEvent.ACTIVE)\n\n    val presenter = (getPresenter() as? Presenter)\n    presenter?.let {\n      triggerRibActionAndEmitEvents(\n        it,\n        RibActionEmitterType.PRESENTER,\n        RibEventType.ATTACHED,\n      ) {\n        it.dispatchLoad()\n      }\n    }\n\n    triggerRibActionAndEmitEvents(\n      this,\n      RibActionEmitterType.INTERACTOR,\n      RibEventType.ATTACHED,\n    ) {\n      didBecomeActive(savedInstanceState)\n    }\n  }\n\n  public open fun dispatchDetach(): P {\n    val presenter = (getPresenter() as? Presenter)\n    presenter?.let {\n      triggerRibActionAndEmitEvents(\n        it,\n        RibActionEmitterType.PRESENTER,\n        RibEventType.DETACHED,\n      ) {\n        it.dispatchUnload()\n      }\n    }\n\n    triggerRibActionAndEmitEvents(\n      this,\n      RibActionEmitterType.INTERACTOR,\n      RibEventType.DETACHED,\n    ) {\n      willResignActive()\n    }\n\n    _lifecycleFlow.tryEmit(InteractorEvent.INACTIVE)\n\n    return getPresenter()\n  }\n\n  @CoreFriendModuleApi\n  public fun setRouterInternal(router: Router<*>) {\n    if (routerDelegate != null) {\n      this.router = router as R\n    }\n  }\n\n  /** @return the currently attached presenter if there is one */\n  @OptIn(CoreFriendModuleApi::class)\n  @VisibleForTesting\n  private fun getPresenter(): P {\n    val presenter: P? =\n      try {\n        if (actualPresenter != null) {\n          actualPresenter\n        } else {\n          injectedPresenter\n        }\n      } catch (e: UninitializedPropertyAccessException) {\n        actualPresenter\n      }\n    checkNotNull(presenter) { \"Attempting to get interactor's presenter before being set.\" }\n    return presenter\n  }\n\n  @OptIn(CoreFriendModuleApi::class)\n  @VisibleForTesting\n  internal fun setPresenter(presenter: P) {\n    actualPresenter = presenter\n  }\n\n  private inner class InitOnceProperty<T> : ReadWriteProperty<Any, T> {\n    private var backingField: T? = null\n\n    override fun getValue(thisRef: Any, property: KProperty<*>): T {\n      if (backingField == null) {\n        throw IllegalStateException(\"Attempting to get value before it has been set.\")\n      }\n      return backingField as T\n    }\n\n    override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {\n      if (backingField != null) {\n        throw IllegalStateException(\"Attempting to set value after it has been set.\")\n      } else {\n        backingField = value\n      }\n    }\n  }\n\n  public companion object {\n    @get:JvmSynthetic internal val lifecycleRange = InteractorEvent.ACTIVE..InteractorEvent.INACTIVE\n\n    private val LIFECYCLE_MAP_FUNCTION =\n      CorrespondingEventsFunction { interactorEvent: InteractorEvent ->\n        when (interactorEvent) {\n          InteractorEvent.ACTIVE -> return@CorrespondingEventsFunction InteractorEvent.INACTIVE\n          else -> throw LifecycleEndedException()\n        }\n      }\n  }\n}\n\n// See https://github.com/Kotlin/kotlinx.coroutines/issues/2514\n@OptIn(ExperimentalForInheritanceCoroutinesApi::class)\nprivate class NonNullSharedFlow<R : Any>(\n  private val upstream: SharedFlow<R?>,\n) : SharedFlow<R> {\n  override val replayCache: List<R>\n    get() = upstream.replayCache.filterNotNull()\n\n  override suspend fun collect(collector: FlowCollector<R>): Nothing {\n    upstream.collect { value ->\n      if (value != null) {\n        collector.emit(value)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/InteractorAndViewModule.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.view.View\n\n/**\n * A module that takes an interactor and a view as dependencies.\n *\n * @param <I> type of interactor.\n * @param <V> type of view.\n */\npublic abstract class InteractorAndViewModule<I : Interactor<*, *>, V : View>(\n  interactor: I,\n  protected val view: V,\n) : InteractorModule<I>(interactor)\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/InteractorBaseComponent.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/**\n * Designates a component that can provide a specific interactor.\n *\n * @param <T> type of interactor that is injected.\n */\npublic interface InteractorBaseComponent<T : Interactor<*, *>> {\n  /**\n   * Inject the interactor.\n   *\n   * @param interactor to inject.\n   */\n  public fun inject(interactor: T)\n}\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/InteractorComponent.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/**\n * Designates a component that can provide a specific interactor. This is identical to [ ]. This\n * exists soley for backwards compatibility with old versions of the Presidio Intellij Plugin.\n *\n * @param <T> type of interactor that is injected.\n * @param <P> type of presenter.\n */\npublic interface InteractorComponent<P : Presenter, T : Interactor<P, *>> :\n  InteractorBaseComponent<T> {\n  /**\n   * Inject the interactor.\n   *\n   * @param interactor to inject.\n   */\n  override fun inject(interactor: T)\n\n  /**\n   * The presenter.\n   *\n   * @return the presenter.\n   */\n  public fun presenter(): P\n}\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/InteractorModule.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/**\n * A module that takes an interactor as a dependency.\n *\n * @param <I> type of interactor.\n */\npublic abstract class InteractorModule<I : Interactor<*, *>>(protected val interactor: I)\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/InteractorType.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.uber.autodispose.lifecycle.LifecycleScopeProvider\nimport com.uber.rib.core.lifecycle.InteractorEvent\n\n/**\n * An interface used as the upper bound of the generic used by [Router]s to avoid cyclic generic\n * types\n */\npublic interface InteractorType : LifecycleScopeProvider<InteractorEvent> {\n  /** @return `true` if the controller is attached, `false` if not. */\n  public fun isAttached(): Boolean\n\n  /**\n   * Handle an activity back press.\n   *\n   * @return whether this interactor took action in response to a back press.\n   */\n  public fun handleBackPress(): Boolean\n}\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/LazyBackingProperty.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.uber.rib.core.internal.CoreFriendModuleApi\nimport kotlin.reflect.KMutableProperty0\n\n/**\n * Lazily sets a value produced by [initializer] into the receiver mutable property and returns it\n * if the property is set to `null`, or returns the value set into the property without calling\n * [initializer].\n *\n * This is similar to [lazy].\n *\n * This function is needed because of Mockito mocking. When we mock a class, mockito does not call\n * any constructor and does not initialize private fields of the class. By having a null (unset)\n * field being dynamically set by a final function, we can overcome this issue.\n *\n * To properly support concurrency, the backing mutable property should be [Volatile].\n */\n@CoreFriendModuleApi\npublic inline fun <T : Any> KMutableProperty0<T?>.setIfNullAndGet(\n  initializer: () -> T,\n): T = get() ?: synchronized(Lock) { get() ?: initializer().also { set(it) } }\n\n@CoreFriendModuleApi public object Lock\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/Presenter.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport androidx.annotation.CallSuper\nimport com.uber.autodispose.ScopeProvider\nimport com.uber.rib.core.internal.CoreFriendModuleApi\nimport com.uber.rib.core.lifecycle.PresenterEvent\nimport io.reactivex.CompletableSource\nimport io.reactivex.Observable\nimport kotlinx.coroutines.channels.BufferOverflow\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.flow.SharedFlow\nimport kotlinx.coroutines.rx2.asObservable\nimport org.checkerframework.checker.guieffect.qual.UIEffect\n\n/**\n * Contains presentation logic. This class exists mainly for legacy reasons. In the past we believed\n * it was useful to have a class between interactors and views to facilitate model transformations\n * and believed these transformations would be complex enough to require its own lifecycle. In\n * practice this caused confusion: if both a presenter and interactor can perform complex rx logic\n * it becomes unclear where you should write your bussiness logic.\n */\npublic abstract class Presenter : ScopeProvider, RibActionEmitter {\n  private val _lifecycleFlow = MutableSharedFlow<PresenterEvent>(1, 0, BufferOverflow.DROP_OLDEST)\n  public open val lifecycleFlow: SharedFlow<PresenterEvent>\n    get() = _lifecycleFlow\n\n  @Volatile private var _lifecycleObservable: Observable<PresenterEvent>? = null\n\n  @OptIn(CoreFriendModuleApi::class)\n  private val lifecycleObservable\n    get() = ::_lifecycleObservable.setIfNullAndGet { lifecycleFlow.asObservable() }\n\n  /** @return `true` if the presenter is loaded, `false` if not. */\n  protected var isLoaded: Boolean = false\n    private set\n\n  public open fun dispatchLoad() {\n    isLoaded = true\n    _lifecycleFlow.tryEmit(PresenterEvent.LOADED)\n    didLoad()\n  }\n\n  public open fun dispatchUnload() {\n    isLoaded = false\n    willUnload()\n    _lifecycleFlow.tryEmit(PresenterEvent.UNLOADED)\n  }\n\n  /** Tells the presenter that it has finished loading. */\n  @CallSuper protected open fun didLoad() {}\n\n  /**\n   * Tells the presenter that it will be destroyed. Presenter subclasses should perform any required\n   * cleanup here.\n   */\n  @UIEffect @CallSuper protected open fun willUnload() {}\n\n  /** @return an observable of this controller's lifecycle events. */\n  public fun lifecycle(): Observable<PresenterEvent> = lifecycleObservable\n\n  @OptIn(CoreFriendModuleApi::class)\n  final override fun requestScope(): CompletableSource =\n    lifecycleFlow.asScopeCompletable(lifecycleRange)\n\n  internal companion object {\n    @get:JvmSynthetic internal val lifecycleRange = PresenterEvent.LOADED..PresenterEvent.UNLOADED\n  }\n}\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/Rib.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/** Holds configuration and settings for riblets. */\npublic open class Rib {\n\n  /** Responsible for app-specific riblet configuration. */\n  public interface Configuration {\n    /**\n     * Called when there is a non-fatal error in the RIB framework. Consumers should route this data\n     * to a place where it can be monitored (crash reporting, monitoring, etc.).\n     *\n     * If no configuration is set, the default implementation of this will crash the app when there\n     * is a non-fatal error.\n     *\n     * @param errorMessage an error message that describes the error.\n     * @param throwable an optional throwable.\n     */\n    public fun handleNonFatalError(errorMessage: String, throwable: Throwable?)\n\n    /**\n     * Called when there is a non-fatal warning in the RIB framework. Consumers should route this\n     * data to a place where it can be monitored (crash reporting, monitoring, etc.).\n     *\n     * NOTE: This API is used in a slightly different way than the\n     * [ ][Configuration.handleNonFatalError] error method. Non-fatal errors should never happen,\n     * warnings however can happen in certain conditions.\n     *\n     * @param warningMessage an error message that describes the error.\n     * @param throwable an optional throwable.\n     */\n    public fun handleNonFatalWarning(warningMessage: String, throwable: Throwable?)\n\n    /**\n     * Called when there is a message that should be logged for debugging. Consumers should route\n     * this data to a debug logging location.\n     *\n     * If no configuration is set, the default implementation of this will drop the messages.\n     *\n     * @param format Message format - See [String.format]\n     * @param args Arguments to use for printing the message.\n     */\n    public fun handleDebugMessage(format: String, vararg args: Any?)\n  }\n\n  /** Default, internal implementation that is used when host app does not set a configuration. */\n  private class DefaultConfiguration : Configuration {\n    override fun handleNonFatalError(errorMessage: String, throwable: Throwable?) {\n      throw RuntimeException(errorMessage, throwable)\n    }\n\n    override fun handleNonFatalWarning(warningMessage: String, throwable: Throwable?) {}\n    override fun handleDebugMessage(format: String, vararg args: Any?) {}\n  }\n\n  public companion object {\n    private var configuration: Configuration? = null\n\n    /**\n     * Sets the configuration to use in the application. This can only be called once before any RIB\n     * code is used. Calling it twice, or calling it after using RIB code will throw an exception.\n     *\n     * @param configurationToSet to set.\n     */\n    @JvmStatic\n    public fun setConfiguration(configurationToSet: Configuration) {\n      if (configuration == null) {\n        configuration = configurationToSet\n      } else {\n        if (configuration is DefaultConfiguration) {\n          throw IllegalStateException(\"Attempting to set a configuration after using RIB code.\")\n        } else {\n          throw IllegalStateException(\n            \"Attempting to set a configuration after one has previously been set.\",\n          )\n        }\n      }\n    }\n\n    @JvmStatic\n    public fun getConfiguration(): Configuration {\n      if (configuration == null) {\n        configuration = DefaultConfiguration()\n      }\n      return configuration!!\n    }\n  }\n}\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibBuilder.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/** The annotation to mark that some object is an Builder. */\n@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS)\n@Retention(AnnotationRetention.BINARY)\npublic annotation class RibBuilder\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.uber.autodispose.coroutinesinterop.asScopeProvider\nimport kotlin.coroutines.CoroutineContext\nimport kotlin.coroutines.EmptyCoroutineContext\nimport kotlinx.coroutines.CancellationException\nimport kotlinx.coroutines.CompletableJob\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.CoroutineStart\nimport kotlinx.coroutines.InternalForInheritanceCoroutinesApi\nimport kotlinx.coroutines.Job\nimport kotlinx.coroutines.SupervisorJob\nimport kotlinx.coroutines.awaitCancellation\nimport kotlinx.coroutines.cancel\nimport kotlinx.coroutines.ensureActive\nimport kotlinx.coroutines.job\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.supervisorScope\nimport kotlinx.coroutines.withContext\n\n/** A manager or helper class bound to a [CoroutineScope] by using a binder like [bind]. */\npublic fun interface RibCoroutineWorker : RibActionEmitter {\n\n  /** Called when worker is started. Children coroutines can be launched in [scope]. */\n  public suspend fun onStart(scope: CoroutineScope)\n\n  /** Called when worker is stopped with [cause]. Should be fast, be non-blocking and not throw. */\n  public fun onStop(cause: Throwable) {}\n}\n\n/** A manager or helper class bound to a [CoroutineScope] by using a binder like [bind]. */\npublic inline fun RibCoroutineWorker(\n  crossinline onStart: suspend CoroutineScope.() -> Unit,\n): RibCoroutineWorker {\n  /*\n   * 'RibCoroutineWorker' is already a functional interface; the purpose of this builder is to allow consumers\n   * to create a 'RibCoroutineWorker' with 'CoroutineScope' in receiver position. E.g.\n   *\n   * Functional interface:\n   * RibCoroutineWorker { scope ->\n   *   scope.launch { ... }\n   * }\n   *\n   * This factory method:\n   * RibCoroutineWorker {\n   *   launch { ... }\n   * }\n   */\n  return RibCoroutineWorker { scope -> scope.onStart() }\n}\n\n// ---- Binder ---- //\n\n/**\n * A handle to interact with [RibCoroutineWorker] binding job. This handle implements [Job], which\n * refers to the completion of [RibCoroutineWorker.onStart]. It can be [joined][join] to make sure\n * `onStart` finished. Note that children coroutines launched in the [CoroutineScope] passed on to\n * `onStart` are not waited: worker is considered bound when `onStart` finishes.\n */\npublic sealed interface BindWorkerHandle : Job {\n  /** Unbinds the worker. */\n  public fun unbind(): Job\n}\n\n// Job is not stable for inheritance, but we are delegating the implementation\n// to a job instance from kotlinx.coroutines itself.\n@OptIn(InternalForInheritanceCoroutinesApi::class)\nprivate class BindWorkerHandleImpl(\n  bindJob: Job,\n  private val unbindJob: Job,\n) : BindWorkerHandle, Job by bindJob {\n  override fun unbind(): Job {\n    unbindJob.cancel(\"Worker was manually unbound.\")\n    return unbindJob\n  }\n}\n\n/**\n * Binds [worker] in a scope that is a child of the [CoroutineScope] receiver.\n *\n * The binding operation runs [RibCoroutineWorker.onStart] in a context inherited from the\n * [CoroutineScope] receiver, but with additional [context] elements that is, by default,\n * [RibDispatchers.Default]. This makes the worker run on the default dispatcher by default. Pass in\n * [EmptyCoroutineContext] instead if you want the worker to not override the dispatcher in the\n * scope (if any), or pass in a custom dispatcher as [context] to specify a different dispatcher. If\n * there is no dispatcher in [CoroutineScope] nor in [context], [RibDispatchers.Default] is used.\n *\n * The scope passed on to [RibCoroutineWorker.onStart] as a parameter is a child scope of the\n * [CoroutineScope] receiver, but with the additional [context] elements and a [SupervisorJob].\n *\n * Binding a worker is an asynchronous operation. To ensure [RibCoroutineWorker.onStart] is\n * finished, callers can [join][BindWorkerHandle.join] the resulting [BindWorkerHandle] when in a\n * coroutine:\n * ```\n * val handle = coroutineScope.bind(worker)\n * handle.join() // wait for onStart to finish\n * ```\n */\n@JvmOverloads\npublic fun CoroutineScope.bind(\n  worker: RibCoroutineWorker,\n  context: CoroutineContext = RibDispatchers.Default,\n): BindWorkerHandle {\n  var bindJob: CompletableJob? = null // A job that completes once worker's onStart completes\n  val unbindJob =\n    launch(context, CoroutineStart.UNDISPATCHED) {\n      val job = createBindingJob()\n      bindJob = job\n      // launch again -- this time, we will dispatch if installed dispatcher\n      // tell us to (CoroutineDispatcher.isDispatchNeeded()).\n      launch { bindAndAwaitCancellation(worker, job) }\n    }\n  // !! is safe here -- outer coroutine was started undispatched.\n  return BindWorkerHandleImpl(bindJob!!, unbindJob)\n}\n\n/** Binds [workers] in a scope that is a child of the [CoroutineScope] receiver. */\n@JvmOverloads\npublic fun CoroutineScope.bind(\n  workers: Iterable<RibCoroutineWorker>,\n  coroutineContext: CoroutineContext = RibDispatchers.Default,\n) {\n  for (worker in workers) {\n    bind(worker, coroutineContext)\n  }\n}\n\nprivate fun CoroutineScope.createBindingJob(): CompletableJob =\n  Job(coroutineContext.job).also {\n    // Cancel `unbindJob` if `bindJob` has cancelled. This is important to abort `onStart` if\n    // `bindJob` gets cancelled externally.\n    // Note that in case of `bindJob` failure (e.g. `onStart` throws), `unbindJob` will\n    // already fail by means of structured concurrency, but that does not happen on normal\n    // cancellation. This `bindJob` cancellation upon the `unbindJob` cancellation is also\n    // already set through structured concurrency.\n    it.invokeOnCompletion { throwable -> if (throwable is CancellationException) cancel(throwable) }\n  }\n\n@Suppress(\"TooGenericExceptionCaught\") // Exception is not swallowed\nprivate suspend fun bindAndAwaitCancellation(worker: RibCoroutineWorker, bindJob: CompletableJob) {\n  try {\n    supervisorScope {\n      worker.onStart(this)\n      // In case no cancellation check was done at all in `onStart` (e.g. it did not suspend),\n      // we want to cancel it before completing.\n      ensureActive()\n      bindJob.complete()\n      awaitCancellation() // Never returns normally, so we are sure an exception will be caught.\n    }\n  } catch (t: Throwable) {\n    bindJob.cancelOrCompleteExceptionally(t)\n    worker.onStop(t)\n  }\n}\n\n/**\n * Cancel the deferred if [throwable] is a [CancellationException], otherwise completes it\n * exceptionally.\n */\nprivate fun CompletableJob.cancelOrCompleteExceptionally(throwable: Throwable) {\n  when (throwable) {\n    is CancellationException -> cancel(throwable)\n    else -> completeExceptionally(throwable)\n  }\n}\n\n// ---- RibCoroutineWorker <-> Worker adapters ---- //\n\n/**\n * Converts a [Worker] to a [RibCoroutineWorker].\n *\n * Returns the instance unchanged if it already implements [RibCoroutineWorker].\n */\npublic fun Worker.asRibCoroutineWorker(): RibCoroutineWorker =\n  this as? RibCoroutineWorker ?: WorkerToRibCoroutineWorkerAdapter(this)\n\n/**\n * Converts a [RibCoroutineWorker] to a [Worker].\n *\n * Returns the instance unchanged if it already implements [Worker]. In that case,\n * [coroutineContext] will not be used.\n */\n@JvmOverloads\npublic fun RibCoroutineWorker.asWorker(\n  coroutineContext: CoroutineContext = RibDispatchers.Default,\n): Worker = this as? Worker ?: RibCoroutineWorkerToWorkerAdapter(this, coroutineContext)\n\ninternal open class WorkerToRibCoroutineWorkerAdapter(\n  private val worker: Worker,\n) : RibCoroutineWorker {\n  override suspend fun onStart(scope: CoroutineScope) {\n    withContext(worker.coroutineContext ?: EmptyCoroutineContext) {\n      worker.onStart(scope.asWorkerScopeProvider())\n    }\n  }\n\n  override fun onStop(cause: Throwable): Unit = worker.onStop()\n}\n\ninternal open class RibCoroutineWorkerToWorkerAdapter(\n  private val ribCoroutineWorker: RibCoroutineWorker,\n  override val coroutineContext: CoroutineContext,\n) : Worker {\n\n  override fun onStart(lifecycle: WorkerScopeProvider) {\n    // We start it undispatched to keep the behavior of immediate binding of Worker when\n    // WorkerBinder.bind is called.\n    // We still want to pass in `coroutineContext` to resume from suspensions in `onStart` in\n    // correct context.\n    lifecycle.coroutineScope.launch(coroutineContext, CoroutineStart.UNDISPATCHED) {\n      supervisorScope {\n        ribCoroutineWorker.onStart(this)\n        // Keep this scope alive until cancelled.\n        // This is particularly important for cases where we do not launch long-running coroutines\n        // with scope, but instead install some completion handler that we expect to be called at\n        // worker unbinding. This is the case with Rx subscriptions with 'autoDispose(scope)'\n        awaitCancellation()\n      }\n    }\n  }\n\n  override fun onStop() {\n    ribCoroutineWorker.onStop(CancellationException(\"Worker is unbinding.\"))\n  }\n}\n\nprivate fun CoroutineScope.asWorkerScopeProvider() = WorkerScopeProvider(asScopeProvider())\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEventType.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\npublic enum class RibEventType {\n  ATTACHED,\n  DETACHED,\n}\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport androidx.annotation.VisibleForTesting\nimport io.reactivex.Observable\nimport kotlinx.coroutines.channels.BufferOverflow\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.flow.SharedFlow\nimport kotlinx.coroutines.flow.asSharedFlow\nimport kotlinx.coroutines.rx2.asObservable\n\npublic object RibEvents {\n  private var extraBufferCapacity: Int = Channel.UNLIMITED\n\n  /**\n   * Sets the extra buffer capacity for [routerEventsFlow] and [ribActionEventsFlow].\n   *\n   * This function must be called on the main thread, and before any usage of:\n   * 1. [routerEventsFlow]\n   * 2. [routerEvents]\n   * 3. [ribActionEventsFlow]\n   * 4. [ribActionEvents]\n   */\n  @JvmStatic\n  public fun setExtraBufferCapacity(capacity: Int) {\n    extraBufferCapacity = capacity\n  }\n\n  private val mutableRouterEvents by lazy {\n    MutableSharedFlow<RibRouterEvent>(0, extraBufferCapacity, BufferOverflow.DROP_OLDEST)\n  }\n\n  private val mutableRibDurationEvents by lazy {\n    MutableSharedFlow<RibActionInfo>(0, extraBufferCapacity, BufferOverflow.DROP_OLDEST)\n  }\n\n  @JvmStatic\n  public val routerEventsFlow: SharedFlow<RibRouterEvent> by lazy {\n    mutableRouterEvents.asSharedFlow()\n  }\n\n  @JvmStatic\n  public val routerEvents: Observable<RibRouterEvent> by lazy { mutableRouterEvents.asObservable() }\n\n  @JvmStatic\n  public val ribActionEventsFlow: SharedFlow<RibActionInfo> by lazy {\n    mutableRibDurationEvents.asSharedFlow()\n  }\n\n  @JvmStatic\n  public val ribActionEvents: Observable<RibActionInfo> by lazy {\n    mutableRibDurationEvents.asObservable()\n  }\n\n  internal var useStateFlowInteractorEvent: Boolean = false\n    private set\n\n  /** Indicates if [ribActionEvents] will be emitting. */\n  public var areRibActionEmissionsAllowed: Boolean = false\n    @VisibleForTesting internal set\n\n  /**\n   * To be called before start observing/collecting on [ribActionEvents] (usually at your earliest\n   * application point)\n   */\n  @JvmStatic\n  public fun enableRibActionEmissions() {\n    this.areRibActionEmissionsAllowed = true\n  }\n\n  /** If true, the [Interactor] will use [MutableStateFlow] for the interactor events. */\n  @JvmStatic\n  public fun useStateFlowInteractorEvent() {\n    this.useStateFlowInteractorEvent = true\n  }\n\n  /**\n   * @param eventType [RibEventType]\n   * @param child [Router]\n   * @param parent [Router] and null for the root ribs that are directly attached to\n   *   RibActivity/Fragment\n   */\n  @JvmStatic\n  public fun emitRouterEvent(eventType: RibEventType, child: Router<*>, parent: Router<*>?) {\n    mutableRouterEvents.tryEmit(RibRouterEvent(eventType, child, parent))\n  }\n\n  /**\n   * Calls related RIB action (e.g. didBecomeActive) and emits emission of ATTACHED/DETACHED events\n   * for each [RibActionEmitter] (e.g. Concrete Interactor, Presenter, Router, Worker).\n   *\n   * @param ribActionEmitter The related Rib Action emitter class\n   * @param ribActionEmitterType The RIB component type (e.g. Interactor, Router, Presenter, Worker)\n   * @param ribEventType RIB event type (e.g. ATTACH/DETACH)\n   * @param ribAction The related RIB action type. e.g. didBecomeActive, willLoad, etc\n   */\n  internal inline fun triggerRibActionAndEmitEvents(\n    ribActionEmitter: RibActionEmitter,\n    ribActionEmitterType: RibActionEmitterType,\n    ribEventType: RibEventType,\n    ribAction: () -> Unit,\n  ) {\n    emitRibEventActionIfNeeded(\n      ribActionEmitter,\n      ribActionEmitterType,\n      ribEventType,\n      RibActionState.STARTED,\n    )\n    ribAction()\n    emitRibEventActionIfNeeded(\n      ribActionEmitter,\n      ribActionEmitterType,\n      ribEventType,\n      RibActionState.COMPLETED,\n    )\n  }\n\n  /**\n   * Emits ATTACHED/DETACHED events for each RIB component.\n   *\n   * @param ribActionEmitter The related Rib Action emitter class\n   * @param ribActionEmitterType The RIB component type (e.g. Interactor, Router, Presenter)\n   * @param ribEventType RIB event type (e.g. ATTACH/DETACH)\n   * @param ribActionState: RIB action state (STARTED/COMPLETED). For example prior and after call\n   *   of Interactor.didBecomeActive\n   */\n  private fun emitRibEventActionIfNeeded(\n    ribActionEmitter: RibActionEmitter,\n    ribActionEmitterType: RibActionEmitterType,\n    ribEventType: RibEventType,\n    ribActionState: RibActionState,\n  ) {\n    if (!areRibActionEmissionsAllowed) {\n      // Unless specified explicitly via [RibEvents.enableRibActionEmissions()] there is no need\n      // to create unnecessary objects if there is no intention on observing/collecting RibAction\n      // events\n      return\n    }\n\n    val ribActionInfo =\n      RibActionInfo(\n        ribActionEmitter.javaClass.name,\n        ribActionEmitterType,\n        ribEventType,\n        ribActionState,\n        Thread.currentThread().name,\n      )\n    mutableRibDurationEvents.tryEmit(ribActionInfo)\n  }\n}\n\n/** Holds relevant RIB event information */\npublic data class RibActionInfo(\n  /** Related RIB Action concrete class name */\n  val ribActionEmitterName: String,\n\n  /** The current RIB event type being bound (e.g. INTERACTOR/PRESENTER/ROUTER/WORKER) */\n  val ribActionEmitterType: RibActionEmitterType,\n\n  /**\n   * Represents the RIB event type (ATTACHED/DETACHED).\n   *\n   * For example for interactor:\n   * - Interactor.didBecomeActive -> ATTACHED\n   * - Interactor.willResignActive -> DETACHED\n   *\n   * For Worker:\n   * - Worker.onStart() -> ATTACHED\n   * - Worker.onStop() -> DETACHED\n   */\n  val ribEventType: RibEventType,\n\n  /** RIB Action state (e.g. event to be called before/after didBecomeActive, willLoad, etc) */\n  val ribActionState: RibActionState,\n\n  /** Original caller thread where the RIB action happens */\n  val originalCallerThreadName: String,\n)\n\n/**\n * Contract for all related Rib Action Types (Interactor, Presenter, Router, Worker) where will be\n * emitting via [ribActionEvents] observable\n */\npublic interface RibActionEmitter\n\npublic enum class RibActionEmitterType {\n  ROUTER,\n  PRESENTER,\n  INTERACTOR,\n  DEPRECATED_WORKER,\n}\n\n/** Represents status for each RibAction */\npublic enum class RibActionState {\n  STARTED,\n  COMPLETED,\n}\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibInteractor.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/** The annotation to mark that some object is an Interactor. */\n@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS)\n@Retention(AnnotationRetention.SOURCE)\npublic annotation class RibInteractor\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibRefWatcher.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport androidx.annotation.MainThread\n\n/**\n * Used to watch objects in order to verify they have no inbound references. Used to find memory\n * leaks.\n */\n@MainThread\npublic open class RibRefWatcher {\n  private var referenceWatcher: ReferenceWatcher? = null\n\n  /**\n   * Initialize Paper to use a [ReferenceWatcher] for observing deleted references.\n   *\n   * @param watcher the watcher.\n   */\n  public open fun setReferenceWatcher(watcher: ReferenceWatcher?) {\n    referenceWatcher = watcher\n  }\n\n  /**\n   * Watch this object to verify it has no inbound references.\n   *\n   * @param objectToWatch the object to watch.\n   */\n  public open fun watchDeletedObject(objectToWatch: Any?) {\n    if (objectToWatch == null) {\n      return\n    }\n    if (isLeakCanaryEnabled || uLeakEnabled) {\n      referenceWatcher?.watch(objectToWatch)\n    }\n  }\n\n  /**\n   * Pipes breadcrumb data to the breadcrumb logger through the referenceWatcher.\n   *\n   * @param eventType Type of breadcrumb event\n   * @param data breadcrumb data\n   * @param parent breadcrumb parent if any\n   */\n  public open fun logBreadcrumb(eventType: String, child: String?, parent: String?) {\n    if (referenceWatcher != null && breadcrumbsEnabled) {\n      if (child == null || parent == null) {\n        referenceWatcher?.logBreadcrumb(eventType, eventType, eventType)\n      } else {\n        referenceWatcher?.logBreadcrumb(eventType, child, parent)\n      }\n    }\n  }\n\n  /** Enables Breadcrumb logging. */\n  public open fun enableBreadcrumbLogging() {\n    breadcrumbsEnabled = true\n  }\n\n  /** Enables LeakCanary. */\n  public open fun enableLeakCanary() {\n    isLeakCanaryEnabled = true\n  }\n\n  /** Disables LeakCanary. */\n  public open fun disableLeakCanary() {\n    isLeakCanaryEnabled = false\n  }\n\n  /** Enables ULeak's Lifecycle tracking functionality. ULeak itself is behind a plugin. */\n  public open fun enableULeakLifecycleTracking() {\n    uLeakEnabled = true\n  }\n\n  /** Disables ULeak's Lifecycle Tracking. */\n  public open fun disableULeakLifecycleTracking() {\n    uLeakEnabled = false\n  }\n\n  /** Interface for classes that watch objects. */\n  public interface ReferenceWatcher {\n    /**\n     * Watch this object to verify it has no inbound references.\n     *\n     * @param objectToWatch the object to watch.\n     */\n    public fun watch(objectToWatch: Any)\n\n    /**\n     * Method to pipe breadcrumbs into the Breadcrumb logger.\n     *\n     * @param eventType Type of Breadcrumb event\n     * @param data The breadcrumb data\n     * @param parent The breadcrumb parent\n     */\n    public fun logBreadcrumb(eventType: String, data: String, parent: String)\n  }\n\n  public companion object {\n    private var ribRefWatcher: RibRefWatcher? = null\n\n    /**\n     * Get an instance of the [RibRefWatcher].\n     *\n     * @return the [RibRefWatcher] instance.\n     */\n    @JvmStatic\n    public fun getInstance(): RibRefWatcher {\n      if (ribRefWatcher == null) {\n        ribRefWatcher = RibRefWatcher()\n      }\n      return ribRefWatcher!!\n    }\n\n    /**\n     * Returns whether or not LeakCanary is enabled.\n     *\n     * @return whether or not LeakCanary is enabled.\n     */\n    @JvmStatic\n    public var isLeakCanaryEnabled: Boolean = false\n      private set\n    private var uLeakEnabled = false\n    private var breadcrumbsEnabled = false\n  }\n}\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibRouterEvent.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/**\n * @param eventType [RibEventType]\n * @param router [Router]\n * @param parentRouter [Router] and null for the root ribs that are directly attached to\n *   RibActivity/Fragment\n */\npublic open class RibRouterEvent(\n  public open val eventType: RibEventType,\n  public open val router: Router<*>,\n  public open val parentRouter: Router<*>?,\n)\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/Router.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.os.Looper\nimport androidx.annotation.CallSuper\nimport androidx.annotation.MainThread\nimport androidx.annotation.VisibleForTesting\nimport com.uber.rib.core.RibEvents.triggerRibActionAndEmitEvents\nimport com.uber.rib.core.internal.CoreFriendModuleApi\nimport java.util.Locale\nimport java.util.concurrent.CopyOnWriteArrayList\n\n/**\n * Responsible for handling the addition and removal of children routers.\n *\n * @param <I> type of interactor this router routes.\n */\npublic abstract class Router<I : InteractorType>\nprotected constructor(\n  component: InteractorBaseComponent<*>?,\n  public open val interactor: I,\n  private val ribRefWatcher: RibRefWatcher,\n  private val mainThread: Thread,\n) : RibActionEmitter {\n  private val children: MutableList<Router<*>> = CopyOnWriteArrayList()\n  private val interactorGeneric: Interactor<*, *>\n    get() = interactor as Interactor<*, *>\n\n  /** @return the Tag. */\n  @CoreFriendModuleApi\n  public var tag: String? = null\n    private set\n  private var savedInstanceState: Bundle? = null\n  private var isLoaded = false\n\n  protected constructor(\n    interactor: I,\n    component: InteractorBaseComponent<*>?,\n  ) : this(component, interactor, RibRefWatcher.getInstance(), getMainThread())\n  protected constructor(\n    interactor: I,\n  ) : this(null, interactor, RibRefWatcher.getInstance(), getMainThread())\n\n  @Suppress(\"UNCHECKED_CAST\")\n  protected fun inject(component: InteractorBaseComponent<*>?) {\n    (component as? InteractorBaseComponent<Interactor<*, *>>)?.inject(interactorGeneric)\n  }\n\n  @OptIn(CoreFriendModuleApi::class)\n  protected open fun attachToInteractor() {\n    interactorGeneric.setRouterInternal(this)\n  }\n\n  /**\n   * Dispatch back press to the associated interactor. Do not override this.\n   *\n   * @return TRUE if the interactor handles the back press.\n   */\n  public open fun handleBackPress(): Boolean {\n    ribRefWatcher.logBreadcrumb(\"BACKPRESS\", null, null)\n    return interactorGeneric.handleBackPress()\n  }\n\n  /** Called after the router has been loaded and initialized. */\n  @Initializer protected open fun didLoad() {}\n\n  /**\n   * Called when a router is being attached. Router subclasses can perform setup here for anything\n   * that is needed again but is cleaned up in willDetach(). Use didLoad() if the setup is only\n   * needed once.\n   */\n  protected open fun willAttach() {}\n\n  /**\n   * Called when a router is being a detached, router subclasses should perform any required clean\n   * up here.\n   */\n  protected open fun willDetach() {}\n\n  @MainThread\n  public open fun attachChild(childRouter: Router<*>) {\n    attachChild(childRouter, childRouter.javaClass.name)\n  }\n\n  /**\n   * Attaches a child router to this router.\n   *\n   * @param childRouter the [Router] to be attached.\n   * @param tag an identifier to namespace saved instance state [Bundle] objects.\n   */\n  @OptIn(CoreFriendModuleApi::class)\n  @MainThread\n  public open fun attachChild(childRouter: Router<*>, tag: String) {\n    for (child in children) {\n      if (tag == child.tag) {\n        Rib.getConfiguration()\n          .handleNonFatalWarning(\n            String.format(\n              Locale.getDefault(),\n              \"There is already a child router with tag: %s\",\n              tag,\n            ),\n            null,\n          )\n      }\n    }\n\n    triggerRibActionAndEmitEvents(\n      childRouter,\n      RibActionEmitterType.ROUTER,\n      RibEventType.ATTACHED,\n    ) {\n      children.add(childRouter)\n    }\n\n    ribRefWatcher.logBreadcrumb(\n      \"ATTACHED\",\n      childRouter.javaClass.simpleName,\n      this.javaClass.simpleName,\n    )\n    RibEvents.emitRouterEvent(RibEventType.ATTACHED, childRouter, this)\n    var childBundle: Bundle? = null\n    if (savedInstanceState != null) {\n      val previousChildren = savedInstanceState?.getBundleExtra(KEY_CHILD_ROUTERS)\n      childBundle = previousChildren?.getBundleExtra(tag)\n    }\n    childRouter.dispatchAttach(childBundle, tag)\n  }\n\n  /**\n   * Detaches the {@param childFactory} from the current [Interactor]. NOTE: No consumers of this\n   * API should ever keep a reference to the detached child router, leak canary will enforce that it\n   * gets garbage collected.\n   *\n   * If you need to keep references to previous routers, use RouterNavigator.\n   *\n   * @param childRouter the [Router] to be detached.\n   */\n  @OptIn(CoreFriendModuleApi::class)\n  @MainThread\n  public open fun detachChild(childRouter: Router<*>) {\n    val isChildRemoved = children.remove(childRouter)\n    val interactor = childRouter.interactor\n    ribRefWatcher.watchDeletedObject(interactor)\n    ribRefWatcher.logBreadcrumb(\n      \"DETACHED\",\n      childRouter.javaClass.simpleName,\n      this.javaClass.simpleName,\n    )\n    if (savedInstanceState != null) {\n      val childrenBundles = savedInstanceState?.getBundleExtra(KEY_CHILD_ROUTERS)\n      val childRouterTag = childRouter.tag\n      if (childRouterTag != null) {\n        childrenBundles?.putBundleExtra(childRouterTag, null)\n      } else {\n        Rib.getConfiguration()\n          .handleNonFatalWarning(\"A RIB tried to detach a child that was never attached\", null)\n      }\n    }\n\n    triggerRibActionAndEmitEvents(\n      childRouter,\n      RibActionEmitterType.ROUTER,\n      RibEventType.DETACHED,\n    ) {\n      childRouter.dispatchDetach()\n    }\n\n    if (isChildRemoved) {\n      RibEvents.emitRouterEvent(RibEventType.DETACHED, childRouter, this)\n    }\n  }\n\n  @CallSuper\n  public open fun dispatchAttach(savedInstanceState: Bundle?) {\n    dispatchAttach(savedInstanceState, javaClass.name)\n  }\n\n  @OptIn(CoreFriendModuleApi::class)\n  @CallSuper\n  @Initializer\n  public open fun dispatchAttach(savedInstanceState: Bundle?, tag: String) {\n    checkForMainThread()\n    if (!isLoaded) {\n      isLoaded = true\n      didLoad()\n    }\n    this.savedInstanceState = savedInstanceState\n    this.tag = tag\n    willAttach()\n    var interactorBundle: Bundle? = null\n    if (this.savedInstanceState != null) {\n      interactorBundle = this.savedInstanceState!!.getBundleExtra(KEY_INTERACTOR)\n    }\n    interactorGeneric.dispatchAttach(interactorBundle)\n  }\n\n  public open fun dispatchDetach() {\n    checkForMainThread()\n\n    interactorGeneric.dispatchDetach()\n    willDetach()\n    for (child in children) {\n      detachChild(child)\n    }\n  }\n\n  /**\n   * Gets the children of this [Router].\n   *\n   * @return Children.\n   */\n  @CoreFriendModuleApi\n  public open fun getChildren(): List<Router<*>> {\n    return children\n  }\n\n  @CoreFriendModuleApi\n  public fun saveInstanceStateInternal(outState: Bundle) {\n    saveInstanceState(outState)\n  }\n\n  @OptIn(CoreFriendModuleApi::class)\n  protected open fun saveInstanceState(outState: Bundle) {\n    val interactorSavedInstanceState = Bundle()\n    interactorGeneric.onSaveInstanceStateInternal(interactorSavedInstanceState)\n    outState.putBundleExtra(KEY_INTERACTOR, interactorSavedInstanceState)\n    val childBundles = Bundle()\n    for (child in children) {\n      val childBundle = Bundle()\n      child.saveInstanceState(childBundle)\n      childBundles.putBundleExtra(child.tag!!, childBundle)\n    }\n    outState.putBundleExtra(KEY_CHILD_ROUTERS, childBundles)\n  }\n\n  private fun checkForMainThread() {\n    if (mainThread !== Thread.currentThread()) {\n      val errorMessage = \"Call must happen on the main thread\"\n      val exception = IllegalStateException(errorMessage)\n      Rib.getConfiguration().handleNonFatalError(errorMessage, exception)\n    }\n  }\n\n  public companion object {\n    @VisibleForTesting public val KEY_CHILD_ROUTERS: String = \"Router.childRouters\"\n\n    @JvmField @VisibleForTesting public val KEY_INTERACTOR: String = \"Router.interactor\"\n\n    @JvmStatic\n    public fun getMainThread(): Thread {\n      return try {\n        Looper.getMainLooper().thread\n      } catch (e: Exception) {\n        Thread.currentThread()\n      }\n    }\n  }\n\n  init {\n    inject(component)\n    attachToInteractor()\n  }\n}\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport kotlin.coroutines.CoroutineContext\nimport kotlin.coroutines.EmptyCoroutineContext\nimport kotlinx.coroutines.CoroutineDispatcher\n\n/**\n * Interface used when creating a manager or helper class that should be bound to an interactor's\n * lifecycle using a binder like [WorkerBinder]. The worker event is decoupled from the interactor's\n * actual lifecycle so that we're not stuck moving these classes around if there are other\n * lifecycles we're interested in.\n *\n * By default a [Worker] will be bound on the [CoroutineDispatcher] defined at [WorkerBinder.bind]\n * call, except when the [Worker]'s [coroutineContext] is overriden with any other value than the\n * default [EmptyCoroutineContext]. The new resulting binding dispatcher (e.g.\n * RibDispatchers.Default) from [Worker] will take priority over the one passed via a\n * [WorkerBinder.bind] call\n */\n@Deprecated(\n  message =\n    \"\"\"\n      com.uber.rib.core.Worker is deprecated in favor of com.uber.rib.core.RibCoroutineWorker\n    \"\"\",\n  replaceWith = ReplaceWith(\"RibCoroutineWorker\"),\n)\npublic interface Worker : RibActionEmitter {\n\n  /**\n   * When overriden, will specify on which [CoroutineContext] the [Worker] will be bound via\n   * [WorkerBinder] (ignoring any [CoroutineDispatcher] being passed via [WorkerBinder])\n   *\n   * For example given list of workers:\n   * 1) workers = listOf(defaultWorker, uiWorker)\n   * 2) Where [uiWorker] overrides [coroutinesContext] with [RibDispatchers.Main]\n   * 3) After calling WorkerBinder.bind(interactor, workers, RibDispatchers.IO). [uiWorker] will be\n   *    guaranteed to be called on RibDispatchers.Main\n   */\n  public val coroutineContext: CoroutineContext\n    get() = EmptyCoroutineContext\n\n  /**\n   * Called when worker is started.\n   *\n   * @param lifecycle The lifecycle of the worker to use for subscriptions.\n   */\n  public fun onStart(lifecycle: WorkerScopeProvider) {}\n\n  /** Called when the worker is stopped. */\n  public fun onStop() {}\n}\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/WorkerBinder.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport androidx.annotation.VisibleForTesting\nimport com.uber.autodispose.ScopeProvider\nimport com.uber.autodispose.lifecycle.LifecycleScopeProvider\nimport com.uber.rib.core.RibEvents.triggerRibActionAndEmitEvents\nimport com.uber.rib.core.internal.CoreFriendModuleApi\nimport com.uber.rib.core.lifecycle.InteractorEvent\nimport com.uber.rib.core.lifecycle.PresenterEvent\nimport com.uber.rib.core.lifecycle.WorkerEvent\nimport io.reactivex.Observable\nimport io.reactivex.subjects.CompletableSubject\nimport kotlin.coroutines.CoroutineContext\nimport kotlin.coroutines.EmptyCoroutineContext\nimport kotlinx.coroutines.CoroutineDispatcher\nimport kotlinx.coroutines.CoroutineStart\nimport kotlinx.coroutines.DelicateCoroutinesApi\nimport kotlinx.coroutines.GlobalScope\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.onCompletion\nimport kotlinx.coroutines.flow.takeWhile\nimport kotlinx.coroutines.launch\n\n/**\n * Resulting CoroutineContext defined at [Worker] that guards against potential nullable cases on\n * tests that have a mocked Worker via Mockito\n */\nprivate val Worker.bindingCoroutineContext: CoroutineContext\n  get() = this.coroutineContext ?: EmptyCoroutineContext\n\n/** Helper class to bind to an interactor's lifecycle to translate it to a [Worker] lifecycle. */\n@Deprecated(\n  message =\n    \"\"\"\n      com.uber.rib.core.Worker is deprecated in favor of com.uber.rib.core.RibCoroutineWorker\n    \"\"\",\n  replaceWith = ReplaceWith(\"coroutineScope.bind(ribCoroutineWorker)\"),\n)\npublic object WorkerBinder {\n\n  /**\n   * Bind a worker (ie. a manager or any other class that needs an interactor's lifecycle) to an\n   * interactor's lifecycle events. Inject this class into your interactor and call this method on\n   * any\n   *\n   * @param interactor The interactor that provides the lifecycle.\n   * @param worker The class that wants to be informed when to start and stop doing work.\n   * @return [WorkerUnbinder] to unbind [Worker]'s lifecycle.\n   */\n  @JvmStatic\n  public fun bind(\n    interactor: Interactor<*, *>,\n    worker: Worker,\n  ): WorkerUnbinder =\n    worker.bind(\n      interactor.lifecycleFlow,\n      Interactor.lifecycleRange,\n    )\n\n  /**\n   * Bind a list of workers (ie. a manager or any other class that needs an interactor's lifecycle)\n   * to an interactor's lifecycle events. Use this class into your interactor and call this method\n   * on attach.\n   *\n   * @param interactor The interactor that provides the lifecycle.\n   * @param workers A list of classes that want to be informed when to start and stop doing work.\n   */\n  @JvmStatic\n  public fun bind(\n    interactor: Interactor<*, *>,\n    workers: List<Worker>,\n  ) {\n    bind(interactor, workers as Iterable<Worker>)\n  }\n\n  /**\n   * Bind workers (i.e. a manager or any other class that needs an interactor lifecycle) to an\n   * interactor lifecycle events. Use this class into your interactor and call this method on\n   * attach.\n   *\n   * @param interactor The interactor that provides the lifecycle.\n   * @param workers A list of classes that want to be informed when to start and stop doing work.\n   */\n  @JvmStatic\n  public fun bind(\n    interactor: Interactor<*, *>,\n    workers: Iterable<Worker>,\n  ) {\n    for (interactorWorker in workers) {\n      bind(interactor, interactorWorker)\n    }\n  }\n\n  /**\n   * Bind a worker (ie. a manager or any other class that needs an presenter's lifecycle) to an\n   * presenter's lifecycle events. Inject this class into your presenter and call this method on any\n   *\n   * @param presenter The presenter that provides the lifecycle.\n   * @param worker The class that wants to be informed when to start and stop doing work.\n   * @return [WorkerUnbinder] to unbind [Worker]'s lifecycle.\n   */\n  @JvmStatic\n  public fun bind(\n    presenter: Presenter,\n    worker: Worker,\n  ): WorkerUnbinder =\n    worker.bind(\n      presenter.lifecycleFlow,\n      Presenter.lifecycleRange,\n    )\n\n  /**\n   * Bind a list of workers (ie. a manager or any other class that needs an presenter's lifecycle)\n   * to an presenter's lifecycle events. Use this class into your presenter and call this method on\n   * attach.\n   *\n   * @param presenter The presenter that provides the lifecycle.\n   * @param workers A list of classes that want to be informed when to start and stop doing work.\n   */\n  @JvmStatic\n  public fun bind(\n    presenter: Presenter,\n    workers: List<Worker>,\n  ) {\n    bind(presenter, workers as Iterable<Worker>)\n  }\n\n  /**\n   * Bind a list of workers (i.e. a manager or any other class that needs a presenter lifecycle) to\n   * a presenter lifecycle events. Use this class into your presenter and call this method on\n   * attach.\n   *\n   * @param presenter The presenter that provides the lifecycle.\n   * @param workers A list of classes that want to be informed when to start and stop doing work.\n   */\n  @JvmStatic\n  public fun bind(\n    presenter: Presenter,\n    workers: Iterable<Worker>,\n  ) {\n    for (worker in workers) {\n      bind(presenter, worker)\n    }\n  }\n\n  @OptIn(CoreFriendModuleApi::class)\n  @JvmStatic\n  @VisibleForTesting\n  @Deprecated(\n    message =\n      \"\"\"\n      This method doesn't support binding on the [CoroutineContext] defined at Worker/WorkerBinder. Due to this, the binding\n      will happen on the caller thread without a possibility to change the threading\n      It also doesn't provide information for [WorkerBinderInfo] when a [WorkerBinderListener] is added\n    \"\"\",\n    replaceWith = ReplaceWith(\"bind(interactor, worker) or bind(presenter, worker)\"),\n  )\n  public fun bind(mappedLifecycle: Observable<WorkerEvent>, worker: Worker): WorkerUnbinder {\n    val disposable =\n      mappedLifecycle\n        .takeWhile { it != WorkerEvent.STOP }\n        .doFinally { worker.onStop() }\n        .subscribe { worker.onStart(WorkerScopeProvider(mappedLifecycle)) }\n    return WorkerUnbinder(disposable::dispose)\n  }\n\n  @JvmStatic\n  public fun mapInteractorLifecycleToWorker(\n    interactorEventObservable: Observable<InteractorEvent>,\n  ): Observable<WorkerEvent> {\n    return interactorEventObservable.map { interactorEvent: InteractorEvent ->\n      when (interactorEvent) {\n        InteractorEvent.ACTIVE -> return@map WorkerEvent.START\n        else -> return@map WorkerEvent.STOP\n      }\n    }\n  }\n\n  @JvmStatic\n  public fun mapPresenterLifecycleToWorker(\n    presenterEventObservable: Observable<PresenterEvent>,\n  ): Observable<WorkerEvent> {\n    return presenterEventObservable.map { presenterEvent: PresenterEvent ->\n      when (presenterEvent) {\n        PresenterEvent.LOADED -> return@map WorkerEvent.START\n        else -> return@map WorkerEvent.STOP\n      }\n    }\n  }\n\n  /**\n   * Bind a worker (ie. a manager or any other class that needs an interactor's lifecycle) to an\n   * interactor's lifecycle events.\n   *\n   * @param lifecycle The interactor's [LifecycleScopeProvider].\n   * @param worker The class that wants to be informed when to start and stop doing work.\n   */\n  @JvmStatic\n  @Deprecated(\n    \"\"\"this method uses {@code LifecycleScopeProvider} for purposes other than\n        AutoDispose. Usage is strongly discouraged as this method may be removed in the future.\"\"\",\n  )\n  public fun bindTo(\n    lifecycle: LifecycleScopeProvider<InteractorEvent>,\n    worker: Worker,\n  ) {\n    bind(mapInteractorLifecycleToWorker(lifecycle.lifecycle()), worker)\n  }\n\n  /**\n   * Bind a worker to a [WorkerEvent] lifecycle provider.\n   *\n   * @param workerLifecycle the worker lifecycle event provider\n   * @param worker the class that wants to be informed when to start and stop doing work\n   */\n  @OptIn(CoreFriendModuleApi::class)\n  @JvmStatic\n  @Deprecated(\n    message =\n      \"\"\"\n      This method is unsafe because it assumes that the 'workerLifecycle' Observable completes after it\n      emits 'WorkerEvent.STOP'. If it does not complete, the subscription will leak.\n    \"\"\",\n    replaceWith = ReplaceWith(\"bind(workerLifecycle, worker)\"),\n  )\n  public fun bindToWorkerLifecycle(\n    workerLifecycle: Observable<WorkerEvent>,\n    worker: Worker,\n  ) {\n    workerLifecycle.subscribe { workerEvent: WorkerEvent ->\n      when (workerEvent) {\n        WorkerEvent.START -> worker.onStart(WorkerScopeProvider(workerLifecycle.hide()))\n        else -> worker.onStop()\n      }\n    }\n  }\n}\n\nprivate fun getJobCoroutineContext(\n  dispatcherAtBinder: CoroutineDispatcher,\n  worker: Worker,\n): CoroutineContext {\n  val workerCoroutineContext = worker.bindingCoroutineContext\n  return if (workerCoroutineContext != EmptyCoroutineContext) {\n    workerCoroutineContext\n  } else {\n    dispatcherAtBinder\n  }\n}\n\nprivate fun <T : Comparable<T>> Worker.bind(\n  lifecycle: Flow<T>,\n  lifecycleRange: ClosedRange<T>,\n): WorkerUnbinder {\n  val dispatcherAtBinder = RibCoroutinesConfig.deprecatedWorkerDispatcher\n  val coroutineContext =\n    getJobCoroutineContext(\n      dispatcherAtBinder,\n      worker = this,\n    )\n  val coroutineStart =\n    if (coroutineContext == RibDispatchers.Unconfined) {\n      CoroutineStart.UNDISPATCHED\n    } else {\n      CoroutineStart.DEFAULT\n    }\n\n  val completable = CompletableSubject.create()\n  val scopeProvider = ScopeProvider { completable }\n  val workerScopeProvider = WorkerScopeProvider(scopeProvider)\n\n  /*\n   * We need `Dispatchers.Unconfined` to react immediately to lifecycle flow emissions, and we need\n   * `CoroutineStart.Undispatched` to prevent coroutines launched in `onStart` with `Dispatchers.Unconfined`\n   * from forming an event loop instead of starting eagerly.\n   *\n   * GlobalScope won't leak the job, because the flow completes when lifecycle completes.\n   */\n  @OptIn(DelicateCoroutinesApi::class)\n  val job =\n    GlobalScope.launch(\n      coroutineContext,\n      start = coroutineStart,\n    ) {\n      lifecycle\n        .takeWhile { it < lifecycleRange.endInclusive }\n        .onCompletion {\n          triggerRibActionAndEmitEvents(\n            this@bind,\n            RibActionEmitterType.DEPRECATED_WORKER,\n            RibEventType.DETACHED,\n          ) {\n            onStop()\n          }\n\n          completable.onComplete()\n        }\n        .collect {\n          triggerRibActionAndEmitEvents(\n            this@bind,\n            RibActionEmitterType.DEPRECATED_WORKER,\n            RibEventType.ATTACHED,\n          ) {\n            onStart(workerScopeProvider)\n          }\n        }\n    }\n  return WorkerUnbinder(job::cancel)\n}\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/WorkerScopeProvider.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.uber.autodispose.ScopeProvider\nimport com.uber.rib.core.internal.CoreFriendModuleApi\nimport com.uber.rib.core.lifecycle.WorkerEvent\nimport io.reactivex.CompletableSource\nimport io.reactivex.Observable\n\n/** [ScopeProvider] for [Worker] instances. */\npublic open class WorkerScopeProvider internal constructor(delegate: ScopeProvider) :\n  ScopeProvider by delegate {\n  @CoreFriendModuleApi\n  public constructor(lifecycle: Observable<WorkerEvent>) : this(lifecycle.asScopeProvider())\n}\n\nprivate fun Observable<*>.asScopeProvider() = asCompletableSource().asScopeProvider()\n\nprivate fun Observable<*>.asCompletableSource() = skip(1).firstElement().ignoreElement()\n\nprivate fun CompletableSource.asScopeProvider() = ScopeProvider { this }\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/WorkerUnbinder.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/**\n * API for unbinding a [Worker] before currently bound lifecycle has ended. Use this if you need to\n * stop your [Worker] before the [Interactor] becomes inactive for example.\n */\n@Deprecated(\n  message =\n    \"\"\"\n      com.uber.rib.core.Worker is deprecated in favor of com.uber.rib.core.RibCoroutineWorker\n    \"\"\",\n)\npublic fun interface WorkerUnbinder {\n  /** Unbind from bound lifecycle and end worker's lifecycle. */\n  public fun unbind()\n}\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/internal/CoreFriendModuleApi.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core.internal\n\n/*\n * Methods that are visible only to rib-base friend modules.\n *\n * Anything marked with this annotation is not intended for public use.\n */\n@RequiresOptIn(level = RequiresOptIn.Level.ERROR)\n@Retention(AnnotationRetention.BINARY)\ninternal annotation class CoreFriendModuleApi\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/lifecycle/InteractorEvent.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core.lifecycle\n\n/** Controller lifecycle events. */\npublic enum class InteractorEvent {\n  ACTIVE,\n  INACTIVE,\n}\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/lifecycle/PresenterEvent.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core.lifecycle\n\n/** Presenter lifecycle events. */\npublic enum class PresenterEvent {\n  LOADED,\n  UNLOADED,\n}\n"
  },
  {
    "path": "libraries/rib-base/src/main/kotlin/com/uber/rib/core/lifecycle/WorkerEvent.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core.lifecycle\n\n/**\n * Enum used represent when a [Worker][com.uber.rib.core.Worker] should stop and start in response\n * to interactor lifecycle events.\n */\npublic enum class WorkerEvent {\n  START,\n  STOP,\n}\n"
  },
  {
    "path": "libraries/rib-base/src/test/kotlin/android/os/Bundle.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage android.os\n\n/** Stub class to have pure Java unit tests. */\nclass Bundle : Parcelable {\n  private val testData: MutableMap<String, Any> = mutableMapOf()\n\n  fun getString(key: String): String? {\n    return testData[key] as String?\n  }\n\n  fun <T : Parcelable?> getParcelable(key: String): T? {\n    return testData[key] as T?\n  }\n\n  fun putParcelable(key: String, value: Parcelable) {\n    testData[key] = value\n  }\n\n  fun putString(key: String, value: String) {\n    testData[key] = value\n  }\n}\n"
  },
  {
    "path": "libraries/rib-base/src/test/kotlin/android/os/Parcelable.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage android.os\n\n/** Stub class to have pure Java unit tests. */\ninterface Parcelable\n"
  },
  {
    "path": "libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.google.common.truth.Truth\nimport com.google.common.truth.Truth.assertThat\nimport com.uber.autodispose.lifecycle.LifecycleEndedException\nimport com.uber.autodispose.lifecycle.LifecycleNotStartedException\nimport com.uber.rib.core.RibEvents.ribActionEvents\nimport com.uber.rib.core.RibEventsUtils.assertRibActionInfo\nimport com.uber.rib.core.RibRefWatcher.Companion.getInstance\nimport com.uber.rib.core.lifecycle.InteractorEvent\nimport io.reactivex.Completable\nimport io.reactivex.observers.TestObserver\nimport kotlinx.coroutines.test.runTest\nimport kotlinx.coroutines.withTimeoutOrNull\nimport org.junit.Assert.fail\nimport org.junit.Before\nimport org.junit.Test\nimport org.mockito.kotlin.any\nimport org.mockito.kotlin.doReturn\nimport org.mockito.kotlin.eq\nimport org.mockito.kotlin.mock\nimport org.mockito.kotlin.never\nimport org.mockito.kotlin.times\nimport org.mockito.kotlin.verify\n\nclass InteractorAndRouterTest {\n\n  private val childInteractor: Interactor<*, *> = mock()\n  private val ribRefWatcher: RibRefWatcher = mock()\n\n  private lateinit var interactor: TestInteractor\n  private lateinit var router: TestRouter\n  private val ribActionInfoObserver = TestObserver<RibActionInfo>()\n\n  @Before\n  fun setup() {\n    val presenter: TestPresenter = mock()\n    val component: InteractorComponent<TestPresenter, TestInteractor> = mock {\n      on { presenter() } doReturn (presenter)\n    }\n    interactor = TestInteractor(childInteractor)\n    router = TestRouter(interactor, component)\n    RibEvents.enableRibActionEmissions()\n  }\n\n  @Test\n  fun attach_shouldAttachChildController() {\n    // Given.\n    ribActionEvents.subscribe(ribActionInfoObserver)\n\n    // When.\n    router.dispatchAttach(null)\n\n    // Then.\n    val ribActionInfoValues = ribActionInfoObserver.values()\n    ribActionInfoValues\n      .last()\n      .assertRibActionInfo(\n        RibEventType.ATTACHED,\n        RibActionEmitterType.INTERACTOR,\n        RibActionState.COMPLETED,\n        \"com.uber.rib.core.InteractorAndRouterTest${'$'}TestInteractor\",\n      )\n    verify(childInteractor).dispatchAttach(null)\n  }\n\n  @Test\n  fun attach_withoutAllowingEmissions_shouldNotEmtRibActionEvents() {\n    // Given.\n    RibEvents.areRibActionEmissionsAllowed = false\n    ribActionEvents.subscribe(ribActionInfoObserver)\n\n    // When.\n    router.dispatchAttach(null)\n\n    // Then.\n    assertThat(ribActionInfoObserver.values()).isEmpty()\n  }\n\n  @Test\n  fun detach_shouldDetachChildController() {\n    // Given.\n    ribActionEvents.subscribe(ribActionInfoObserver)\n    router.dispatchAttach(null)\n\n    // When.\n    router.dispatchDetach()\n\n    // Then.\n    val ribActionInfoValues = ribActionInfoObserver.values()\n    ribActionInfoValues\n      .last()\n      .assertRibActionInfo(\n        RibEventType.DETACHED,\n        RibActionEmitterType.ROUTER,\n        RibActionState.COMPLETED,\n        \"com.uber.rib.core.FakeRouter\",\n      )\n    verify(childInteractor).dispatchDetach()\n  }\n\n  @Test\n  @Throws(Exception::class)\n  fun correspondingEvents_whenActive_shouldReturnInactive() {\n    Truth.assertThat(interactor.correspondingEvents().apply(InteractorEvent.ACTIVE))\n      .isEqualTo(InteractorEvent.INACTIVE)\n  }\n\n  @Test(expected = LifecycleEndedException::class)\n  @Throws(Exception::class)\n  fun correspondingEvents_whenInactive_shouldCrash() {\n    interactor.correspondingEvents().apply(InteractorEvent.INACTIVE)\n  }\n\n  @Test\n  fun saveInstanceState_whenDetached_shouldNotSaveChildControllerState() {\n    // When.\n    val outState: Bundle = mock()\n    interactor.onSaveInstanceStateInternal(outState)\n\n    // Then.\n    verify(childInteractor, times(0)).onSaveInstanceStateInternal(outState)\n  }\n\n  @Test\n  fun childControllers_shouldHaveRightLifecycle() {\n    val parentInteractor = TestInteractorA()\n    val component: InteractorComponent<TestPresenter, TestInteractorA> =\n      object : InteractorComponent<TestPresenter, TestInteractorA> {\n        override fun inject(interactor: TestInteractorA) {}\n        override fun presenter(): TestPresenter {\n          return TestPresenter()\n        }\n      }\n    val router = TestRouterA(parentInteractor, component)\n    val parentObserver = RecordingObserver<InteractorEvent>()\n    parentInteractor.lifecycle().subscribe(parentObserver)\n    router.dispatchAttach(null)\n    Truth.assertThat(parentObserver.takeNext()).isEqualTo(InteractorEvent.ACTIVE)\n    val childA = TestChildInteractor()\n    val childComponent: InteractorComponent<TestPresenter, TestChildInteractor> =\n      object : InteractorComponent<TestPresenter, TestChildInteractor> {\n        override fun inject(interactor: TestChildInteractor) {}\n        override fun presenter(): TestPresenter {\n          return TestPresenter()\n        }\n      }\n    val childRouter = TestChildRouter(childA, childComponent)\n    val childObserverA = RecordingObserver<InteractorEvent>()\n    childA.lifecycle().subscribe(childObserverA)\n    router.attachChild(childRouter)\n    Truth.assertThat(childObserverA.takeNext()).isEqualTo(InteractorEvent.ACTIVE)\n    val childB = TestChildInteractor()\n    val childObserverB = RecordingObserver<InteractorEvent>()\n    childB.lifecycle().subscribe(childObserverB)\n    val childBRouter = TestChildRouter(childB, childComponent)\n    childRouter.attachChild(childBRouter)\n    Truth.assertThat(childObserverB.takeNext()).isEqualTo(InteractorEvent.ACTIVE)\n    router.dispatchDetach()\n    Truth.assertThat(parentObserver.takeNext()).isEqualTo(InteractorEvent.INACTIVE)\n    Truth.assertThat(childObserverA.takeNext()).isEqualTo(InteractorEvent.INACTIVE)\n    Truth.assertThat(childObserverB.takeNext()).isEqualTo(InteractorEvent.INACTIVE)\n  }\n\n  @Test\n  fun detachChild_whenOneChild_shouldWatchOneDeletedInteractor() {\n    val rootInteractor = TestInteractorB()\n    val component: InteractorComponent<TestPresenter, TestInteractorB> =\n      object : InteractorComponent<TestPresenter, TestInteractorB> {\n        override fun inject(interactor: TestInteractorB) {}\n        override fun presenter(): TestPresenter {\n          return TestPresenter()\n        }\n      }\n    val router = TestRouterB(component, rootInteractor, ribRefWatcher)\n    router.dispatchAttach(null)\n    val childInteractor = TestInteractorB()\n    val childRouter = TestRouterB(childInteractor, component)\n    router.attachChild(childRouter)\n    verify(ribRefWatcher, never()).watchDeletedObject(any())\n\n    // Action: Detach the child interactor.\n    router.detachChild(childRouter)\n\n    // Verify: the reference watcher observes the detached interactor and child.\n    verify(ribRefWatcher).watchDeletedObject(eq(childInteractor))\n  }\n\n  @Test\n  fun detachChild_whenTwoNestedChildren_shouldWatchTwoNestedDeletions() {\n    val component: InteractorComponent<TestPresenter, TestInteractorB> =\n      object : InteractorComponent<TestPresenter, TestInteractorB> {\n        override fun inject(interactor: TestInteractorB) {}\n        override fun presenter(): TestPresenter {\n          return TestPresenter()\n        }\n      }\n    val rootRouter = TestRouterB(component, TestInteractorB(), ribRefWatcher)\n    val child = addTwoNestedChildInteractors()\n    verify(ribRefWatcher, never()).watchDeletedObject(any())\n\n    // Action: Detach all child interactors.\n    rootRouter.detachChild(child)\n\n    // Verify: called four times. Twice for each interactor.\n    verify(ribRefWatcher, times(2)).watchDeletedObject(any())\n  }\n\n  @Test\n  fun init_useStateFlow_eventIsNotFired() = runTest {\n    // Given\n    setupInteractorForStateFlow()\n    var eventCount = 0\n    val lifecycleFlow = interactor.lifecycleFlow\n\n    // When\n    withTimeoutOrNull(10) { lifecycleFlow.collect { eventCount++ } }\n\n    // Then\n    assertThat(eventCount).isEqualTo(0)\n  }\n\n  @Test\n  fun init_eventIsNotFired() = runTest {\n    // When\n    var eventCount = 0\n    val lifecycleFlow = interactor.lifecycleFlow\n\n    // When\n    withTimeoutOrNull(10) { lifecycleFlow.collect { eventCount++ } }\n\n    // Then\n    assertThat(eventCount).isEqualTo(0)\n  }\n\n  @Test\n  fun requestScope_initAndUseStateFlow_throwsAnError() {\n    // Given\n    setupInteractorForStateFlow()\n\n    // When\n    try {\n      interactor.requestScope()\n      fail(\"Expected LifecycleEndedException\")\n    } catch (e: LifecycleNotStartedException) {\n      // Then\n      // Expected exception, do nothing\n    }\n  }\n\n  @Test\n  fun requestScope_initWith_throwsAnError() {\n    // When\n    try {\n      interactor.requestScope()\n      fail(\"Expected LifecycleEndedException\")\n    } catch (e: LifecycleNotStartedException) {\n      // Then\n      // Expected exception, do nothing\n    }\n  }\n\n  @Test\n  fun requestScope_useStateFlowAndAttached_requestScopeIsNotCompleted() {\n    // Given\n    setupInteractorForStateFlow()\n    interactor.dispatchAttach(null)\n\n    // When\n    Completable.wrap(interactor.requestScope())\n      .test()\n      // Then\n      .assertNotComplete()\n  }\n\n  @Test\n  fun requestScope_attached_requestScopeIsNotCompleted() {\n    // Given\n    interactor.dispatchAttach(null)\n\n    // When\n    Completable.wrap(interactor.requestScope())\n      .test()\n      // Then\n      .assertNotComplete()\n  }\n\n  @Test\n  fun requestScope_useStateFlowAndDetached_requestScopeIsCompleted() {\n    // Given\n    setupInteractorForStateFlow()\n    interactor.dispatchAttach(null)\n    val requestScope = Completable.wrap(interactor.requestScope())\n    interactor.dispatchDetach()\n\n    // When\n    requestScope\n      .test()\n      // Then\n      .assertComplete()\n  }\n\n  @Test\n  fun requestScope_detached_requestScopeIsCompleted() {\n    // Given\n    interactor.dispatchAttach(null)\n    val requestScope = Completable.wrap(interactor.requestScope())\n    interactor.dispatchDetach()\n\n    // When\n    requestScope\n      .test()\n      // Then\n      .assertComplete()\n  }\n\n  private fun setupInteractorForStateFlow() {\n    RibEvents.useStateFlowInteractorEvent()\n    val presenter: TestPresenter = mock()\n    val component: InteractorComponent<TestPresenter, TestInteractor> = mock {\n      on { presenter() } doReturn (presenter)\n    }\n    interactor = TestInteractor(childInteractor)\n    router = TestRouter(interactor, component)\n  }\n\n  private fun addTwoNestedChildInteractors(): Router<TestInteractorB> {\n    val component: InteractorComponent<TestPresenter, TestInteractorB> =\n      object : InteractorComponent<TestPresenter, TestInteractorB> {\n        override fun inject(interactor: TestInteractorB) {}\n        override fun presenter(): TestPresenter {\n          return TestPresenter()\n        }\n      }\n    router.dispatchAttach(null)\n    val childRouter1 = TestRouterB(component, TestInteractorB(), ribRefWatcher)\n    val childRouter2 = TestRouterB(component, TestInteractorB(), ribRefWatcher)\n    router.attachChild(childRouter1)\n    childRouter1.attachChild(childRouter2)\n    return childRouter1\n  }\n\n  private class TestInteractor(private val mChildInteractor: Interactor<*, *>) :\n    Interactor<TestPresenter, Router<TestInteractor>>() {\n    val mockedWorker: Worker = mock()\n    override fun didBecomeActive(savedInstanceState: Bundle?) {\n      super.didBecomeActive(savedInstanceState)\n      val router: Router<*> = FakeRouter(mChildInteractor, getInstance(), Thread.currentThread())\n      WorkerBinder.bind(this, mockedWorker)\n      this.router.attachChild(router)\n    }\n\n    override fun onSaveInstanceState(outState: Bundle) {\n      super.onSaveInstanceState(outState)\n      outState.putString(TEST_KEY, TEST_VALUE)\n    }\n  }\n\n  private class TestRouter(\n    interactor: TestInteractor,\n    component: InteractorComponent<TestPresenter, TestInteractor>,\n  ) : Router<TestInteractor>(component, interactor, getInstance(), Thread.currentThread()) {\n    init {\n      interactor.setPresenter(component.presenter())\n    }\n  }\n\n  private open class TestPresenter : Presenter()\n\n  private class TestRouterA(\n    interactor: TestInteractorA,\n    component: InteractorComponent<TestPresenter, TestInteractorA>,\n  ) : Router<TestInteractorA>(component, interactor, getInstance(), Thread.currentThread()) {\n    private var savedInstanceState: Bundle? = null\n    override fun dispatchAttach(savedInstanceState: Bundle?, tag: String) {\n      super.dispatchAttach(savedInstanceState, tag)\n      this.savedInstanceState = savedInstanceState\n    }\n\n    init {\n      interactor.setPresenter(component.presenter())\n    }\n  }\n\n  private class TestInteractorA : Interactor<TestPresenter, Router<TestInteractorA>>()\n  private class TestInteractorB : Interactor<TestPresenter, Router<TestInteractorB>>()\n  private class TestRouterB : Router<TestInteractorB> {\n    constructor(\n      interactor: TestInteractorB,\n      component: InteractorComponent<TestPresenter, TestInteractorB>,\n    ) : super(component, interactor, getInstance(), Thread.currentThread()) {\n      interactor.setPresenter(component.presenter())\n    }\n\n    constructor(\n      component: InteractorComponent<TestPresenter, TestInteractorB>,\n      interactor: TestInteractorB,\n      ribRefWatcher: RibRefWatcher,\n    ) : super(component, interactor, ribRefWatcher, Thread.currentThread()) {\n      interactor.setPresenter(component.presenter())\n    }\n  }\n\n  private class TestChildInteractor : Interactor<TestPresenter, Router<TestChildInteractor>>()\n  private class TestChildRouter(\n    interactor: TestChildInteractor,\n    component: InteractorComponent<TestPresenter, TestChildInteractor>,\n  ) : Router<TestChildInteractor>(component, interactor, getInstance(), Thread.currentThread()) {\n    init {\n      interactor.setPresenter(component.presenter())\n    }\n  }\n\n  companion object {\n    private const val TEST_KEY = \"test_key\"\n    private const val TEST_VALUE = \"test_value\"\n  }\n}\n"
  },
  {
    "path": "libraries/rib-base/src/test/kotlin/com/uber/rib/core/LazyBackingPropertyTest.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.google.common.truth.Truth.assertThat\nimport com.uber.rib.core.internal.CoreFriendModuleApi\nimport io.reactivex.Observable\nimport kotlinx.coroutines.channels.produce\nimport kotlinx.coroutines.channels.toList\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.test.runTest\nimport org.junit.Test\nimport org.mockito.kotlin.mock\n\nclass LazyBackingPropertyTest {\n  @Volatile private var _expensiveObject: ExpensiveObject? = null\n\n  @OptIn(CoreFriendModuleApi::class)\n  private val expensiveObject\n    get() = ::_expensiveObject.setIfNullAndGet { ExpensiveObject() }\n\n  @Test\n  fun `Stress test fetching a LazyBackingProperty from multiple concurrent coroutines`() = runTest {\n    val set =\n      produce(RibDispatchers.IO) { repeat(10_000) { launch { send(expensiveObject) } } }\n        .toList()\n        .toSet()\n    assertThat(set).hasSize(1)\n  }\n\n  @Test\n  fun `Value is preserved on mocked class`() {\n    val instance = mock<ClassToBeMocked>()\n    instance.prop.test().assertValues(1, 2, 3)\n  }\n}\n\nprivate open class ClassToBeMocked {\n  @Volatile private var _prop: Observable<Int>? = null\n\n  @OptIn(CoreFriendModuleApi::class)\n  val prop: Observable<Int>\n    get() = ::_prop.setIfNullAndGet { Observable.just(1, 2, 3) }\n}\n\nprivate class ExpensiveObject {\n  init {\n    Thread.sleep(100) // Simulate expensive object to make concurrent access more frequent\n  }\n}\n"
  },
  {
    "path": "libraries/rib-base/src/test/kotlin/com/uber/rib/core/RecordingObserver.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.google.common.truth.Truth\nimport io.reactivex.Observer\nimport io.reactivex.disposables.Disposable\nimport java.util.NoSuchElementException\nimport java.util.concurrent.BlockingDeque\nimport java.util.concurrent.LinkedBlockingDeque\nimport java.util.concurrent.TimeUnit\n\n/**\n * RecordingObserver implementation from RxBinding.\n *\n * @param <T> Parametrized type.\n */\ninternal class RecordingObserver<T : Any> : Observer<T> {\n  private val events: BlockingDeque<Any> = LinkedBlockingDeque()\n\n  override fun onSubscribe(disposable: Disposable) {}\n\n  override fun onComplete() {\n    events.addLast(OnCompleted())\n  }\n\n  override fun onError(e: Throwable) {\n    events.addLast(OnError(e))\n  }\n\n  override fun onNext(t: T) {\n    events.addLast(OnNext(t))\n  }\n\n  fun takeNext(): T {\n    val event: OnNext = takeEvent(OnNext::class.java)\n    return event.value as T\n  }\n\n  private fun <E> takeEvent(wanted: Class<E>): E {\n    val event: Any? =\n      try {\n        events.pollFirst(1, TimeUnit.SECONDS)\n      } catch (e: InterruptedException) {\n        throw RuntimeException(e)\n      }\n    if (event == null) {\n      throw NoSuchElementException(\"No event found while waiting for \" + wanted.simpleName)\n    }\n    Truth.assertThat(event).isInstanceOf(wanted)\n    return event as E\n  }\n\n  private class OnNext(val value: Any) {\n    override fun toString() = \"OnNext[$value]\"\n  }\n\n  private class OnCompleted {\n    override fun toString() = \"OnCompleted\"\n  }\n\n  private class OnError(private val throwable: Throwable) {\n    override fun toString() = \"OnError[$throwable]\"\n  }\n}\n"
  },
  {
    "path": "libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibCoroutineWorkerTest.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.google.common.truth.Truth.assertThat\nimport com.jakewharton.rxrelay2.BehaviorRelay\nimport com.uber.autodispose.coroutinesinterop.autoDispose\nimport com.uber.rib.core.WorkerBinder.mapInteractorLifecycleToWorker\nimport com.uber.rib.core.lifecycle.InteractorEvent\nimport io.reactivex.subjects.PublishSubject\nimport kotlin.coroutines.CoroutineContext\nimport kotlin.coroutines.EmptyCoroutineContext\nimport kotlinx.coroutines.CancellationException\nimport kotlinx.coroutines.CoroutineDispatcher\nimport kotlinx.coroutines.CoroutineExceptionHandler\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Delay\nimport kotlinx.coroutines.DelicateCoroutinesApi\nimport kotlinx.coroutines.ExperimentalCoroutinesApi\nimport kotlinx.coroutines.InternalCoroutinesApi\nimport kotlinx.coroutines.Runnable\nimport kotlinx.coroutines.awaitCancellation\nimport kotlinx.coroutines.cancel\nimport kotlinx.coroutines.currentCoroutineContext\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.invoke\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.newSingleThreadContext\nimport kotlinx.coroutines.supervisorScope\nimport kotlinx.coroutines.test.StandardTestDispatcher\nimport kotlinx.coroutines.test.TestCoroutineScheduler\nimport kotlinx.coroutines.test.TestDispatcher\nimport kotlinx.coroutines.test.UnconfinedTestDispatcher\nimport kotlinx.coroutines.test.advanceTimeBy\nimport kotlinx.coroutines.test.advanceUntilIdle\nimport kotlinx.coroutines.test.runCurrent\nimport kotlinx.coroutines.test.runTest\nimport kotlinx.coroutines.withContext\nimport org.junit.Rule\nimport org.junit.Test\nimport org.mockito.kotlin.mock\n\nprivate const val ON_START_DELAY_DURATION_MILLIS = 100L\nprivate const val INNER_COROUTINE_DELAY_DURATION_MILLIS = 200L\n\n@OptIn(ExperimentalCoroutinesApi::class)\nclass RibCoroutineWorkerTest {\n  @get:Rule val coroutineRule = RibCoroutinesRule()\n  private val worker = TestWorker()\n\n  @Test\n  fun bindWorkHandle_onJoin_thenJoinsBindingOperation() = runTest {\n    val handle = bind(worker, UnconfinedTestDispatcher(testScheduler))\n    assertThat(worker.onStartStarted).isTrue()\n    assertThat(worker.onStartFinished).isFalse()\n    handle.join()\n    assertThat(worker.onStartFinished).isTrue()\n    handle.unbind()\n  }\n\n  @Test\n  fun bind_onErrorOnStartAndOnStop_propagatesExceptionWithSuppressed() = runTest {\n    var throwable: Throwable? = null\n    val ceh = CoroutineExceptionHandler { _, t -> throwable = t }\n    withContext(ceh) {\n      supervisorScope {\n        val onStartErrorMsg = \"onStart failure\"\n        val onStopErrorMsg = \"onStop failure\"\n        worker.doOnStart { error(onStartErrorMsg) }\n        worker.doOnStop { error(onStopErrorMsg) }\n        bind(worker).join()\n        assertThat(throwable).isInstanceOf(IllegalStateException::class.java)\n        assertThat(throwable).hasMessageThat().isEqualTo(onStartErrorMsg)\n        val suppressed = throwable?.suppressed?.firstOrNull()\n        assertThat(suppressed).isInstanceOf(IllegalStateException::class.java)\n        assertThat(suppressed).hasMessageThat().isEqualTo(onStopErrorMsg)\n        assertThat(worker.onStartFinished).isTrue()\n        assertThat(worker.onStopRan).isTrue()\n      }\n    }\n  }\n\n  @Test(expected = IllegalStateException::class)\n  fun bindWorkHandle_onError_thenPropagatesException() = runTest {\n    worker.doOnStart { error(\"onStart failure\") }\n    bind(worker)\n  }\n\n  @Test\n  fun unbindHandler_onJoin_thenJoinsUnbindOperation() = runTest {\n    val bindHandle = bind(worker)\n    bindHandle.join()\n    val unbindHandle = bindHandle.unbind()\n    assertThat(worker.onStopRan).isFalse()\n    unbindHandle.join()\n    assertThat(worker.onStopRan).isTrue()\n    assertThat(worker.onStopCause).isInstanceOf(CancellationException::class.java)\n    assertThat(worker.onStopCause).hasMessageThat().isEqualTo(\"Worker was manually unbound.\")\n  }\n\n  @Test\n  fun onScopeCancelledAfterBinding_workerIsUnboundAutomatically() = runTest {\n    val cancellationMsg = \"Scope cancelled\"\n    launch {\n      bind(worker, EmptyCoroutineContext).join()\n      cancel(cancellationMsg)\n    }\n    advanceUntilIdle()\n    assertThat(worker.onStopRan).isTrue()\n    assertThat(worker.onStopCause).isInstanceOf(CancellationException::class.java)\n    assertThat(worker.onStopCause).hasMessageThat().isEqualTo(cancellationMsg)\n  }\n\n  @Test\n  fun onScopeCancelledBeforeBinding_parentScopeCanCompleteNormally() = runTest {\n    launch {\n      bind(worker)\n      // bind was called, but coroutine that runs onStart is not started yet.\n      cancel(\"Scope cancelling\")\n    }\n    // if test fails to finish, there are unfinished jobs.\n  }\n\n  @Test\n  fun onBindingJobCancelledBeforeBinding_parentScopeCanCompleteNormally() = runTest {\n    launch { bind(worker).cancel(\"Cancelling bind work\") }\n    // if test fails to finish, there are unfinished jobs.\n  }\n\n  @Test\n  fun onScopeCancelledOnWorkerOnStart_parentScopeCanCompleteNormally() = runTest {\n    val cancellationMsg = \"Cancelling on onStart\"\n    worker.doOnStart { currentCoroutineContext().cancel(CancellationException(cancellationMsg)) }\n    bind(worker).join()\n  }\n\n  @OptIn(DelicateCoroutinesApi::class)\n  @Test\n  fun onBindingWithCustomDispatcher_dispatchesToCustomDispatcher() = runTest {\n    val callingContext = newSingleThreadContext(\"Calling context\")\n    val executionContext = newSingleThreadContext(\"Execution context\")\n    executionContext.use { execCtx ->\n      callingContext.use { callingCtx ->\n        withContext(callingCtx) {\n          val handle = bind(worker, execCtx)\n          handle.join()\n          assertThat(worker.onStartThread!!.name).startsWith(\"Execution context\")\n          handle.unbind().join()\n          assertThat(worker.onStopThread!!.name).startsWith(\"Execution context\")\n        }\n      }\n    }\n  }\n\n  @Test\n  fun onBindingOnCorrectContext_doNotPayForDispatch() = runTest {\n    val dispatcher = ImmediateDispatcher(testScheduler)\n    dispatcher {\n      dispatcher.setThreadId()\n      assertThat(dispatcher.dispatchCount).isEqualTo(1)\n      val handle = bind(worker, EmptyCoroutineContext)\n      assertThat(dispatcher.dispatchCount).isEqualTo(1) // no new dispatch done\n      assertThat(worker.onStartStarted).isTrue() // started undispatched\n      assertThat(worker.onStartThread!!.id).isEqualTo(Thread.currentThread().id)\n      // run delay on onStart\n      advanceTimeBy(ON_START_DELAY_DURATION_MILLIS)\n      runCurrent()\n      assertThat(worker.onStartFinished).isTrue()\n      assertThat(dispatcher.dispatchCount).isEqualTo(1) // no new dispatch done\n      handle.unbind()\n      assertThat(worker.onStopThread!!.id).isEqualTo(Thread.currentThread().id)\n      assertThat(worker.onStopRan).isTrue()\n    }\n  }\n\n  @Test\n  fun asWorker_autoDisposeWithCoroutineScope_lateEmissionIsReceivedBySubscriber() = runTest {\n    val router = mock<Router<*>>()\n    val interactor = object : Interactor<Any, Router<*>>() {}\n    val subject = PublishSubject.create<Unit>()\n    var gotEmission = false\n    val ribCoroutineWorker = RibCoroutineWorker {\n      subject.autoDispose(this).subscribe { gotEmission = true }\n    }\n    val worker = ribCoroutineWorker.asWorker()\n    InteractorHelper.attach(interactor, Any(), router, null)\n    WorkerBinder.bind(interactor, worker)\n    runCurrent()\n    subject.onNext(Unit)\n    assertThat(gotEmission).isTrue()\n  }\n\n  @Test\n  fun asWorker_autoDisposeWithCoroutineScope_unbindingWorkerDisposesSubscription() = runTest {\n    val router = mock<Router<*>>()\n    val interactor = object : Interactor<Any, Router<*>>() {}\n    val subject = PublishSubject.create<Unit>()\n    var started = false\n    var disposed = false\n    val ribCoroutineWorker = RibCoroutineWorker { scope ->\n      started = true\n      subject.doOnDispose { disposed = true }.autoDispose(scope).subscribe()\n    }\n    val worker = ribCoroutineWorker.asWorker()\n    InteractorHelper.attach(interactor, Any(), router, null)\n    val unbinder = WorkerBinder.bind(interactor, worker)\n    runCurrent()\n    subject.onNext(Unit)\n    assertThat(started).isTrue()\n    assertThat(disposed).isFalse()\n    unbinder.unbind()\n    runCurrent()\n    assertThat(disposed).isTrue()\n  }\n\n  @Test\n  fun testHelperFunction() = runTest {\n    // Sanity - assert initial state.\n    assertThat(worker.onStartStarted).isFalse()\n    assertThat(worker.onStartFinished).isFalse()\n    assertThat(worker.innerCoroutineStarted).isFalse()\n    assertThat(worker.innerCoroutineIdle).isFalse()\n    assertThat(worker.innerCoroutineCompleted).isFalse()\n    assertThat(worker.onStopRan).isFalse()\n    test(worker) {\n      // Quick check that suspend functions can be called inside this block\n      delay(0)\n      // Assert onStart and inner coroutine started but have not finished (it has delays)\n      assertThat(it.onStartStarted).isTrue()\n      assertThat(it.innerCoroutineStarted).isTrue()\n      assertThat(it.onStartFinished).isFalse()\n      // Advance time so only onStart finishes\n      advanceTimeBy(ON_START_DELAY_DURATION_MILLIS)\n      runCurrent()\n      assertThat(it.onStartFinished).isTrue()\n      assertThat(it.innerCoroutineIdle).isFalse()\n      // Advance time so inner coroutine becomes idle (reaches awaitCancellation).\n      val remainingTime = INNER_COROUTINE_DELAY_DURATION_MILLIS - testScheduler.currentTime\n      advanceTimeBy(remainingTime)\n      runCurrent()\n      assertThat(it.innerCoroutineIdle).isTrue()\n      assertThat(it.innerCoroutineCompleted).isFalse()\n      // onStop should only be called after the lambda returns\n      assertThat(it.onStopRan).isFalse()\n    }\n    // Worker should be unbound at this point.\n    assertThat(worker.innerCoroutineCompleted).isTrue()\n    assertThat(worker.onStopRan).isTrue()\n    assertThat(worker.onStopCause).isInstanceOf(CancellationException::class.java)\n    assertThat(worker.onStopCause).hasMessageThat().isEqualTo(\"Worker was manually unbound.\")\n  }\n\n  @Test\n  fun testClassThatIsBothWorkerAndRibCoroutineWorker() = runTest {\n    var workerOnStartCalled = false\n    var workerOnStopCalled = false\n    var ribCoroutineWorkerOnStartCalled = false\n    var ribCoroutineWorkerOnStopCalled = false\n    val worker =\n      WorkerAndRibCoroutineWorker(\n        workerOnStart = { workerOnStartCalled = true },\n        workerOnStop = { workerOnStopCalled = true },\n        ribCoroutineWorkerOnStart = { ribCoroutineWorkerOnStartCalled = true },\n        ribCoroutineWorkerOnStop = { ribCoroutineWorkerOnStopCalled = true },\n      )\n    val lifecycle = BehaviorRelay.createDefault(InteractorEvent.ACTIVE)\n    val workerUnbinder =\n      WorkerBinder.bind(mapInteractorLifecycleToWorker(lifecycle), worker.asWorker())\n    assertThat(workerOnStartCalled).isTrue()\n    assertThat(workerOnStopCalled).isFalse()\n    assertThat(ribCoroutineWorkerOnStartCalled).isFalse()\n    assertThat(ribCoroutineWorkerOnStopCalled).isFalse()\n    workerUnbinder.unbind()\n    assertThat(workerOnStopCalled).isTrue()\n    assertThat(ribCoroutineWorkerOnStartCalled).isFalse()\n    assertThat(ribCoroutineWorkerOnStopCalled).isFalse()\n    workerOnStartCalled = false\n    workerOnStopCalled = false\n    val unbinder = bind(worker.asRibCoroutineWorker()).also { it.join() }\n    assertThat(workerOnStartCalled).isFalse()\n    assertThat(workerOnStopCalled).isFalse()\n    assertThat(ribCoroutineWorkerOnStartCalled).isTrue()\n    assertThat(ribCoroutineWorkerOnStopCalled).isFalse()\n    unbinder.unbind().join()\n    assertThat(workerOnStartCalled).isFalse()\n    assertThat(workerOnStopCalled).isFalse()\n    assertThat(ribCoroutineWorkerOnStopCalled).isTrue()\n  }\n}\n\n@OptIn(InternalCoroutinesApi::class)\nprivate class ImmediateDispatcher(\n  scheduler: TestCoroutineScheduler,\n  private val delegate: TestDispatcher = StandardTestDispatcher(scheduler),\n) : CoroutineDispatcher(), Delay by delegate {\n  private var threadId: Long? = null\n  var dispatchCount = 0\n    private set\n\n  override fun isDispatchNeeded(context: CoroutineContext): Boolean {\n    val expectedThreadId = threadId ?: return true\n    return Thread.currentThread().id != expectedThreadId\n  }\n\n  override fun dispatch(context: CoroutineContext, block: Runnable) {\n    ++dispatchCount\n    delegate.dispatch(context, block)\n  }\n\n  fun setThreadId() {\n    threadId = Thread.currentThread().id\n  }\n}\n\nprivate class TestWorker : RibCoroutineWorker {\n  var onStartStarted = false\n  var onStartFinished = false\n  var onStartThread: Thread? = null\n  var onStopCause: Throwable? = null\n  var onStopRan = false\n  var onStopThread: Thread? = null\n  var innerCoroutineStarted = false\n  var innerCoroutineIdle = false\n  var innerCoroutineCompleted = false\n\n  private var _doOnStart: suspend () -> Unit = {}\n  private var _doOnStop: () -> Unit = {}\n\n  fun doOnStart(block: suspend () -> Unit) {\n    _doOnStart = block\n  }\n\n  fun doOnStop(block: () -> Unit) {\n    _doOnStop = block\n  }\n\n  override suspend fun onStart(scope: CoroutineScope) {\n    onStartStarted = true\n    onStartThread = Thread.currentThread()\n    try {\n      scope.launch {\n        try {\n          innerCoroutineStarted = true\n          delay(INNER_COROUTINE_DELAY_DURATION_MILLIS)\n          innerCoroutineIdle = true\n          awaitCancellation()\n        } finally {\n          innerCoroutineCompleted = true\n        }\n      }\n      delay(ON_START_DELAY_DURATION_MILLIS)\n      _doOnStart()\n    } finally {\n      onStartFinished = true\n    }\n  }\n\n  override fun onStop(cause: Throwable) {\n    onStopThread = Thread.currentThread()\n    onStopCause = cause\n    try {\n      _doOnStop()\n    } finally {\n      onStopRan = true\n    }\n  }\n}\n\n/**\n * This pattern is *not* recommended. New classes should *only* implement [RibCoroutineWorker]. If a\n * class already implements [Worker] and is to be used as a [RibCoroutineWorker], it should either:\n * 1. Migrate away from [Worker] to [RibCoroutineWorker], or\n * 2. Be converted to [RibCoroutineWorker] using [Worker.asRibCoroutineWorker].\n */\nprivate class WorkerAndRibCoroutineWorker(\n  val workerOnStart: (WorkerScopeProvider) -> Unit,\n  val workerOnStop: () -> Unit,\n  val ribCoroutineWorkerOnStart: (CoroutineScope) -> Unit,\n  val ribCoroutineWorkerOnStop: (Throwable) -> Unit,\n) : Worker, RibCoroutineWorker {\n  // Worker impl\n  override fun onStart(lifecycle: WorkerScopeProvider) = workerOnStart(lifecycle)\n  override fun onStop() = workerOnStop()\n\n  // RibCoroutineWorker impl\n  override suspend fun onStart(scope: CoroutineScope) = ribCoroutineWorkerOnStart(scope)\n  override fun onStop(cause: Throwable) = ribCoroutineWorkerOnStop(cause)\n}\n"
  },
  {
    "path": "libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibEventsTest.kt",
    "content": "/*\n * Copyright (C) 2024. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.google.common.truth.Truth.assertThat\nimport kotlinx.coroutines.ExperimentalCoroutinesApi\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.test.runCurrent\nimport kotlinx.coroutines.test.runTest\nimport org.junit.After\nimport org.junit.Before\nimport org.junit.Ignore\nimport org.junit.Test\nimport org.mockito.kotlin.mock\n\n@OptIn(ExperimentalCoroutinesApi::class)\n@Ignore(\n  \"\"\"\n    Test only passes when running in isolation: RibEvents flows might've been accessed\n    when running full suite.\n  \"\"\",\n)\nclass RibEventsTest {\n  private val extraBufferCapacity = 16\n\n  @Before\n  fun setUp() {\n    RibEvents.setExtraBufferCapacity(extraBufferCapacity)\n  }\n\n  @After\n  fun tearDown() {\n    RibEvents.setExtraBufferCapacity(Channel.UNLIMITED)\n  }\n\n  @Test\n  fun setExtraBufferCapacityTest() = runTest {\n    val results = mutableListOf<RibRouterEvent>()\n    backgroundScope.launch { RibEvents.routerEventsFlow.collect(results::add) }\n    runCurrent()\n    repeat(32) { RibEvents.emitRouterEvent(RibEventType.ATTACHED, mock(), mock()) }\n    runCurrent()\n    assertThat(results.size).isEqualTo(16)\n  }\n}\n"
  },
  {
    "path": "libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibEventsUtils.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.google.common.truth.Truth\n\nobject RibEventsUtils {\n  internal fun RibActionInfo.assertRibActionInfo(\n    expectedRibEventType: RibEventType,\n    expectedRibActionEmitterType: RibActionEmitterType,\n    ribActionState: RibActionState,\n    ribClassName: String,\n  ) {\n    Truth.assertThat(this.ribEventType).isEqualTo(expectedRibEventType)\n    Truth.assertThat(this.ribActionEmitterType).isEqualTo(expectedRibActionEmitterType)\n    Truth.assertThat(this.ribActionState).isEqualTo(ribActionState)\n    Truth.assertThat(this.ribActionEmitterName).isEqualTo(ribClassName)\n  }\n}\n"
  },
  {
    "path": "libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibRefWatcherTest.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport org.junit.After\nimport org.junit.Test\nimport org.mockito.kotlin.mock\nimport org.mockito.kotlin.never\nimport org.mockito.kotlin.verify\nimport org.mockito.kotlin.verifyNoInteractions\n\nclass RibRefWatcherTest {\n  private val referenceWatcher: RibRefWatcher.ReferenceWatcher = mock()\n  private val ribRefWatcher = RibRefWatcher()\n\n  @After\n  fun tearDown() {\n    ribRefWatcher.disableLeakCanary()\n    ribRefWatcher.disableULeakLifecycleTracking()\n  }\n\n  @Test\n  fun watchDeletedObject_whenObjectIsNull_shouldDoNothing() {\n    ribRefWatcher.enableLeakCanary()\n    ribRefWatcher.setReferenceWatcher(referenceWatcher)\n    ribRefWatcher.watchDeletedObject(null)\n    verifyNoInteractions(referenceWatcher)\n  }\n\n  @Test\n  fun watchDeletedObject_whenReferenceWatcherIsNull_shouldDoNothing() {\n    ribRefWatcher.enableLeakCanary()\n    ribRefWatcher.watchDeletedObject(Any())\n    verifyNoInteractions(referenceWatcher)\n  }\n\n  @Test\n  fun watchDeletedObject_whenReferenceObjectIsNotNull_shouldTellReferenceWatcher() {\n    ribRefWatcher.enableLeakCanary()\n    val obj = Any()\n    ribRefWatcher.setReferenceWatcher(referenceWatcher)\n    ribRefWatcher.watchDeletedObject(obj)\n    verify(referenceWatcher).watch(obj)\n  }\n\n  @Test\n  fun watchDeletedObject_whenNonNullRefWithDisabledLeakCanary_shouldDoNothing() {\n    val obj = Any()\n    ribRefWatcher.setReferenceWatcher(referenceWatcher)\n    ribRefWatcher.watchDeletedObject(obj)\n    verify(referenceWatcher, never()).watch(obj)\n  }\n\n  @Test\n  fun watchDeletedObject_whenObjectIsNullWithULeak_shouldDoNothing() {\n    ribRefWatcher.enableULeakLifecycleTracking()\n    ribRefWatcher.setReferenceWatcher(referenceWatcher)\n    ribRefWatcher.watchDeletedObject(null)\n    verifyNoInteractions(referenceWatcher)\n  }\n\n  @Test\n  fun watchDeletedObject_whenReferenceWatcherIsNullULeakEnabled_shouldDoNothing() {\n    ribRefWatcher.enableULeakLifecycleTracking()\n    ribRefWatcher.watchDeletedObject(Any())\n    verifyNoInteractions(referenceWatcher)\n  }\n\n  @Test\n  fun watchDeletedObject_whenReferenceObjectIsNotNullULeak_shouldTellReferenceWatcher() {\n    ribRefWatcher.enableULeakLifecycleTracking()\n    val obj = Any()\n    ribRefWatcher.setReferenceWatcher(referenceWatcher)\n    ribRefWatcher.watchDeletedObject(obj)\n    verify(referenceWatcher).watch(obj)\n  }\n\n  @Test\n  fun watchDeletedObject_whenNonNullRefULeakDisabled_shouldDoNothing() {\n    val obj = Any()\n    ribRefWatcher.setReferenceWatcher(referenceWatcher)\n    ribRefWatcher.watchDeletedObject(obj)\n    verify(referenceWatcher, never()).watch(obj)\n  }\n}\n"
  },
  {
    "path": "libraries/rib-base/src/test/kotlin/com/uber/rib/core/RouterTest.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.google.common.truth.Truth.assertThat\nimport java.util.concurrent.atomic.AtomicBoolean\nimport org.junit.Test\nimport org.mockito.kotlin.mock\n\nclass RouterTest {\n\n  private val component: InteractorComponent<*, *> = mock()\n  private val interactor: Interactor<*, *> = mock()\n  private val ribRefWatcher: RibRefWatcher = mock()\n\n  @Test\n  fun didLoad_shouldBeCalledAfterInstantiation() {\n    val didLoad = AtomicBoolean(false)\n    val router: Router<*> =\n      object :\n        Router<Interactor<*, *>>(\n          component,\n          interactor,\n          ribRefWatcher,\n          Thread.currentThread(),\n        ) {\n        override fun attachToInteractor() {\n          // ignore the Interactor since we're only testing the Router\n        }\n\n        override fun didLoad() {\n          super.didLoad()\n          didLoad.set(true)\n        }\n      }\n    router.dispatchAttach(null)\n    assertThat(didLoad.get()).isTrue()\n  }\n}\n"
  },
  {
    "path": "libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.google.common.truth.Truth.assertThat\nimport com.jakewharton.rxrelay2.BehaviorRelay\nimport com.uber.rib.core.RibEvents.ribActionEvents\nimport com.uber.rib.core.RibEventsUtils.assertRibActionInfo\nimport com.uber.rib.core.WorkerBinder.bind\nimport com.uber.rib.core.WorkerBinder.bindToWorkerLifecycle\nimport com.uber.rib.core.WorkerBinder.mapInteractorLifecycleToWorker\nimport com.uber.rib.core.WorkerBinder.mapPresenterLifecycleToWorker\nimport com.uber.rib.core.lifecycle.InteractorEvent\nimport com.uber.rib.core.lifecycle.PresenterEvent\nimport com.uber.rib.core.lifecycle.WorkerEvent\nimport io.reactivex.observers.TestObserver\nimport kotlinx.coroutines.CoroutineDispatcher\nimport kotlinx.coroutines.ExperimentalCoroutinesApi\nimport kotlinx.coroutines.test.advanceUntilIdle\nimport kotlinx.coroutines.test.runTest\nimport org.junit.Before\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.junit.runners.Parameterized\nimport org.mockito.kotlin.any\nimport org.mockito.kotlin.mock\nimport org.mockito.kotlin.spy\nimport org.mockito.kotlin.times\nimport org.mockito.kotlin.verify\n\n@OptIn(ExperimentalCoroutinesApi::class)\n@RunWith(Parameterized::class)\nclass WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) {\n  @get:Rule val ribCoroutinesRule = RibCoroutinesRule()\n\n  private val worker =\n    mock<Worker>().run {\n      if (adaptFromRibCoroutineWorker) {\n        spy(this.asRibCoroutineWorker().asWorker())\n      } else {\n        this\n      }\n    }\n\n  private val fakeWorker = FakeWorker()\n  private val interactor = FakeInteractor<Presenter, Router<*>>()\n  private val ribActionInfoObserver = TestObserver<RibActionInfo>()\n\n  @Before\n  fun setUp() {\n    RibEvents.enableRibActionEmissions()\n  }\n\n  @Test\n  fun bind_whenInteractorAttached_shouldStartWorker() {\n    val lifecycle = BehaviorRelay.createDefault(InteractorEvent.ACTIVE)\n    bind(mapInteractorLifecycleToWorker(lifecycle), worker)\n    verify(worker).onStart(any())\n  }\n\n  @Test\n  fun bind_whenInteractorDetached_shouldStopWorker() {\n    val lifecycle = BehaviorRelay.createDefault(InteractorEvent.ACTIVE)\n    bind(mapInteractorLifecycleToWorker(lifecycle), worker)\n    lifecycle.accept(InteractorEvent.INACTIVE)\n    verify(worker).onStop()\n  }\n\n  @Test\n  fun unbind_whenInteractorAttached_shouldStopWorker() {\n    val lifecycle = BehaviorRelay.createDefault(InteractorEvent.ACTIVE)\n    val unbinder = bind(mapInteractorLifecycleToWorker(lifecycle), worker)\n    unbinder.unbind()\n    verify(worker).onStop()\n  }\n\n  @Test\n  fun unbind_whenOutsideInteractorLifecycle_shouldNotCallStopAgain() {\n    val lifecycle = BehaviorRelay.createDefault(InteractorEvent.INACTIVE)\n    val unbinder = bind(mapInteractorLifecycleToWorker(lifecycle), worker)\n    verify(worker, times(1)).onStop()\n    unbinder.unbind()\n    verify(worker, times(1)).onStop()\n  }\n\n  @Test\n  fun onInactive_whenAfterUnbind_shouldNotCallStopAgain() {\n    val lifecycle = BehaviorRelay.createDefault(InteractorEvent.ACTIVE)\n    val unbinder = bind(mapInteractorLifecycleToWorker(lifecycle), worker)\n    unbinder.unbind()\n    verify(worker, times(1)).onStop()\n    lifecycle.accept(InteractorEvent.INACTIVE)\n    verify(worker, times(1)).onStop()\n  }\n\n  @Test\n  fun bindToWorkerLifecycle_whenStartEventEmitted_shouldStartWorker() {\n    val lifecycle = BehaviorRelay.createDefault(WorkerEvent.START)\n    bindToWorkerLifecycle(lifecycle, worker)\n    verify(worker).onStart(any())\n  }\n\n  @Test\n  fun bindToWorkerLifecycle_whenStopEventEmitted_shouldStopWorker() {\n    val lifecycle = BehaviorRelay.createDefault(WorkerEvent.START)\n    bindToWorkerLifecycle(lifecycle, worker)\n    lifecycle.accept(WorkerEvent.STOP)\n    verify(worker).onStop()\n  }\n\n  @Test\n  fun bind_whenPresenterAttached_shouldStartWorker() {\n    val lifecycle = BehaviorRelay.createDefault(PresenterEvent.LOADED)\n    bind(mapPresenterLifecycleToWorker(lifecycle), worker)\n    verify(worker).onStart(any())\n  }\n\n  @Test\n  fun bind_whenPresenterDetached_shouldStopWorker() {\n    val lifecycle = BehaviorRelay.createDefault(PresenterEvent.LOADED)\n    bind(mapPresenterLifecycleToWorker(lifecycle), worker)\n    lifecycle.accept(PresenterEvent.UNLOADED)\n    verify(worker).onStop()\n  }\n\n  @Test\n  fun bind_onStartIsCalledEagerly() = runTest {\n    val interactor = object : Interactor<Any, Router<*>>() {}\n    var onStartCalled = false\n    val worker = Worker { onStartCalled = true }\n    InteractorHelper.attach(interactor, Unit, mock(), null)\n    bind(interactor, worker)\n    assertThat(onStartCalled).isTrue()\n  }\n\n  @Test\n  fun bind_whenSubscribeToLifecycleInWorker_observerIsCalledEagerly() = runTest {\n    val interactor = object : Interactor<Any, Router<*>>() {}\n    var enteredUnconfined = false\n    val worker = Worker {\n      val subscription = interactor.lifecycle().subscribe { enteredUnconfined = true }\n      assertThat(enteredUnconfined).isTrue()\n      subscription.dispose()\n    }\n    InteractorHelper.attach(interactor, Unit, mock(), null)\n    bind(interactor, worker)\n    assertThat(enteredUnconfined).isTrue()\n  }\n\n  @Test\n  fun unbind_whenPresenterAttached_shouldStopWorker() {\n    val lifecycle = BehaviorRelay.createDefault(PresenterEvent.LOADED)\n    val unbinder = bind(mapPresenterLifecycleToWorker(lifecycle), worker)\n    unbinder.unbind()\n    verify(worker).onStop()\n  }\n\n  @Test\n  fun unbind_whenOutsidePresenterLifecycle_shouldNotCallStopAgain() {\n    val lifecycle = BehaviorRelay.createDefault(PresenterEvent.UNLOADED)\n    val unbinder = bind(mapPresenterLifecycleToWorker(lifecycle), worker)\n    verify(worker, times(1)).onStop()\n    unbinder.unbind()\n    verify(worker, times(1)).onStop()\n  }\n\n  @Test\n  fun bind_withUnconfinedCoroutineDispatchers_shouldReportBinderInformationForOnStart() = runTest {\n    ribActionEvents.subscribe(ribActionInfoObserver)\n    bindFakeWorker()\n    val ribActionInfoValues = ribActionInfoObserver.values()\n    ribActionInfoValues\n      .last()\n      .assertRibActionInfo(\n        RibEventType.ATTACHED,\n        RibActionEmitterType.DEPRECATED_WORKER,\n        RibActionState.COMPLETED,\n        ribClassName = \"com.uber.rib.core.FakeWorker\",\n      )\n  }\n\n  @Test\n  fun bind_withDisabledRibActionEvents_shouldNotEmitActionEvents() = runTest {\n    RibEvents.areRibActionEmissionsAllowed = false\n    ribActionEvents.subscribe(ribActionInfoObserver)\n    bindFakeWorker()\n    assertThat(ribActionInfoObserver.values()).isEmpty()\n  }\n\n  @Test\n  fun bind_multipleWorkers_shouldReportBinderUiWorker() = runTest {\n    ribActionEvents.subscribe(ribActionInfoObserver)\n    val uiWorker = UiWorker()\n    prepareInteractor()\n    val workers = listOf(fakeWorker, fakeWorker, uiWorker)\n    bind(interactor, workers)\n    advanceUntilIdle()\n    val ribActionInfoValues = ribActionInfoObserver.values()\n    ribActionInfoValues\n      .last()\n      .assertRibActionInfo(\n        RibEventType.ATTACHED,\n        RibActionEmitterType.DEPRECATED_WORKER,\n        RibActionState.COMPLETED,\n        ribClassName = \"com.uber.rib.core.UiWorker\",\n      )\n  }\n\n  @Test\n  fun unbind_withUnconfinedCoroutineDispatchers_shouldReportBinderDurationForOnStop() = runTest {\n    ribActionEvents.subscribe(ribActionInfoObserver)\n    val unbinder = bindFakeWorker()\n    unbinder.unbind()\n    val ribActionInfoValues = ribActionInfoObserver.values()\n    ribActionInfoValues\n      .last()\n      .assertRibActionInfo(\n        RibEventType.DETACHED,\n        RibActionEmitterType.DEPRECATED_WORKER,\n        RibActionState.COMPLETED,\n        ribClassName = \"com.uber.rib.core.FakeWorker\",\n      )\n  }\n\n  private fun bindFakeWorker(): WorkerUnbinder {\n    prepareInteractor()\n    return bind(interactor, fakeWorker)\n  }\n\n  private fun prepareInteractor() {\n    interactor.attach()\n    interactor.enableTestScopeOverride()\n  }\n\n  companion object {\n    @JvmStatic\n    @Parameterized.Parameters(name = \"adaptFromRibCoroutineWorker = {0}\")\n    fun data() = listOf(arrayOf(true), arrayOf(false))\n  }\n}\n\nprivate fun Worker(onStartBlock: (WorkerScopeProvider) -> Unit) =\n  object : Worker {\n    override fun onStart(lifecycle: WorkerScopeProvider) {\n      onStartBlock(lifecycle)\n    }\n  }\n\nclass UiWorker : Worker {\n  override val coroutineContext: CoroutineDispatcher = RibDispatchers.Main\n}\n"
  },
  {
    "path": "libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerScopeProviderTest.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.jakewharton.rxrelay2.BehaviorRelay\nimport com.jakewharton.rxrelay2.Relay\nimport com.uber.rib.core.internal.CoreFriendModuleApi\nimport com.uber.rib.core.lifecycle.WorkerEvent\nimport io.reactivex.observers.TestObserver\nimport org.junit.Before\nimport org.junit.Test\n\nclass WorkerScopeProviderTest {\n\n  private val lifecycle: Relay<WorkerEvent> = BehaviorRelay.create()\n  private lateinit var testObserver: TestObserver<Any>\n\n  @OptIn(CoreFriendModuleApi::class)\n  @Before\n  fun setUp() {\n    testObserver = TestObserver()\n    val workerScopeProvider = WorkerScopeProvider(lifecycle.hide())\n    workerScopeProvider.requestScope().subscribe(testObserver)\n  }\n\n  @Test\n  fun byDefaultTestObserve_shouldNotComplete() {\n    testObserver.assertNoErrors()\n    testObserver.assertNotComplete()\n  }\n\n  @Test\n  fun whenStartLifecycleEmitted_shouldNotComplete() {\n    lifecycle.accept(WorkerEvent.START)\n    testObserver.assertNoErrors()\n    testObserver.assertNotComplete()\n  }\n\n  @Test\n  fun whenStartLifecycleEmittedThenStopEventEmitted_shouldComplete() {\n    lifecycle.accept(WorkerEvent.START)\n    lifecycle.accept(WorkerEvent.STOP)\n    testObserver.assertComplete()\n    testObserver.assertNoErrors()\n  }\n\n  @Test\n  fun whenStartLifecycleEmittedThenStartEventEmittedAgain_shouldAlsoComplete() {\n    lifecycle.accept(WorkerEvent.START)\n    lifecycle.accept(WorkerEvent.START)\n    testObserver.assertComplete()\n    testObserver.assertNoErrors()\n  }\n}\n"
  },
  {
    "path": "libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerTest.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.google.common.truth.Truth\nimport kotlin.coroutines.CoroutineContext\nimport kotlin.coroutines.EmptyCoroutineContext\nimport org.junit.Test\n\nclass WorkerTest {\n\n  @Test\n  fun threadingType_withDefaultWorker_shouldHaveEmptyCoroutineContextAsDefault() {\n    val defaultWorker = DefaultWorker()\n    Truth.assertThat(defaultWorker.coroutineContext).isEqualTo(EmptyCoroutineContext)\n  }\n\n  @Test\n  fun threadingType_withBackgroundWorker_shouldUseDefaultDispatchers() {\n    val backgroundWorker = BackgroundWorker()\n    Truth.assertThat(backgroundWorker.coroutineContext).isEqualTo(RibDispatchers.Default)\n  }\n\n  private class DefaultWorker : Worker\n\n  private class BackgroundWorker : Worker {\n    override val coroutineContext: CoroutineContext = RibDispatchers.Default\n  }\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/README.md",
    "content": "# rib-compiler\n\nBase library for writing RIB annotation processors.\n"
  },
  {
    "path": "libraries/rib-compiler-app/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.kotlin.library\")\n    alias(libs.plugins.maven.publish)\n}\n\ndependencies {\n    api(project(\":libraries:rib-base\"))\n\n    implementation(libs.autocommon)\n    implementation(libs.javapoet)\n    implementation(libs.dagger.library)\n\n    compileOnly(libs.android.api)\n\n    testImplementation(testLibs.compile.testing)\n}\n\n// https://code.google.com/p/android/issues/detail?id=64887\ntasks.register<Copy>(\"copyTestResources\") {\n    from(\"$projectDir/src/test/resources\")\n    into(\"$buildDir/classes/test\")\n}\n\ntasks.processTestResources {\n    dependsOn(\"copyTestResources\")\n}\n\ntasks.test.configure {\n    // See: https://github.com/google/compile-testing/releases/tag/v0.22.0\n    jvmArgs(\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED\",\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED\",\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED\",\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED\",\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED\",\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED\",\n    )\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/gradle.properties",
    "content": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPOM_NAME=RIBs (Compiler App)\nPOM_ARTIFACT_ID=rib-compiler-app\nPOM_PACKAGING=jar\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/main/kotlin/com/uber/rib/compiler/AnnotatedClass.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport javax.lang.model.element.TypeElement\n\n/** Information to a class. */\npublic abstract class AnnotatedClass(\n  /** @return the type element that this wraps. */\n  public open val typeElement: TypeElement,\n) {\n\n  /**\n   * Get the root name of the element. For instance, if the annotated class was \"FooInteractor\",\n   * this would return \"Foo\".\n   *\n   * @return the root name.\n   */\n  public open val rootName: String by lazy {\n    typeElement.simpleName.toString().substringBefore(nameSuffix)\n  }\n\n  /** Set if code has been generated. */\n  public open var isCodeGenerated: Boolean = false\n\n  /** @return the annotated class name suffix */\n  public abstract val nameSuffix: String\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/main/kotlin/com/uber/rib/compiler/AnnotationVerifier.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport javax.lang.model.element.TypeElement\nimport javax.lang.model.util.Elements\nimport javax.lang.model.util.Types\n\n/**\n * Verifier that checks if the type element is valid and returns information as [AnnotatedClass].\n *\n * @param <T> the actual [AnnotatedClass] info that it generates if valid.\n */\ninternal abstract class AnnotationVerifier<T : AnnotatedClass>(\n  protected val errorReporter: ErrorReporter,\n  protected val elementUtils: Elements,\n  protected val typesUtils: Types,\n) {\n\n  /**\n   * Verify that a given type has applied annotations correctly.\n   *\n   * @param type the type to check.\n   * @return an [AnnotatedClass] object for the type.\n   * @throws VerificationFailedException when verification fails.\n   */\n  @Throws(VerificationFailedException::class) abstract fun verify(type: TypeElement): T\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/main/kotlin/com/uber/rib/compiler/CompilerUtils.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport javax.lang.model.element.Element\nimport javax.lang.model.element.PackageElement\n\n/** Handy functions for generating code. */\npublic open class CompilerUtils {\n  public companion object {\n    /**\n     * Returns the name of the package that the given type is in. If the type is in the default\n     * (unnamed) package then the name is the empty string.\n     */\n    @JvmStatic\n    public fun packageNameOf(type: Element): String {\n      var type = type\n      while (true) {\n        val enclosing =\n          type.enclosingElement\n            ?: throw RuntimeException(\"null value from type.getEnclosingElement()\")\n        if (enclosing is PackageElement) {\n          return enclosing.qualifiedName.toString()\n        }\n        type = enclosing\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/main/kotlin/com/uber/rib/compiler/Constants.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\n/** Constant values used by the annotation processor. */\npublic open class Constants {\n  public companion object {\n    /** {@inheritDoc} */\n    public const val INTERACTOR_SUFFIX: String = \"Interactor\"\n  }\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/main/kotlin/com/uber/rib/compiler/ErrorReporter.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport javax.annotation.processing.Messager\nimport javax.lang.model.element.Element\nimport javax.tools.Diagnostic\n\n/** Holds utilities for reporting errors. */\npublic open class ErrorReporter(private val messager: Messager) {\n  @JvmOverloads\n  internal open fun reportError(message: CharSequence, element: Element? = null) {\n    messager.printMessage(Diagnostic.Kind.ERROR, message, element)\n    messager.printMessage(Diagnostic.Kind.NOTE, message, element)\n  }\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/main/kotlin/com/uber/rib/compiler/Generator.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport java.io.IOException\nimport javax.annotation.processing.ProcessingEnvironment\n\n/**\n * Base class to encapsulate generating something.\n *\n * @param <T> the [AnnotatedClass] that the [Generator.generate] needs.\n */\npublic abstract class Generator<T : AnnotatedClass>(\n  protected val processingEnvironment: ProcessingEnvironment,\n  protected val errorReporter: ErrorReporter,\n) {\n\n  /**\n   * Generator something for an interactor.\n   *\n   * @param builder metadata.\n   * @throws IOException when something goes wrong.\n   */\n  @Throws(IOException::class) public abstract fun generate(builder: T)\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/main/kotlin/com/uber/rib/compiler/InteractorAnnotatedClass.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport javax.lang.model.element.TypeElement\nimport javax.lang.model.element.VariableElement\n\npublic open class InteractorAnnotatedClass(\n  typeElement: TypeElement,\n  /** @return the list of dependencies. */\n  public open val dependencies: List<VariableElement>,\n  /** @return Whether this interactor extends BasicInteractor. */\n  public open val isBasic: Boolean,\n) : AnnotatedClass(typeElement) {\n\n  open override val nameSuffix: String\n    get() = Constants.INTERACTOR_SUFFIX\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/main/kotlin/com/uber/rib/compiler/InteractorAnnotationVerifier.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport com.uber.rib.core.Interactor\nimport java.util.ArrayList\nimport javax.inject.Inject\nimport javax.lang.model.element.ElementKind\nimport javax.lang.model.element.ExecutableElement\nimport javax.lang.model.element.TypeElement\nimport javax.lang.model.element.VariableElement\nimport javax.lang.model.util.ElementFilter\nimport javax.lang.model.util.Elements\nimport javax.lang.model.util.Types\n\n/** Verify that an [RibInteractor] annotation is applied correctly. */\ninternal open class InteractorAnnotationVerifier(\n  errorReporter: ErrorReporter,\n  elementUtils: Elements,\n  typesUtils: Types,\n) : AnnotationVerifier<InteractorAnnotatedClass>(errorReporter, elementUtils, typesUtils) {\n\n  /**\n   * Verify that a given type has applied annotations correctly.\n   *\n   * @param type the type to check.\n   * @return an [InteractorAnnotatedClass] object for the type.\n   * @throws VerificationFailedException when verification fails.\n   */\n  @Throws(VerificationFailedException::class)\n  override fun verify(type: TypeElement): InteractorAnnotatedClass {\n    val hasConstructor = hasConstructor(type)\n    var result = validateInteractorSubclass(type)\n    result = result && validateInteractorSuffix(type)\n    return if (!result) {\n      throw VerificationFailedException()\n    } else {\n      val fields: List<VariableElement>\n      fields =\n        if (hasConstructor) {\n          getConstructorParameters(type)\n        } else {\n          getInjectFields(type)\n        }\n      InteractorAnnotatedClass(type, fields, hasConstructor)\n    }\n  }\n\n  private fun hasConstructor(type: TypeElement): Boolean {\n    val constructors: MutableList<ExecutableElement> = ArrayList()\n    for (element in elementUtils.getAllMembers(type)) {\n      if (element.kind == ElementKind.CONSTRUCTOR) {\n        constructors.add(element as ExecutableElement)\n      }\n    }\n    return constructors.size != 1 || constructors[0].parameters.isNotEmpty()\n  }\n\n  /**\n   * Validates Interactor name's suffix.\n   *\n   * @param type\n   * @return if given type element is valid.\n   */\n  private fun validateInteractorSuffix(type: TypeElement): Boolean {\n    return if (!type.simpleName.toString().endsWith(Constants.INTERACTOR_SUFFIX)) {\n      errorReporter.reportError(\"$type does not end in Interactor.\")\n      false\n    } else {\n      true\n    }\n  }\n\n  /**\n   * Validate if current class is a subclass of Interactor.\n   *\n   * @param type\n   * @return if given type element is valid.\n   */\n  private fun validateInteractorSubclass(type: TypeElement): Boolean {\n    val interactorElement = elementUtils.getTypeElement(Interactor::class.java.name)\n    val rawElement =\n      typesUtils.getDeclaredType(\n        interactorElement,\n        typesUtils.getWildcardType(null, null),\n        typesUtils.getWildcardType(null, null),\n      )\n    return if (!typesUtils.isSubtype(type.asType(), rawElement)) {\n      errorReporter.reportError(\n        \"$type is annotated with @RibInteractor but is not an Interactor subclass.\",\n        type,\n      )\n      false\n    } else {\n      true\n    }\n  }\n\n  /**\n   * Get the fields annotated with [Inject].\n   *\n   * @param type the type to get fields.\n   * @return the list of all inject fields.\n   */\n  private fun getInjectFields(type: TypeElement): List<VariableElement> {\n    val fields = ElementFilter.fieldsIn(type.enclosedElements)\n    val injectFields = ArrayList<VariableElement>()\n    for (field in fields) {\n      if (field.getAnnotation(Inject::class.java) != null) {\n        injectFields.add(field)\n      }\n    }\n    return injectFields\n  }\n\n  private fun getConstructorParameters(type: TypeElement): List<VariableElement> {\n    val constructors = ElementFilter.constructorsIn(type.enclosedElements)\n    return constructors[0].parameters\n  }\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/main/kotlin/com/uber/rib/compiler/ProcessContext.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport javax.lang.model.util.Elements\nimport javax.lang.model.util.Types\n\n/**\n * The interface defines the context that shared across [ProcessorPipeline] during annotation\n * processing.\n */\npublic interface ProcessContext {\n  /** @return the [ErrorReporter] */\n  public val errorReporter: ErrorReporter?\n\n  /** @return the [Elements] */\n  public val elementUtils: Elements?\n\n  /** @return the [Types] */\n  public val typesUtils: Types?\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/main/kotlin/com/uber/rib/compiler/ProcessorPipeline.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport javax.annotation.processing.RoundEnvironment\nimport javax.lang.model.element.TypeElement\nimport javax.lang.model.util.Elements\nimport javax.lang.model.util.Types\n\n/** The processor pipeline that processes one specific annotation. */\npublic abstract class ProcessorPipeline(protected var processContext: ProcessContext) {\n  protected var errorReporter: ErrorReporter? = processContext.errorReporter\n  protected var elementUtils: Elements? = processContext.elementUtils\n  protected var typesUtils: Types? = processContext.typesUtils\n\n  /** Process the annotation that is the same as processor */\n  @Throws(Throwable::class)\n  public fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {\n    processAnnotations(annotations, roundEnv)\n    return false\n  }\n\n  /** @return the annotation that this Processor pipeline works on. */\n  public abstract val annotationType: Class<out Annotation>\n\n  /** Processes the annotation. */\n  @Throws(Throwable::class)\n  protected abstract fun processAnnotations(\n    annotations: Set<TypeElement>,\n    roundEnv: RoundEnvironment,\n  )\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/main/kotlin/com/uber/rib/compiler/RibInteractorProcessorPipeline.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport com.uber.rib.core.RibInteractor\nimport java.io.IOException\nimport javax.lang.model.element.TypeElement\n\n/** The processor pipeline for [RibInteractor] */\npublic open class RibInteractorProcessorPipeline(\n  processContext: ProcessContext,\n  private val interactorGenerator: Generator<InteractorAnnotatedClass>?,\n) : TypeProcessorPipeline(processContext) {\n  private val annotationVerifier =\n    InteractorAnnotationVerifier(errorReporter!!, elementUtils!!, typesUtils!!)\n  private val builderAnnotatedClassesList = mutableListOf<InteractorAnnotatedClass>()\n\n  /** @return the annotation type this processor pipeline deals with. */\n  override val annotationType: Class<out Annotation>\n    get() = SUPPORT_ANNOTATION_TYPE\n\n  /**\n   * Process the type elements.\n   *\n   * @param annotatedClasses annotation types.\n   * @throws Throwable exception during annotation process.\n   */\n  @Throws(Throwable::class)\n  override fun processTypeElements(annotatedClasses: List<TypeElement>) {\n    parseTypeElements(annotatedClasses)\n    generateSource()\n  }\n\n  /** Generate the source code. */\n  @Throws(IOException::class)\n  protected fun generateSource() {\n    if (interactorGenerator == null) {\n      return\n    }\n    for (interactorAnnotatedClass in builderAnnotatedClassesList) {\n      interactorGenerator.generate(interactorAnnotatedClass!!)\n    }\n  }\n\n  /**\n   * Verifies the element and get [InteractorAnnotatedClass] info.\n   *\n   * @param annotatedTypes the annotated types.\n   */\n  @Throws(Throwable::class)\n  protected fun parseTypeElements(annotatedTypes: List<TypeElement>) {\n    for (typeElement in annotatedTypes) {\n      val builderAnnotatedClass = annotationVerifier.verify(typeElement)\n      builderAnnotatedClassesList.add(builderAnnotatedClass)\n    }\n  }\n\n  public companion object {\n    @JvmField public val SUPPORT_ANNOTATION_TYPE: Class<RibInteractor> = RibInteractor::class.java\n  }\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/main/kotlin/com/uber/rib/compiler/RibProcessor.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport java.util.ArrayList\nimport javax.annotation.processing.AbstractProcessor\nimport javax.annotation.processing.ProcessingEnvironment\nimport javax.annotation.processing.RoundEnvironment\nimport javax.lang.model.SourceVersion\nimport javax.lang.model.element.TypeElement\nimport javax.lang.model.util.Elements\nimport javax.lang.model.util.Types\n\n/** Process the annotations with [ProcessorPipeline]. */\npublic abstract class RibProcessor : AbstractProcessor(), ProcessContext {\n\n  override var errorReporter: ErrorReporter? = null\n    protected set\n  override var elementUtils: Elements? = null\n    protected set\n  override var typesUtils: Types? = null\n    protected set\n\n  public var processorPipelines: MutableList<ProcessorPipeline> = ArrayList()\n\n  @Synchronized\n  override fun init(processingEnv: ProcessingEnvironment) {\n    super.init(processingEnv)\n    elementUtils = processingEnv.elementUtils\n    errorReporter = ErrorReporter(processingEnv.messager)\n    typesUtils = processingEnv.typeUtils\n    processorPipelines.addAll(getProcessorPipelines(this))\n  }\n\n  override fun getSupportedSourceVersion(): SourceVersion {\n    return SourceVersion.latestSupported()\n  }\n\n  override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {\n    if (roundEnv.processingOver()) {\n      return false\n    }\n    for (processorPipeline in processorPipelines) {\n      try {\n        processorPipeline.process(annotations, roundEnv)\n      } catch (e: Throwable) {\n        errorReporter?.reportError(\n          \"Fatal error running ${processorPipeline.annotationType.simpleName} processor: ${e.message}\",\n        )\n      }\n    }\n    return false\n  }\n\n  /**\n   * Get list of [ProcessorPipeline] to process each annotation.\n   *\n   * @param processContext the [ProcessContext].\n   * @return the list of processor pipelines.\n   */\n  protected abstract fun getProcessorPipelines(\n    processContext: ProcessContext,\n  ): List<ProcessorPipeline>\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/main/kotlin/com/uber/rib/compiler/TypeProcessorPipeline.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport java.util.ArrayList\nimport javax.annotation.processing.RoundEnvironment\nimport javax.lang.model.element.Element\nimport javax.lang.model.element.TypeElement\n\n/** Base ProcessorPipeline that parses the annotated elements as type element. */\npublic abstract class TypeProcessorPipeline(\n  processContext: ProcessContext,\n) : ProcessorPipeline(processContext) {\n  /**\n   * Process the annotations.\n   *\n   * @param annotations\n   * @param roundEnv\n   * @throws Throwable\n   */\n  @Throws(Throwable::class)\n  override fun processAnnotations(annotations: Set<TypeElement>, roundEnv: RoundEnvironment) {\n    val annotatedElements: Collection<Element> = roundEnv.getElementsAnnotatedWith(annotationType)\n    val annotatedTypes: MutableList<TypeElement> = ArrayList(annotatedElements.size)\n    for (e in annotatedElements) {\n      annotatedTypes.add(e as TypeElement)\n    }\n    processTypeElements(annotatedTypes)\n  }\n\n  /**\n   * Process the list of [TypeElement].\n   *\n   * @param annotatedClasses the type element list.\n   * @throws Throwable\n   */\n  @Throws(Throwable::class)\n  protected abstract fun processTypeElements(annotatedClasses: List<TypeElement>)\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/main/kotlin/com/uber/rib/compiler/VerificationFailedException.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\n/**\n * Exception thrown when annotation processing should be aborted for the current class. Processing\n * can continue on other classes. Throwing this exception does not cause a compiler error, so either\n * one should explicitly be emitted or it should be clear that the compiler will be producing its\n * own error for other reasons.\n */\ninternal class VerificationFailedException : Exception()\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/test/kotlin/com/uber/rib/compiler/InteractorAnnotationVerifierTest.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport com.google.common.truth.Truth\nimport com.google.testing.compile.JavaSourcesSubjectFactory\nimport org.junit.Test\n\nclass InteractorAnnotationVerifierTest : InteractorProcessorTestBase() {\n\n  @Test\n  fun verify_whenTypeElementIsValid_shouldCompile() {\n    addResourceToSources(\"fixtures/AnnotatedInteractor.java\")\n    assertCompiles()\n  }\n\n  @Test\n  fun verify_whenTypeElementIsInteractorWithoutProperSuffix_shouldWriteErrorMessage() {\n    addResourceToSources(\"fixtures/AnnotatedInteractorNoSuffix.java\")\n    assertFailsWithError(\"test.AnnotatedInteractorNoSuffix does not end in Interactor.\")\n  }\n\n  @Test\n  fun verify_whenInteractorHasAConstructor_shouldCompile() {\n    addResourceToSources(\"fixtures/CustomConstructorInteractor.java\")\n    assertCompiles()\n  }\n\n  @Test\n  fun verify_whenTypeElementIsNotInteractor_shouldWriteErrorMessage() {\n    addResourceToSources(\"fixtures/AnnotatedNonInteractor.java\")\n    assertFailsWithError(\n      \"test.AnnotatedNonInteractor is annotated with @RibInteractor but is not an Interactor subclass\",\n    )\n  }\n\n  private fun assertFailsWithError(expectedErrorMessage: String) {\n    Truth.assert_()\n      .about(JavaSourcesSubjectFactory.javaSources())\n      .that(sources)\n      .withCompilerOptions(\"-source\", \"1.7\", \"-target\", \"1.7\")\n      .processedWith(ribProcessor)\n      .failsToCompile()\n      .withErrorContaining(expectedErrorMessage)\n  }\n\n  private fun assertCompiles() {\n    Truth.assert_()\n      .about(JavaSourcesSubjectFactory.javaSources())\n      .that(sources)\n      .withCompilerOptions(\"-source\", \"1.7\", \"-target\", \"1.7\")\n      .processedWith(ribProcessor)\n      .compilesWithoutError()\n  }\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/test/kotlin/com/uber/rib/compiler/InteractorProcessorTestBase.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport com.google.common.collect.ImmutableSet\nimport com.google.testing.compile.JavaFileObjects\nimport java.util.ArrayList\nimport javax.tools.JavaFileObject\nimport org.junit.Before\n\nabstract class InteractorProcessorTestBase {\n  protected lateinit var ribProcessor: RibProcessor\n  protected lateinit var sources: ArrayList<JavaFileObject>\n\n  @Before\n  fun setup() {\n    ribProcessor =\n      object : RibProcessor() {\n        override fun getProcessorPipelines(\n          processContext: ProcessContext,\n        ): List<ProcessorPipeline> {\n          return listOf(\n            RibInteractorProcessorPipeline(processContext, null),\n          )\n        }\n\n        override fun getSupportedAnnotationTypes(): Set<String> {\n          return ImmutableSet.of(\n            RibInteractorProcessorPipeline.SUPPORT_ANNOTATION_TYPE.canonicalName,\n          )\n        }\n      }\n    sources = ArrayList()\n  }\n\n  protected fun addResourceToSources(file: String) {\n    sources.add(JavaFileObjects.forResource(file))\n  }\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/test/resources/fixtures/AnnotatedInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage test;\n\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.Presenter;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.core.Router;\n\n@RibInteractor\npublic class AnnotatedInteractor extends Interactor<Presenter, Router<?>> {\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/test/resources/fixtures/AnnotatedInteractorNoSuffix.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage test;\n\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.Presenter;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.core.Router;\n\n@RibInteractor\npublic class AnnotatedInteractorNoSuffix extends Interactor<Presenter, Router<?>> {\n\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/test/resources/fixtures/AnnotatedNonInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage test;\n\nimport com.uber.rib.core.RibInteractor;\n\n@RibInteractor\npublic class AnnotatedNonInteractor {\n\n}\n"
  },
  {
    "path": "libraries/rib-compiler-app/src/test/resources/fixtures/CustomConstructorInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage test;\n\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.InteractorComponent;\nimport com.uber.rib.core.Presenter;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.core.Router;\n\n@RibInteractor\npublic class CustomConstructorInteractor extends Interactor<Presenter, Router<?>> {\n\n    public CustomConstructorInteractor(String a, InteractorComponent dependency) { }\n}\n"
  },
  {
    "path": "libraries/rib-compiler-test/README.md",
    "content": "# rib-compiler-test\n\nAnnotation processor that makes writing RIB tests easier. To simplify issues with\nsome build systems this generates the test utilities directory into the src source set\ninstead of the test source set. Not a big deal: test utilities will get stripped out\nby proguard.\n"
  },
  {
    "path": "libraries/rib-compiler-test/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.kotlin.library\")\n    alias(libs.plugins.kotlin.kapt)\n    alias(libs.plugins.maven.publish)\n}\n\ndependencies {\n    api(project(\":libraries:rib-compiler-app\"))\n    implementation(libs.javapoet)\n\n    compileOnly(libs.androidx.annotation)\n    compileOnly(libs.autoservice)\n    compileOnly(libs.android.api)\n    kapt(libs.autoservice)\n\n    testImplementation(libs.androidx.annotation)\n    testImplementation(testLibs.compile.testing)\n    testImplementation(files(\"libs/tools.jar\"))\n}\n\n// https://code.google.com/p/android/issues/detail?id=64887\ntasks.register<Copy>(\"copyTestResources\") {\n    from(\"$projectDir/src/test/resources\")\n    into(\"$buildDir/classes/test\")\n}\n\ntasks.processTestResources {\n    dependsOn(\"copyTestResources\")\n}\n\ntasks.test.configure {\n    // See: https://github.com/google/compile-testing/releases/tag/v0.22.0\n    jvmArgs(\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED\",\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED\",\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED\",\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED\",\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED\",\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED\",\n    )\n}\n"
  },
  {
    "path": "libraries/rib-compiler-test/gradle.properties",
    "content": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPOM_NAME=RIBs (Compiler Test)\nPOM_ARTIFACT_ID=rib-compiler-test\nPOM_PACKAGING=jar\n"
  },
  {
    "path": "libraries/rib-compiler-test/src/main/kotlin/com/uber/rib/compiler/Constants.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\n/** Constant values used by the annotation processor. */\npublic open class Constants {\n  public companion object {\n    public const val INTERACTOR_TEST_CREATOR_PREFIX: String = \"Test\"\n    public const val INTERACTOR_TEST_CREATOR_SUFFIX: String = \"Interactor\"\n    public const val INTERACTOR_TEST_CREATOR_METHOD_NAME: String = \"create\"\n  }\n}\n"
  },
  {
    "path": "libraries/rib-compiler-test/src/main/kotlin/com/uber/rib/compiler/InteractorTestGenerator.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport com.google.common.base.Joiner\nimport com.google.common.collect.ImmutableList\nimport com.squareup.javapoet.JavaFile\nimport com.squareup.javapoet.MethodSpec\nimport com.squareup.javapoet.ParameterSpec\nimport com.squareup.javapoet.TypeName\nimport com.squareup.javapoet.TypeSpec\nimport com.uber.rib.compiler.CompilerUtils.Companion.packageNameOf\nimport java.io.IOException\nimport javax.annotation.processing.ProcessingEnvironment\nimport javax.lang.model.element.Modifier\n\n/**\n * Generates a dagger scope for a RibBuilder. Example: <code>\n *\n * @Scope\n * @Retention(SOURCE) public @interface LoggedInScope { } </code>\n */\npublic open class InteractorTestGenerator(\n  processingEnvironment: ProcessingEnvironment,\n  errorReporter: ErrorReporter,\n) : Generator<InteractorAnnotatedClass>(processingEnvironment, errorReporter) {\n  @Throws(IOException::class)\n  override fun generate(annotatedInteractor: InteractorAnnotatedClass) {\n    if (annotatedInteractor.isCodeGenerated) {\n      return\n    }\n    val interactorTestBaseClassName =\n      (Constants.INTERACTOR_TEST_CREATOR_PREFIX +\n        annotatedInteractor.rootName +\n        Constants.INTERACTOR_TEST_CREATOR_SUFFIX)\n    val constructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE).build()\n    val createInteractor = createMethodSpec(annotatedInteractor)\n    val testBaseClass =\n      TypeSpec.classBuilder(interactorTestBaseClassName)\n        .addMethod(constructor)\n        .addMethod(createInteractor)\n        .addModifiers(Modifier.PUBLIC)\n        .build()\n    val packageName = packageNameOf(annotatedInteractor.typeElement)\n    JavaFile.builder(packageName, testBaseClass).build().writeTo(processingEnvironment.filer)\n    annotatedInteractor.isCodeGenerated = true\n  }\n\n  private fun createMethodSpec(interactor: InteractorAnnotatedClass): MethodSpec {\n    val builder =\n      MethodSpec.methodBuilder(Constants.INTERACTOR_TEST_CREATOR_METHOD_NAME)\n        .returns(TypeName.get(interactor.typeElement.asType()))\n        .addModifiers(ImmutableList.of(Modifier.PUBLIC, Modifier.STATIC))\n    for (dependency in interactor.dependencies) {\n      val paramSpect =\n        ParameterSpec.builder(\n            TypeName.get(dependency.asType()),\n            dependency.simpleName.toString(),\n            Modifier.FINAL,\n          )\n          .build()\n      builder.addParameter(paramSpect)\n    }\n    val interactorName = interactor.typeElement.simpleName.toString()\n    return if (interactor.isBasic) {\n      val params = Joiner.on(\", \").join(interactor.dependencies)\n      builder.addStatement(\"return new \\$L(\\$L)\", interactorName, params).build()\n    } else {\n      builder.addStatement(\"\\$L interactor = new \\$L()\", interactorName, interactorName)\n      for (dependencies in interactor.dependencies) {\n        builder.addStatement(\n          \"interactor.\\$L = \\$L\",\n          dependencies.simpleName.toString(),\n          dependencies.simpleName.toString(),\n        )\n      }\n      builder.addStatement(\"return interactor\").build()\n    }\n  }\n}\n"
  },
  {
    "path": "libraries/rib-compiler-test/src/main/kotlin/com/uber/rib/compiler/RibTestProcessor.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport com.google.auto.service.AutoService\nimport com.google.common.collect.ImmutableList\nimport com.google.common.collect.ImmutableSet\nimport javax.annotation.processing.ProcessingEnvironment\nimport javax.annotation.processing.Processor\n\n/** Process the annotations for all added annotation processor pipelines. */\n@AutoService(Processor::class)\npublic open class RibTestProcessor : RibProcessor() {\n  private var interactorTestGenerator: InteractorTestGenerator? = null\n\n  @Synchronized\n  override fun init(processingEnv: ProcessingEnvironment) {\n    interactorTestGenerator =\n      InteractorTestGenerator(processingEnv, ErrorReporter(processingEnv.messager))\n    super.init(processingEnv)\n  }\n\n  override fun getSupportedAnnotationTypes(): Set<String> {\n    return ImmutableSet.of(RibInteractorProcessorPipeline.SUPPORT_ANNOTATION_TYPE.canonicalName)\n  }\n\n  override fun getProcessorPipelines(processContext: ProcessContext): List<ProcessorPipeline> {\n    return ImmutableList.of<ProcessorPipeline>(\n      RibInteractorProcessorPipeline(processContext, interactorTestGenerator),\n    )\n  }\n}\n"
  },
  {
    "path": "libraries/rib-compiler-test/src/test/kotlin/com/uber/rib/compiler/InteractorTestGeneratorProcessorTestBase.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport com.google.common.collect.ImmutableSet\nimport com.google.testing.compile.JavaFileObjects\nimport java.util.ArrayList\nimport javax.annotation.processing.ProcessingEnvironment\nimport javax.tools.JavaFileObject\nimport org.junit.Before\n\nabstract class InteractorTestGeneratorProcessorTestBase {\n  protected lateinit var ribInteractorProcessor: RibProcessor\n  protected lateinit var sources: ArrayList<JavaFileObject>\n\n  @Before\n  fun setup() {\n    ribInteractorProcessor =\n      object : RibProcessor() {\n        var interactorTestGenerator: InteractorTestGenerator? = null\n\n        @Synchronized\n        override fun init(processingEnv: ProcessingEnvironment) {\n          interactorTestGenerator =\n            InteractorTestGenerator(processingEnv, ErrorReporter(processingEnv.messager))\n          super.init(processingEnv)\n        }\n\n        override fun getProcessorPipelines(\n          processContext: ProcessContext,\n        ): List<ProcessorPipeline> {\n          return listOf(\n            RibInteractorProcessorPipeline(processContext, interactorTestGenerator),\n          )\n        }\n\n        override fun getSupportedAnnotationTypes(): Set<String> {\n          return ImmutableSet.of(\n            RibInteractorProcessorPipeline.SUPPORT_ANNOTATION_TYPE.canonicalName,\n          )\n        }\n      }\n    sources = ArrayList()\n  }\n\n  protected fun addResourceToSources(file: String) {\n    sources.add(getResourceFile(file))\n  }\n\n  protected fun getResourceFile(file: String): JavaFileObject {\n    return JavaFileObjects.forResource(file)\n  }\n}\n"
  },
  {
    "path": "libraries/rib-compiler-test/src/test/kotlin/com/uber/rib/compiler/InteractorTestGeneratorTest.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.compiler\n\nimport com.google.common.truth.Truth\nimport com.google.testing.compile.JavaSourcesSubjectFactory\nimport org.junit.Test\n\nclass InteractorTestGeneratorTest : InteractorTestGeneratorProcessorTestBase() {\n  @Test\n  fun processor_withAnInteractor_shouldGenerateTestHelper() {\n    val expectedTestCreator = getResourceFile(\"fixtures/TestAnnotatedInteractor.java\")\n    addResourceToSources(\"fixtures/AnnotatedInteractor.java\")\n    Truth.assert_()\n      .about(JavaSourcesSubjectFactory.javaSources())\n      .that(sources)\n      .withCompilerOptions(\"-source\", \"1.8\", \"-target\", \"1.8\")\n      .processedWith(ribInteractorProcessor)\n      .compilesWithoutError()\n      .and()\n      .generatesSources(expectedTestCreator)\n  }\n\n  @Test\n  fun processor_withABasicInteractor_shouldGenerateTestHelper() {\n    val expectedTestCreator = getResourceFile(\"fixtures/TestAnnotatedBasicInteractor.java\")\n    addResourceToSources(\"fixtures/AnnotatedBasicInteractor.java\")\n    Truth.assert_()\n      .about(JavaSourcesSubjectFactory.javaSources())\n      .that(sources)\n      .withCompilerOptions(\"-source\", \"1.8\", \"-target\", \"1.8\")\n      .processedWith(ribInteractorProcessor)\n      .compilesWithoutError()\n      .and()\n      .generatesSources(expectedTestCreator)\n  }\n}\n"
  },
  {
    "path": "libraries/rib-compiler-test/src/test/resources/fixtures/AnnotatedBasicInteractor.java",
    "content": "package fixtures;\n\nimport com.uber.rib.core.BasicInteractor;\nimport com.uber.rib.core.Presenter;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.core.Router;\n\nimport javax.inject.Inject;\n\n@RibInteractor\npublic class AnnotatedBasicInteractor extends BasicInteractor<Presenter, Router<?>> {\n\n    private final Boolean fieldOne;\n    private final Integer fieldTwo;\n\n    public AnnotatedBasicInteractor(Presenter presenter, Boolean fieldOne, Integer fieldTwo) {\n        super(presenter);\n        this.fieldOne = fieldOne;\n        this.fieldTwo = fieldTwo;\n    }\n}\n"
  },
  {
    "path": "libraries/rib-compiler-test/src/test/resources/fixtures/AnnotatedInteractor.java",
    "content": "package fixtures;\n\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.Presenter;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.core.Router;\n\nimport javax.inject.Inject;\n\n@RibInteractor\npublic class AnnotatedInteractor extends Interactor<Presenter, Router<?>> {\n\n    @Inject Boolean fieldOne;\n    @Inject Integer fieldTwo;\n}\n"
  },
  {
    "path": "libraries/rib-compiler-test/src/test/resources/fixtures/TestAnnotatedBasicInteractor.java",
    "content": "package fixtures;\n\nimport com.uber.rib.core.Presenter;\nimport java.lang.Boolean;\nimport java.lang.Integer;\n\npublic class TestAnnotatedBasicInteractor {\n    private TestAnnotatedBasicInteractor() {\n    }\n\n    public static AnnotatedBasicInteractor create(final Presenter presenter, final Boolean fieldOne,\n          final Integer fieldTwo) {\n        return new AnnotatedBasicInteractor(presenter, fieldOne, fieldTwo);\n    }\n}\n"
  },
  {
    "path": "libraries/rib-compiler-test/src/test/resources/fixtures/TestAnnotatedInteractor.java",
    "content": "package fixtures;\n\nimport java.lang.Boolean;\nimport java.lang.Integer;\n\npublic class TestAnnotatedInteractor {\n    private TestAnnotatedInteractor() {\n    }\n\n    public static AnnotatedInteractor create(final Boolean fieldOne, final Integer fieldTwo) {\n        AnnotatedInteractor interactor = new AnnotatedInteractor();\n        interactor.fieldOne = fieldOne;\n        interactor.fieldTwo = fieldTwo;\n        return interactor;\n    }\n}\n"
  },
  {
    "path": "libraries/rib-compiler-test/src/test/resources/fixtures/TestForcedBasicInteractor.java",
    "content": "package fixtures;\n\nimport com.uber.rib.core.Presenter;\nimport java.lang.Boolean;\nimport java.lang.Integer;\n\npublic class TestForcedBasicInteractor {\n    private TestForcedBasicInteractor() {\n    }\n\n    public static ForcedBasicInteractor create(final Presenter presenter, final Boolean fieldOne,\n          final Integer fieldTwo) {\n        return new ForcedBasicInteractor(presenter, fieldOne, fieldTwo);\n    }\n}\n"
  },
  {
    "path": "libraries/rib-coroutines/README.md",
    "content": "# rib-coroutines\n\nThis module is responsible for defining the coroutines extensions for the rib-base module.\n\n## Installation\n```gradle\ndependencies {\n  implementation 'com.uber.rib:rib-coroutines:0.16.5'\n}\n```\n\n## Usage\n\nRibCoroutinesConfig is a global configuration object that allows for setting custom Dispatchers and Exception Handlers. This is useful for cases like Testing or when you are unable to inject Dispatchers.\nFor configuration in app, this should be configured in the Application onCreate() before usage.\n\n```kotlin\nRibCoroutinesConfig.dispatchers = DefaultRibDispatchers()\n\nRibCoroutinesConfig.exceptionHandler = CoroutineExceptionHandler { _, exception ->\n    Log.e(exception)\n}\n```\n\n\nLifecycle based components such as Interactors and Workers receive a dedicated coroutineScope for usage. The coroutine is cancelled when the ScopeProvider completes.\n\n```kotlin\nMyInteractor: Interactor {\n  override fun didBecomeActive(savedInstanceState: Bundle?) {\n    super.didBecomeActive(savedInstanceState)\n    coroutineScope.launch {\n      //Do things in this coroutine\n    }\n  }\n}\n```\n"
  },
  {
    "path": "libraries/rib-coroutines/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.kotlin.library\")\n    alias(libs.plugins.maven.publish)\n}\n\ndependencies {\n    api(libs.autodispose.coroutines)\n    api(libs.kotlinx.coroutines.android)\n    api(libs.kotlinx.coroutines.rx2)\n\n    compileOnly(libs.android.api)\n\n    implementation(libs.autodispose.lifecycle)\n\n    testImplementation(project(\":libraries:rib-base\"))\n    testImplementation(project(\":libraries:rib-test\"))\n    testImplementation(project(\":libraries:rib-coroutines-test\"))\n    testImplementation(testLibs.junit)\n    testImplementation(testLibs.mockito)\n    testImplementation(testLibs.mockito.kotlin)\n    testImplementation(testLibs.truth)\n    testImplementation(testLibs.kotlinx.coroutines.test)\n    testImplementation(libs.androidx.annotation)\n    testImplementation(libs.android.api)\n}\n"
  },
  {
    "path": "libraries/rib-coroutines/gradle.properties",
    "content": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPOM_NAME=RIBs (Coroutines)\nPOM_ARTIFACT_ID=rib-coroutines\nPOM_PACKAGING=jar\n"
  },
  {
    "path": "libraries/rib-coroutines/src/main/kotlin/com/uber/rib/core/RibCoroutineScopes.kt",
    "content": "/*\n * Copyright (C) 2022. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.app.Application\nimport com.uber.autodispose.ScopeProvider\nimport com.uber.autodispose.lifecycle.LifecycleEndedException\nimport com.uber.rib.core.internal.CoroutinesFriendModuleApi\nimport io.reactivex.CompletableObserver\nimport io.reactivex.disposables.Disposable\nimport java.util.WeakHashMap\nimport kotlin.coroutines.CoroutineContext\nimport kotlin.coroutines.EmptyCoroutineContext\nimport kotlin.reflect.KProperty\nimport kotlinx.coroutines.CoroutineName\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.SupervisorJob\nimport kotlinx.coroutines.cancel\nimport kotlinx.coroutines.job\n\n/**\n * [CoroutineScope] tied to this [ScopeProvider]. This scope will be canceled when ScopeProvider is\n * completed\n *\n * This scope is bound to\n * [RibDispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate].\n *\n * Calling this property outside of the lifecycle of the [ScopeProvider] will throw\n * [OutsideScopeException][com.uber.autodispose.OutsideScopeException]. By setting\n * [RibCoroutinesConfig.shouldCoroutineScopeFailSilentlyOnLifecycleEnded] to `true`, accessing this\n * property after the [ScopeProvider] has completed will instead return a [CoroutineScope] that is\n * immediately cancelled.\n */\n@OptIn(CoroutinesFriendModuleApi::class)\npublic val ScopeProvider.coroutineScope: CoroutineScope by LazyCoroutineScope {\n  val context = createCoroutineContext()\n  try {\n    ScopeProviderCoroutineScope(this, context)\n  } catch (e: LifecycleEndedException) {\n    if (RibCoroutinesConfig.shouldCoroutineScopeFailSilentlyOnLifecycleEnded) {\n      CoroutineScope(context).also {\n        it.cancel(\"ScopeProvider is outside of scope. context = $context\", e)\n      }\n    } else {\n      throw e\n    }\n  }\n}\n\n/**\n * [CoroutineScope] tied to this [Application]. This scope will not be cancelled, it lives for the\n * full application process.\n *\n * This scope is bound to\n * [RibDispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate]\n */\n@OptIn(CoroutinesFriendModuleApi::class)\npublic val Application.coroutineScope: CoroutineScope by LazyCoroutineScope {\n  CoroutineScope(createCoroutineContext())\n}\n\nprivate fun Any.createCoroutineContext() =\n  SupervisorJob() +\n    RibDispatchers.Main.immediate +\n    CoroutineName(\"${this::class.simpleName}:coroutineScope\") +\n    (RibCoroutinesConfig.exceptionHandler ?: EmptyCoroutineContext)\n\nprivate class ScopeProviderCoroutineScope(\n  scopeProvider: ScopeProvider,\n  coroutineContext: CoroutineContext,\n) :\n  ScopeProvider by scopeProvider,\n  CoroutineScope by CoroutineScope(coroutineContext),\n  CompletableObserver {\n\n  init {\n    requestScope().subscribe(this)\n  }\n\n  override fun onSubscribe(d: Disposable) {\n    coroutineContext.job.invokeOnCompletion { d.dispose() }\n  }\n\n  override fun onComplete() {\n    cancel()\n  }\n\n  override fun onError(e: Throwable) {\n    cancel(\"ScopeProvider completed with error\", e)\n  }\n}\n\n@CoroutinesFriendModuleApi\npublic class LazyCoroutineScope<This : Any>(private val initializer: This.() -> CoroutineScope) {\n  public companion object {\n    private val values = WeakHashMap<Any, CoroutineScope>()\n\n    // Used to get and set Test overrides from rib-coroutines-test utils\n    public operator fun get(provider: Any): CoroutineScope? = values[provider]\n    public operator fun set(provider: Any, scope: CoroutineScope?) {\n      values[provider] = scope\n    }\n  }\n  public operator fun getValue(thisRef: This, property: KProperty<*>): CoroutineScope =\n    synchronized(LazyCoroutineScope) {\n      return values.getOrPut(thisRef) {\n        thisRef.initializer().apply {\n          coroutineContext.job.invokeOnCompletion {\n            synchronized(LazyCoroutineScope) { values.remove(thisRef) }\n          }\n        }\n      }\n    }\n}\n"
  },
  {
    "path": "libraries/rib-coroutines/src/main/kotlin/com/uber/rib/core/RibCoroutinesConfig.kt",
    "content": "/*\n * Copyright (C) 2022. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport kotlinx.coroutines.CoroutineDispatcher\nimport kotlinx.coroutines.CoroutineExceptionHandler\nimport kotlinx.coroutines.Dispatchers\n\n/**\n * Config object to specify global overrides for Rib Coroutine behavior including Dispatchers and\n * exception handling\n */\npublic object RibCoroutinesConfig {\n  /**\n   * Specify [RibDispatchersProvider] that provide default [CoroutineDispatcher]'s for Rib based\n   * scopes. Defaults to standard [Dispatchers]. Useful in areas where injecting Dispatchers is not\n   * ideal, such as Test.\n   */\n  @JvmStatic public var dispatchers: RibDispatchersProvider = DefaultRibDispatchers()\n\n  /**\n   * Specify [CoroutineExceptionHandler] to be used with Rib based scopes. Defaults to throwing\n   * exception. Useful for specifying additional information before passed to\n   * [Thread.UncaughtExceptionHandler].\n   */\n  @JvmStatic public var exceptionHandler: CoroutineExceptionHandler? = null\n\n  /**\n   * When set, the `coroutineScope` extension property will fail silently (i.e. not throw) when\n   * accessed after the scope has completed.\n   *\n   * Defaults to `false`.\n   */\n  @JvmStatic public var shouldCoroutineScopeFailSilentlyOnLifecycleEnded: Boolean = false\n\n  /**\n   * Specify the [CoroutineDispatcher] to be used while binding a [com.uber.rib.Worker] via\n   * [WorkerBinder]\n   *\n   * IMPORTANT: This shouldn't be used outside [WorkerBinder] given that [WorkerBinder]/[Worker] are\n   * deprecated\n   */\n  @Deprecated(\n    message =\n      \"\"\"\n      This dispatcher is only intended to be used within the [com.uber.rib.core.WorkerBinder].\n      For adding and binding new RIB workers please use [RibCoroutineWorker]\n      \"\"\",\n  )\n  @JvmStatic\n  public var deprecatedWorkerDispatcher: CoroutineDispatcher = RibDispatchers.Unconfined\n}\n"
  },
  {
    "path": "libraries/rib-coroutines/src/main/kotlin/com/uber/rib/core/RibDispatchers.kt",
    "content": "/*\n * Copyright (C) 2022. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport kotlin.coroutines.ContinuationInterceptor\nimport kotlinx.coroutines.CoroutineDispatcher\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.CoroutineStart\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.IO_PARALLELISM_PROPERTY_NAME\nimport kotlinx.coroutines.MainCoroutineDispatcher\nimport kotlinx.coroutines.MainScope\n\npublic object RibDispatchers : RibDispatchersProvider {\n  override val Default: CoroutineDispatcher\n    get() = RibCoroutinesConfig.dispatchers.Default\n  override val Main: MainCoroutineDispatcher\n    get() = RibCoroutinesConfig.dispatchers.Main\n  override val IO: CoroutineDispatcher\n    get() = RibCoroutinesConfig.dispatchers.IO\n  override val Unconfined: CoroutineDispatcher\n    get() = RibCoroutinesConfig.dispatchers.Unconfined\n}\n\npublic data class DefaultRibDispatchers(\n  override val Default: CoroutineDispatcher = Dispatchers.Default,\n  override val Main: MainCoroutineDispatcher = Dispatchers.Main,\n  override val IO: CoroutineDispatcher = Dispatchers.IO,\n  override val Unconfined: CoroutineDispatcher = Dispatchers.Unconfined,\n) : RibDispatchersProvider\n\n/** Allows providing default Dispatchers used for Rib CoroutineScopes */\npublic interface RibDispatchersProvider {\n\n  /**\n   * The Default [CoroutineDispatcher] that behaves as [Dispatchers.Default].\n   *\n   * The default [CoroutineDispatcher] that is used by all standard builders like\n   * [launch][CoroutineScope.launch], [async][CoroutineScope.async], etc if no dispatcher nor any\n   * other [ContinuationInterceptor] is specified in their context.\n   *\n   * It is backed by a shared pool of threads on JVM. By default, the maximal level of parallelism\n   * used by this dispatcher is equal to the number of CPU cores, but is at least two. Level of\n   * parallelism X guarantees that no more than X tasks can be executed in this dispatcher in\n   * parallel.\n   */\n  public val Default: CoroutineDispatcher\n\n  /**\n   * The Main [CoroutineDispatcher] that behaves as [Dispatchers.Main].\n   *\n   * A coroutine dispatcher that is confined to the Main thread operating with UI objects. This\n   * dispatcher can be used either directly or via [MainScope] factory. Usually such dispatcher is\n   * single-threaded.\n   *\n   * Access to this property may throw [IllegalStateException] if no main thread dispatchers are\n   * present in the classpath.\n   *\n   * Depending on platform and classpath it can be mapped to different dispatchers:\n   * - On JS and Native it is equivalent of [Default] dispatcher.\n   * - On JVM it is either Android main thread dispatcher, JavaFx or Swing EDT dispatcher. It is\n   *   chosen by\n   *   [`ServiceLoader`](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html).\n   *\n   * In order to work with `Main` dispatcher, the following artifacts should be added to project\n   * runtime dependencies:\n   * - `kotlinx-coroutines-android` for Android Main thread dispatcher\n   * - `kotlinx-coroutines-javafx` for JavaFx Application thread dispatcher\n   * - `kotlinx-coroutines-swing` for Swing EDT dispatcher\n   *\n   * In order to set a custom `Main` dispatcher for testing purposes, add the\n   * `kotlinx-coroutines-test` artifact to project test dependencies.\n   *\n   * Implementation note: [MainCoroutineDispatcher.immediate] is not supported on Native and JS\n   * platforms.\n   */\n  public val Main: MainCoroutineDispatcher\n\n  /**\n   * The IO [CoroutineDispatcher] that behaves as [Dispatchers.IO]\n   *\n   * A coroutine dispatcher that is not confined to any specific thread. It executes initial\n   * continuation of the coroutine in the current call-frame and lets the coroutine resume in\n   * whatever thread that is used by the corresponding suspending function, without mandating any\n   * specific threading policy. Nested coroutines launched in this dispatcher form an event-loop to\n   * avoid stack overflows.\n   *\n   * ### Event loop\n   * Event loop semantics is a purely internal concept and have no guarantees on the order of\n   * execution except that all queued coroutines will be executed on the current thread in the\n   * lexical scope of the outermost unconfined coroutine.\n   *\n   * For example, the following code:\n   * ```\n   * withContext(Dispatchers.Unconfined) {\n   *    println(1)\n   *    withContext(Dispatchers.Unconfined) { // Nested unconfined\n   *        println(2)\n   *    }\n   *    println(3)\n   * }\n   * println(\"Done\")\n   * ```\n   *\n   * Can print both \"1 2 3\" and \"1 3 2\", this is an implementation detail that can be changed. But\n   * it is guaranteed that \"Done\" will be printed only when both `withContext` are completed.\n   *\n   * Note that if you need your coroutine to be confined to a particular thread or a thread-pool\n   * after resumption, but still want to execute it in the current call-frame until its first\n   * suspension, then you can use an optional [CoroutineStart] parameter in coroutine builders like\n   * [launch][kotlinx.coroutines.launch] and [async][kotlinx.coroutines.async] setting it to the\n   * value of [CoroutineStart.UNDISPATCHED].\n   */\n  public val IO: CoroutineDispatcher\n\n  /**\n   * The Unconfined [CoroutineDispatcher] that behaves as [Dispatchers.Unconfined]\n   *\n   * The [CoroutineDispatcher] that is designed for offloading blocking IO tasks to a shared pool of\n   * threads.\n   *\n   * Additional threads in this pool are created and are shutdown on demand. The number of threads\n   * used by tasks in this dispatcher is limited by the value of\n   * \"`kotlinx.coroutines.io.parallelism`\" ([IO_PARALLELISM_PROPERTY_NAME]) system property. It\n   * defaults to the limit of 64 threads or the number of cores (whichever is larger).\n   *\n   * Moreover, the maximum configurable number of threads is capped by the\n   * `kotlinx.coroutines.scheduler.max.pool.size` system property. If you need a higher number of\n   * parallel threads, you should use a custom dispatcher backed by your own thread pool.\n   *\n   * ### Implementation note\n   *\n   * This dispatcher shares threads with the [Default][Dispatchers.Default] dispatcher, so using\n   * `withContext(Dispatchers.IO) { ... }` when already running on the\n   * [Default][Dispatchers.Default] dispatcher does not lead to an actual switching to another\n   * thread &mdash; typically execution continues in the same thread. As a result of thread sharing,\n   * more than 64 (default parallelism) threads can be created (but not used) during operations over\n   * IO dispatcher.\n   */\n  public val Unconfined: CoroutineDispatcher\n}\n"
  },
  {
    "path": "libraries/rib-coroutines/src/main/kotlin/com/uber/rib/core/internal/CoroutinesFriendModuleApi.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core.internal\n\n/*\n * Methods that are visible only to rib-coroutines friend modules.\n *\n * Anything marked with this annotation is not intended for public use.\n */\n@RequiresOptIn(level = RequiresOptIn.Level.ERROR)\n@Retention(AnnotationRetention.BINARY)\ninternal annotation class CoroutinesFriendModuleApi\n"
  },
  {
    "path": "libraries/rib-coroutines/src/test/kotlin/com/uber/rib/core/RibCoroutineScopesTest.kt",
    "content": "/*\n * Copyright (C) 2024. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.google.common.truth.Truth.assertThat\nimport com.uber.autodispose.lifecycle.LifecycleEndedException\nimport com.uber.autodispose.lifecycle.LifecycleNotStartedException\nimport kotlin.contracts.ExperimentalContracts\nimport kotlin.contracts.InvocationKind\nimport kotlin.contracts.contract\nimport kotlinx.coroutines.ExperimentalCoroutinesApi\nimport kotlinx.coroutines.Job\nimport kotlinx.coroutines.awaitCancellation\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.test.runCurrent\nimport kotlinx.coroutines.test.runTest\nimport org.junit.Assert.assertThrows\nimport org.junit.Before\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.junit.runners.Parameterized\nimport org.mockito.kotlin.mock\n\n@OptIn(ExperimentalCoroutinesApi::class)\n@RunWith(Parameterized::class)\nclass RibCoroutineScopesTest(private val failSilentlyOnLifecycleEnded: Boolean) {\n  @get:Rule val ribCoroutinesRule = RibCoroutinesRule()\n  private val interactor = TestInteractor()\n\n  @Before\n  fun setUp() {\n    RibCoroutinesConfig.shouldCoroutineScopeFailSilentlyOnLifecycleEnded =\n      failSilentlyOnLifecycleEnded\n  }\n\n  @Test\n  fun coroutineScope_whenCalledBeforeActive_throws() {\n    assertThrows(LifecycleNotStartedException::class.java) { interactor.coroutineScope }\n  }\n\n  @Test\n  fun coroutineScope_whenCalledAfterInactive_throws() {\n    interactor.attachAndDetach {}\n    if (failSilentlyOnLifecycleEnded) {\n      assertThat(interactor.coroutineScope.isActive).isFalse()\n    } else {\n      assertThrows(LifecycleEndedException::class.java) { interactor.coroutineScope }\n    }\n  }\n\n  @Test\n  fun coroutineScope_whenCalledWhileActive_cancelsWhenInactive() = runTest {\n    var launched = false\n    val job: Job\n    interactor.attachAndDetach {\n      job =\n        coroutineScope.launch {\n          launched = true\n          awaitCancellation()\n        }\n      runCurrent()\n      assertThat(launched).isTrue()\n      assertThat(job.isActive).isTrue()\n    }\n    assertThat(job.isCancelled).isTrue()\n  }\n\n  companion object {\n    @JvmStatic\n    @Parameterized.Parameters(name = \"failSilentlyOnLifecycleEnded = {0}\")\n    fun data() = listOf(arrayOf(true), arrayOf(false))\n  }\n}\n\nprivate class TestInteractor : Interactor<Unit, Router<*>>()\n\n@OptIn(ExperimentalContracts::class)\nprivate inline fun TestInteractor.attachAndDetach(block: TestInteractor.() -> Unit) {\n  contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }\n  InteractorHelper.attach(this, Unit, mock(), null)\n  block()\n  InteractorHelper.detach(this)\n}\n"
  },
  {
    "path": "libraries/rib-coroutines-test/README.md",
    "content": "# rib-coroutines-test\n\nThis module is responsible for defining the coroutines test utils for the rib-coroutines module.\n\n## Installation\n```gradle\ndependencies {\n  implementation 'com.uber.rib:rib-coroutines-test:0.16.5'\n}\n```\n\n## Usage\n\nRibCoroutinesRule is a Junit Rule to enable automatic setup and cleanup of TestCoroutineDispatchers with the RibCoroutineConfig.dispatchers global configuration by constructing in Tests.\n\n```kotlin\n@get:Rule var ribCoroutineRule = RibCoroutinesRule()\n```\n\nTestRibCoroutineScopes provides extension functions to enable overriding the coroutineScopes in Interactors and Workers.\n\nOnce enabled, ScopeProvider.testCoroutineScopeOverride and ScopeProvider.coroutineScope are the same instance with the only difference being ScopeProvider.testCoroutineScopeOverride is a convenience API which casts it as a TestCoroutineScope.\n\n```kotlin\n@Test\nfun testCoroutineScope()  = runBlockingTest {\n    val interactor = FakeInteractor<Presenter, Router<*>>()\n    interactor.attach()\n\n    //Enable TestCoroutineScopeOverride\n    interactor.enableTestCoroutineScopeOverride()\n\n    interactor.coroutineScope.launch {\n        //Running in TestCoroutineScope\n    }\n\n    //Disable TestCoroutineScopeOverride\n    interactor.disableTestCoroutineScopeOverride()\n    //Cleanup TestCoroutineScope\n    interactor.testCoroutineScopeOverride!!.cleanupTestCoroutines()\n}\n```\n"
  },
  {
    "path": "libraries/rib-coroutines-test/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nplugins {\n    id(\"ribs.kotlin.library\")\n    alias(libs.plugins.maven.publish)\n}\n\nkotlin.compilerOptions {\n    optIn.add(\"com.uber.rib.core.internal.CoroutinesFriendModuleApi\")\n}\n\ndependencies {\n    api(project(\":libraries:rib-coroutines\"))\n    api(testLibs.kotlinx.coroutines.test)\n    api(testLibs.junit)\n\n    compileOnly(libs.android.api)\n\n    testImplementation(project(\":libraries:rib-base\"))\n    testImplementation(project(\":libraries:rib-test\"))\n    testImplementation(testLibs.junit)\n    testImplementation(testLibs.mockito)\n    testImplementation(testLibs.mockito.kotlin)\n    testImplementation(testLibs.truth)\n    testImplementation(testLibs.kotlinx.coroutines.test)\n    testImplementation(libs.kotlinx.coroutines.android)\n    testImplementation(libs.androidx.annotation)\n    testImplementation(libs.android.api)\n}\n"
  },
  {
    "path": "libraries/rib-coroutines-test/gradle.properties",
    "content": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPOM_NAME=RIBs (Coroutines)\nPOM_ARTIFACT_ID=rib-coroutines-test\nPOM_PACKAGING=jar\n"
  },
  {
    "path": "libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt",
    "content": "/*\n * Copyright (C) 2022. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport kotlinx.coroutines.CoroutineDispatcher\nimport org.junit.rules.TestWatcher\nimport org.junit.runner.Description\n\n/**\n * RibCoroutinesRule is a Junit TestRule to act as a managed TestCoroutineScope in test and to\n * facilitate install and cleanup of Test Dispatchers\n */\npublic class RibCoroutinesRule(\n  public val ribDispatchers: TestRibDispatchers = TestRibDispatchers(),\n) : TestWatcher() {\n\n  private var originalDeprecatedWorkerDispatcher: CoroutineDispatcher? = null\n  override fun starting(description: Description) {\n    ribDispatchers.installTestDispatchers()\n    originalDeprecatedWorkerDispatcher = RibCoroutinesConfig.deprecatedWorkerDispatcher\n    RibCoroutinesConfig.deprecatedWorkerDispatcher = ribDispatchers.Unconfined\n  }\n\n  override fun finished(description: Description) {\n    ribDispatchers.resetTestDispatchers()\n    RibCoroutinesConfig.deprecatedWorkerDispatcher = originalDeprecatedWorkerDispatcher!!\n  }\n}\n"
  },
  {
    "path": "libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/TestRibCoroutineScopes.kt",
    "content": "/*\n * Copyright (C) 2022. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.app.Application\nimport com.uber.autodispose.ScopeProvider\nimport com.uber.autodispose.coroutinesinterop.autoDispose\nimport io.reactivex.Completable\nimport io.reactivex.CompletableSource\nimport kotlin.coroutines.CoroutineContext\nimport kotlinx.coroutines.SupervisorJob\nimport kotlinx.coroutines.cancel\nimport kotlinx.coroutines.test.TestScope\n\n/** returns the [TestScope] override currently installed for testing. */\npublic val ScopeProvider.testScopeOverride: TestScope?\n  // Due to custom friend path usage, reference to LazyCoroutineScope will stay red in IDE\n  get() =\n    synchronized(LazyCoroutineScope) {\n      val testScope = LazyCoroutineScope[this]\n      return if (testScope != null && testScope is TestScope) testScope else null\n    }\n\n/**\n * Overrides [ScopeProvider.coroutineScope] with a [TestScope] with lifecycle integration for\n * testing. Accessible directly as [TestScope] via [ScopeProvider.TestScopeOverride].\n */\npublic fun ScopeProvider.enableTestScopeOverride(\n  context: CoroutineContext = SupervisorJob(),\n): Unit = synchronized(LazyCoroutineScope) { LazyCoroutineScope[this] = asTestScope(context) }\n\n/** Disables the [ScopeProvider.coroutineScope] override with [TestScope] */\npublic fun ScopeProvider.disableTestScopeOverride(): Unit =\n  synchronized(LazyCoroutineScope) { LazyCoroutineScope[this] = null }\n\n/** returns the [TestScope] override currently installed for testing. */\npublic val Application.testScopeOverride: TestScope?\n  // Due to custom friend path usage, reference to LazyCoroutineScope will stay red in IDE\n  get() =\n    synchronized(LazyCoroutineScope) {\n      val testScope = LazyCoroutineScope[this]\n      return if (testScope != null && testScope is TestScope) testScope else null\n    }\n\n/**\n * Overrides [ScopeProvider.coroutineScope] with a [TestScope] with lifecycle integration for\n * testing. Accessible directly as [TestScope] via [ScopeProvider.TestScopeOverride].\n */\npublic fun Application.enableTestScopeOverride(context: CoroutineContext = SupervisorJob()): Unit =\n  synchronized(LazyCoroutineScope) { LazyCoroutineScope[this] = TestScope(context) }\n\n/** Disables the [ScopeProvider.coroutineScope] override with [TestScope] */\npublic fun Application.disableTestScopeOverride(): Unit =\n  synchronized(LazyCoroutineScope) { LazyCoroutineScope[this] = null }\n\n/** Returns a new [TestScope] from the [ScopeProvider] */\npublic fun ScopeProvider.asTestScope(context: CoroutineContext = SupervisorJob()): TestScope {\n  return requestScope().asTestScope(context)\n}\n\n/** Returns a new [TestScope] from the [CompletableSource] */\npublic fun CompletableSource.asTestScope(context: CoroutineContext = SupervisorJob()): TestScope {\n  val scope = TestScope(context)\n  Completable.wrap(this).autoDispose(scope).subscribe({ scope.cancel() }) { e ->\n    scope.cancel(\"OnError\", e)\n  }\n\n  return scope\n}\n"
  },
  {
    "path": "libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/TestRibDispatchers.kt",
    "content": "/*\n * Copyright (C) 2022. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.ExperimentalCoroutinesApi\nimport kotlinx.coroutines.MainCoroutineDispatcher\nimport kotlinx.coroutines.test.StandardTestDispatcher\nimport kotlinx.coroutines.test.TestCoroutineScheduler\nimport kotlinx.coroutines.test.TestDispatcher\nimport kotlinx.coroutines.test.UnconfinedTestDispatcher\nimport kotlinx.coroutines.test.resetMain\nimport kotlinx.coroutines.test.setMain\n\n@OptIn(ExperimentalCoroutinesApi::class)\npublic data class TestRibDispatchers(\n  /**\n   * [TestCoroutineScheduler] to be used by all other [TestDispatcher] when using the default\n   * constructor.\n   *\n   * Note that when passing in custom dispatchers, this test scheduler will not be used.\n   */\n  private val testScheduler: TestCoroutineScheduler? = TestCoroutineScheduler(),\n  override val Default: TestDispatcher = StandardTestDispatcher(testScheduler),\n  override val IO: TestDispatcher = StandardTestDispatcher(testScheduler),\n  override val Unconfined: TestDispatcher = UnconfinedTestDispatcher(testScheduler),\n  val MainTestDelegate: TestDispatcher = StandardTestDispatcher(testScheduler),\n) : RibDispatchersProvider {\n\n  public fun installTestDispatchers() {\n    // MainTestCoroutineDispatcher is Internal, so we need to wrap it through the main API\n    Dispatchers.setMain(MainTestDelegate)\n    RibCoroutinesConfig.dispatchers = this\n  }\n\n  public fun resetTestDispatchers() {\n    Dispatchers.resetMain()\n    RibCoroutinesConfig.dispatchers = DefaultRibDispatchers()\n  }\n\n  override val Main: MainCoroutineDispatcher\n    get() = Dispatchers.Main\n}\n"
  },
  {
    "path": "libraries/rib-coroutines-test/src/test/kotlin/com/uber/rib/core/RibCoroutinesRuleTest.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.google.common.truth.Truth.assertThat\nimport kotlinx.coroutines.ExperimentalCoroutinesApi\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.test.advanceTimeBy\nimport kotlinx.coroutines.test.runCurrent\nimport kotlinx.coroutines.test.runTest\nimport org.junit.Rule\nimport org.junit.Test\n\n@OptIn(ExperimentalCoroutinesApi::class)\nclass RibCoroutinesRuleTest {\n  @get:Rule val ribCoroutinesRule = RibCoroutinesRule()\n\n  @Test\n  internal fun testScheduler() = runTest {\n    var reached = false\n    launch(RibDispatchers.Default) {\n      delay(100)\n      reached = true\n    }\n    advanceTimeBy(100)\n    runCurrent()\n    assertThat(reached).isTrue()\n  }\n}\n"
  },
  {
    "path": "libraries/rib-coroutines-test/src/test/kotlin/com/uber/rib/core/RibDispatchersTest.kt",
    "content": "/*\n * Copyright (C) 2022. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.google.common.truth.Truth.assertThat\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.test.TestDispatcher\nimport org.junit.Test\n\ninternal class RibDispatchersTest {\n\n  @Test\n  internal fun testInitialInstances() {\n    assertThat(RibDispatchers.Default).isEqualTo(Dispatchers.Default)\n    assertThat(RibDispatchers.IO).isEqualTo(Dispatchers.IO)\n    assertThat(RibDispatchers.Main).isEqualTo(Dispatchers.Main)\n    assertThat(RibDispatchers.Unconfined).isEqualTo(Dispatchers.Unconfined)\n  }\n\n  @Test\n  internal fun testSetConfigDelegate() {\n    assertThat(RibDispatchers.Default).isNotInstanceOf(TestDispatcher::class.java)\n    assertThat(RibDispatchers.IO).isNotInstanceOf(TestDispatcher::class.java)\n    assertThat(RibDispatchers.Main).isNotInstanceOf(TestDispatcher::class.java)\n    assertThat(RibDispatchers.Unconfined).isNotInstanceOf(TestDispatcher::class.java)\n\n    val testDispatchers = TestRibDispatchers()\n    testDispatchers.installTestDispatchers()\n\n    assertThat(RibDispatchers.Default).isEqualTo(testDispatchers.Default)\n    assertThat(RibDispatchers.IO).isEqualTo(testDispatchers.IO)\n    assertThat(RibDispatchers.Main).isEqualTo(testDispatchers.Main)\n    assertThat(RibDispatchers.Unconfined).isEqualTo(testDispatchers.Unconfined)\n\n    testDispatchers.resetTestDispatchers()\n  }\n}\n"
  },
  {
    "path": "libraries/rib-coroutines-test/src/test/kotlin/com/uber/rib/core/RibScopesTest.kt",
    "content": "/*\n * Copyright (C) 2022. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport android.app.Application\nimport com.google.common.truth.Truth.assertThat\nimport kotlin.coroutines.CoroutineContext\nimport kotlinx.coroutines.CoroutineExceptionHandler\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.test.TestScope\nimport kotlinx.coroutines.test.runTest\nimport org.junit.Rule\nimport org.junit.Test\nimport org.mockito.kotlin.mock\n\ninternal class RibScopesTest {\n\n  @get:Rule var rule = RibCoroutinesRule()\n\n  @Test\n  internal fun testScopeLifecycle() = runTest {\n    val interactor = FakeInteractor<Presenter, Router<*>>()\n    interactor.attach()\n    val job =\n      interactor.coroutineScope.launch {\n        while (isActive) {\n          delay(5L)\n        }\n      }\n    assertThat(job.isActive).isTrue()\n    interactor.detach()\n    assertThat(job.isActive).isFalse()\n  }\n\n  @Test\n  internal fun testScopeLifecycleWithTestScope() = runTest {\n    val interactor = FakeInteractor<Presenter, Router<*>>()\n    interactor.attach()\n    interactor.enableTestScopeOverride()\n\n    val job =\n      interactor.coroutineScope.launch {\n        while (isActive) {\n          delay(5L)\n        }\n      }\n    assertThat(job.isActive).isTrue()\n    interactor.detach()\n    assertThat(job.isActive).isFalse()\n  }\n\n  @Test()\n  internal fun testScopeCaching() {\n    val interactor1 = FakeInteractor<Presenter, Router<*>>()\n    val interactor2 = FakeInteractor<Presenter, Router<*>>()\n    interactor1.attach()\n    interactor2.attach()\n\n    val interactor1mainScope1 = interactor1.coroutineScope\n    val interactor1mainScope2 = interactor1.coroutineScope\n    val interactor2mainScope1 = interactor2.coroutineScope\n\n    assertThat(interactor1mainScope1).isEqualTo(interactor1mainScope2)\n    assertThat(interactor1mainScope1).isNotEqualTo(interactor2mainScope1)\n  }\n\n  // Bad test: The RuntimeException thrown is actually NoSuchElementException (handler exceptions is\n  // empty).\n  @Test(expected = RuntimeException::class)\n  internal fun testUncaughtHandler() = runTest {\n    val handler = TestUncaughtExceptionCaptor()\n    RibCoroutinesConfig.exceptionHandler = handler\n\n    val interactor = FakeInteractor<Presenter, Router<*>>()\n    interactor.attach()\n    interactor.coroutineScope.launch { throw RuntimeException(\"mainScope failed\") }\n    throw (handler.exceptions.first())\n  }\n\n  // Bad test: The RuntimeException is actually thrown by Interactor.requestScope(), because it is\n  // called before\n  // attaching the interactor.\n  @Test(expected = RuntimeException::class)\n  internal fun testException() = runTest {\n    val interactor = FakeInteractor<Presenter, Router<*>>()\n    interactor.enableTestScopeOverride()\n    interactor.attach()\n    interactor.coroutineScope.launch { throw RuntimeException(\"mainScope failed\") }\n  }\n\n  @Test()\n  internal fun testSetTestScopeOverride() {\n    val interactor = FakeInteractor<Presenter, Router<*>>()\n    interactor.attach()\n\n    assertThat(interactor.testScopeOverride).isNull()\n\n    interactor.enableTestScopeOverride()\n    val testScope = interactor.testScopeOverride\n    val realScope = interactor.coroutineScope\n    assertThat(testScope).isInstanceOf(TestScope::class.java)\n    assertThat(testScope).isEqualTo(realScope)\n\n    interactor.disableTestScopeOverride()\n    val testScope2 = interactor.testScopeOverride\n    val realScope2 = interactor.coroutineScope\n    assertThat(testScope2).isNull()\n    assertThat(realScope2).isNotInstanceOf(TestScope::class.java)\n  }\n\n  @Test()\n  internal fun testSetTestScopeOnApplicationOverride() {\n    // Can use mock since all logic is in extension function.\n    val application: Application = mock()\n\n    assertThat(application.testScopeOverride).isNull()\n\n    application.enableTestScopeOverride()\n    val testScope = application.testScopeOverride\n    val realScope = application.coroutineScope\n    assertThat(testScope).isInstanceOf(TestScope::class.java)\n    assertThat(testScope).isEqualTo(realScope)\n\n    application.disableTestScopeOverride()\n    val testScope2 = application.testScopeOverride\n    val realScope2 = application.coroutineScope\n    assertThat(testScope2).isNull()\n    assertThat(realScope2).isNotInstanceOf(TestScope::class.java)\n  }\n\n  @Test\n  fun testScopeReattaching() {\n    val interactor = object : BasicInteractor<Presenter, Router<*>>(mock()) {}\n    with(interactor) {\n      dispatchAttach(null)\n      with(coroutineScope) {\n        assertThat(isActive).isTrue()\n        dispatchDetach()\n        // after dispatching detach, we expect the same instance captured before to be inactive\n        assertThat(isActive).isFalse()\n      }\n      dispatchAttach(null)\n      // The previous instance of coroutineScope is permanently cancelled,\n      // but ScopeProvider.coroutineScope should now return a new, active instance.\n      assertThat(coroutineScope.isActive).isTrue()\n    }\n  }\n\n  private class TestUncaughtExceptionCaptor : CoroutineExceptionHandler {\n    var exceptions = mutableListOf<Throwable>()\n\n    override val key: CoroutineContext.Key<*> = CoroutineExceptionHandler\n    override fun handleException(context: CoroutineContext, exception: Throwable) {\n      exceptions.add(exception)\n    }\n  }\n}\n"
  },
  {
    "path": "libraries/rib-debug-utils/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport org.jetbrains.kotlin.gradle.dsl.JvmTarget\n\nplugins {\n    id(\"ribs.kotlin.library\")\n    alias(libs.plugins.maven.publish)\n}\n\nkotlin {\n    explicitApi()\n    jvmToolchain(17)\n\n    compilerOptions {\n        jvmTarget = JvmTarget.JVM_1_8\n        optIn.add(\"com.uber.rib.core.internal.CoreFriendModuleApi\")\n    }\n}\n\ndependencies {\n    implementation(project(\":libraries:rib-base\"))\n}\n"
  },
  {
    "path": "libraries/rib-debug-utils/gradle.properties",
    "content": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPOM_NAME=RIBs (Debug Utils)\nPOM_ARTIFACT_ID=rib-debug-utils\nPOM_PACKAGING=jar\n"
  },
  {
    "path": "libraries/rib-debug-utils/src/main/kotlin/com/uber/rib/core/RouterDebugUtils.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/** Debugging utilities when working with Routers. */\npublic object RouterDebugUtils {\n  private const val ARM_RIGHT = \"└── \"\n  private const val INTERSECTION = \"├── \"\n  private const val LINE = \"│   \"\n  private const val SPACE = \"    \"\n\n  /**\n   * Prints out the tree of routers from this point.\n   *\n   * @param router [Router] root router of a RIB tree\n   * @param prefix [String] text to print before the tree\n   * @param isTail [Boolean] true if is tail node; otherwise, false\n   */\n  @JvmStatic\n  @JvmOverloads\n  public fun printRouterSubtree(router: Router<*>, prefix: String = \"\", isTail: Boolean = true) {\n    Rib.getConfiguration()\n      .handleDebugMessage(prefix + (if (isTail) ARM_RIGHT else INTERSECTION) + router.tag)\n    val children = router.getChildren()\n    for (i in 0 until children.size - 1) {\n      printRouterSubtree(children[i], prefix + if (isTail) SPACE else LINE, false)\n    }\n    if (children.size > 0) {\n      printRouterSubtree(children[children.size - 1], prefix + if (isTail) SPACE else LINE, true)\n    }\n  }\n}\n"
  },
  {
    "path": "libraries/rib-router-navigator/README.md",
    "content": "# rib-router-navigator\n\nA utility for handling multiple mutually exclusive child routers. These module is by no means a\nmandatory part of the RIB framework.\n"
  },
  {
    "path": "libraries/rib-router-navigator/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nplugins {\n    id(\"ribs.kotlin.library\")\n    alias(libs.plugins.maven.publish)\n}\n\ndependencies {\n    implementation(libs.checkerqual)\n    implementation(libs.autodispose.coroutines)\n    implementation(libs.kotlinx.coroutines.android)\n    implementation(libs.kotlinx.coroutines.rx2)\n    implementation(project(\":libraries:rib-base\"))\n    compileOnly(libs.androidx.annotation)\n    compileOnly(libs.android.api)\n\n    testImplementation(testLibs.junit)\n    testImplementation(testLibs.mockito.kotlin)\n    testImplementation(testLibs.truth)\n}\n"
  },
  {
    "path": "libraries/rib-router-navigator/gradle.properties",
    "content": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPOM_NAME=RIBs (Router Navigator)\nPOM_ARTIFACT_ID=rib-router-navigator\nPOM_PACKAGING=jar\n"
  },
  {
    "path": "libraries/rib-router-navigator/src/main/kotlin/com/uber/rib/core/RouterAndState.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport androidx.annotation.VisibleForTesting\nimport java.util.concurrent.locks.ReentrantLock\nimport kotlin.concurrent.withLock\n\n/**\n * Internal class for keeping track of a navigation stack.\n *\n * @param state The associated state\n * @param attachTransition The [RouterNavigator.AttachTransition] associated with this state.\n * @param detachTransition The [RouterNavigator.DetachTransition] associated with this state.\n * @param forceRouterCaching Override [RouterNavigatorState.isCacheable] behavior\n */\ninternal class RouterAndState<R : Router<*>, StateT : RouterNavigatorState>(\n  val state: StateT,\n  private val attachTransition: RouterNavigator.AttachTransition<R, StateT>,\n  detachTransition: RouterNavigator.DetachTransition<R, StateT>?,\n  @get:VisibleForTesting val forceRouterCaching: Boolean = false,\n) {\n  private val routerAccessor = SafeRouterAccessor(attachTransition::buildRouter)\n\n  /**\n   * Gets or creates the [Router] associated with this state. Router will be destroyed after\n   * [RouterNavigator.DetachCallback.onPostDetachFromHost] and if [StateT] is cacheable.\n   */\n  internal val router: R by routerAccessor::router\n\n  internal fun willAttachToHost(previousState: StateT?, isPush: Boolean) =\n    routerAccessor.safeOperation { safeRouter ->\n      attachTransition.willAttachToHost(safeRouter, previousState, state, isPush)\n    }\n\n  internal fun willDetachFromHost(newState: StateT?, isPush: Boolean) =\n    routerAccessor.safeOperation { safeRouter ->\n      detachCallback.willDetachFromHost(safeRouter, state, newState, isPush)\n    }\n\n  internal fun onPostDetachFromHost(newState: StateT?, isPush: Boolean) =\n    routerAccessor.safeOperation { safeRouter ->\n      detachCallback.onPostDetachFromHost(safeRouter, newState, isPush)\n    }\n\n  private val detachCallback: RouterNavigator.DetachCallback<R, StateT> by lazy {\n    RouterDestroyerCallbackWrapper(\n      baseCallback = wrapDetachTransitionIfNeed(detachTransition),\n      onDestroy = {\n        routerAccessor.safeConditionalDestroy(state) { !forceRouterCaching && !state.isCacheable() }\n      },\n    )\n  }\n\n  private fun wrapDetachTransitionIfNeed(\n    detachTransition: RouterNavigator.DetachTransition<R, StateT>?,\n  ): RouterNavigator.DetachCallback<R, StateT>? {\n    return (detachTransition as? RouterNavigator.DetachCallback)\n      ?: detachTransition?.let { DetachCallbackWrapper(it) }\n  }\n\n  /**\n   * Wrapper class to wrap [transitionCallback] calls into the new [RouterNavigator.DetachCallback]\n   * format.\n   */\n  private inner class DetachCallbackWrapper(\n    private val transitionCallback: RouterNavigator.DetachTransition<R, StateT>,\n  ) : RouterNavigator.DetachCallback<R, StateT>() {\n\n    override fun willDetachFromHost(\n      router: R,\n      previousState: StateT,\n      newState: StateT?,\n      isPush: Boolean,\n    ) = transitionCallback.willDetachFromHost(router, previousState, newState, isPush)\n  }\n\n  /**\n   * Wrapper class to wrap [RouterNavigator.DetachCallback] and call [onDestroy] after\n   * [RouterNavigator.DetachCallback.onPostDetachFromHost]\n   */\n  private inner class RouterDestroyerCallbackWrapper(\n    private val baseCallback: RouterNavigator.DetachCallback<R, StateT>?,\n    private val onDestroy: () -> Unit,\n  ) : RouterNavigator.DetachCallback<R, StateT>() {\n\n    override fun willDetachFromHost(\n      router: R,\n      previousState: StateT,\n      newState: StateT?,\n      isPush: Boolean,\n    ) {\n      baseCallback?.willDetachFromHost(router, previousState, newState, isPush)\n    }\n\n    override fun onPostDetachFromHost(router: R, newState: StateT?, isPush: Boolean) {\n      baseCallback?.onPostDetachFromHost(router, newState, isPush)\n      onDestroy.invoke()\n    }\n  }\n\n  private class SafeRouterAccessor<R : Router<*>>(\n    private val routerBuilder: () -> R,\n  ) {\n    private val lock = ReentrantLock()\n    private var _router: R? = null\n\n    val router: R\n      get() =\n        lock.withLock {\n          _router\n            ?: routerBuilder().let { newRouter ->\n              log(\"Router ${newRouter.javaClass.simpleName} was created\")\n              _router = newRouter\n              newRouter\n            }\n        }\n\n    fun safeOperation(operation: (R) -> Unit) = lock.withLock { operation(router) }\n\n    fun safeConditionalDestroy(state: RouterNavigatorState, condition: () -> Boolean) =\n      lock.withLock {\n        if (condition.invoke()) {\n          _router?.javaClass?.simpleName?.let { routerName ->\n            log(\"Destroying router $routerName was destroyed\")\n            _router = null\n          }\n            ?: run { log(\"Router of ${state.stateName()} state already destroyed\") }\n        }\n      }\n  }\n\n  companion object {\n    /** Writes out to the debug log. */\n    private fun log(text: String) {\n      Rib.getConfiguration().handleDebugMessage(\"%s: $text\", \"RouterNavigator\")\n    }\n  }\n}\n"
  },
  {
    "path": "libraries/rib-router-navigator/src/main/kotlin/com/uber/rib/core/RouterNavigator.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport androidx.annotation.IntRange\nimport org.checkerframework.checker.guieffect.qual.PolyUIEffect\nimport org.checkerframework.checker.guieffect.qual.PolyUIType\nimport org.checkerframework.checker.guieffect.qual.UIEffect\n\n/**\n * Simple utility for switching a child router based on a state.\n *\n * @param <StateT> type of state to switch on.\n */\npublic interface RouterNavigator<StateT : RouterNavigatorState> {\n  /** Determine how pushes will affect the stack */\n  public enum class Flag {\n    /** Push a new state to the top of the navigation stack. */\n    DEFAULT,\n\n    /** Push a state that will not be retained when the next state is pushed. */\n    TRANSIENT,\n\n    /**\n     * Start looking at the stack from the top until we see the state that is being pushed. If it is\n     * found, remove all states that we traversed and transition the app to the found state. If the\n     * state is not found in the stack, create a new instance and transition to it pushing it on top\n     * of the stack.\n     */\n    CLEAR_TOP,\n\n    /**\n     * First create a new instance of the state and push it to the top of the stack. Then traverse\n     * down the stack and and delete any instances of the state being pushed.\n     */\n    SINGLE_TOP,\n\n    /**\n     * Search through the stack for the state and if found, move the state to the top but don’t\n     * change the stack otherwise. If the state doesn’t exist in the stack, create a new instance\n     * and push to the top.\n     */\n    REORDER_TO_TOP,\n\n    /** Clears the previous stack (no back stack) and pushes the state on to the top of the stack */\n    NEW_TASK,\n\n    /**\n     * Clears the previous stack (no back stack) and pushes the state on to the top of the stack. If\n     * the state is already on the top of the stack, it is detached and replaced.\n     */\n    NEW_TASK_REPLACE,\n\n    /**\n     * Remove the top state in the stack if it is not empty and then push a new state to the top of\n     * the stack.\n     */\n    REPLACE_TOP,\n  }\n\n  /** Pop the current state and rewind to the previous state (if there is a previous state). */\n  public fun popState()\n\n  /**\n   * Switch to a new state - this will switch out children if the state is not the current active\n   * state already.\n   *\n   * NOTE: This will retain the Riblet in memory (if [RouterNavigatorState.isCacheable] is TRUE)\n   * until it is popped or detached by a push with certain flags.\n   *\n   * @param newState to switch to.\n   * @param attachTransition method to attach child router.\n   * @param detachTransition method to clean up child router when removed.\n   * @param <R> router type to detach. </R>\n   */\n  public fun <R : Router<*>> pushState(\n    newState: StateT,\n    attachTransition: AttachTransition<R, StateT>,\n    detachTransition: DetachTransition<R, StateT>?,\n  )\n\n  /**\n   * Switch to a new state - this will switch out children if the state is not the current active\n   * state already. The transition will be controlled by the [StackRouterNavigator.Flag] provided.\n   *\n   * NOTE: This will retain the Riblet in memory (if [RouterNavigatorState.isCacheable] is TRUE)\n   * until it is popped or detached by a push with certain flags.\n   *\n   * @param newState to switch to.\n   * @param attachTransition method to attach child router.\n   * @param detachTransition method to clean up child router when removed.\n   * @param <R> router type to detach. </R>\n   */\n  public fun <R : Router<*>> pushState(\n    newState: StateT,\n    flag: Flag,\n    attachTransition: AttachTransition<R, StateT>,\n    detachTransition: DetachTransition<R, StateT>?,\n  )\n\n  /**\n   * Switch to a new state - this will switch out children if the state is not the current active\n   * state already.\n   *\n   * NOTE: This will retain the Riblet in memory (if [RouterNavigatorState.isCacheable] is TRUE)\n   * until it is popped. To push transient, riblets, use [RouterNavigator.pushTransientState]\n   *\n   * Deprecated: Use pushState(newState, attachTransition, detachTransition)\n   *\n   * @param newState to switch to.\n   * @param attachTransition method to attach child router.\n   * @param detachTransition method to clean up child router when removed.\n   * @param <R> router type to detach. </R>\n   */\n  @Deprecated(\"\")\n  public fun <R : Router<*>> pushRetainedState(\n    newState: StateT,\n    attachTransition: AttachTransition<R, StateT>,\n    detachTransition: DetachTransition<R, StateT>?,\n  )\n\n  /**\n   * Switch to a new state - this will switch out children if the state is not the current active\n   * state already.\n   *\n   * NOTE: This will retain the Riblet in memory (if [RouterNavigatorState.isCacheable] is TRUE)\n   * until it is popped. To push transient, riblets, use [RouterNavigator.pushTransientState]\n   *\n   * Deprecated: Use pushState(newState, attachTransition, null)\n   *\n   * @param newState to switch to.\n   * @param attachTransition method to attach child router.\n   * @param <R> [Router] type. </R>\n   */\n  @Deprecated(\"\")\n  public fun <R : Router<*>> pushRetainedState(\n    newState: StateT,\n    attachTransition: AttachTransition<R, StateT>,\n  )\n\n  /**\n   * Switch to a new transient state - this will switch out children if the state is not the current\n   * active state already.\n   *\n   * NOTE: Transient states do not live in the back navigation stack.\n   *\n   * Deprecated: Use pushState(newState, Flag.TRANSIENT, attachTransition, detachTransition)\n   *\n   * @param newState to switch to.\n   * @param attachTransition method to attach child router.\n   * @param detachTransition method to clean up child router when removed.\n   * @param <R> router type to detach. </R>\n   */\n  @Deprecated(\"\")\n  public fun <R : Router<*>> pushTransientState(\n    newState: StateT,\n    attachTransition: AttachTransition<R, StateT>,\n    detachTransition: DetachTransition<R, StateT>?,\n  )\n\n  /**\n   * Switch to a new transient state - this will switch out children if the state is not the current\n   * active state already.\n   *\n   * NOTE: Transient states do not live in the back navigation stack.\n   *\n   * Deprecated: Use pushState(newState, Flag.TRANSIENT, attachTransition, null)\n   *\n   * @param newState to switch to.\n   * @param attachTransition method to attach child router.\n   * @param <R> [Router] type. </R>\n   */\n  @Deprecated(\"\")\n  public fun <R : Router<*>> pushTransientState(\n    newState: StateT,\n    attachTransition: AttachTransition<R, StateT>,\n  )\n\n  /**\n   * Peek the top [Router] on the stack.\n   *\n   * @return the top [Router] on the stack.\n   */\n  public fun peekRouter(): Router<*>?\n\n  /**\n   * Peek the top [StateT] on the stack.\n   *\n   * @return the top [StateT] on the stack.\n   */\n  public fun peekState(): StateT?\n\n  /**\n   * Gets the size of the navigation stack.\n   *\n   * @return Size of the navigation stack.\n   */\n  @IntRange(from = 0) public fun size(): Int\n\n  /**\n   * Must be called when host interactor is going to detach. This will pop the current active router\n   * and clear the entire stack.\n   */\n  public fun hostWillDetach()\n\n  /**\n   * Allows consumers to write custom attachment logic when switching states.\n   *\n   * @param <StateT> state type. </StateT>\n   */\n  public interface AttachTransition<RouterT : Router<*>, StateT : RouterNavigatorState> {\n    /**\n     * Constructs a new [RouterT] instance. This will only be called once.\n     *\n     * @return the newly attached child router.\n     */\n    public fun buildRouter(): RouterT\n\n    /**\n     * Prepares the router for a state transition. [StackRouterNavigator] will handling attaching\n     * the router, but consumers of this should handle adding any views.\n     *\n     * @param router [RouterT] that is being attached.\n     * @param previousState state the navigator is transition from (if any).\n     * @param newState state the navigator is transitioning to.\n     */\n    @UIEffect\n    public fun willAttachToHost(\n      router: RouterT,\n      previousState: StateT?,\n      newState: StateT,\n      isPush: Boolean,\n    )\n  }\n\n  /**\n   * Allows consumers to write custom detachment logic when the state is changing. This allows for\n   * custom state prior to and immediately post detach.\n   *\n   * @param <RouterT> [RouterT]\n   * @param <StateT> [StateT] </StateT></RouterT>\n   */\n  public abstract class DetachCallback<RouterT : Router<*>, StateT : RouterNavigatorState> :\n    DetachTransition<RouterT, StateT> {\n    override fun willDetachFromHost(\n      router: RouterT,\n      previousState: StateT,\n      newState: StateT?,\n      isPush: Boolean,\n    ) {}\n\n    /**\n     * Notifies the consumer that the [StackRouterNavigator] has detached the supplied [ ].\n     * Consumers can complete any post detachment behavior here.\n     *\n     * @param router [Router]\n     * @param newState [StateT]\n     */\n    public open fun onPostDetachFromHost(router: RouterT, newState: StateT?, isPush: Boolean) {}\n  }\n\n  /**\n   * Allows consumers to write custom detachment logic wen switching states.\n   *\n   * @param <RouterT> router type to detach.\n   * @param <StateT> state type. </StateT></RouterT>\n   */\n  @PolyUIType\n  public interface DetachTransition<RouterT : Router<*>, StateT : RouterNavigatorState> {\n    /**\n     * Notifies consumer that [StackRouterNavigator] is going to detach this router. Consumers\n     * should remove any views and perform any required cleanup.\n     *\n     * @param router being removed.\n     * @param previousState state the navigator is transitioning out of.\n     * @param newState state the navigator is transition in to (if any).\n     */\n    @PolyUIEffect\n    public fun willDetachFromHost(\n      router: RouterT,\n      previousState: StateT,\n      newState: StateT?,\n      isPush: Boolean,\n    )\n  }\n}\n"
  },
  {
    "path": "libraries/rib-router-navigator/src/main/kotlin/com/uber/rib/core/RouterNavigatorEvent.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/** Event which is triggered when [StackRouterNavigator] is used to navigate between routers. */\npublic open class RouterNavigatorEvent(\n  /** @return the [RouterNavigatorEventType] */\n  public open val eventType: RouterNavigatorEventType,\n\n  /** @return the instance of Parent [Router] */\n  public open val parentRouter: Router<*>,\n\n  /** @return the instance of child [Router] */\n  public open val router: Router<*>,\n)\n"
  },
  {
    "path": "libraries/rib-router-navigator/src/main/kotlin/com/uber/rib/core/RouterNavigatorEventType.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/** Enum consisting of event types that occur when [RouterNavigator] is used for transition. */\npublic enum class RouterNavigatorEventType {\n  WILL_ATTACH_TO_HOST,\n}\n"
  },
  {
    "path": "libraries/rib-router-navigator/src/main/kotlin/com/uber/rib/core/RouterNavigatorEvents.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport io.reactivex.Observable\nimport kotlinx.coroutines.channels.BufferOverflow\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.rx2.asObservable\n\n/** Class that provides its instance to emit or subscribe to [RouterNavigatorEvent] */\npublic class RouterNavigatorEvents private constructor() {\n\n  private val _events = MutableSharedFlow<RouterNavigatorEvent>(0, 1, BufferOverflow.DROP_OLDEST)\n\n  /** @return the stream which can be subcribed to listen for [RouterNavigatorEvent] */\n  public val events: Observable<RouterNavigatorEvent> = _events.asObservable()\n\n  @JvmSynthetic // Hide from Java consumers. In Java, `getEvents` resolves to the `events` property.\n  @JvmName(\"_getEvents\")\n  @Deprecated( // Deprecate for Kotlin consumers.\n    message = \"Use the 'events' property\",\n    replaceWith = ReplaceWith(\"events\"),\n  )\n  public fun getEvents(): Observable<RouterNavigatorEvent> = events\n\n  /**\n   * Emits a new [RouterNavigatorEvent] on the stream.\n   *\n   * @param eventType type of the navigation event.\n   * @param parent router instance to which child will attach to.\n   * @param child router instance which getting attached.\n   */\n  public fun emitEvent(eventType: RouterNavigatorEventType, parent: Router<*>, child: Router<*>) {\n    _events.tryEmit(RouterNavigatorEvent(eventType, parent, child))\n  }\n\n  public companion object {\n    /** @return the singleton instance */\n    @JvmStatic public val instance: RouterNavigatorEvents = RouterNavigatorEvents()\n  }\n}\n"
  },
  {
    "path": "libraries/rib-router-navigator/src/main/kotlin/com/uber/rib/core/RouterNavigatorFactory.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/**\n * Factory for the creation of [RouterNavigator]s.\n *\n * Sets up the [RouterNavigatorFactory] to generate [RouterNavigator]s using the provided\n * [Strategy]. This allows implementers to include migration strategies, feature flagging, and other\n * tactics to select a different implementation of the [RouterNavigator].\n *\n * @param creationStrategy [Strategy] `null` for the default strategy.\n */\npublic class RouterNavigatorFactory(private val creationStrategy: Strategy?) {\n  /**\n   * Generate a new [RouterNavigator].\n   *\n   * @param hostRouter Hosting [Router]\n   * @param <StateT> [StateT] type for the [RouterNavigator]\n   * @return A new [RouterNavigator]\n   */\n  public fun <StateT : RouterNavigatorState> create(\n    hostRouter: Router<*>,\n  ): RouterNavigator<StateT> {\n    return creationStrategy?.create(hostRouter) ?: StackRouterNavigator(hostRouter)\n  }\n\n  /** Strategy to employ when using this factory to generate new [RouterNavigator]s. */\n  public interface Strategy {\n    /**\n     * Generate a new [RouterNavigator].\n     *\n     * @param hostRouter Hosting [Router]\n     * @param <StateT> [StateT] type for the [RouterNavigator]\n     * @return A new [RouterNavigator]\n     */\n    public fun <StateT : RouterNavigatorState> create(\n      hostRouter: Router<*>,\n    ): RouterNavigator<StateT>\n  }\n}\n"
  },
  {
    "path": "libraries/rib-router-navigator/src/main/kotlin/com/uber/rib/core/RouterNavigatorState.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/** Represents states for [StackRouterNavigator]. Most often implemented with an enum. */\npublic interface RouterNavigatorState {\n\n  /** @return identifier for a [StackRouterNavigator] state. */\n  public fun stateName(): String {\n    return if (this.javaClass.isEnum) {\n      (this as Enum<*>).name\n    } else {\n      throw java.lang.AssertionError(\"Must be implemented by enum or override stateName()\")\n    }\n  }\n\n  /**\n   * @return Boolean flag configure router caching behavior between transactions.\n   *\n   * TRUE - same instance of router will be reused in all [RouterNavigator.AttachTransition] (not\n   * recommended as might produce memory leak. Usage of [Router.dispatchAttach] and\n   * [Router.saveInstanceState] is preferred option)\n   *\n   * FALSE - router instance will be destroyed after\n   * [RouterNavigator.DetachCallback.onPostDetachFromHost] and will be recreated for next\n   * [RouterNavigator.AttachTransition]\n   */\n  public fun isCacheable(): Boolean = false\n}\n"
  },
  {
    "path": "libraries/rib-router-navigator/src/main/kotlin/com/uber/rib/core/StackRouterNavigator.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport androidx.annotation.IntRange\nimport androidx.annotation.VisibleForTesting\nimport com.uber.rib.core.RouterNavigatorEvents.Companion.instance\nimport java.util.ArrayDeque\nimport java.util.Locale\n\n/**\n * Simple utility for switching a child router based on a state.\n *\n * @param hostRouter Host router\n * @param forceRouterCaching Override [RouterNavigatorState.isCacheable] behavior\n */\npublic open class StackRouterNavigator<StateT : RouterNavigatorState>\n@JvmOverloads\nconstructor(\n  private val hostRouter: Router<*>,\n  // Enabled by default for safe migration to newer lib version\n  private val forceRouterCaching: Boolean = true,\n) : RouterNavigator<StateT> {\n\n  @VisibleForTesting internal val navigationStack = ArrayDeque<RouterAndState<*, StateT>>()\n  private val hostRouterName: String = hostRouter.javaClass.simpleName\n  private var currentTransientRouterAndState: RouterAndState<*, StateT>? = null\n\n  override fun popState() {\n    // If we are in a transient state, go ahead and pop that state.\n    var fromState: RouterAndState<*, StateT>? = null\n    if (currentTransientRouterAndState != null) {\n      fromState = currentTransientRouterAndState\n      val fromRouterName = fromState?.router?.javaClass?.simpleName\n      currentTransientRouterAndState = null\n      log(\n        String.format(\n          Locale.getDefault(),\n          \"Preparing to pop existing transient state for router: %s\",\n          fromRouterName,\n        ),\n      )\n    } else {\n      if (!navigationStack.isEmpty()) {\n        fromState = navigationStack.pop()\n        val fromRouterName: String = fromState.router.javaClass.simpleName\n        log(\n          String.format(\n            Locale.getDefault(),\n            \"Preparing to pop existing state for router: %s\",\n            fromRouterName,\n          ),\n        )\n      }\n    }\n    if (fromState != null) {\n      // Pull the incoming state (So we can restore it.)\n      var toState: RouterAndState<*, StateT>? = null\n      if (!navigationStack.isEmpty()) {\n        toState = navigationStack.peek()\n      }\n      detachInternal(fromState, toState?.state, false)\n      if (toState != null) {\n        attachInternal(fromState, toState, false)\n      }\n    } else {\n      log(\"No state to pop. No action will be taken.\")\n    }\n  }\n\n  override fun <R : Router<*>> pushState(\n    newState: StateT,\n    attachTransition: RouterNavigator.AttachTransition<R, StateT>,\n    detachTransition: RouterNavigator.DetachTransition<R, StateT>?,\n  ) {\n    pushState(newState, RouterNavigator.Flag.DEFAULT, attachTransition, detachTransition)\n  }\n\n  override fun <R : Router<*>> pushState(\n    newState: StateT,\n    flag: RouterNavigator.Flag,\n    attachTransition: RouterNavigator.AttachTransition<R, StateT>,\n    detachTransition: RouterNavigator.DetachTransition<R, StateT>?,\n  ) {\n    val fromState = peekState()\n    val currentRouterAndState = peekCurrentRouterAndState()\n    if (fromState != null && fromState.stateName() != newState.stateName()) {\n      if (currentRouterAndState?.router != null) {\n        detachInternal(currentRouterAndState, newState, true)\n      }\n    }\n    val newStateIsTop = fromState != null && fromState.stateName() == newState.stateName()\n    if (currentTransientRouterAndState != null) {\n      if (!(newStateIsTop && flag == RouterNavigator.Flag.TRANSIENT)) {\n        currentTransientRouterAndState = null\n      }\n    }\n    val newRouterAndState: RouterAndState<*, StateT>\n    when (flag) {\n      RouterNavigator.Flag.DEFAULT -> {\n        if (newStateIsTop) {\n          detachInternal(currentRouterAndState, newState, true)\n        }\n        newRouterAndState = buildNewState(newState, attachTransition, detachTransition)\n        navigationStack.push(newRouterAndState)\n        attachInternal(currentRouterAndState, newRouterAndState, true)\n      }\n      RouterNavigator.Flag.TRANSIENT -> {\n        if (newStateIsTop) {\n          return\n        }\n        newRouterAndState = buildNewState(newState, attachTransition, detachTransition)\n        currentTransientRouterAndState = newRouterAndState\n        attachInternal(currentRouterAndState, newRouterAndState, true)\n      }\n      RouterNavigator.Flag.CLEAR_TOP -> {\n        if (newStateIsTop) {\n          return\n        }\n        clearTop(currentRouterAndState, newState, attachTransition, detachTransition)\n      }\n      RouterNavigator.Flag.SINGLE_TOP -> {\n        if (newStateIsTop) {\n          return\n        }\n        removeStateFromStack(newState)\n        newRouterAndState = buildNewState(newState, attachTransition, detachTransition)\n        navigationStack.push(newRouterAndState)\n        attachInternal(currentRouterAndState, newRouterAndState, true)\n      }\n      RouterNavigator.Flag.REORDER_TO_TOP -> {\n        if (newStateIsTop) {\n          return\n        }\n        reorderTop(currentRouterAndState, newState, attachTransition, detachTransition)\n      }\n      RouterNavigator.Flag.NEW_TASK ->\n        if (currentRouterAndState != null && newStateIsTop) {\n          navigationStack.clear()\n          navigationStack.push(currentRouterAndState)\n        } else {\n          detachAll()\n          newRouterAndState = buildNewState(newState, attachTransition, detachTransition)\n          attachInternal(currentRouterAndState, newRouterAndState, true)\n          navigationStack.push(newRouterAndState)\n        }\n      RouterNavigator.Flag.NEW_TASK_REPLACE -> {\n        detachAll()\n        newRouterAndState = buildNewState(newState, attachTransition, detachTransition)\n        attachInternal(currentRouterAndState, newRouterAndState, true)\n        navigationStack.push(newRouterAndState)\n      }\n      RouterNavigator.Flag.REPLACE_TOP -> {\n        if (!navigationStack.isEmpty()) {\n          navigationStack.pop()\n        }\n        newRouterAndState = buildNewState(newState, attachTransition, detachTransition)\n        navigationStack.push(newRouterAndState)\n        attachInternal(currentRouterAndState, newRouterAndState, true)\n      }\n    }\n  }\n\n  override fun peekRouter(): Router<*>? {\n    val top = peekCurrentRouterAndState() ?: return null\n    return top.router\n  }\n\n  override fun peekState(): StateT? {\n    val top = peekCurrentRouterAndState() ?: return null\n    return top.state\n  }\n\n  @IntRange(from = 0)\n  override fun size(): Int {\n    return navigationStack.size\n  }\n\n  /**\n   * This will pop the current active router and clear the entire stack.\n   *\n   * NOTE: This must be called when host interactor is going to detach.\n   */\n  public open fun detachAll() {\n    log(\n      String.format(\n        Locale.getDefault(),\n        \"Detaching RouterNavigator from host -> %s\",\n        hostRouterName,\n      ),\n    )\n    val currentRouterAndState = peekCurrentRouterAndState()\n    detachInternal(currentRouterAndState, null as StateT?, false)\n    currentTransientRouterAndState = null\n    navigationStack.clear()\n  }\n\n  private fun <R : Router<*>> buildNewState(\n    newState: StateT,\n    attachTransition: RouterNavigator.AttachTransition<R, StateT>,\n    detachTransition: RouterNavigator.DetachTransition<R, StateT>?,\n  ): RouterAndState<R, StateT> {\n    return RouterAndState(\n      newState,\n      attachTransition,\n      detachTransition,\n      forceRouterCaching = forceRouterCaching,\n    )\n  }\n\n  /**\n   * Handles the attachment logic for a router.\n   *\n   * @param fromRouterState From router state.\n   * @param toRouterState New state.\n   * @param isPush True if this is from a push.\n   */\n  private fun attachInternal(\n    fromRouterState: RouterAndState<*, StateT>?,\n    toRouterState: RouterAndState<*, StateT>,\n    isPush: Boolean,\n  ) {\n    val toRouterName: String = toRouterState.router.javaClass.simpleName\n    log(String.format(Locale.getDefault(), \"Calling willAttachToHost for %s\", toRouterName))\n    instance.emitEvent(\n      RouterNavigatorEventType.WILL_ATTACH_TO_HOST,\n      hostRouter,\n      toRouterState.router,\n    )\n    toRouterState.willAttachToHost(fromRouterState?.state, isPush)\n    log(\n      String.format(\n        Locale.getDefault(),\n        \"Attaching %s as a child of %s\",\n        toRouterName,\n        hostRouterName,\n      ),\n    )\n    hostRouter.attachChild(toRouterState.router)\n  }\n\n  /**\n   * Handles the detachment of a router.\n   *\n   * @param fromRouterState Previous state\n   * @param toState New state\n   * @param isPush True if this is caused by a push\n   */\n  private fun detachInternal(\n    fromRouterState: RouterAndState<*, StateT>?,\n    toState: StateT?,\n    isPush: Boolean,\n  ) {\n    if (fromRouterState == null) {\n      log(\"No router to transition from. Call to detach will be dropped.\")\n      return\n    }\n    val fromRouterName: String = fromRouterState.router.javaClass.simpleName\n    log(String.format(Locale.getDefault(), \"Calling willDetachFromHost for %s\", fromRouterName))\n    fromRouterState.willDetachFromHost(toState, isPush)\n\n    log(String.format(Locale.getDefault(), \"Detaching %s from %s\", fromRouterName, hostRouterName))\n    hostRouter.detachChild(fromRouterState.router)\n\n    log(String.format(Locale.getDefault(), \"Calling onPostDetachFromHost for %s\", fromRouterName))\n    fromRouterState.onPostDetachFromHost(toState, isPush)\n  }\n\n  private fun peekCurrentRouterAndState(): RouterAndState<*, StateT>? {\n    return if (currentTransientRouterAndState != null) {\n      currentTransientRouterAndState\n    } else {\n      navigationStack.peek()\n    }\n  }\n\n  private fun <R : Router<*>> clearTop(\n    currentRouterAndState: RouterAndState<*, StateT>?,\n    newState: StateT,\n    attachTransition: RouterNavigator.AttachTransition<R, StateT>,\n    detachTransition: RouterNavigator.DetachTransition<R, StateT>?,\n  ) {\n    var found = false\n    var navigationIterator = navigationStack.iterator()\n    while (navigationIterator.hasNext()) {\n      if (navigationIterator.next().state == newState) {\n        found = true\n        break\n      }\n    }\n    if (found) {\n      navigationIterator = navigationStack.iterator()\n      while (navigationIterator.hasNext()) {\n        val routerAndState = navigationIterator.next()\n        if (routerAndState.state == newState) {\n          attachInternal(currentRouterAndState, routerAndState, true)\n          break\n        } else {\n          navigationIterator.remove()\n        }\n      }\n    } else {\n      val newRouterAndState = buildNewState(newState, attachTransition, detachTransition)\n      navigationStack.push(newRouterAndState)\n      attachInternal(currentRouterAndState, newRouterAndState, true)\n    }\n  }\n\n  private fun <R : Router<*>> reorderTop(\n    currentRouterAndState: RouterAndState<*, StateT>?,\n    newState: StateT,\n    attachTransition: RouterNavigator.AttachTransition<R, StateT>,\n    detachTransition: RouterNavigator.DetachTransition<R, StateT>?,\n  ) {\n    var found = false\n    val navigationIterator = navigationStack.iterator()\n    while (navigationIterator.hasNext()) {\n      val routerAndState = navigationIterator.next()\n      if (routerAndState.state == newState) {\n        navigationIterator.remove()\n        navigationStack.push(routerAndState)\n        attachInternal(currentRouterAndState, routerAndState, true)\n        found = true\n        break\n      }\n    }\n    if (!found) {\n      val newRouterAndState = buildNewState(newState, attachTransition, detachTransition)\n      navigationStack.push(newRouterAndState)\n      attachInternal(currentRouterAndState, newRouterAndState, true)\n    }\n  }\n\n  private fun removeStateFromStack(state: StateT) {\n    val navigationIterator = navigationStack.iterator()\n    while (navigationIterator.hasNext()) {\n      if (navigationIterator.next().state == state) {\n        navigationIterator.remove()\n      }\n    }\n  }\n\n  /*\n  Deprecated methods\n   */\n  @Deprecated(\"\")\n  override fun <R : Router<*>> pushRetainedState(\n    newState: StateT,\n    attachTransition: RouterNavigator.AttachTransition<R, StateT>,\n    detachTransition: RouterNavigator.DetachTransition<R, StateT>?,\n  ) {\n    pushState(newState, attachTransition, detachTransition)\n  }\n\n  @Deprecated(\"\")\n  override fun <R : Router<*>> pushRetainedState(\n    newState: StateT,\n    attachTransition: RouterNavigator.AttachTransition<R, StateT>,\n  ) {\n    pushState(newState, attachTransition, null)\n  }\n\n  @Deprecated(\"\")\n  override fun <R : Router<*>> pushTransientState(\n    newState: StateT,\n    attachTransition: RouterNavigator.AttachTransition<R, StateT>,\n    detachTransition: RouterNavigator.DetachTransition<R, StateT>?,\n  ) {\n    pushState(newState, RouterNavigator.Flag.TRANSIENT, attachTransition, detachTransition)\n  }\n\n  @Deprecated(\"\")\n  override fun <R : Router<*>> pushTransientState(\n    newState: StateT,\n    attachTransition: RouterNavigator.AttachTransition<R, StateT>,\n  ) {\n    pushState(newState, RouterNavigator.Flag.TRANSIENT, attachTransition, null)\n  }\n\n  @Deprecated(\"\")\n  override fun hostWillDetach() {\n    detachAll()\n  }\n\n  public companion object {\n    /** Writes out to the debug log. */\n    private fun log(text: String) {\n      Rib.getConfiguration().handleDebugMessage(\"%s: $text\", \"RouterNavigator\")\n    }\n  }\n\n  /**\n   * Constructor.\n   *\n   * @param hostRouter to add and remove children to.\n   */\n  init {\n    log(\n      String.format(\n        Locale.getDefault(),\n        \"Installed new RouterNavigator: Hosting Router -> %s\",\n        hostRouterName,\n      ),\n    )\n  }\n}\n"
  },
  {
    "path": "libraries/rib-router-navigator/src/test/kotlin/com/uber/rib/core/RouterAndStateTest.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.google.common.truth.Truth.assertThat\nimport java.util.concurrent.CountDownLatch\nimport kotlin.concurrent.thread\nimport org.junit.Test\nimport org.mockito.kotlin.any\nimport org.mockito.kotlin.anyOrNull\nimport org.mockito.kotlin.doAnswer\nimport org.mockito.kotlin.doReturn\nimport org.mockito.kotlin.eq\nimport org.mockito.kotlin.mock\nimport org.mockito.kotlin.same\nimport org.mockito.kotlin.verify\nimport org.mockito.kotlin.whenever\n\nclass RouterAndStateTest {\n\n  private val state = mock<RouterNavigatorState>()\n  private val attachTransition =\n    mock<RouterNavigator.AttachTransition<Router<*>, RouterNavigatorState>> {\n      on { buildRouter() } doAnswer { mock() } // Creates new instance per each invocation\n    }\n  private val detachTransition =\n    mock<RouterNavigator.DetachTransition<Router<*>, RouterNavigatorState>>()\n  private val detachCallback =\n    mock<RouterNavigator.DetachCallback<Router<*>, RouterNavigatorState>>()\n\n  @Test\n  fun router_whenStateIsNotCacheable_NotForcedToCache_shouldDestroyRouterAfterDetach() {\n    whenever(state.isCacheable()).doReturn(false)\n\n    val routerAndState =\n      RouterAndState(state, attachTransition, detachTransition, forceRouterCaching = false)\n\n    val router1 = routerAndState.router\n    routerAndState.onPostDetachFromHost(mock(), false)\n    val router2 = routerAndState.router\n\n    assertThat(router1).isNotSameInstanceAs(router2)\n  }\n\n  @Test\n  fun router_whenStateIsCacheable_NotForcedToCache_shouldReuseRouterOnNextAttach() {\n    whenever(state.isCacheable()).doReturn(true)\n\n    val routerAndState =\n      RouterAndState(state, attachTransition, detachTransition, forceRouterCaching = false)\n\n    val router1 = routerAndState.router\n    routerAndState.onPostDetachFromHost(mock(), false)\n    val router2 = routerAndState.router\n\n    assertThat(router1).isSameInstanceAs(router2)\n  }\n\n  @Test\n  fun router_whenStateIsNotCacheable_ForcedToCache_shouldReuseRouterOnNextAttach() {\n    whenever(state.isCacheable()).doReturn(false)\n\n    val routerAndState =\n      RouterAndState(state, attachTransition, detachTransition, forceRouterCaching = true)\n\n    val router1 = routerAndState.router\n    routerAndState.onPostDetachFromHost(mock(), false)\n    val router2 = routerAndState.router\n\n    assertThat(router1).isSameInstanceAs(router2)\n  }\n\n  @Test\n  fun router_whenStateIsCacheable_ForcedToCache_shouldReuseRouterOnNextAttach() {\n    whenever(state.isCacheable()).doReturn(true)\n\n    val routerAndState =\n      RouterAndState(state, attachTransition, detachTransition, forceRouterCaching = true)\n\n    val router1 = routerAndState.router\n    routerAndState.onPostDetachFromHost(mock(), false)\n    val router2 = routerAndState.router\n\n    assertThat(router1).isSameInstanceAs(router2)\n  }\n\n  @Test\n  fun willAttachToHost_shouldCallProvidedAttachCallback() {\n    val routerAndState = RouterAndState(state, attachTransition, detachTransition)\n\n    routerAndState.willAttachToHost(null, false)\n\n    verify(attachTransition)\n      .willAttachToHost(same(routerAndState.router), eq(null), same(state), eq(false))\n  }\n\n  @Test\n  fun willDetachFromHost_whenDetachTransactionIsNotCallback_shouldCallProvidedDetachCallback() {\n    val routerAndState = RouterAndState(state, attachTransition, detachTransition)\n\n    routerAndState.willDetachFromHost(null, false)\n\n    verify(detachTransition)\n      .willDetachFromHost(same(routerAndState.router), same(state), eq(null), eq(false))\n  }\n\n  @Test\n  fun willDetachFromHost_whenDetachTransactionIsCallback_shouldCallProvidedDetachCallback() {\n    val routerAndState = RouterAndState(state, attachTransition, detachCallback)\n\n    routerAndState.willDetachFromHost(null, false)\n\n    verify(detachCallback)\n      .willDetachFromHost(same(routerAndState.router), same(state), eq(null), eq(false))\n  }\n\n  @Test\n  fun willDetachFromHost_whenDetachTransactionIsNotCallback_shouldCallProvidedDetachTransaction() {\n    val routerAndState = RouterAndState(state, attachTransition, detachTransition)\n\n    val router1 = routerAndState.router\n    routerAndState.willDetachFromHost(null, false)\n\n    verify(detachTransition).willDetachFromHost(same(router1), same(state), eq(null), eq(false))\n  }\n\n  @Test\n  fun onPostDetachFromHost_shouldCallProvidedDetachCallback() {\n    val routerAndState = RouterAndState(state, attachTransition, detachCallback)\n\n    val router1 = routerAndState.router\n    routerAndState.onPostDetachFromHost(null, false)\n\n    verify(detachCallback).onPostDetachFromHost(same(router1), eq(null), eq(false))\n  }\n\n  @Test\n  fun router_whenCurrentlyDestroyingTheInstance_shouldCallBuilderTwice() {\n    val createdRouters = mutableSetOf<Router<*>>()\n    val destroyedRouters = mutableSetOf<Router<*>>()\n\n    whenever(attachTransition.buildRouter()).doAnswer {\n      mock<Router<*>>().apply(createdRouters::add)\n    }\n    whenever(detachCallback.onPostDetachFromHost(any(), anyOrNull(), any())).then {\n      destroyedRouters.add(it.arguments[0] as Router<*>)\n    }\n\n    val routerAndState =\n      RouterAndState(\n        state,\n        attachTransition,\n        detachCallback,\n      )\n\n    val threadsCount = 10\n    val latch = CountDownLatch(threadsCount)\n\n    // Create 10 threads with 1000 routers usage flows inside each\n    repeat(threadsCount) {\n      thread {\n        repeat(1000) {\n          routerAndState.willAttachToHost(null, false)\n          routerAndState.willDetachFromHost(null, false)\n          routerAndState.onPostDetachFromHost(null, false)\n        }\n        latch.countDown()\n      }\n    }\n\n    latch.await()\n\n    // Verify that all attached routers were detached\n    assertThat(createdRouters.subtract(destroyedRouters)).isEmpty()\n  }\n}\n"
  },
  {
    "path": "libraries/rib-router-navigator/src/test/kotlin/com/uber/rib/core/StackRouterNavigatorTest.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.google.common.truth.Truth\nimport org.junit.Before\nimport org.junit.Test\nimport org.mockito.kotlin.doAnswer\nimport org.mockito.kotlin.doReturn\nimport org.mockito.kotlin.mock\nimport org.mockito.kotlin.never\nimport org.mockito.kotlin.times\nimport org.mockito.kotlin.verify\nimport org.mockito.kotlin.verifyNoInteractions\n\nclass StackRouterNavigatorTest {\n  private enum class TestState : RouterNavigatorState {\n    STATE_1,\n    STATE_2,\n    STATE_3,\n  }\n\n  private val hostRouter: Router<*> = mock()\n  private val router1: Router<*> = mock()\n  private val router2: Router<*> = mock()\n  private val router3: Router<*> = mock()\n\n  private val attachTransition1: RouterNavigator.AttachTransition<Router<*>, TestState> = mock {\n    on { buildRouter() } doReturn router1\n  }\n  private val attachTransition2: RouterNavigator.AttachTransition<Router<*>, TestState> = mock {\n    on { buildRouter() } doReturn router2\n  }\n  private val attachTransition3: RouterNavigator.AttachTransition<Router<*>, TestState> = mock {\n    on { buildRouter() } doReturn router3\n  }\n\n  private val detachTransition1: RouterNavigator.DetachTransition<Router<*>, TestState> = mock()\n  private val detachTransition2: RouterNavigator.DetachTransition<Router<*>, TestState> = mock()\n  private val detachTransition3: RouterNavigator.DetachTransition<Router<*>, TestState> = mock()\n\n  private lateinit var routerNavigator: StackRouterNavigator<TestState>\n\n  @Before\n  fun setup() {\n    routerNavigator = StackRouterNavigator(hostRouter)\n  }\n\n  @Test\n  fun hostWillDetach_whenThereIsAnAttachedRouter_shouldRunDetachRunner() {\n    routerNavigator.pushRetainedState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.hostWillDetach()\n    verify(detachTransition1).willDetachFromHost(router1, TestState.STATE_1, null, false)\n  }\n\n  @Test\n  fun hostWillDetach_whenThereIsAnAttachedRouter_andAttachesAgain_shouldReattach() {\n    routerNavigator.pushRetainedState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.hostWillDetach()\n    routerNavigator.pushRetainedState(TestState.STATE_1, attachTransition1, detachTransition1)\n    verify(attachTransition1, times(2)).willAttachToHost(router1, null, TestState.STATE_1, true)\n  }\n\n  @Test\n  fun pushRetained_whenNotInitialPush_shouldRunPreviousDetachRunnerAndRunNewAttachRunnerWithCorrectState() {\n    routerNavigator.pushRetainedState(TestState.STATE_1, attachTransition1, detachTransition1)\n    verify(attachTransition1).willAttachToHost(router1, null, TestState.STATE_1, true)\n    verify(hostRouter).attachChild(router1)\n    verifyNoInteractions(detachTransition1)\n    routerNavigator.pushRetainedState(TestState.STATE_2, attachTransition2, detachTransition2)\n    verify(detachTransition1)\n      .willDetachFromHost(router1, TestState.STATE_1, TestState.STATE_2, true)\n    verify(hostRouter).detachChild(router1)\n    verify(attachTransition2).willAttachToHost(router2, TestState.STATE_1, TestState.STATE_2, true)\n    verifyNoInteractions(detachTransition2)\n  }\n\n  @Test\n  fun pushTransientDeprecated_whenNotInitialPush_shouldRunPreviousDetachRunnerAndRunNewAttachRunnerWithCorrectState() {\n    routerNavigator.pushRetainedState(TestState.STATE_1, attachTransition1, detachTransition1)\n    verify(attachTransition1).willAttachToHost(router1, null, TestState.STATE_1, true)\n    verify(hostRouter).attachChild(router1)\n    verifyNoInteractions(detachTransition1)\n    routerNavigator.pushTransientState(TestState.STATE_2, attachTransition2, detachTransition2)\n    verify(detachTransition1)\n      .willDetachFromHost(router1, TestState.STATE_1, TestState.STATE_2, true)\n    verify(hostRouter).detachChild(router1)\n    verify(attachTransition2).willAttachToHost(router2, TestState.STATE_1, TestState.STATE_2, true)\n    verifyNoInteractions(detachTransition2)\n  }\n\n  @Test\n  fun pushRetained_whenChildRouterSwitchesStateImmediately_shouldSwitchToCorrectState() {\n    val hostRouter: Router<*> = mock {\n      on { attachChild(router2) } doAnswer\n        {\n          routerNavigator.pushState(TestState.STATE_3, attachTransition3, detachTransition3)\n        }\n    }\n    routerNavigator = StackRouterNavigator(hostRouter)\n    routerNavigator.pushRetainedState(TestState.STATE_1, attachTransition1, detachTransition1)\n    verify(attachTransition1).willAttachToHost(router1, null, TestState.STATE_1, true)\n    verify(hostRouter).attachChild(router1)\n    verifyNoInteractions(detachTransition1)\n    routerNavigator.pushRetainedState(TestState.STATE_2, attachTransition2, detachTransition2)\n    verify(detachTransition1)\n      .willDetachFromHost(router1, TestState.STATE_1, TestState.STATE_2, true)\n    verify(attachTransition2).willAttachToHost(router2, TestState.STATE_1, TestState.STATE_2, true)\n    verify(detachTransition2)\n      .willDetachFromHost(router2, TestState.STATE_2, TestState.STATE_3, true)\n    verify(attachTransition3).willAttachToHost(router3, TestState.STATE_2, TestState.STATE_3, true)\n    verify(attachTransition3, never())\n      .willAttachToHost(router3, TestState.STATE_1, TestState.STATE_3, true)\n  }\n\n  @Test\n  fun pushTransientDeprecated_whenChildRouterSwitchesStateImmediately_shouldSwitchToCorrectState() {\n    val hostRouter: Router<*> = mock {\n      on { attachChild(router2) } doAnswer\n        {\n          routerNavigator.pushState(TestState.STATE_3, attachTransition3, detachTransition3)\n        }\n    }\n    routerNavigator = StackRouterNavigator(hostRouter)\n    routerNavigator.pushRetainedState(TestState.STATE_1, attachTransition1, detachTransition1)\n    verify(attachTransition1).willAttachToHost(router1, null, TestState.STATE_1, true)\n    verify(hostRouter).attachChild(router1)\n    verifyNoInteractions(detachTransition1)\n    routerNavigator.pushTransientState(TestState.STATE_2, attachTransition2, detachTransition2)\n    verify(detachTransition1)\n      .willDetachFromHost(router1, TestState.STATE_1, TestState.STATE_2, true)\n    verify(attachTransition2).willAttachToHost(router2, TestState.STATE_1, TestState.STATE_2, true)\n    verify(detachTransition2)\n      .willDetachFromHost(router2, TestState.STATE_2, TestState.STATE_3, true)\n    verify(attachTransition3).willAttachToHost(router3, TestState.STATE_2, TestState.STATE_3, true)\n    verify(attachTransition3, never())\n      .willAttachToHost(router3, TestState.STATE_1, TestState.STATE_3, true)\n  }\n\n  @Test\n  fun pushRetained_overACurrentTransientRouter_shouldPopTheTransientRouter() {\n    routerNavigator.pushRetainedState(TestState.STATE_1, attachTransition1, detachTransition1)\n    verify(attachTransition1).willAttachToHost(router1, null, TestState.STATE_1, true)\n    routerNavigator.pushTransientState(TestState.STATE_2, attachTransition2, detachTransition2)\n    verify(detachTransition1)\n      .willDetachFromHost(router1, TestState.STATE_1, TestState.STATE_2, true)\n    routerNavigator.pushRetainedState(TestState.STATE_3, attachTransition3, detachTransition3)\n    verify(detachTransition2)\n      .willDetachFromHost(router2, TestState.STATE_2, TestState.STATE_3, true)\n    routerNavigator.popState()\n    verify(detachTransition3)\n      .willDetachFromHost(router3, TestState.STATE_3, TestState.STATE_1, false)\n    verify(attachTransition1).willAttachToHost(router1, TestState.STATE_3, TestState.STATE_1, false)\n  }\n\n  @Test\n  fun detachAll_whenThereIsAnAttachedRouter_shouldRunDetachRunner() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.detachAll()\n    verify(detachTransition1).willDetachFromHost(router1, TestState.STATE_1, null, false)\n  }\n\n  @Test\n  fun detachAll_whenThereIsAnAttachedRouter_andAttachesAgain_shouldReattach() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.detachAll()\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    verify(attachTransition1, times(2)).willAttachToHost(router1, null, TestState.STATE_1, true)\n  }\n\n  @Test\n  fun pushDefault_whenNotInitialPush_shouldRunPreviousDetachRunnerAndRunNewAttachRunnerWithCorrectState() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    verify(attachTransition1).willAttachToHost(router1, null, TestState.STATE_1, true)\n    verify(hostRouter).attachChild(router1)\n    verifyNoInteractions(detachTransition1)\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    verify(detachTransition1)\n      .willDetachFromHost(router1, TestState.STATE_1, TestState.STATE_2, true)\n    verify(hostRouter).detachChild(router1)\n    verify(attachTransition2).willAttachToHost(router2, TestState.STATE_1, TestState.STATE_2, true)\n    verifyNoInteractions(detachTransition2)\n  }\n\n  @Test\n  fun pushTransient_whenNotInitialPush_shouldRunPreviousDetachRunnerAndRunNewAttachRunnerWithCorrectState() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    verify(attachTransition1).willAttachToHost(router1, null, TestState.STATE_1, true)\n    verify(hostRouter).attachChild(router1)\n    verifyNoInteractions(detachTransition1)\n    routerNavigator.pushState(\n      TestState.STATE_2,\n      RouterNavigator.Flag.TRANSIENT,\n      attachTransition2,\n      detachTransition2,\n    )\n    verify(detachTransition1)\n      .willDetachFromHost(router1, TestState.STATE_1, TestState.STATE_2, true)\n    verify(hostRouter).detachChild(router1)\n    verify(attachTransition2).willAttachToHost(router2, TestState.STATE_1, TestState.STATE_2, true)\n    verifyNoInteractions(detachTransition2)\n  }\n\n  @Test\n  fun pushDefault_whenChildRouterSwitchesStateImmediately_shouldSwitchToCorrectState() {\n    val hostRouter: Router<*> = mock {\n      on { attachChild(router2) } doAnswer\n        {\n          routerNavigator.pushState(TestState.STATE_3, attachTransition3, detachTransition3)\n        }\n    }\n    routerNavigator = StackRouterNavigator(hostRouter)\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    verify(attachTransition1).willAttachToHost(router1, null, TestState.STATE_1, true)\n    verify(hostRouter).attachChild(router1)\n    verifyNoInteractions(detachTransition1)\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    verify(detachTransition1)\n      .willDetachFromHost(router1, TestState.STATE_1, TestState.STATE_2, true)\n    verify(attachTransition2).willAttachToHost(router2, TestState.STATE_1, TestState.STATE_2, true)\n    verify(detachTransition2)\n      .willDetachFromHost(router2, TestState.STATE_2, TestState.STATE_3, true)\n    verify(attachTransition3).willAttachToHost(router3, TestState.STATE_2, TestState.STATE_3, true)\n    verify(attachTransition3, never())\n      .willAttachToHost(router3, TestState.STATE_1, TestState.STATE_3, true)\n  }\n\n  @Test\n  fun pushTransient_whenChildRouterSwitchesStateImmediately_shouldSwitchToCorrectState() {\n    val hostRouter: Router<*> = mock {\n      on { attachChild(router2) } doAnswer\n        {\n          routerNavigator.pushState(TestState.STATE_3, attachTransition3, detachTransition3)\n        }\n    }\n    routerNavigator = StackRouterNavigator(hostRouter)\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    verify(attachTransition1).willAttachToHost(router1, null, TestState.STATE_1, true)\n    verify(hostRouter).attachChild(router1)\n    verifyNoInteractions(detachTransition1)\n    routerNavigator.pushState(\n      TestState.STATE_2,\n      RouterNavigator.Flag.TRANSIENT,\n      attachTransition2,\n      detachTransition2,\n    )\n    verify(detachTransition1)\n      .willDetachFromHost(router1, TestState.STATE_1, TestState.STATE_2, true)\n    verify(attachTransition2).willAttachToHost(router2, TestState.STATE_1, TestState.STATE_2, true)\n    verify(detachTransition2)\n      .willDetachFromHost(router2, TestState.STATE_2, TestState.STATE_3, true)\n    verify(attachTransition3).willAttachToHost(router3, TestState.STATE_2, TestState.STATE_3, true)\n    verify(attachTransition3, never())\n      .willAttachToHost(router3, TestState.STATE_1, TestState.STATE_3, true)\n  }\n\n  @Test\n  fun pushDefault_overACurrentTransientRouter_shouldPopTheTransientRouter() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    verify(attachTransition1).willAttachToHost(router1, null, TestState.STATE_1, true)\n    routerNavigator.pushState(\n      TestState.STATE_2,\n      RouterNavigator.Flag.TRANSIENT,\n      attachTransition2,\n      detachTransition2,\n    )\n    verify(detachTransition1)\n      .willDetachFromHost(router1, TestState.STATE_1, TestState.STATE_2, true)\n    routerNavigator.pushState(TestState.STATE_3, attachTransition3, detachTransition3)\n    verify(detachTransition2)\n      .willDetachFromHost(router2, TestState.STATE_2, TestState.STATE_3, true)\n    routerNavigator.popState()\n    verify(detachTransition3)\n      .willDetachFromHost(router3, TestState.STATE_3, TestState.STATE_1, false)\n    verify(attachTransition1).willAttachToHost(router1, TestState.STATE_3, TestState.STATE_1, false)\n  }\n\n  @Test\n  fun pushClearTop_whenNotInStack_shouldPushToTopOfStack() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    routerNavigator.pushState(\n      TestState.STATE_3,\n      RouterNavigator.Flag.CLEAR_TOP,\n      attachTransition3,\n      detachTransition3,\n    )\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_3)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_2)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_1)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isNull()\n  }\n\n  @Test\n  fun pushClearTop_whenInStack_shouldRemoveStatesToThatState() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    routerNavigator.pushState(\n      TestState.STATE_1,\n      RouterNavigator.Flag.CLEAR_TOP,\n      attachTransition3,\n      detachTransition3,\n    )\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_1)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isNull()\n    verify(attachTransition1).willAttachToHost(router1, TestState.STATE_2, TestState.STATE_1, true)\n    verify(attachTransition3, never())\n      .willAttachToHost(router3, TestState.STATE_1, TestState.STATE_3, true)\n  }\n\n  @Test\n  fun pushClearTop_whenTopOfStack_shouldNotReorderOrAttach() {\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.pushState(\n      TestState.STATE_1,\n      RouterNavigator.Flag.CLEAR_TOP,\n      attachTransition3,\n      detachTransition3,\n    )\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_1)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_2)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isNull()\n    verify(attachTransition1).willAttachToHost(router1, TestState.STATE_2, TestState.STATE_1, true)\n    verify(attachTransition3, never())\n      .willAttachToHost(router3, TestState.STATE_1, TestState.STATE_3, true)\n  }\n\n  @Test\n  fun pushSingleTop_whenNotInStack_shouldPushToTopOfStack() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    routerNavigator.pushState(\n      TestState.STATE_3,\n      RouterNavigator.Flag.SINGLE_TOP,\n      attachTransition3,\n      detachTransition3,\n    )\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_3)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_2)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_1)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isNull()\n  }\n\n  @Test\n  fun pushSingleTop_whenInStack_shouldPushStateToTopAndRemoveOtherInstances() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    routerNavigator.pushState(\n      TestState.STATE_1,\n      RouterNavigator.Flag.SINGLE_TOP,\n      attachTransition3,\n      detachTransition3,\n    )\n    verify(attachTransition3).willAttachToHost(router3, TestState.STATE_2, TestState.STATE_1, true)\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_1)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_2)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isNull()\n  }\n\n  @Test\n  fun pushSingleTop_whenTopOfStack_shouldNotReorderStack() {\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.pushState(\n      TestState.STATE_1,\n      RouterNavigator.Flag.SINGLE_TOP,\n      attachTransition3,\n      detachTransition3,\n    )\n    verify(attachTransition3, never())\n      .willAttachToHost(router3, TestState.STATE_2, TestState.STATE_1, true)\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_1)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_2)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isNull()\n  }\n\n  @Test\n  fun pushReorderTop_whenNotInStack_shouldPushToTopOfStack() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    routerNavigator.pushState(\n      TestState.STATE_3,\n      RouterNavigator.Flag.REORDER_TO_TOP,\n      attachTransition3,\n      detachTransition3,\n    )\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_3)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_2)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_1)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isNull()\n  }\n\n  @Test\n  fun pushReorderTop_whenInStack_shouldRemoveStatesToThatState() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    routerNavigator.pushState(\n      TestState.STATE_1,\n      RouterNavigator.Flag.REORDER_TO_TOP,\n      attachTransition3,\n      detachTransition3,\n    )\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_1)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_2)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isNull()\n    verify(attachTransition1).willAttachToHost(router1, TestState.STATE_2, TestState.STATE_1, true)\n    verify(attachTransition3, never())\n      .willAttachToHost(router3, TestState.STATE_1, TestState.STATE_3, true)\n  }\n\n  @Test\n  fun pushReorderTop_whenTopOfStackStack_shouldNotReorderStack() {\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.pushState(\n      TestState.STATE_1,\n      RouterNavigator.Flag.REORDER_TO_TOP,\n      attachTransition3,\n      detachTransition3,\n    )\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_1)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_2)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isNull()\n    verify(attachTransition1).willAttachToHost(router1, TestState.STATE_2, TestState.STATE_1, true)\n    verify(attachTransition3, never())\n      .willAttachToHost(router3, TestState.STATE_1, TestState.STATE_3, true)\n  }\n\n  @Test\n  fun pushNewTask_detachesCurrentAndClearsCurrentStack_andShouldPushToTopOfStack() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    routerNavigator.pushState(\n      TestState.STATE_3,\n      RouterNavigator.Flag.NEW_TASK,\n      attachTransition3,\n      detachTransition3,\n    )\n    verify(detachTransition2).willDetachFromHost(router2, TestState.STATE_2, null, false)\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_3)\n    Truth.assertThat(routerNavigator.size()).isEqualTo(1)\n  }\n\n  @Test\n  fun pushNewTask_whenCurrentTopIsNewState_onlyClearsTheBackStack() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    routerNavigator.pushState(\n      TestState.STATE_2,\n      RouterNavigator.Flag.NEW_TASK,\n      attachTransition3,\n      detachTransition3,\n    )\n    verifyNoInteractions(detachTransition2)\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_2)\n    Truth.assertThat(routerNavigator.size()).isEqualTo(1)\n  }\n\n  @Test\n  fun pushNewTaskReplace_detachesCurrentAndClearsCurrentStack_andShouldPushToTopOfStack() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    routerNavigator.pushState(\n      TestState.STATE_3,\n      RouterNavigator.Flag.NEW_TASK_REPLACE,\n      attachTransition3,\n      detachTransition3,\n    )\n    verify(detachTransition2).willDetachFromHost(router2, TestState.STATE_2, null, false)\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_3)\n    Truth.assertThat(routerNavigator.size()).isEqualTo(1)\n  }\n\n  @Test\n  fun pushNewTaskReplace_whenCurrentTopIsNewState_detachesAllAndPushesToTopOfStack() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    routerNavigator.pushState(\n      TestState.STATE_2,\n      RouterNavigator.Flag.NEW_TASK_REPLACE,\n      attachTransition3,\n      detachTransition3,\n    )\n    verify(detachTransition2).willDetachFromHost(router2, TestState.STATE_2, null, false)\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_2)\n    Truth.assertThat(routerNavigator.size()).isEqualTo(1)\n  }\n\n  @Test\n  fun pushReplaceTop_removeExistingTopOfStack_andShouldPushNewStateToTopOfStack() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    routerNavigator.pushState(\n      TestState.STATE_3,\n      RouterNavigator.Flag.REPLACE_TOP,\n      attachTransition3,\n      detachTransition3,\n    )\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_3)\n    Truth.assertThat(routerNavigator.size()).isEqualTo(2)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_1)\n    Truth.assertThat(routerNavigator.size()).isEqualTo(1)\n  }\n\n  @Test\n  fun pushReplaceTop_whenStackIsEmpty_shouldPushNewStateToTopOfStack() {\n    routerNavigator.pushState(\n      TestState.STATE_1,\n      RouterNavigator.Flag.REPLACE_TOP,\n      attachTransition3,\n      detachTransition3,\n    )\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_1)\n    Truth.assertThat(routerNavigator.size()).isEqualTo(1)\n  }\n\n  @Test\n  fun pop_whenThereIsSomethingToPopTo_shouldRemoveCurrentItemAndReaddPreviousItemWithCorrectState() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, null)\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    routerNavigator.popState()\n    verify(detachTransition2)\n      .willDetachFromHost(router2, TestState.STATE_2, TestState.STATE_1, false)\n    verify(hostRouter).detachChild(router2)\n    verify(attachTransition1).willAttachToHost(router1, TestState.STATE_2, TestState.STATE_1, false)\n  }\n\n  @Test\n  fun pop_whenThereIsSomethingInTheStackAndATransientState_shouldRemoveTransientTate() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, null)\n    routerNavigator.pushState(TestState.STATE_2, attachTransition2, detachTransition2)\n    routerNavigator.pushState(\n      TestState.STATE_3,\n      RouterNavigator.Flag.TRANSIENT,\n      attachTransition3,\n      detachTransition3,\n    )\n    routerNavigator.popState()\n    verify(detachTransition3)\n      .willDetachFromHost(router3, TestState.STATE_3, TestState.STATE_2, false)\n  }\n\n  @Test\n  fun pop_whenThereIsNothingToPopTo_shouldRemoveCurrentItem() {\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.popState()\n    verify(detachTransition1).willDetachFromHost(router1, TestState.STATE_1, null, false)\n    verify(hostRouter).detachChild(router1)\n  }\n\n  @Test\n  fun pop_whenTheRouterNavigatorIsEmpty_shouldNotCrash() {\n    routerNavigator.popState()\n  }\n\n  @Test\n  fun peekRouter() {\n    Truth.assertThat(routerNavigator.peekRouter()).isNull()\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    Truth.assertThat(routerNavigator.peekRouter()).isEqualTo(router1)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekRouter()).isNull()\n  }\n\n  @Test\n  fun peekState() {\n    Truth.assertThat(routerNavigator.peekState()).isNull()\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    Truth.assertThat(routerNavigator.peekState()).isEqualTo(TestState.STATE_1)\n    routerNavigator.popState()\n    Truth.assertThat(routerNavigator.peekState()).isNull()\n  }\n\n  @Test\n  fun buildNewState_whenForceRouterCachingDisabled_shouldPassCorrectConfigInConstructor() {\n    routerNavigator = StackRouterNavigator(hostRouter, forceRouterCaching = false)\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.navigationStack.forEach { routerAndState ->\n      Truth.assertThat(routerAndState.forceRouterCaching).isEqualTo(false)\n    }\n  }\n\n  @Test\n  fun buildNewState_whenForceRouterCachingEnabled_shouldPassCorrectConfigInConstructor() {\n    routerNavigator = StackRouterNavigator(hostRouter, forceRouterCaching = true)\n    routerNavigator.pushState(TestState.STATE_1, attachTransition1, detachTransition1)\n    routerNavigator.navigationStack.forEach { routerAndState ->\n      Truth.assertThat(routerAndState.forceRouterCaching).isEqualTo(true)\n    }\n  }\n}\n"
  },
  {
    "path": "libraries/rib-router-navigator/src/test/kotlin/os/Bundle.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage os\n\n/** Stub class to have pure Java unit tests. */\nclass Bundle : Parcelable {\n  private val testData: MutableMap<String, Any> = HashMap()\n\n  fun getString(key: String) = testData[key] as String?\n\n  fun <T : Parcelable?> getParcelable(key: String) = testData[key] as T?\n\n  fun putParcelable(key: String, value: Parcelable) {\n    testData[key] = value\n  }\n\n  fun putString(key: String, value: String) {\n    testData[key] = value\n  }\n}\n"
  },
  {
    "path": "libraries/rib-router-navigator/src/test/kotlin/os/Parcelable.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage os\n\n/** Stub class to have pure Java unit tests. */\ninterface Parcelable\n"
  },
  {
    "path": "libraries/rib-screen-stack-base/README.md",
    "content": "# Rib Screen Stack Base\n\nThis module defines the interface for a `ScreenStack` utility. This\nutility can be used when you want to push an entirely new \"Screen\".\n\nVisually, this acts similarly to pushing a new activity. However, the pushed RIB\nbecomes a child of the existing RIB in the RIB hierarchy. As a result it can\naccess state from its parent's DI graph.\n\nDepending on the amount of animation customization provided, this utility\ncan be quite easy to implement.\n"
  },
  {
    "path": "libraries/rib-screen-stack-base/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nplugins {\n    id(\"ribs.android.library\")\n    alias(libs.plugins.maven.publish)\n}\n\nandroid {\n    namespace = \"com.ubercab.core.screenstack.base\"\n}\n\ndependencies {\n    api(libs.rxjava2)\n    api(libs.rxrelay2)\n    api(libs.rxbinding)\n    implementation(libs.androidx.annotation)\n    implementation(libs.autodispose.coroutines)\n    implementation(libs.kotlinx.coroutines.android)\n    implementation(libs.kotlinx.coroutines.rx2)\n}\n"
  },
  {
    "path": "libraries/rib-screen-stack-base/gradle.properties",
    "content": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPOM_NAME=RIBs (Screen Stack Base)\nPOM_ARTIFACT_ID=rib-screen-stack-base\nPOM_PACKAGING=jar\n"
  },
  {
    "path": "libraries/rib-screen-stack-base/src/main/kotlin/com/uber/rib/core/screenstack/ScreenStackBase.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core.screenstack\n\nimport androidx.annotation.IntRange\n\n/** Implementation of a view based screen stack. */\npublic interface ScreenStackBase {\n  /**\n   * Pushes a new screen into the stack. The new screen view will have its type inspected in order\n   * to manipulate the status bar background and icon colors.\n   *\n   * This will use the default animation.\n   *\n   * @param viewProvider to create a new view to be displayed.\n   */\n  public fun pushScreen(viewProvider: ViewProvider)\n\n  /**\n   * Pushes a new screen into the stack. The new screen view will have its type inspected in order\n   * to manipulate the status bar background and icon colors.\n   *\n   * @param viewProvider to create a new view to be displayed.\n   * @param shouldAnimate whether the addition of the screen should be animated using the default\n   *   transition.\n   */\n  public fun pushScreen(viewProvider: ViewProvider, shouldAnimate: Boolean)\n\n  /** Removes the current screen from the stack. This will use animations. */\n  public fun popScreen()\n\n  /**\n   * Removes the current screen from the stack. Allows enabling/disabling of the animation used in\n   * the screen transaction.\n   *\n   * @param shouldAnimate Whether the removal of the screen should be animated.\n   */\n  public fun popScreen(shouldAnimate: Boolean)\n\n  /**\n   * Pops back to the specified index. (Starting at 0).\n   *\n   * -1 will clear the entire stack, and restore any original content (if supported).\n   *\n   * @param index Index to pop back to.\n   * @param shouldAnimate If true, we should animate to the final entry that we are popping to. If\n   *   false, no animations will be used.\n   */\n  public fun popBackTo(@IntRange(from = -1) index: Int, shouldAnimate: Boolean)\n\n  /**\n   * Try to handle a back press. Pass the back press to children, if they exist. Or pop the top item\n   * off the stack. This will by default use animations.\n   *\n   * @return True if the back press is handled.\n   */\n  public fun handleBackPress(): Boolean\n\n  /**\n   * Try to handle a back press. Pass the back press to children, if they exist. Or pop the top item\n   * off the stack.\n   *\n   * @param shouldAnimate True if we should use animations. False otherwise.\n   * @return TRUE if the back press is handled.\n   */\n  public fun handleBackPress(shouldAnimate: Boolean): Boolean\n\n  /**\n   * Gets the size of the stack.\n   *\n   * @return Size.\n   */\n  @IntRange(from = 0) public fun size(): Int\n}\n"
  },
  {
    "path": "libraries/rib-screen-stack-base/src/main/kotlin/com/uber/rib/core/screenstack/ViewProvider.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core.screenstack\n\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.annotation.CallSuper\nimport com.uber.rib.core.screenstack.lifecycle.ScreenStackEvent\nimport io.reactivex.Observable\nimport kotlinx.coroutines.channels.BufferOverflow\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.flow.SharedFlow\nimport kotlinx.coroutines.rx2.asObservable\n\n/** Interface to provide [View] instances to [ScreenStackBase]. */\npublic abstract class ViewProvider {\n  private val _lifecycleFlow = MutableSharedFlow<ScreenStackEvent>(1, 0, BufferOverflow.DROP_OLDEST)\n  public open val lifecycleFlow: SharedFlow<ScreenStackEvent>\n    get() = _lifecycleFlow\n\n  public open fun buildViewInternal(parentView: ViewGroup): View {\n    _lifecycleFlow.tryEmit(ScreenStackEvent.BUILT)\n    return buildView(parentView)\n  }\n\n  /**\n   * Builds a view to be displayed in a [ScreenStackBase] instance.\n   *\n   * @param parentView parent [ViewGroup] that the view will be displayed in.\n   * @return the view to be displayed.\n   */\n  public abstract fun buildView(parentView: ViewGroup): View\n\n  /** @return an observable that emits events for this view provider's lifecycle. */\n  public fun lifecycle(): Observable<ScreenStackEvent> = lifecycleFlow.asObservable()\n\n  /**\n   * Callers can implement this in order to complete additional work when a call to\n   * [ ][.onViewRemoved] is performed.\n   */\n  protected open fun doOnViewRemoved() {}\n\n  /** Notifies the view provider that the view has been popped from the stack. */\n  public fun onViewRemoved() {\n    _lifecycleFlow.tryEmit(ScreenStackEvent.REMOVED)\n    doOnViewRemoved()\n  }\n\n  /**\n   * Asks the view provider to handle a back press.\n   *\n   * @return TRUE if the provider handled the back press.\n   */\n  public open fun onBackPress(): Boolean {\n    return false\n  }\n\n  /** Notifies the view provider that view is at the top of the stack and visible. */\n  @CallSuper\n  public open fun onViewAppeared() {\n    _lifecycleFlow.tryEmit(ScreenStackEvent.APPEARED)\n  }\n\n  /** Notifies the view provider that the view is no longer at the top of the stack. */\n  @CallSuper\n  public open fun onViewHidden() {\n    _lifecycleFlow.tryEmit(ScreenStackEvent.HIDDEN)\n  }\n}\n"
  },
  {
    "path": "libraries/rib-screen-stack-base/src/main/kotlin/com/uber/rib/core/screenstack/lifecycle/ScreenStackEvent.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core.screenstack.lifecycle\n\nimport com.uber.rib.core.screenstack.ScreenStackBase\n\n/** Lifecycle events that can be emitted by [ScreenStackBase] view providers. */\npublic enum class ScreenStackEvent {\n  BUILT,\n  APPEARED,\n  HIDDEN,\n  REMOVED,\n}\n"
  },
  {
    "path": "libraries/rib-test/README.md",
    "content": "# rib-test\n\nThis module is responsible for providing test related utilities that are\nuseful for testing RIBs.\n"
  },
  {
    "path": "libraries/rib-test/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.kotlin.library\")\n    alias(libs.plugins.maven.publish)\n}\n\nkotlin.compilerOptions {\n    optIn.add(\"com.uber.rib.core.internal.CoreFriendModuleApi\")\n}\n\ndependencies {\n    api(project(\":libraries:rib-base\"))\n    implementation(libs.rxjava2)\n    api(testLibs.junit)\n    api(testLibs.truth)\n    api(testLibs.mockito)\n    api(testLibs.kotlinx.coroutines.test)\n    implementation(testLibs.mockito.kotlin)\n}\n"
  },
  {
    "path": "libraries/rib-test/gradle.properties",
    "content": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPOM_NAME=RIBs (Test Utils)\nPOM_ARTIFACT_ID=rib-test\nPOM_PACKAGING=jar\n"
  },
  {
    "path": "libraries/rib-test/src/main/kotlin/com/uber/rib/core/AndroidRecordingRx2Observer.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.google.common.truth.Truth.assertThat\nimport io.reactivex.Observer\nimport io.reactivex.disposables.Disposable\nimport java.util.NoSuchElementException\nimport java.util.concurrent.BlockingDeque\nimport java.util.concurrent.LinkedBlockingDeque\nimport java.util.concurrent.TimeUnit\n\n/**\n * AndroidRecordingObserver implementation from RxBinding.\n *\n * @param <T> Parametrized type.\n */\npublic open class AndroidRecordingRx2Observer<T : Any> : Observer<T> {\n  private val events: BlockingDeque<Any> = LinkedBlockingDeque()\n\n  override fun onSubscribe(disposable: Disposable) {}\n\n  override fun onComplete() {\n    events.addLast(OnCompleted())\n  }\n\n  override fun onError(e: Throwable) {\n    events.addLast(OnError(e))\n  }\n\n  override fun onNext(t: T) {\n    events.addLast(OnNext(t))\n  }\n\n  public open fun takeNext(): T {\n    val event: OnNext = takeEvent(OnNext::class.java)\n    return event.value as T\n  }\n\n  public open fun takeError(): Throwable {\n    val event: OnError = takeEvent(OnError::class.java)\n    return event.throwable\n  }\n\n  private inline fun <E> takeEvent(wanted: Class<E>): E {\n    val event: Any =\n      try {\n        events.pollFirst(1, TimeUnit.SECONDS)\n      } catch (e: InterruptedException) {\n        throw RuntimeException(e)\n      } ?: throw NoSuchElementException(\"No event found while waiting for \" + wanted.simpleName)\n    assertThat(event).isInstanceOf(wanted)\n    return event as E\n  }\n\n  public open fun assertNoMoreEvents() {\n    try {\n      val event: Any = takeEvent(Any::class.java)\n      throw IllegalStateException(\"Expected no more events but got $event\")\n    } catch (ignored: NoSuchElementException) {\n      // Can be ignored in this case\n    }\n  }\n\n  private class OnNext(val value: Any) {\n    override fun toString() = \"OnNext[$value]\"\n  }\n\n  private class OnCompleted {\n    override fun toString() = \"OnCompleted\"\n  }\n\n  private class OnError(val throwable: Throwable) {\n    override fun toString() = \"OnError[$throwable]\"\n  }\n}\n"
  },
  {
    "path": "libraries/rib-test/src/main/kotlin/com/uber/rib/core/FakeComponent.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\npublic class FakeComponent<P : Presenter, T : Interactor<P, *>>\nprivate constructor(\n  private val presenter: P,\n) : InteractorComponent<P, T> {\n\n  override fun inject(interactor: T) {}\n\n  override fun presenter(): P {\n    return presenter\n  }\n\n  public companion object {\n    @JvmStatic\n    public fun <T : Interactor<FakePresenter, *>> withFakePresenterFor(\n      interactorClass: Class<T>,\n    ): FakeComponent<FakePresenter, T> {\n      return FakeComponent(FakePresenter())\n    }\n  }\n}\n"
  },
  {
    "path": "libraries/rib-test/src/main/kotlin/com/uber/rib/core/FakeInteractor.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n@SuppressWarnings(\"RibInteractorOnIteractor\")\npublic open class FakeInteractor<P : Any, R : Router<*>> : Interactor<P, R>() {\n  public open fun attach() {\n    actualPresenter = FakePresenter() as P\n    router = FakeRouter(this) as R\n    dispatchAttach(null)\n  }\n\n  public open fun detach() {\n    dispatchDetach()\n  }\n}\n"
  },
  {
    "path": "libraries/rib-test/src/main/kotlin/com/uber/rib/core/FakePresenter.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\npublic class FakePresenter : Presenter()\n"
  },
  {
    "path": "libraries/rib-test/src/main/kotlin/com/uber/rib/core/FakeRouter.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/**\n * Router for testing that does not automatically attach to the interactor.\n *\n * @param <I>\n */\npublic class FakeRouter<I : Interactor<*, *>> : Router<I> {\n  public constructor(interactor: I) : super(interactor)\n  public constructor(\n    interactor: I,\n    ribRefWatcher: RibRefWatcher,\n    mainThread: Thread,\n  ) : super(null, interactor, ribRefWatcher, mainThread)\n\n  override fun attachToInteractor() {\n    // do not automatically attach this\n  }\n}\n"
  },
  {
    "path": "libraries/rib-test/src/main/kotlin/com/uber/rib/core/FakeWorker.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\npublic class FakeWorker : Worker\n"
  },
  {
    "path": "libraries/rib-test/src/main/kotlin/com/uber/rib/core/InteractorHelper.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport org.mockito.AdditionalMatchers.or\nimport org.mockito.kotlin.isA\nimport org.mockito.kotlin.isNull\nimport org.mockito.kotlin.verify\n\n/** The helper to test [Interactor]. */\npublic object InteractorHelper {\n  /**\n   * Attaches the [Interactor] using a mock router.\n   *\n   * @param <P> the type of presenter.\n   * @param <R> the type of router.\n   * @param interactor the [Interactor].\n   * @param presenter the presenter for the [Interactor].\n   * @param router the mock router for the [Interactor].\n   * @param savedInstanceState the saved [Bundle].\n   */\n  @JvmStatic\n  public fun <P : Any, R : Router<*>> attach(\n    interactor: Interactor<P, R>,\n    presenter: P,\n    router: R,\n    savedInstanceState: Bundle?,\n  ) {\n    interactor.actualPresenter = presenter\n    interactor.setRouterInternal(router)\n    interactor.dispatchAttach(savedInstanceState)\n  }\n\n  /**\n   * Reattaches the [Interactor] without trying to set the router.\n   *\n   * @param interactor the [Interactor].\n   * @param savedInstanceState the saved [Bundle].\n   */\n  @JvmStatic\n  public fun reattach(interactor: Interactor<*, *>, savedInstanceState: Bundle?) {\n    interactor.dispatchAttach(savedInstanceState)\n  }\n\n  /**\n   * Detaches the [Interactor].\n   *\n   * @param controller the [Interactor].\n   */\n  @JvmStatic\n  public fun detach(controller: Interactor<*, *>) {\n    controller.dispatchDetach()\n  }\n\n  /**\n   * Verifies that the [Interactor] is attached.\n   *\n   * @param interactor the [Interactor].\n   */\n  @JvmStatic\n  public fun verifyAttached(interactor: Interactor<*, *>) {\n    verify(interactor).dispatchAttach(or(isNull(), isA<Bundle>()))\n  }\n\n  /**\n   * Verifies that the [Interactor] is detached.\n   *\n   * @param interactor the [Interactor].\n   */\n  @JvmStatic\n  public fun verifyDetached(interactor: Interactor<*, *>) {\n    verify(interactor).dispatchDetach()\n  }\n}\n"
  },
  {
    "path": "libraries/rib-test/src/main/kotlin/com/uber/rib/core/PresenterHelper.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\n/** The helper to test [Presenter]. */\npublic object PresenterHelper {\n  /**\n   * Loads the given [Presenter].\n   *\n   * @param presenter the presenter.\n   */\n  @JvmStatic\n  public fun load(presenter: Presenter) {\n    presenter.dispatchLoad()\n  }\n\n  /**\n   * Unloads the given [Presenter].\n   *\n   * @param presenter the presenter.\n   */\n  @JvmStatic\n  public fun unload(presenter: Presenter) {\n    presenter.dispatchUnload()\n  }\n}\n"
  },
  {
    "path": "libraries/rib-test/src/main/kotlin/com/uber/rib/core/RibTestBasePlaceholder.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\npublic open class RibTestBasePlaceholder\n"
  },
  {
    "path": "libraries/rib-test/src/main/kotlin/com/uber/rib/core/RouterHelper.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport org.mockito.AdditionalMatchers.or\nimport org.mockito.InOrder\nimport org.mockito.kotlin.any\nimport org.mockito.kotlin.eq\nimport org.mockito.kotlin.isA\nimport org.mockito.kotlin.isNull\nimport org.mockito.kotlin.never\nimport org.mockito.kotlin.times\nimport org.mockito.kotlin.verify\nimport org.mockito.verification.VerificationMode\n\n/** The helper to test [Router]. */\npublic object RouterHelper {\n  /**\n   * Dispatches attachment to a router.\n   *\n   * @param router to attach.\n   * @param <R> type of router.\n   */\n  @JvmStatic\n  public fun <R : Router<*>> attach(router: R) {\n    router.dispatchAttach(null)\n  }\n\n  /**\n   * Detaches the [Router].\n   *\n   * @param router the [Router].\n   */\n  @JvmStatic\n  public fun detach(router: Router<*>) {\n    router.dispatchDetach()\n  }\n\n  /**\n   * Verifies that the [Router] is attached.\n   *\n   * @param router the [Router].\n   */\n  @JvmStatic\n  public fun verifyAttached(router: Router<*>) {\n    verify(router).dispatchAttach(or(isNull(), isA<Bundle>()), any())\n  }\n\n  /**\n   * Verifies that the [Router] was attached a certain number of times\n   *\n   * @param router the [Router].\n   * @param times the number of times attached\n   */\n  @JvmStatic\n  public fun verifyAttached(router: Router<*>, times: Int) {\n    verify(router, times(times)).dispatchAttach(or(isNull(), isA<Bundle>()), any())\n  }\n\n  /**\n   * Verifies that the [Router] is attached.\n   *\n   * @param order [InOrder] for ordered verification.\n   * @param router the [Router].\n   */\n  @JvmStatic\n  public fun verifyAttached(order: InOrder, router: Router<*>) {\n    order.verify(router).dispatchAttach(or(isNull(), isA<Bundle>()), any())\n  }\n\n  /**\n   * Verified that the [Router] is attached with a specific tag.\n   *\n   * @param router the [Router].\n   * @param tag the expected tag.\n   */\n  @JvmStatic\n  public fun verifyAttached(router: Router<*>, tag: String) {\n    verify(router).dispatchAttach(or(isNull(), isA<Bundle>()), eq(tag))\n  }\n\n  /**\n   * Verifies that the [Router] is attached with an additional [VerificationMode].\n   *\n   * @param router the [Router].\n   * @param mode The mockito verification mode. ie. `times(1)`.\n   */\n  @JvmStatic\n  public fun verifyAttached(router: Router<*>, mode: VerificationMode) {\n    verify(router, mode).dispatchAttach(or(isNull(), isA<Bundle>()), any())\n  }\n\n  /**\n   * Verifies that the [Router] is not attached.\n   *\n   * @param router the [Router].\n   */\n  @JvmStatic\n  public fun verifyNotAttached(router: Router<*>) {\n    verify(router, never()).dispatchAttach(or(isNull(), isA<Bundle>()), any())\n  }\n\n  /**\n   * Verifies that the [Router] is detached.\n   *\n   * @param router the [Router].\n   */\n  @JvmStatic\n  public fun verifyDetached(router: Router<*>) {\n    verify(router).dispatchDetach()\n  }\n\n  /**\n   * Verifies that the [Router] is detached.\n   *\n   * @param order [InOrder] for ordered verification.\n   * @param router the [Router].\n   */\n  @JvmStatic\n  public fun verifyDetached(order: InOrder, router: Router<*>) {\n    order.verify(router).dispatchDetach()\n  }\n\n  /**\n   * Verifies that the [Router] is detached with an additional [VerificationMode].\n   *\n   * @param router the [Router].\n   * @param mode The mockito verification mode. ie. `times(1)`.\n   */\n  @JvmStatic\n  public fun verifyDetached(router: Router<*>, mode: VerificationMode) {\n    verify(router, mode).dispatchDetach()\n  }\n\n  /**\n   * Verifies that the [Router] is not detached.\n   *\n   * @param router the [Router].\n   */\n  @JvmStatic\n  public fun verifyNotDetached(router: Router<*>) {\n    verify(router, never()).dispatchDetach()\n  }\n}\n"
  },
  {
    "path": "libraries/rib-test/src/main/kotlin/com/uber/rib/core/TestRibCoroutineWorker.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.ExperimentalCoroutinesApi\nimport kotlinx.coroutines.test.StandardTestDispatcher\nimport kotlinx.coroutines.test.TestScope\nimport kotlinx.coroutines.test.advanceTimeBy\nimport kotlinx.coroutines.test.advanceUntilIdle\nimport kotlinx.coroutines.test.runCurrent\n\n/**\n * Binds [worker], runs [testBody] with the worker, and unbinds worker after [testBody] returns.\n *\n * This function calls [runCurrent] on the [TestScope] immediately after binding the [worker]. This\n * means that if, and only if, there's no delay in [RibCoroutineWorker.onStart] function, worker\n * will already be bound at the start of [testBody] lambda. If there are delays, calling\n * [advanceTimeBy] or [advanceUntilIdle] at the start of [testBody] is needed to complete the\n * binding.\n *\n * The same rationale applies for coroutines launched in the [CoroutineScope] parameter of\n * [RibCoroutineWorker.onStart]: if there are no delays involved, coroutines will be run until idle\n * or completed, otherwise, the aforementioned time advancing API must be used.\n */\n@OptIn(ExperimentalCoroutinesApi::class)\npublic inline fun <T : RibCoroutineWorker> TestScope.test(\n  worker: T,\n  testBody: TestScope.(T) -> Unit,\n) {\n  val dispatcher = StandardTestDispatcher(testScheduler)\n  val handle = bind(worker, dispatcher)\n  runCurrent()\n  try {\n    testBody(worker)\n  } finally {\n    handle.unbind()\n    runCurrent()\n  }\n}\n"
  },
  {
    "path": "libraries/rib-test/src/main/kotlin/com/uber/rib/core/WorkerHelper.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.core\n\nimport com.uber.rib.core.lifecycle.WorkerEvent\nimport io.reactivex.Observable\n\n/** Helper to unit test [Worker] instances. */\npublic object WorkerHelper {\n  /**\n   * Creates a [WorkerScopeProvider] that can be driven by a test observable.\n   *\n   * @param lifecycle to wrap.\n   * @return a [WorkerScopeProvider].\n   */\n  @JvmStatic\n  public fun createScopeProvider(lifecycle: Observable<WorkerEvent>): WorkerScopeProvider {\n    return WorkerScopeProvider(lifecycle)\n  }\n}\n"
  },
  {
    "path": "libraries/rib-workflow/README.md",
    "content": "# rib-workflow\n\nDeeplinking framework that represents each deeplink as a workflow of reactive steps. See tutorial 4.\n"
  },
  {
    "path": "libraries/rib-workflow/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nplugins {\n    id(\"ribs.android.library\")\n    alias(libs.plugins.maven.publish)\n}\n\nandroid {\n    namespace = \"com.uber.rib.workflow\"\n}\n\ndependencies {\n    implementation(libs.androidx.annotation)\n    implementation(libs.rxandroid2)\n    implementation(libs.rxkotlin)\n    api(libs.guava.android)\n    api(libs.rxjava2)\n    api(project(\":libraries:rib-android\"))\n    testImplementation(testLibs.junit)\n    testImplementation(testLibs.truth)\n    testImplementation(testLibs.mockito)\n}\n"
  },
  {
    "path": "libraries/rib-workflow/gradle.properties",
    "content": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPOM_NAME=RIBs (Workflow)\nPOM_ARTIFACT_ID=rib-workflow\nPOM_PACKAGING=android\n"
  },
  {
    "path": "libraries/rib-workflow/src/main/kotlin/com/uber/rib/workflow/core/ActionableItem.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workflow.core\n\nimport com.uber.rib.core.lifecycle.InteractorEvent\nimport io.reactivex.Observable\n\n/** Represents an item that [Step] operations can be performed on. */\npublic fun interface ActionableItem {\n  /**\n   * @return a lifecycle observable that can be observed so a workflow knows when to start a step\n   *   for this actionable item.\n   */\n  public fun lifecycle(): Observable<InteractorEvent>\n}\n"
  },
  {
    "path": "libraries/rib-workflow/src/main/kotlin/com/uber/rib/workflow/core/Step.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workflow.core\n\nimport com.google.common.base.Optional\nimport com.uber.rib.core.lifecycle.InteractorEvent\nimport com.uber.rib.workflow.core.internal.WorkflowFriendModuleApi\nimport io.reactivex.Observable\nimport io.reactivex.Single\nimport io.reactivex.android.schedulers.AndroidSchedulers\nimport io.reactivex.functions.BiFunction\n\n/**\n * Represents a unit of work for workflows.\n *\n * @param <T> type of return value (if any) for this step.\n * @param <A> type of [ActionableItem] this step returns when finished.\n */\npublic open class Step<T, A : ActionableItem>\nprivate constructor(\n  private val stepDataSingle: Single<Optional<Data<T, A>>>,\n) {\n\n  /**\n   * Chains another step to be performed after this step completes. If the previous step results in\n   * an error and does not emit a new actionable item, future chained onStep calls will not be\n   * called.\n   *\n   * @param func to return the next step when this current step completes. This function will\n   *   receive the result of the previous step and the next actionable item to take an action on.\n   * @param <T2> the value type returned by the next step.\n   * @param <A2> the actionable item type returned by the next step.\n   * @return a [Step] to chain more calls to.\n   */\n  @OptIn(WorkflowFriendModuleApi::class)\n  @SuppressWarnings(\"RxJavaToSingle\") // Replace singleOrError() with firstOrError()\n  public open fun <T2, A2 : ActionableItem> onStep(\n    func: BiFunction<T, A, Step<T2, A2>>,\n  ): Step<T2, A2> {\n    return Step(\n      asObservable()\n        .flatMap { optionalData: Optional<Data<T, A>> ->\n          if (optionalData.isPresent) {\n            val data = optionalData.get()\n            val value =\n              checkNotNull(data.getValue()) { \"RxJava BiFunction can't accept null values.\" }\n            func.apply(value, data.actionableItem).asObservable()\n          } else {\n            Observable.just(Optional.absent())\n          }\n        }\n        .singleOrError(),\n    )\n  }\n\n  @OptIn(WorkflowFriendModuleApi::class)\n  internal open fun asResultObservable(): Observable<Optional<T & Any>> {\n    return asObservable().map { data -> Optional.fromNullable(data.orNull()?.getValue()) }\n  }\n\n  @WorkflowFriendModuleApi\n  public open fun asObservable(): Observable<Optional<Data<T, A>>> {\n    val cachedObservable: Observable<Optional<Data<T, A>>> =\n      stepDataSingle.toObservable().observeOn(AndroidSchedulers.mainThread()).cache()\n    return cachedObservable.flatMap { dataOptional: Optional<Data<T, A>> ->\n      if (dataOptional.isPresent) {\n        dataOptional\n          .get()\n          .actionableItem\n          .lifecycle()\n          .filter { interactorEvent -> interactorEvent === InteractorEvent.ACTIVE }\n          .zipWith(cachedObservable) { _, data -> data }\n      } else {\n        Observable.just(Optional.absent())\n      }\n    }\n  }\n\n  /**\n   * Data model for the result of a step.\n   *\n   * @param <T> type of return value (if any) for this step.\n   * @param <A> type of [ActionableItem] this step returns when finished.\n   * @param value for this instance.\n   * @param actionableItem for this instance.\n   */\n  public open class Data<T, A : ActionableItem>(\n    private val value: T,\n    internal val actionableItem: A,\n  ) {\n\n    @WorkflowFriendModuleApi public open fun getValue(): T = value\n\n    public companion object {\n      /**\n       * Convenience function to create a [Step.Data] instance that does not have a return value\n       * type.\n       *\n       * @param actionableItem to advance to.\n       * @param <A> type of actionable item.\n       * @return a new [Step.Data] instance. </A>\n       */\n      @JvmStatic\n      public fun <A : ActionableItem> toActionableItem(actionableItem: A): Data<NoValue, A> {\n        return Data(NoValueHolder.INSTANCE, actionableItem)\n      }\n    }\n  }\n\n  /** Used to indicate that a step has no return value. */\n  public open class NoValue\n\n  /** Initialization On Demand Singleton for [NoValue]. */\n  private object NoValueHolder {\n    val INSTANCE = NoValue()\n  }\n\n  public companion object {\n    /**\n     * Create a new step with a single that always returns a value.\n     *\n     * @param stepDataSingle - a single that returns a result for this step.\n     * @param <T> type of return value (if any) for this step.\n     * @param <A> type of [ActionableItem] this step returns when finished\n     * @return a new [Step].\n     */\n    @JvmStatic\n    public fun <T, A : ActionableItem> from(stepDataSingle: Single<Data<T, A>>): Step<T, A> {\n      return Step(stepDataSingle.map { Optional.of(it) })\n    }\n\n    /**\n     * Create a new step with a single that can emit an absent result.\n     *\n     * Absent results should be used when a step could not complete its action and can not move\n     * forward.\n     *\n     * @param stepDataSingle - a single that returns a result for this step.\n     * @param <T> type of return value (if any) for this step.\n     * @param <A> type of [ActionableItem] this step returns when finished\n     * @return a new [Step].\n     */\n    @JvmStatic\n    public fun <T, A : ActionableItem> fromOptional(\n      stepDataSingle: Single<Optional<Data<T, A>>>,\n    ): Step<T, A> = Step(stepDataSingle)\n  }\n}\n"
  },
  {
    "path": "libraries/rib-workflow/src/main/kotlin/com/uber/rib/workflow/core/Workflow.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workflow.core\n\nimport com.google.common.base.Optional\nimport io.reactivex.Single\n\n/**\n * Encapsulates a series of [Step] instances to be performed in a sequential sequence.\n *\n * @param <TReturnValue> expected return value for the entire workflow.\n * @param <TRootActionableItem> initial [ActionableItem] type for this workflow.\n *   </TRootActionableItem></TReturnValue>\n */\npublic abstract class Workflow<TReturnValue, TRootActionableItem : ActionableItem> {\n  /**\n   * Creates a single to execute a workflow.\n   *\n   * @param rootActionableItem actionable item to start the workflow with.\n   * @return an Rx [Single] that will return the workflow when subscribed to.\n   */\n  @SuppressWarnings(\"RxJavaToSingle\") // Replace singleOrError() with firstOrError()\n  public open fun createSingle(\n    rootActionableItem: TRootActionableItem,\n  ): Single<Optional<TReturnValue & Any>> {\n    return getSteps(rootActionableItem).asResultObservable().singleOrError()\n  }\n\n  /**\n   * @param rootActionableItem to create steps from.\n   * @return steps to be performed for this workflow.\n   */\n  protected abstract fun getSteps(\n    rootActionableItem: TRootActionableItem,\n  ): Step<TReturnValue, out ActionableItem>\n}\n"
  },
  {
    "path": "libraries/rib-workflow/src/main/kotlin/com/uber/rib/workflow/core/internal/WorkflowFriendModuleApi.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workflow.core.internal\n\n/*\n * Methods that are visible only to rib-workflow friend modules.\n *\n * Anything marked with this annotation is not intended for public use.\n */\n@RequiresOptIn(level = RequiresOptIn.Level.ERROR)\n@Retention(AnnotationRetention.BINARY)\ninternal annotation class WorkflowFriendModuleApi\n"
  },
  {
    "path": "libraries/rib-workflow/src/test/kotlin/com/uber/rib/workflow/core/AndroidSchedulersRule.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workflow.core\n\nimport io.reactivex.Scheduler\nimport io.reactivex.android.plugins.RxAndroidPlugins\nimport io.reactivex.functions.Function\nimport java.util.concurrent.Callable\nimport org.junit.rules.TestWatcher\nimport org.junit.runner.Description\n\n/**\n * JUnit rule to set AndroidSchedulers for tests. Inlined into this module to facilitate testing.\n *\n * @param restoreHandlers if true, the rule will save off the original schedulers and restore them\n *   after. Almost always want this to be false and is so by default.\n */\nclass AndroidSchedulersRule\n@JvmOverloads\nconstructor(\n  private val restoreHandlers: Boolean = false,\n) : TestWatcher() {\n\n  private val delegatingMainThreadScheduler =\n    DelegatingScheduler.forType(DelegatingScheduler.SchedulerType.MAIN_THREAD)\n  private val originalInitMainThreadInitHandler: Function<Callable<Scheduler>, Scheduler>? = null\n  private val originalMainThreadHandler: Function<Scheduler, Scheduler>? = null\n\n  override fun starting(description: Description) {\n    if (restoreHandlers) {\n      // https://github.com/ReactiveX/RxAndroid/pull/358\n      //            originalInitMainThreadInitHandler =\n      // RxAndroidPlugins.getInitMainThreadScheduler();\n      //            originalMainThreadHandler = RxAndroidPlugins.getMainThreadScheduler();\n    }\n    RxAndroidPlugins.reset()\n    RxAndroidPlugins.setInitMainThreadSchedulerHandler { delegatingMainThreadScheduler }\n    RxAndroidPlugins.setMainThreadSchedulerHandler { delegatingMainThreadScheduler }\n  }\n\n  override fun finished(description: Description) {\n    RxAndroidPlugins.reset()\n    if (restoreHandlers) {\n      // https://github.com/ReactiveX/RxAndroid/pull/358\n      //\n      // RxAndroidPlugins.setInitMainThreadSchedulerHandler(originalInitMainThreadInitHandler);\n      //            RxAndroidPlugins.setMainThreadSchedulerHandler(originalMainThreadHandler);\n    }\n  }\n\n  /**\n   * Replaces the main thread scheduler with a new scheduler.\n   *\n   * @param scheduler to replace the main thread scheduler with.\n   */\n  @Synchronized\n  fun setMainThreadScheduler(scheduler: Scheduler) {\n    delegatingMainThreadScheduler.setActiveScheduler(scheduler)\n  }\n}\n"
  },
  {
    "path": "libraries/rib-workflow/src/test/kotlin/com/uber/rib/workflow/core/DelegatingScheduler.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workflow.core\n\nimport androidx.annotation.VisibleForTesting\nimport io.reactivex.Scheduler\nimport io.reactivex.schedulers.Schedulers\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.atomic.AtomicReference\n\nclass DelegatingScheduler\nprivate constructor(\n  @get:VisibleForTesting val schedulerType: SchedulerType,\n) : Scheduler() {\n\n  private val activeScheduler = AtomicReference(Schedulers.trampoline())\n\n  @VisibleForTesting\n  enum class SchedulerType {\n    MAIN_THREAD,\n  }\n\n  override fun createWorker(): Worker {\n    return activeScheduler().createWorker()\n  }\n\n  override fun now(unit: TimeUnit): Long {\n    return activeScheduler().now(unit)\n  }\n\n  @VisibleForTesting\n  @Synchronized\n  fun activeScheduler(): Scheduler {\n    return activeScheduler.get()\n  }\n\n  @Synchronized\n  fun setActiveScheduler(activeScheduler: Scheduler) {\n    this.activeScheduler.set(activeScheduler)\n  }\n\n  companion object {\n    fun forType(schedulerType: SchedulerType) = DelegatingScheduler(schedulerType)\n  }\n}\n"
  },
  {
    "path": "libraries/rib-workflow/src/test/kotlin/com/uber/rib/workflow/core/StepTest.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workflow.core\n\nimport com.google.common.base.Optional\nimport com.google.common.truth.Truth.assertThat\nimport com.uber.rib.core.lifecycle.InteractorEvent\nimport com.uber.rib.workflow.core.Step.Data\nimport com.uber.rib.workflow.core.internal.WorkflowFriendModuleApi\nimport io.reactivex.Observable\nimport io.reactivex.observers.TestObserver\nimport io.reactivex.subjects.BehaviorSubject\nimport io.reactivex.subjects.PublishSubject\nimport org.junit.Before\nimport org.junit.Rule\nimport org.junit.Test\n\n@OptIn(WorkflowFriendModuleApi::class)\nclass StepTest {\n\n  @get:Rule var androidSchedulersRuleRx2 = AndroidSchedulersRule()\n\n  private val interactorLifecycleSubject = BehaviorSubject.create<InteractorEvent>()\n  private val returnValueSubject: PublishSubject<Optional<Data<Any, ActionableItem>>> =\n    PublishSubject.create()\n  private lateinit var step: Step<Any, ActionableItem>\n\n  @Before\n  fun setup() {\n    step = Step.fromOptional(returnValueSubject.singleOrError())\n  }\n\n  @Test\n  fun asObservable_withInactiveLifecycle_shouldWaitForActiveLifecycleBeforeEmitting() {\n    val returnValue = Any()\n    val testSubscriber: TestObserver<Optional<Data<Any, ActionableItem>>> =\n      TestObserver<Optional<Data<Any, ActionableItem>>>()\n    step.asObservable().subscribe(testSubscriber)\n    testSubscriber.assertNoValues()\n    testSubscriber.assertNoErrors()\n    testSubscriber.assertNotComplete()\n    returnValueSubject.onNext(\n      Optional.of(Data(returnValue, ActionableItem { interactorLifecycleSubject.hide() })),\n    )\n    returnValueSubject.onComplete()\n    testSubscriber.assertNoValues()\n    testSubscriber.assertNoErrors()\n    testSubscriber.assertNotComplete()\n    interactorLifecycleSubject.onNext(InteractorEvent.ACTIVE)\n    testSubscriber.assertValueCount(1)\n    assertThat(testSubscriber.values()[0].get().getValue()).isEqualTo(returnValue)\n    testSubscriber.assertComplete()\n    testSubscriber.assertNoErrors()\n  }\n\n  @Test\n  fun asObservable_withActiveLifecycle_shouldEmitWithoutWaiting() {\n    val returnValue = Any()\n    val testSubscriber: TestObserver<Optional<Data<Any, ActionableItem>>> =\n      TestObserver<Optional<Data<Any, ActionableItem>>>()\n    interactorLifecycleSubject.onNext(InteractorEvent.ACTIVE)\n    step.asObservable().subscribe(testSubscriber)\n    testSubscriber.assertNoValues()\n    testSubscriber.assertNoErrors()\n    testSubscriber.assertNotComplete()\n    returnValueSubject.onNext(\n      Optional.of(Data(returnValue, ActionableItem { interactorLifecycleSubject.hide() })),\n    )\n    returnValueSubject.onComplete()\n    testSubscriber.assertValueCount(1)\n    assertThat(testSubscriber.values()[0].get().getValue()).isEqualTo(returnValue)\n    testSubscriber.assertComplete()\n    testSubscriber.assertNoErrors()\n  }\n\n  @Test\n  fun onStep_withASuccessFullFirstAction_shouldProperlyChainTheNextStep() {\n    val returnValue = Any()\n    val secondReturnValue = Any()\n    val testSubscriber: TestObserver<Optional<Data<Any, ActionableItem>>> =\n      TestObserver<Optional<Data<Any, ActionableItem>>>()\n    interactorLifecycleSubject.onNext(InteractorEvent.ACTIVE)\n    step\n      .onStep { o, actionableItem ->\n        Step.from(\n          Observable.just(Data(secondReturnValue, actionableItem)).singleOrError(),\n        )\n      }\n      .asObservable()\n      .subscribe(testSubscriber)\n    returnValueSubject.onNext(\n      Optional.of(Data(returnValue, ActionableItem { interactorLifecycleSubject.hide() })),\n    )\n    returnValueSubject.onComplete()\n    testSubscriber.assertValueCount(1)\n    assertThat(testSubscriber.values()[0].get().getValue()).isEqualTo(secondReturnValue)\n    testSubscriber.assertComplete()\n    testSubscriber.assertNoErrors()\n  }\n\n  @Test\n  fun onStep_withAnUnsuccessfulFirstAction_shouldTerminateTheWholeChain() {\n    val testSubscriber: TestObserver<Optional<Data<Any, ActionableItem>>> =\n      TestObserver<Optional<Data<Any, ActionableItem>>>()\n    val secondReturnValue = Any()\n    interactorLifecycleSubject.onNext(InteractorEvent.ACTIVE)\n    step\n      .onStep { _, actionableItem ->\n        Step.from(\n          Observable.just(Data(secondReturnValue, actionableItem)).singleOrError(),\n        )\n      }\n      .asObservable()\n      .subscribe(testSubscriber)\n    returnValueSubject.onNext(Optional.absent())\n    returnValueSubject.onComplete()\n    testSubscriber.assertValueCount(1)\n    assertThat(testSubscriber.values()[0].isPresent).isFalse()\n    testSubscriber.assertComplete()\n    testSubscriber.assertNoErrors()\n  }\n}\n"
  },
  {
    "path": "libraries/rib-workflow/src/test/kotlin/com/uber/rib/workflow/core/WorkflowTest.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workflow.core\n\nimport com.google.common.base.Optional\nimport com.google.common.truth.Truth.assertThat\nimport com.uber.rib.core.lifecycle.InteractorEvent\nimport com.uber.rib.workflow.core.Step.Companion.from\nimport com.uber.rib.workflow.core.Step.Data\nimport io.reactivex.android.plugins.RxAndroidPlugins\nimport io.reactivex.observers.TestObserver\nimport io.reactivex.schedulers.Schedulers\nimport io.reactivex.subjects.BehaviorSubject\nimport io.reactivex.subjects.PublishSubject\nimport org.junit.After\nimport org.junit.Before\nimport org.junit.Rule\nimport org.junit.Test\n\nclass WorkflowTest {\n  @get:Rule var androidSchedulersRuleRx2 = AndroidSchedulersRule()\n\n  private val interactorLifecycleSubject = BehaviorSubject.create<InteractorEvent>()\n  private val returnValueSubject: PublishSubject<Data<Any, ActionableItem>> =\n    PublishSubject.create()\n\n  @Before\n  fun setup() {\n    RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }\n  }\n\n  @After\n  fun cleanup() {\n    RxAndroidPlugins.reset()\n  }\n\n  @Test\n  fun createSingle_shouldReturnASingleThatRunsTheWorkflow() {\n    val actionableItem = ActionableItem { interactorLifecycleSubject }\n\n    val workflow: Workflow<Any, ActionableItem> =\n      object : Workflow<Any, ActionableItem>() {\n        override fun getSteps(rootActionableItem: ActionableItem): Step<Any, ActionableItem> {\n          return from(returnValueSubject.singleOrError())\n        }\n      }\n\n    val testSubscriber = TestObserver<Optional<Any>>()\n    workflow.createSingle(actionableItem).subscribe(testSubscriber)\n\n    interactorLifecycleSubject.onNext(InteractorEvent.ACTIVE)\n    val returnValue = Any()\n    returnValueSubject.onNext(Data(returnValue, actionableItem))\n    returnValueSubject.onComplete()\n\n    testSubscriber.assertValueCount(1)\n    assertThat(testSubscriber.values()[0].get()).isEqualTo(returnValue)\n    testSubscriber.assertComplete()\n    testSubscriber.assertNoErrors()\n  }\n}\n"
  },
  {
    "path": "libraries/rib-workflow-test/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.android.library\")\n    alias(libs.plugins.maven.publish)\n}\n\nandroid {\n    namespace = \"com.uber.rib.workflow.test\"\n\n    // This module is only testing utilities. Given this code isn't intended to be run inside a production\n    // android app this module confuses android lint. Let's just disable lint errors here.\n    lint {\n        abortOnError = false\n        disable.add(\"InvalidPackage\")\n    }\n}\n\nkotlin.compilerOptions {\n    optIn.add(\"com.uber.rib.workflow.core.internal.WorkflowFriendModuleApi\")\n}\n\ndependencies {\n    api(project(\":libraries:rib-workflow\"))\n    api(libs.guava.android)\n    api(libs.rxjava2)\n    implementation(libs.androidx.annotation)\n    implementation(testLibs.truth)\n}\n"
  },
  {
    "path": "libraries/rib-workflow-test/gradle.properties",
    "content": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPOM_NAME=RIBs (Workflow Test Utils)\nPOM_ARTIFACT_ID=rib-workflow-test\nPOM_PACKAGING=android\n"
  },
  {
    "path": "libraries/rib-workflow-test/src/main/kotlin/com/uber/rib/workflow/core/StepTester.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.workflow.core\n\nimport com.google.common.base.Optional\nimport com.google.common.truth.Truth.assertThat\nimport io.reactivex.Observable\nimport io.reactivex.observers.TestObserver\n\n/** Utility to expose [Observable] instances on a [Step] for unit testing purposes. */\npublic object StepTester {\n  /**\n   * Exposes a [Step] instances observable for testing purposes.\n   *\n   * @param step to expose observable for.\n   * @param <T> type of return value for a step.\n   * @param <A> type of next actionable item for a step.\n   * @return a [Observable] that runs the steps action. </A></T>\n   */\n  @JvmStatic\n  public fun <T, A : ActionableItem> exposeObservable(\n    step: Step<T, A>,\n  ): Observable<Optional<Step.Data<T, A>>> {\n    return step.asObservable()\n  }\n\n  /**\n   * Exposes the [com.uber.rib.workflow.core.Step.Data] of a [Step]\n   *\n   * @param step to expose data for.\n   * @param <T> type of return value for a step.\n   * @param <A> type of next actionable item for a step.\n   * @return the data of the step </A></T>\n   */\n  @JvmStatic\n  public fun <T, A : ActionableItem> exposeStepData(step: Step.Data<T, A>): T? {\n    return step.getValue()\n  }\n\n  /**\n   * Asserts that no [Step] has been emitted from the [TestObserver]\n   *\n   * @param testSubscriber the step subscriber to assert on.\n   * @param <T> type of return value for a step.\n   * @param <A> type of next actionable item for a step. </A></T>\n   */\n  @JvmStatic\n  public fun <T, A : ActionableItem> assertStepNotYetEmitted(\n    testSubscriber: TestObserver<Optional<Step.Data<T, A>>>,\n  ) {\n    testSubscriber.run {\n      assertNoValues()\n      assertNotComplete()\n      assertNoErrors()\n    }\n  }\n\n  /**\n   * Asserts that exactly one [Step] has been emitted from the [TestObserver]\n   *\n   * @param testSubscriber the step subscriber to assert on.\n   * @param <T> type of return value for a step.\n   * @param <A> type of next actionable item for a step. </A></T>\n   */\n  @JvmStatic\n  public fun <T, A : ActionableItem> assertStepEmitted(\n    testSubscriber: TestObserver<Optional<Step.Data<T, A>>>,\n  ) {\n    testSubscriber.assertValueCount(1)\n    val stepData: Optional<Step.Data<T, A>> = testSubscriber.values()[0]\n    assertThat(stepData.isPresent).isTrue()\n    testSubscriber.assertComplete()\n    testSubscriber.assertNoErrors()\n  }\n}\n"
  },
  {
    "path": "releasing.sh",
    "content": "#!/usr/bin/env bash\nset -e\nROOT_DIR=\"$(git rev-parse --show-toplevel)\"\nANDROID_DIR=\"$ROOT_DIR\"\n\n# Change the version in `gradle.properties` to a non-SNAPSHOT version\nsed -i '' 's|-SNAPSHOT||' $ANDROID_DIR/gradle.properties\nNEW_VERSION=$(grep -Eo \"\\d+\\.\\d+\\.\\d+\" $ANDROID_DIR/gradle.properties)\necho \"Preparing to release: $NEW_VERSION\"\n\n# Update the `README.md` with the new version\nOLD_VERSION=$(grep -iE \"'com.uber.rib:rib-\" $ROOT_DIR/README.md | grep -Eo \"\\d+\\.\\d+\\.\\d+\" | sort | uniq)\nfind $ROOT_DIR -type f -name 'README.md' | xargs sed -i '' \"s|$OLD_VERSION|$NEW_VERSION|g\"\n\n# Update the `CHANGELOG.md` for the impending release\nvim $ROOT_DIR/CHANGELOG.md\n\n# Commit new version prep\ngit commit -am \"Prepare for release $NEW_VERSION\"\n\n# Tag new version\ngit tag -a \"v$NEW_VERSION\" -m \"Version $NEW_VERSION\"\n\n# Clean build, push to sonatype, and release the repos\necho \"Building and releasing...\"\npushd $ANDROID_DIR\n./gradlew clean publish\npopd\n\n# Prepare for next snapshot\nNEXT_PATCH=$(expr $(echo $NEW_VERSION | sed 's|^.*\\.||') + 1)\nNEXT_VERSION=\"$(echo $NEW_VERSION | grep -o '^.*\\.')$NEXT_PATCH-SNAPSHOT\"\necho \"Next dev version is $NEXT_VERSION\"\nsed -i '' \"s|$NEW_VERSION|$NEXT_VERSION|\" $ANDROID_DIR/gradle.properties\n\n# Commit next snapshot prep\ngit commit -am \"Prepare next development version\"\n\n# Push to remote\nREMOTE=$(git remote -v | grep -Ei \"uber/ribs.*push\" | awk '{print $1}')\necho \"Pushing to remote: $REMOTE\"\ngit push $REMOTE && git push $REMOTE --tags\n"
  },
  {
    "path": "settings.gradle",
    "content": "import org.gradle.api.initialization.resolve.RepositoriesMode\n\npluginManagement {\n    repositories {\n        gradlePluginPortal()\n        google()\n        mavenCentral()\n    }\n}\n\ndependencyResolutionManagement {\n    // rib-intellij-plugin project applies the IntelliJ plugin, which applies custom repositories.\n    // Otherwise, this should be FAIL_ON_PROJECT_REPO.\n    repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)\n    repositories {\n        google()\n        mavenCentral()\n    }\n    versionCatalogs {\n        appLibs {\n            from(files(\"gradle/app-libs.versions.toml\"))\n        }\n        testLibs {\n            from(files(\"gradle/test-libs.versions.toml\"))\n        }\n    }\n}\n\nincludeBuild 'conventions'\n\ninclude ':libraries:rib-test'\ninclude ':libraries:rib-android'\ninclude ':libraries:rib-android-compose'\ninclude ':libraries:rib-android-core'\ninclude ':libraries:rib-base'\ninclude ':libraries:rib-compiler-app'\ninclude ':libraries:rib-compiler-test'\ninclude ':libraries:rib-coroutines'\ninclude ':libraries:rib-coroutines-test'\ninclude ':libraries:rib-debug-utils'\ninclude ':libraries:rib-screen-stack-base'\ninclude ':libraries:rib-router-navigator'\ninclude ':libraries:rib-workflow'\ninclude ':libraries:rib-workflow-test'\ninclude ':tooling:rib-intellij-plugin'\ninclude ':tooling:rib-intellij-plugin:native:intellij-broadcast-rib'\ninclude ':tooling:rib-flipper-plugin'\ninclude ':tooling:utils:intellij-broadcast-core'\ninclude ':tutorials:tutorial1'\ninclude ':tutorials:tutorial2'\ninclude ':tutorials:tutorial3'\ninclude ':tutorials:tutorial3-completed'\ninclude ':tutorials:tutorial4'\ninclude ':demos:flipper'\ninclude ':demos:intellij'\ninclude ':demos:memory-leaks'\ninclude ':demos:compose'\ninclude ':demos:rib-workers'\n"
  },
  {
    "path": "tooling/rib-flipper-plugin/README.md",
    "content": "# RIBs Plugin for Flipper Debugging tool\n\nWe have created a Flipper plugin to help visualize your application RIBs tree in realtime.\n\nFor details on installation and usage, please see the [RIBs Flipper Plugin documentation section](https://github.com/uber/RIBs/wiki/Android-Tooling#ribs-flipper-plugin-for-android) on the [Android tooling wiki page](https://github.com/uber/RIBs/wiki/Android-Tooling).\n"
  },
  {
    "path": "tooling/rib-flipper-plugin/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.android.library\")\n    alias(libs.plugins.maven.publish)\n}\n\nandroid {\n    namespace = \"com.uber.rib.flipper\"\n}\n\ndependencies {\n    api(project(\":libraries:rib-android\"))\n    api(project(\":libraries:rib-android-core\"))\n    api(project(\":libraries:rib-base\"))\n    api(libs.rxkotlin)\n    api(libs.rxrelay2)\n    api(libs.rxjava2)\n    implementation(libs.androidx.annotation)\n    implementation(libs.androidx.appcompat)\n    implementation(libs.guava.android)\n    implementation(libs.flipper)\n}\n"
  },
  {
    "path": "tooling/rib-flipper-plugin/desktop/README.md",
    "content": "# RIBs Plugin for Flipper Debugging tool\n\nThis folder contains the desktop javascript plugin to be used with Flipper-enabled applications.\n\nFor setup and local development instructions, please see the Flipper official documentation for [loading custom plugins](https://fbflipper.com/docs/extending/loading-custom-plugins), using plugins path value of ```<path_to_repo>/android/tooling/rib-flipper-plugin/```.\n"
  },
  {
    "path": "tooling/rib-flipper-plugin/desktop/package.json",
    "content": "{\n  \"$schema\": \"https://fbflipper.com/schemas/plugin-package/v2.json\",\n  \"name\": \"flipper-plugin-ribtree\",\n  \"id\": \"ribtree\",\n  \"title\": \"Rib Explorer\",\n  \"version\": \"1.0.1\",\n  \"description\": \"Rib Tree Visualizer\",\n  \"keywords\": [\n    \"flipper-plugin\"\n  ],\n  \"icon\": \"apps\",\n  \"flipperBundlerEntry\": \"src/index.js\",\n  \"main\": \"dist/bundle.js\",\n  \"author\": \"oliviern@uber.com\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"lint\": \"flipper-pkg lint\",\n    \"prepack\": \"flipper-pkg lint && flipper-pkg bundle\",\n    \"build\": \"flipper-pkg bundle\",\n    \"watch\": \"flipper-pkg bundle --watch\"\n  },\n  \"dependencies\": {\n    \"d3\": \"^5.9.7\"\n  },\n  \"peerDependencies\": {\n    \"flipper\": \"latest\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"latest\",\n    \"@types/react-dom\": \"latest\",\n    \"flipper\": \"latest\",\n    \"flipper-pkg\": \"latest\"\n  }\n}\n"
  },
  {
    "path": "tooling/rib-flipper-plugin/desktop/src/TreeChart.js",
    "content": "import React, { Component } from 'react'\nimport * as d3 from \"d3\";\n\nvar searchTerm = '';\nvar activeNodeId = '';\n\nclass TreeChart extends Component {\n\n   constructor(props) {\n       super(props);\n       this.renderTree = this.renderTree.bind(this);\n   }\n\n   componentDidMount() {\n      this.renderTree();\n   }\n\n   componentDidUpdate() {\n     this.renderTree();\n   }\n\n   getTextOpacity(d) {\n     if (!searchTerm || d.data.name.toLowerCase().includes(searchTerm)) {\n        return 1;\n     }\n     return 0.1;\n   }\n\n   renderTree() {\n        searchTerm = this.props.searchTerm.toLowerCase();\n        activeNodeId = this.props.activeNodeId;\n\n        var data = this.props.data;\n        var diagonal = d3.linkHorizontal().x(function(d) { return d.y; }).y(function(d) { return d.x; });\n\n       var innerWidth = this.props.width - this.props.margin.right - this.props.margin.left;\n       var innerHeight = this.props.height - this.props.margin.top - this.props.margin.bottom;\n       var marginRight = this.props.margin.right;\n       this.tree = d3.tree().size([innerWidth, innerHeight]);\n\n       var treeRoot = d3.hierarchy(this.props.data[0]);\n       this.tree(treeRoot);\n\n       var nodes = treeRoot.descendants().reverse();\n       var links = treeRoot.links();\n\n       var maxDepth = d3.max(nodes, d => d.depth);\n       var maxWidth = innerWidth;\n       var marginLeft = this.props.margin.left;\n       nodes.forEach(function(d) { d.y = (d.depth * maxWidth) / maxDepth + marginLeft});\n\n       var maxHeight = d3.max(nodes, d => d.y);\n       var windowHeight = innerHeight;\n       nodes.forEach(function(d) { d.x = (d.x * windowHeight) / maxHeight });\n\n       var svg = d3.select(this.node);\n       var node = svg.selectAll(\"g.node\").data(nodes, function(d) { return d.data.id });\n\n       var nodeEnter = node.enter().append(\"g\")\n         .attr(\"class\", \"node\")\n         .attr(\"transform\", function(d) {\n           var parentX = d.parent ? d.parent.x : d.x;\n           var parentY = d.parent ? d.parent.y : d.y;\n           return \"translate(\" + parentY + \",\" + parentX + \")\"; })\n         .on(\"click\", this.props.onNodeClick)\n         .on(\"mouseover\", this.props.onNodeMouseOver)\n         .on(\"mouseout\", this.props.onNodeMouseOut);\n\n       nodeEnter.append(\"circle\")\n         .attr(\"r\", 0)\n         .style(\"stroke-width\", \"2px\")\n         .style(\"stroke\", function(d) { return d.data.hasView ? \"#fbb\" : \"steelblue\" })\n         .style(\"fill\", \"#fff\");\n\n       nodeEnter.append(\"text\")\n         .attr(\"x\", function(d) { return d.depth == maxDepth ? marginRight : 0; })\n         .attr(\"y\", \"-10\")\n         .style(\"font-size\", this.textStyle)\n         .attr(\"text-anchor\", function(d) { return d.depth == maxDepth ? \"end\" : \"middle\"; })\n         .text(function(d) { return d.data.name; })\n         .style(\"fill-opacity\", 0);\n\n       var nodeEnterUpdate = nodeEnter.transition()\n         .duration(this.props.duration)\n         .attr(\"transform\", function(d) { return \"translate(\" + d.y + \",\" + d.x + \")\"; });\n       nodeEnterUpdate.select(\"circle\").attr(\"r\", 6);\n       nodeEnterUpdate.select(\"text\").style(\"fill-opacity\", this.getTextOpacity);\n\n       var nodeUpdate = node.transition()\n         .duration(this.props.duration)\n         .attr(\"transform\", function(d) { return \"translate(\" + d.y + \",\" + d.x + \")\"; })\n         .select(\"text\").style(\"fill-opacity\", this.getTextOpacity);\n\n      var nodeUpdate = node.select(\"circle\")\n         .style(\"fill\", function(d) { return d.data.id == activeNodeId ? (d.data.hasView ? \"#fdd\" : \"#6ac\") : \"#fff\" });\n\n       var nodeExit = node.exit().transition()\n          .duration(this.props.duration)\n          .remove();\n       nodeExit.select(\"circle\").attr(\"r\", 0);\n       nodeExit.select(\"text\").style(\"fill-opacity\", 0);\n\n       var link = svg.selectAll(\"path.link\").data(links, function(d) { return d.target.data.id; });\n\n       link.enter().insert(\"path\", \"g\")\n          .attr(\"class\", \"link\")\n          .style(\"fill\", \"none\")\n          .style(\"stroke\", \"#ccc\")\n          .style(\"stroke-width\", \"1px\")\n          .attr(\"d\", function(d) {\n            var x = d.source && d.source.x ? d.source.x : innerHeight / 2;\n            var y = d.source && d.source.y ? d.source.y : 0;\n            var o = {x, y};\n            return diagonal({source: o, target: o});\n          })\n          .transition()\n            .duration(this.props.duration)\n            .attr(\"d\", diagonal);\n\n       link.transition()\n            .duration(this.props.duration)\n            .attr(\"d\", diagonal);\n\n       link.exit().remove();\n   }\n\n   render() {\n      return <svg ref={node => this.node = node} width={this.props.width} height={this.props.height}></svg>;\n   }\n}\nexport default TreeChart\n"
  },
  {
    "path": "tooling/rib-flipper-plugin/desktop/src/index.js",
    "content": "import {\n  colors,\n  Button,\n  DetailSidebar,\n  ErrorBlock,\n  FlipperPlugin,\n  Glyph,\n  LoadingIndicator,\n  ManagedDataInspector,\n  Panel,\n  PersistedState,\n  SearchInput,\n  SearchBox,\n  SearchIcon,\n  styled,\n  Toolbar,\n  Tooltip} from 'flipper';\nimport React, {Component} from 'react';\nimport * as d3 from \"d3\";\nimport {exec} from 'child_process';\nimport {getHostForNode, getHostForNodeRecursive} from './utils';\nimport TreeChart from './TreeChart';\n\nvar ICON_BUG = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADC0lEQVRYR72XO4hTQRSGv7isq4uIiGn0wkZI4QN05YCCIFhZiYUYCx8rPlBZGxstbBRtxNoHPlbBB4LBykZUrERtojaKrCmyGhYhoILgY3VdOWHuZXZ27r25gWQgxZ2cmfnPOf/5z0yOmSMHTDnTHZvTjaNRKpV6yuXyZDfnIgAi0lupVP7Yh3djrgnA53nC4YuBcaAJ1rG7ABxpFTiQUwAz8puywV7gGtDj2B0CzotIfyuRDJ2exgGPR82MeAB9Al4A203KeoGJIAj66/X6zyxpnAag1dAZuwlTLbrHP2AYuJzlcLXNTMJisdhXrVZ/ecpXpw6HIFp1ph0SqudJY4uIPGyFB2kk1IPcCO0EbqQA+AosdGxU2Lyak0TCaJEJ5yzgNzAIvI4BcRPYAxSAmkVgnzPNMk4iYROAlctnwFJgiZlbBBwA5gNPROSpCfsx4Jyz1nUmErwkEuqiHcBd463dH24DB4FmyRlAQ0YfwuBsAh6rzgH3HEBRAGNJmM/n5zUaje9ABVgH/PWEfTQIgsF6vf7D899LQPVBgLkiMukS00fC/cBzEalaxppvzbtvfABWhLLsMXgDrLHSuBxYD4zEKeFW4DqwwNnsjkmHe8YosDIBgM0hXfsN2CciD0IHfST0MVYJuNaXgiQAIjLbCfuUO+cjYdmQa8gK3TtAw5c5AtaCW8oFYJu9iY+EobarnbL3InAG2JCRhGq+UduzqQT9Vi2xqymxHWuda4PRn6tsIZY0En4xDlwBtINGI1M7BpSc99tJQVpTytKO3Yuq4kmqgkcisjmtKbXSjk8Ap2J0IK0MTwKnw7W+aKS1Yy3J90aIrgK7HSBJSjgHUCFaFifDae14l/F8QA+1bkE2hjgSXjLkVdsxE4WRrCTU98Eq4K0VOm23dhTiUmBzazXwSi+xvlRkacfher2O9ZkPHwCN2Ecratp622rHAyIyHsPiz0YbXADaF1Q13Zu0ghprh4RJLyUl1/FCoTBcq9WOAmeT2O47PI2EHX2mxSphtx+o7qW0Y89wqwSnnfEf3gFzRYyLvhoAAAAASUVORK5CYII=';\n\nexport default class RibTree extends FlipperPlugin {\n\n  constructor(props) {\n    super(props);\n\n    this.state = {\n       selectedNodeId: '',\n       searchTerm: '',\n       activeNodeId: '',\n    };\n\n    // Listener used to resize tree graph and make sure it fits within window.\n    window.addEventListener(\"resize\", this.onWindowResize.bind(this));\n  }\n\n  static defaultPersistedState = {\n    sessionId: null,\n    treeData: [],\n    idToNodes: {},\n  };\n\n  static persistedStateReducer = (\n    persistedState: PersistedState,\n    method: string,\n    data: Object,\n  ) => {\n    var newState;\n\n    // when new session is detected, clear previous data..\n    if (data.sessionId != persistedState.sessionId) {\n        newState = {\n           sessionId: data.sessionId,\n           treeData: [],\n           idToNodes: {},\n         };\n    } else {\n        newState = {\n           sessionId: persistedState.sessionId,\n           treeData: persistedState.treeData.slice(0),\n           idToNodes: {...persistedState.idToNodes},\n        }\n    }\n\n    var parentNode = newState.idToNodes[data.parent.id];\n    var childNode = newState.idToNodes[data.router.id];\n\n    if (method === 'ATTACHED') {\n        console.log(\"Ribtree: attach \" + data.parent.name + \"(\" + data.parent.id + \") -> \" + data.router.name + \"(\" + data.router.id + \")\");\n        if (!childNode && !parentNode) {\n            parentNode = {\n              parent: {},\n              ...data.parent,\n              children: []\n            };\n            newState.treeData.push(parentNode);\n            newState.idToNodes[parentNode.id] = parentNode;\n        }\n        if (childNode) {\n            if (!parentNode) {\n              parentNode = {\n                 parent: {},\n                 ...data.parent,\n                 children: [childNode]\n               };\n              if (childNode.parent && childNode.parent.id) {\n                console.error(\"Ribtree: Child node should have no parent!\");\n                return;\n              }\n              childNode.parent = parentNode;\n\n              newState.treeData = newState.treeData.filter(function(data) {\n                return data.id != childNode.id;\n              });\n\n              newState.treeData.push(parentNode);\n              newState.idToNodes[parentNode.id] = parentNode;\n            } else {\n               if (childNode.parent && childNode.parent.id) {\n                  console.error(\"Ribtree: Child node should have no parent!\");\n                  return;\n               } else {\n                  parentNode.children.push(childNode);\n                  childNode.parent = parentNode;\n                  newState.treeData = newState.treeData.filter(function(node) {\n                     return node.id != childNode.id;\n                  });\n               }\n            }\n        } else {\n           childNode = {\n             ...data.router,\n             parent: parentNode,\n             children: []\n           };\n           parentNode.children.push(childNode);\n           newState.idToNodes[childNode.id] = childNode;\n        }\n    }\n    if (method === 'DETACHED') {\n       console.log(\"Ribtree: detach \" + data.parent.name + \"(\" + data.parent.id + \") -> \" + data.router.name + \"(\" + data.router.id + \")\");\n       if (!childNode) {\n         console.log(\"Ribtree: Child node does not exists!\");\n         return;\n       } else {\n         if (parentNode) {\n           parentNode.children = parentNode.children.filter(function(node) {\n             return node.id != data.router.id;\n           });\n           if (!parentNode.children.length && !parentNode.parent.id) {\n             newState.idToNodes[parentNode.id] = undefined;\n           }\n         }\n         newState.idToNodes[data.router.id] = undefined;\n         newState.treeData = newState.treeData.filter(function(node) {\n           return node.id != childNode.id && node.children.length;\n         });\n       }\n    }\n    return newState;\n  };\n\n  componentDidMount() {\n    this._currentActivity = null;\n  }\n\n  componentWillUnmount() {\n  }\n\n  onWindowResize() {\n    this.forceUpdate();\n  }\n\n  async onNodeClick(node) {\n    // clears screenshot and view data of previously selected node\n    if (this.state.selectedNodeId && this.props.persistedState.idToNodes[this.state.selectedNodeId]) {\n      this.props.persistedState.idToNodes[this.state.selectedNodeId].viewData = undefined;\n    }\n\n    this.setState({\n      selectedNodeId: node.data.id\n    });\n  }\n\n  // Note: the activity containing the UI elements need to be on top of the stack for permalink to work\n  onEditLayout(node) {\n    this.props.selectPlugin('Inspector', node.viewClassName);\n  }\n\n  onNodeMouseOver(node) {\n    this.setState({\n      activeNodeId: node.data.id\n    });\n    var request = {\n      id: node.data.id\n    };\n    this.client.call(\"SHOW_HIGHLIGHT\", request);\n  }\n\n  onNodeMouseOut(node) {\n    this.setState({\n      activeNodeId: undefined\n    });\n    var request = {\n      id: node.data.id\n    };\n    this.client.call(\"HIDE_HIGHLIGHT\", request);\n  }\n\n  onValueChanged(e) {\n    var searchTerm = e.target.value;\n    this.setState({searchTerm});\n  }\n\n  render() {\n    if (!this.props.persistedState.treeData.length) {\n      return <ErrorBlock error=\"Your application hasn't rendered a RIB tree yet!\" />;\n    }\n\n    var that = this;\n\n    // Use last element in root arrays, to support multiple rib-based activities simultaneously\n    var treeData = this.props.persistedState.treeData;\n    var hosts = [];\n    var rootIndex = 0;\n    var lastHost;\n    treeData.forEach(function(node, i) {\n       var host = getHostForNode(node);\n       if (host) {\n         if (host != lastHost) {\n           rootIndex = i;\n           hosts.push(host.substring(host.lastIndexOf('.') + 1));\n         }\n         lastHost = host;\n       }\n    });\n    this._currentActivity = lastHost;\n\n    var hostElements = [];\n    hosts.forEach(function(name, i) {\n       hostElements.push(<span>{name}</span>);\n       if (i < hosts.length - 1) {\n         hostElements.push(<Glyph name=\"arrow-right\" color={colors.macOSTitleBarIcon} size={16} />);\n       }\n    });\n\n    return <div onMeasure={this.onMeasure}>\n      <Toolbar>\n        <SearchBox style={{width: 200, height: 30, margin: 6}}>\n          <SearchIcon\n            name=\"magnifying-glass\"\n            color={colors.macOSTitleBarIcon}\n            size={16}\n          />\n          <SearchInput\n            placeholder={'Search RIB'}\n            onChange={(e) => this.onValueChanged(e)}\n            value={this.state.searchTerm}\n          />\n        </SearchBox>\n        <div style={{marginLeft: 'auto', marginRight: 'auto'}}>\n          <Tooltip title={'Stack of Rib-based android activities. Application can have several activities rendering different Rib tree simultaneously.'}>\n            {hostElements}\n          </Tooltip>\n        </div>\n        <Tooltip title={'Submit bug or request feature'}>\n          <Button href={'https://github.com/facebook/flipper/issues'} style={{marginLeft: 'auto', marginRight: 10}} compact={true}>\n            <img src={ICON_BUG} style={{width: 20, height: 20}} />\n          </Button>\n        </Tooltip>\n      </Toolbar>\n      <TreeChart\n         data={[treeData[rootIndex]]}\n         width={window.innerWidth - 350}\n         height={window.innerHeight}\n         margin={{top: 20, right: 20, bottom: 20, left: 20}}\n         duration={500}\n         onNodeClick={this.onNodeClick.bind(this)}\n         onNodeMouseOver={this.onNodeMouseOver.bind(this)}\n         onNodeMouseOut={this.onNodeMouseOut.bind(this)}\n         searchTerm={this.state.searchTerm}\n         activeNodeId={this.state.activeNodeId}\n      />\n     </div>;\n  }\n}\n"
  },
  {
    "path": "tooling/rib-flipper-plugin/desktop/src/utils.js",
    "content": "// Returns the host activity for the given node\nexport function getHostForNode(node) {\n    var host = getHostForNodeRecursive(node, true);\n    if (host) {\n      return host;\n    }\n    return getHostForNodeRecursive(node, false);\n}\n\nexport function getHostForNodeRecursive(node, fromParent) {\n    if (!node) {\n      return;\n    }\n    if (node.hostClassName) {\n      return node.hostClassName;\n    }\n    if (fromParent) {\n      return getHostForNodeRecursive(node.parent, fromParent);\n    } else if (node.children) {\n      for (var i = 0; i < node.children.length; i++) {\n        var host = getHostForNodeRecursive(node.children[i], fromParent);\n        if (host) {\n           return host;\n        }\n      }\n    }\n}\n\n"
  },
  {
    "path": "tooling/rib-flipper-plugin/gradle.properties",
    "content": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPOM_NAME=RIBs (Flipper Plugin)\nPOM_ARTIFACT_ID=rib-flipper-plugin\nPOM_PACKAGING=jar\n"
  },
  {
    "path": "tooling/rib-flipper-plugin/src/main/kotlin/com/uber/rib/flipper/RibEventPayload.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.flipper\n\nimport android.app.Activity\nimport android.content.Context\nimport android.content.ContextWrapper\nimport android.view.View\nimport com.facebook.flipper.core.FlipperObject\nimport com.uber.rib.core.RibEventType\nimport com.uber.rib.core.Router\nimport com.uber.rib.core.ViewRouter\n\n/** Payload used by Rib Flipper Plugin. */\ninternal class RibEventPayload(\n  private val sessionId: String,\n  private val eventType: RibEventType,\n  private val routerInfo: RouterInfo,\n  private val parentRouterInfo: RouterInfo,\n) {\n\n  companion object {\n    const val EVENT_PARAMETER_ID: String = \"id\"\n    const val EVENT_PARAMETER_HOST_CLASSNAME: String = \"hostClassName\"\n    const val EVENT_PARAMETER_ROUTER_CLASSNAME: String = \"routerClassName\"\n    const val EVENT_PARAMETER_SESSION_ID: String = \"sessionId\"\n    const val EVENT_PARAMETER_ROUTER: String = \"router\"\n    const val EVENT_PARAMETER_PARENT: String = \"parent\"\n    const val EVENT_PARAMETER_NAME: String = \"name\"\n    const val EVENT_PARAMETER_HAS_VIEW: String = \"hasView\"\n  }\n\n  val eventName: String\n    get() = eventType.toString()\n\n  fun toFlipperPayload(): FlipperObject {\n    return FlipperObject.Builder()\n      .put(EVENT_PARAMETER_SESSION_ID, sessionId)\n      .put(EVENT_PARAMETER_ROUTER, routerInfo.toFlipperPayload())\n      .put(EVENT_PARAMETER_PARENT, parentRouterInfo.toFlipperPayload())\n      .build()\n  }\n\n  internal class RouterInfo(\n    val id: String,\n    val name: String,\n    val className: String,\n    val hasView: Boolean,\n    val activityClassName: String,\n  ) {\n    companion object {\n      private const val ROUTER_NAME_PREFIX: String = \"Router\"\n\n      fun fromRouter(router: Router<*>?, routerId: String) =\n        RouterInfo(\n          id = routerId,\n          name = router?.javaClass?.simpleName?.replace(ROUTER_NAME_PREFIX, \"\") ?: \"\",\n          className = router?.javaClass?.simpleName ?: \"\",\n          hasView = router is ViewRouter<*, *>,\n          activityClassName = if (router is ViewRouter<*, *>) getActivityClassName(router) else \"\",\n        )\n\n      private fun getActivityClassName(router: ViewRouter<*, *>): String {\n        val view: View = router.view\n        var context: Context? = view.context\n        while (context is ContextWrapper) {\n          if (context is Activity) {\n            return context.javaClass.name\n          }\n          context = context.baseContext\n        }\n        return \"\"\n      }\n    }\n\n    fun toFlipperPayload(): FlipperObject {\n      return FlipperObject.Builder()\n        .put(EVENT_PARAMETER_ID, id)\n        .put(EVENT_PARAMETER_NAME, name)\n        .put(EVENT_PARAMETER_ROUTER_CLASSNAME, className)\n        .put(EVENT_PARAMETER_HAS_VIEW, hasView)\n        .put(EVENT_PARAMETER_HOST_CLASSNAME, activityClassName)\n        .build()\n    }\n  }\n}\n"
  },
  {
    "path": "tooling/rib-flipper-plugin/src/main/kotlin/com/uber/rib/flipper/RibTreeMessageType.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.flipper\n\n/** Types of messages used by Rib Flipper Plugin. */\npublic enum class RibTreeMessageType {\n  SHOW_HIGHLIGHT,\n  HIDE_HIGHLIGHT,\n}\n"
  },
  {
    "path": "tooling/rib-flipper-plugin/src/main/kotlin/com/uber/rib/flipper/RibTreePlugin.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.flipper\n\nimport com.facebook.flipper.core.FlipperConnection\nimport com.facebook.flipper.core.FlipperObject\nimport com.facebook.flipper.core.FlipperPlugin\nimport com.facebook.flipper.core.FlipperResponder\nimport com.uber.rib.core.RibDebugOverlay\nimport com.uber.rib.core.RibEvents\nimport com.uber.rib.core.RibRouterEvent\nimport com.uber.rib.core.Router\nimport com.uber.rib.core.ViewRouter\nimport com.uber.rib.flipper.RibEventPayload.Companion.EVENT_PARAMETER_ID\nimport com.uber.rib.flipper.RibTreeMessageType.HIDE_HIGHLIGHT\nimport com.uber.rib.flipper.RibTreeMessageType.SHOW_HIGHLIGHT\nimport io.reactivex.disposables.Disposable\nimport io.reactivex.subjects.ReplaySubject\nimport java.lang.ref.WeakReference\nimport java.util.UUID\nimport java.util.WeakHashMap\n\n/** Flipper debug tool plugin to help with RIBs developement. */\npublic class RibTreePlugin : FlipperPlugin {\n  private var connection: FlipperConnection? = null\n  private var disposable: Disposable? = null\n  private val events: ReplaySubject<RibEventPayload> = ReplaySubject.create(EVENTS_CAPACITY)\n  private val sessionId: String = UUID.randomUUID().toString()\n  private val idsToOverlay: MutableMap<String, WeakReference<RibDebugOverlay>> =\n    HashMap<String, WeakReference<RibDebugOverlay>>()\n  private val routersToId: WeakHashMap<Router<*>, String> = WeakHashMap<Router<*>, String>()\n\n  public companion object {\n    private val TAG: String = RibTreePlugin::class.java.simpleName\n    private const val EVENTS_CAPACITY = 1000\n  }\n\n  init {\n    // Start listening to rib events right away, since flipper client might connect only later on\n    RibEvents.routerEvents\n      .filter { e: RibRouterEvent -> e.parentRouter != null }\n      .map { e: RibRouterEvent ->\n        val router: Router<*> = e.router\n        val routerId = createRouterIdIfNeeded(router)\n        val parentRouter: Router<*>? = e.parentRouter\n        val parentRouterId = createRouterIdIfNeeded(parentRouter)\n        RibEventPayload(\n          sessionId = sessionId,\n          eventType = e.eventType,\n          routerInfo = RibEventPayload.RouterInfo.fromRouter(router, routerId),\n          parentRouterInfo = RibEventPayload.RouterInfo.fromRouter(parentRouter, parentRouterId),\n        )\n      }\n      .subscribe(events)\n  }\n\n  override fun getId(): String {\n    return \"ribtree\"\n  }\n\n  override fun onConnect(connection: FlipperConnection) {\n    android.util.Log.d(\"RibTreeFlipperPlugin\", \"onConnect()\")\n    this.connection = connection\n    disposable =\n      events.subscribe { e: RibEventPayload ->\n        this.connection?.send(e.eventName, e.toFlipperPayload())\n      }\n    connection.receive(SHOW_HIGHLIGHT.toString()) { params: FlipperObject, _: FlipperResponder? ->\n      val id: String = params.getString(EVENT_PARAMETER_ID)\n      val router: Router<*>? = getRouterById(id)\n      if (router is ViewRouter<*, *>) {\n        val view: android.view.View = router.view\n        val overlay = RibDebugOverlay()\n        view.getOverlay().add(overlay)\n        view.invalidate()\n        idsToOverlay.put(id, WeakReference<RibDebugOverlay>(overlay))\n      }\n    }\n    connection.receive(HIDE_HIGHLIGHT.toString()) { params: FlipperObject, _: FlipperResponder? ->\n      val id: String = params.getString(EVENT_PARAMETER_ID)\n      val router: Router<*>? = getRouterById(id)\n      if (router is ViewRouter<*, *>) {\n        val view: android.view.View = router.view\n        val overlayRef: WeakReference<RibDebugOverlay> = idsToOverlay[id] ?: return@receive\n        idsToOverlay.remove(id)\n        val overlay: RibDebugOverlay = overlayRef.get() ?: return@receive\n        view.getOverlay().remove(overlay)\n        view.invalidate()\n      }\n    }\n  }\n\n  override fun onDisconnect() {\n    android.util.Log.d(TAG, \"onDisconnect()\")\n    disposable?.dispose()\n    disposable = null\n    connection = null\n  }\n\n  override fun runInBackground(): Boolean {\n    return true\n  }\n\n  @Synchronized\n  private fun getRouterById(id: String): Router<*>? {\n    for ((key, value) in routersToId.entries) {\n      if (value.compareTo(id, ignoreCase = true) == 0) {\n        return key\n      }\n    }\n    return null\n  }\n\n  @Synchronized\n  private fun createRouterIdIfNeeded(router: Router<*>?): String {\n    var id: String? = routersToId[router]\n    if (id == null) {\n      id = UUID.randomUUID().toString()\n      routersToId[router] = id\n    }\n    return id\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/README.md",
    "content": "# RIBs Code Generation Plugin for Android Studio and IntelliJ\n\nWe have created an Android Studio / IntelliJ plugin to generate RIBs scaffolding, making RIB usage and adoption easier.\n\nFor details on installation and usage, please see the [RIBs Code Generation Plugin documentation section](https://github.com/uber/RIBs/wiki/Android-Tooling#ribs-code-generation-plugin-for-android-studio-and-intellij) on the [Android tooling wiki page](https://github.com/uber/RIBs/wiki/Android-Tooling).\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2025. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport groovy.xml.XmlSlurper\nimport groovy.xml.slurpersupport.GPathResult\nimport org.jetbrains.kotlin.gradle.dsl.JvmTarget\nimport org.jetbrains.kotlin.gradle.tasks.KotlinCompile\n\nplugins {\n    kotlin(\"jvm\")\n    alias(libs.plugins.intellij.platform)\n}\n\ngroup = \"com.uber.rib\"\n\nval pluginXml: GPathResult = XmlSlurper().parse(file(\"src/main/resources/META-INF/plugin.xml\"))\nval pluginVersion: String = pluginXml.getProperty(\"version\").toString()\nversion = pluginVersion\n\nrepositories {\n    mavenLocal()\n    google()\n    mavenCentral()\n\n    intellijPlatform {\n        defaultRepositories()\n    }\n}\n\nintellijPlatform {\n    pluginConfiguration {\n        version.set(pluginVersion)\n        ideaVersion {\n            sinceBuild = \"223\"\n        }\n        name.set(\"uber-ribs\")\n    }\n    sandboxContainer.set(File(\"${project.gradle.gradleHomeDir}/caches/intellij\"))\n}\n\nkotlin {\n    jvmToolchain(17)\n}\n\ndependencies {\n    intellijPlatform {\n        bundledPlugin(\"com.intellij.java\")\n        bundledPlugin(\"org.jetbrains.android\")\n        androidStudio(libs.versions.android.studio)\n        testFramework(org.jetbrains.intellij.platform.gradle.TestFrameworkType.Platform)\n    }\n    testImplementation(project(\":libraries:rib-test\"))\n    testImplementation(project(\":libraries:rib-compiler-test\"))\n    testImplementation(libs.dagger.compiler)\n    testImplementation(libs.dagger.library)\n    testImplementation(testLibs.truth)\n    testImplementation(testLibs.compile.testing)\n    testImplementation(testLibs.mockito)\n    testImplementation(libs.androidx.annotation)\n    testImplementation(libs.android.api)\n}\n\ntasks.register<Jar>(\"sourcesJar\") {\n    dependsOn(tasks.classes)\n    archiveClassifier.set(\"sources\")\n    from(sourceSets.main.get().allSource)\n}\n\ntasks {\n    withType<JavaCompile>().configureEach {\n        sourceCompatibility = JavaVersion.VERSION_17.toString()\n        targetCompatibility = JavaVersion.VERSION_17.toString()\n    }\n    withType<KotlinCompile>().configureEach {\n        compilerOptions.jvmTarget.set(JvmTarget.JVM_17)\n    }\n}\n\nafterEvaluate {\n    artifacts {\n        archives(project.tasks.named(\"sourcesJar\"))\n        archives(project.tasks.named(\"buildPlugin\"))\n    }\n}\n\ntasks.test.configure {\n    // See: https://github.com/google/compile-testing/releases/tag/v0.22.0\n    jvmArgs(\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED\",\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED\",\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED\",\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED\",\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED\",\n        \"--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED\",\n    )\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/gradle.properties",
    "content": "kotlin.stdlib.default.dependency = false\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/native/gradle.properties",
    "content": "kotlin.stdlib.default.dependency = false\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/native/intellij-broadcast-rib/README.md",
    "content": "# Intellij Broadcast Core Debugging tool\n\nThis Android library implements support for exposing RIB tree via IntelliJ Debugging broadcast receiver.\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/native/intellij-broadcast-rib/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.android.library\")\n}\n\nandroid {\n    namespace = \"com.uber.debug.broadcast.rib\"\n}\n\ndependencies {\n    api(project(\":libraries:rib-android\"))\n    api(project(\":libraries:rib-android-core\"))\n    api(project(\":libraries:rib-base\"))\n    api(project(\":tooling:utils:intellij-broadcast-core\"))\n    api(libs.rxkotlin)\n    api(libs.rxrelay2)\n    api(libs.rxjava2)\n    implementation(libs.androidx.annotation)\n    implementation(libs.androidx.appcompat)\n    implementation(libs.guava.android)\n    implementation(libs.flipper)\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/native/intellij-broadcast-rib/src/main/java/com/uber/debug/broadcast/rib/RibHierarchyDebugBroadcastHandler.java",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.debug.broadcast.rib;\n\nimport static com.uber.debug.broadcast.rib.RibHierarchyUtils.getFriendlyResourceId;\nimport static com.uber.debug.broadcast.rib.RibHierarchyUtils.viewIncludesTarget;\n\nimport android.app.Activity;\nimport android.app.ActivityManager;\nimport android.content.Context;\nimport android.content.ContextWrapper;\nimport android.graphics.Color;\nimport android.util.Log;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport androidx.annotation.Nullable;\nimport com.uber.debug.broadcast.core.DebugBroadcastReceiver.Handler;\nimport com.uber.debug.broadcast.core.DebugBroadcastRequest;\nimport com.uber.debug.broadcast.rib.RibHierarchyPayload.RibActivity;\nimport com.uber.debug.broadcast.rib.RibHierarchyPayload.RibApplication;\nimport com.uber.debug.broadcast.rib.RibHierarchyPayload.RibNode;\nimport com.uber.debug.broadcast.rib.RibHierarchyPayload.RibView;\nimport com.uber.rib.core.RibDebugOverlay;\nimport com.uber.rib.core.RibRouterEvent;\nimport com.uber.rib.core.Router;\nimport com.uber.rib.core.ViewRouter;\nimport io.reactivex.Observable;\nimport io.reactivex.Observer;\nimport io.reactivex.disposables.Disposable;\nimport java.lang.ref.WeakReference;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.WeakHashMap;\n\n/*\n * Debug broadcast handler responsible for exposing RIB hierarchy.\n */\npublic class RibHierarchyDebugBroadcastHandler\n    implements Observer<RibRouterEvent>, Handler<RibHierarchyPayload> {\n\n  public static final String COMMAND_RIB_HIERARCHY = \"RIB_HIERARCHY\";\n  public static final String COMMAND_RIB_HIGHLIGHT = \"RIB_HIGHLIGHT\";\n\n  private static final String TAG = RibHierarchyDebugBroadcastHandler.class.getSimpleName();\n  private static final String INTENT_EXTRA_ID = \"ID\";\n  private static final String INTENT_EXTRA_VISIBLE = \"VISIBLE\";\n  private static final String COMMAND_RIB_LOCATE = \"RIB_LOCATE\";\n  private static final UUID NULL_ID = new UUID(0, 0);\n  private static final int VIEW_TAG_INFLATE_ORIGIN = \"inflateOrigin\".hashCode();\n  private static final RibHierarchyPayload EMPTY_RESPONSE =\n      new RibHierarchyPayload(\"\", new RibApplication(\"\"));\n\n  private final List<UUID> roots = new ArrayList<>();\n  private final HashMap<UUID, UUID> parent = new HashMap<>();\n  private final HashMap<UUID, List<UUID>> children = new HashMap<>();\n  private final Map<UUID, WeakReference<RibDebugOverlay>> idsToOverlay = new HashMap<>();\n  private final WeakHashMap<Router, UUID> routersToId = new WeakHashMap<>();\n  private final WeakHashMap<View, UUID> viewsToId = new WeakHashMap<>();\n  private UUID highlightId = NULL_ID;\n  private String processName = \"\";\n  private @Nullable RibTouchOverlayView mTouchOverlay = null;\n  private @Nullable DebugBroadcastRequest mPendingLocateRequest = null;\n\n  @SuppressWarnings(\"RxJavaSubscribeInConstructor\")\n  public RibHierarchyDebugBroadcastHandler(\n      Context context, Observable<RibRouterEvent> ribEventsStream) {\n    this.processName = getCurrentProcessName(context);\n    ribEventsStream.subscribe(this);\n  }\n\n  @Override\n  public boolean canHandle(DebugBroadcastRequest request) {\n    return request.isCommand(COMMAND_RIB_HIERARCHY)\n        || request.isCommand(COMMAND_RIB_HIGHLIGHT)\n        || request.isCommand(COMMAND_RIB_LOCATE);\n  }\n\n  @Override\n  public void handle(DebugBroadcastRequest request) {\n    boolean isVisible;\n    switch (request.getCommand()) {\n      case COMMAND_RIB_HIERARCHY:\n        request.respond(buildRibHierarchyPayload());\n        break;\n      case COMMAND_RIB_HIGHLIGHT:\n        UUID id = UUID.fromString(request.getStringExtra(INTENT_EXTRA_ID));\n        isVisible = Boolean.valueOf(request.getStringExtra(INTENT_EXTRA_VISIBLE));\n        setOverlayVisibility(id, isVisible);\n        request.respond(EMPTY_RESPONSE);\n        break;\n      case COMMAND_RIB_LOCATE:\n        isVisible = Boolean.valueOf(request.getStringExtra(INTENT_EXTRA_VISIBLE));\n        setTouchOverlayVisibility(isVisible);\n        mPendingLocateRequest = isVisible ? request : null;\n        if (!isVisible) {\n          request.respond(EMPTY_RESPONSE);\n        }\n        break;\n    }\n  }\n\n  @Override\n  public void onSubscribe(Disposable d) {}\n\n  @Override\n  public void onNext(RibRouterEvent ribRouterEvent) {\n    Router childRouter = ribRouterEvent.getRouter();\n    Router parentRouter = ribRouterEvent.getParentRouter();\n    if (parentRouter == null) {\n      return;\n    }\n    UUID childId = createRouterIdIfNeeded(childRouter);\n    UUID parentId = createRouterIdIfNeeded(parentRouter);\n    try {\n      switch (ribRouterEvent.getEventType()) {\n        case ATTACHED:\n          addChild(parentId, childId);\n          break;\n        case DETACHED:\n          removeChild(parentId, childId);\n          break;\n        default:\n          throw new UnsupportedOperationException(\n              \"Unknown command: \" + ribRouterEvent.getEventType());\n      }\n    } catch (IllegalArgumentException e) {\n      String message =\n          String.format(\n              Locale.US,\n              \"Error processing RibEvent %s: parent=%s child=%s\",\n              ribRouterEvent.getEventType().toString(),\n              parentRouter.getClass().getSimpleName(),\n              childRouter.getClass().getSimpleName());\n      Log.w(TAG, message);\n    }\n  }\n\n  @Override\n  public void onError(Throwable e) {}\n\n  @Override\n  public void onComplete() {}\n\n  private synchronized RibHierarchyPayload buildRibHierarchyPayload() {\n    return buildRibHierarchyPayload(null);\n  }\n\n  private synchronized RibHierarchyPayload buildRibHierarchyPayload(\n      @Nullable TargetInfo targetInfo) {\n    RibApplication ribApplication = new RibApplication(processName);\n    for (UUID rootId : roots) {\n      Activity activity = getActivityRecursive(rootId);\n      if (activity == null || !activity.hasWindowFocus()) {\n        continue;\n      }\n      Router router = getRouterFromId(rootId);\n      if (router != null) {\n        View view = router instanceof ViewRouter ? ((ViewRouter) router).getView() : null;\n        RibView ribView = view != null ? buildRibViewRecursive(view, targetInfo, 0) : null;\n        RibNode rootNode = new RibNode(router.getClass().getName(), rootId, ribView);\n        buildTreeRecursive(rootNode, targetInfo, 0);\n        RibActivity ribActivity = new RibActivity(activity, rootNode);\n        ribApplication.addActivity(ribActivity);\n      }\n    }\n    return targetInfo != null\n        ? new RibHierarchyWithSelectionPayload(\n            android.os.Build.MODEL, ribApplication, targetInfo.nodeId(), targetInfo.viewId())\n        : new RibHierarchyPayload(android.os.Build.MODEL, ribApplication);\n  }\n\n  private synchronized RibView buildRibViewRecursive(\n      View view, @Nullable TargetInfo targetInfo, int depth) {\n    RibView ribView = buildRibView(view);\n\n    if (targetInfo != null\n        && depth > targetInfo.viewDepth\n        && viewIncludesTarget(view, targetInfo.targetX, targetInfo.targetY)) {\n      targetInfo.setView(ribView, depth);\n    }\n\n    if (view instanceof ViewGroup) {\n      for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {\n        View child = ((ViewGroup) view).getChildAt(i);\n        ribView.addChildren(buildRibViewRecursive(child, targetInfo, depth + 1));\n      }\n    }\n    return ribView;\n  }\n\n  private synchronized RibView buildRibView(View view) {\n    UUID id = createViewIdIfNeeded(view);\n    Object tag = view.getTag(VIEW_TAG_INFLATE_ORIGIN);\n    String layoutId =\n        tag != null ? getFriendlyResourceId(view.getContext().getResources(), (int) tag) : \"\";\n    String viewId = getFriendlyResourceId(view.getContext().getResources(), view.getId());\n    return new RibView(view.getClass().getName(), id, viewId, layoutId);\n  }\n\n  private synchronized void buildTreeRecursive(\n      RibNode node, @Nullable TargetInfo targetInfo, int depth) {\n    List<UUID> childIds = children.get(node.getId());\n    if (childIds == null) {\n      return;\n    }\n    for (UUID childId : childIds) {\n      Router router = getRouterFromId(childId);\n      if (router == null) {\n        throw new IllegalArgumentException();\n      }\n      View view = router instanceof ViewRouter ? ((ViewRouter) router).getView() : null;\n      RibView ribView = view != null ? buildRibView(view) : null;\n      RibNode childNode = new RibNode(router.getClass().getName(), childId, ribView);\n      if (targetInfo != null\n          && view != null\n          && depth > targetInfo.nodeDepth\n          && viewIncludesTarget(view, targetInfo.targetX, targetInfo.targetY)) {\n        targetInfo.setNode(childNode, depth);\n      }\n      node.addChildren(childNode);\n      buildTreeRecursive(childNode, targetInfo, depth + 1);\n    }\n  }\n\n  @Nullable\n  private synchronized UUID getRouterId(Router router) {\n    return routersToId.get(router);\n  }\n\n  @Nullable\n  private synchronized Router getRouterFromId(UUID id) {\n    for (Map.Entry<Router, UUID> entry : routersToId.entrySet()) {\n      if (entry.getValue().equals(id)) {\n        return entry.getKey();\n      }\n    }\n    return null;\n  }\n\n  @Nullable\n  private synchronized View getViewFromId(UUID id) {\n    for (Map.Entry<View, UUID> entry : viewsToId.entrySet()) {\n      if (entry.getValue().equals(id)) {\n        return entry.getKey();\n      }\n    }\n    return null;\n  }\n\n  @Nullable\n  private View getView(UUID id) {\n    View view = null;\n    Router router = getRouterFromId(id);\n    if (router != null) {\n      if (router instanceof ViewRouter) {\n        view = ((ViewRouter) router).getView();\n      }\n    } else {\n      view = getViewFromId(id);\n    }\n    return view;\n  }\n\n  private synchronized void addChild(UUID parentId, UUID childId) {\n    if (!children.containsKey(parentId)) {\n      children.put(parentId, new ArrayList<>());\n    }\n\n    List list = children.get(parentId);\n    if (list.contains(childId)) {\n      throw new IllegalArgumentException(\"child already added\");\n    }\n    if (parent.containsKey(childId)) {\n      throw new IllegalArgumentException(\"parent already set\");\n    }\n\n    list.add(childId);\n    parent.put(childId, parentId);\n\n    roots.remove(childId);\n    if (!parent.containsKey(parentId) && !roots.contains(parentId)) {\n      roots.add(parentId);\n    }\n  }\n\n  private synchronized void removeChild(UUID parentId, UUID childId) {\n    if (!children.containsKey(parentId)) {\n      throw new IllegalArgumentException();\n    }\n    List list = children.get(parentId);\n    if (!list.contains(childId)) {\n      throw new IllegalArgumentException(\"child not already added\");\n    }\n    if (!parent.containsKey(childId)) {\n      throw new IllegalArgumentException(\"parent not set\");\n    }\n\n    list.remove(childId);\n    if (list.isEmpty()) {\n      children.remove(parentId);\n      roots.remove(parentId);\n    }\n    parent.remove(childId);\n\n    if (highlightId.equals(childId)) {\n      setOverlayVisibility(highlightId, false);\n    }\n  }\n\n  private boolean setOverlayVisibility(UUID id, boolean isVisible) {\n    if (isVisible) {\n      setOverlayVisibility(highlightId, false);\n    }\n    if (!isVisible && highlightId.equals(id)) {\n      highlightId = NULL_ID;\n    }\n\n    View view = getView(id);\n    if (view != null) {\n      if (isVisible) {\n        RibDebugOverlay overlay = new RibDebugOverlay();\n        view.getOverlay().add(overlay);\n        view.invalidate();\n        idsToOverlay.put(id, new WeakReference<>(overlay));\n        highlightId = id;\n      } else {\n        WeakReference<RibDebugOverlay> overlayRef = idsToOverlay.get(id);\n        if (overlayRef == null) {\n          return false;\n        }\n        idsToOverlay.remove(id);\n        RibDebugOverlay overlay = overlayRef.get();\n        if (overlay == null) {\n          return false;\n        }\n        view.getOverlay().remove(overlay);\n        view.invalidate();\n      }\n    }\n    return true;\n  }\n\n  @Nullable\n  private Activity getActivity(ViewRouter router) {\n    View view = router.getView();\n    if (view != null) {\n      Context context = view.getContext();\n      while (context instanceof ContextWrapper) {\n        if (context instanceof Activity) {\n          return ((Activity) context);\n        }\n        context = ((ContextWrapper) context).getBaseContext();\n      }\n    }\n    return null;\n  }\n\n  @Nullable\n  private synchronized Activity getActivityRecursive(UUID id) {\n    Router router = getRouterFromId(id);\n    if (router instanceof ViewRouter) {\n      Activity activity = getActivity((ViewRouter) router);\n      if (activity != null) {\n        return activity;\n      }\n    }\n    List<UUID> childIds = children.get(id);\n    if (childIds != null) {\n      for (UUID childId : childIds) {\n        Activity activity = getActivityRecursive(childId);\n        if (activity != null) {\n          return activity;\n        }\n      }\n    }\n    return null;\n  }\n\n  private static String getCurrentProcessName(Context context) {\n    int pid = android.os.Process.myPid();\n    ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);\n    List<ActivityManager.RunningAppProcessInfo> processInfoList = manager.getRunningAppProcesses();\n    if (processInfoList != null) {\n      for (ActivityManager.RunningAppProcessInfo processInfo : processInfoList) {\n        if (processInfo.pid == pid) {\n          return processInfo.processName;\n        }\n      }\n    }\n    return \"<Application>\";\n  }\n\n  private UUID createRouterIdIfNeeded(Router router) {\n    return computeUuidIfAbsent(routersToId, router);\n  }\n\n  private UUID createViewIdIfNeeded(View view) {\n    return computeUuidIfAbsent(viewsToId, view);\n  }\n\n  private static <T> UUID computeUuidIfAbsent(Map<T, UUID> map, T key) {\n    synchronized (map) {\n      UUID id = map.get(key);\n      if (id == null) {\n        id = UUID.randomUUID();\n        map.put(key, id);\n      }\n      return id;\n    }\n  }\n\n  private void setTouchOverlayVisibility(boolean active) {\n    for (UUID rootId : roots) {\n      Activity activity = getActivityRecursive(rootId);\n      if (activity == null || !activity.hasWindowFocus()) {\n        continue;\n      }\n      ViewGroup rootView = (ViewGroup) activity.getWindow().getDecorView().getRootView();\n      if (rootView != null) {\n        if (mTouchOverlay != null && mTouchOverlay.getParent() != null) {\n          ((ViewGroup) mTouchOverlay.getParent()).removeView(mTouchOverlay);\n          mTouchOverlay = null;\n        }\n        if (active) {\n          mTouchOverlay = new RibTouchOverlayView(activity.getBaseContext());\n          rootView.addView(mTouchOverlay);\n          rootView.bringChildToFront(mTouchOverlay);\n        }\n        break;\n      }\n    }\n  }\n\n  static class TargetInfo {\n    int targetX;\n    int targetY;\n\n    @Nullable RibNode node;\n    @Nullable RibView view;\n    int nodeDepth;\n    int viewDepth;\n\n    public TargetInfo(int x, int y) {\n      this.targetX = x;\n      this.targetY = y;\n      this.nodeDepth = -1;\n    }\n\n    public void setNode(RibNode node, int depth) {\n      this.node = node;\n      this.nodeDepth = depth;\n    }\n\n    public void setView(RibView view, int depth) {\n      this.view = view;\n      this.viewDepth = depth;\n    }\n\n    public String nodeId() {\n      return node != null ? node.getId().toString() : \"\";\n    }\n\n    public String viewId() {\n      return view != null ? view.getId().toString() : \"\";\n    }\n  }\n\n  @SuppressWarnings(\"UViewExtends\")\n  class RibTouchOverlayView extends View {\n\n    public RibTouchOverlayView(Context context) {\n      super(context);\n      setBackgroundColor(Color.argb(50, 0, 0, 255));\n    }\n\n    @Override\n    public boolean onTouchEvent(final MotionEvent event) {\n      if (event.getAction() == MotionEvent.ACTION_UP) {\n        setTouchOverlayVisibility(false);\n        if (mPendingLocateRequest != null) {\n          TargetInfo targetInfo = new TargetInfo((int) event.getX(), (int) event.getY());\n          Object payload = buildRibHierarchyPayload(targetInfo);\n          mPendingLocateRequest.respond(payload);\n          mPendingLocateRequest = null;\n        }\n      }\n      return true;\n    }\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/native/intellij-broadcast-rib/src/main/java/com/uber/debug/broadcast/rib/RibHierarchyPayload.java",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.debug.broadcast.rib;\n\nimport android.app.Activity;\nimport androidx.annotation.Nullable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\npublic class RibHierarchyPayload {\n\n  private final String name;\n  private final RibApplication application;\n\n  public RibHierarchyPayload(String name, RibApplication application) {\n    this.name = name;\n    this.application = application;\n  }\n\n  public RibApplication getApplication() {\n    return application;\n  }\n\n  public static class RibApplication {\n    String name;\n    List<RibActivity> activities;\n\n    public RibApplication(String name) {\n      this.name = name;\n      this.activities = new ArrayList<>();\n    }\n\n    public void addActivity(RibActivity activity) {\n      if (activities.contains(activity)) {\n        return;\n      }\n      activities.add(activity);\n    }\n\n    public List<RibActivity> getActivities() {\n      return activities;\n    }\n  }\n\n  public static class RibActivity {\n    private final String name;\n    private final RibNode rootRib;\n\n    public RibActivity(Activity activity, RibNode rootRib) {\n      this.name = activity.getClass().getName();\n      this.rootRib = rootRib;\n    }\n\n    public RibNode getRootRib() {\n      return rootRib;\n    }\n  }\n\n  public static class RibNode {\n    private final UUID id;\n    private final String name;\n    private final List<RibNode> children;\n    @Nullable private final RibView view;\n\n    public RibNode(String name, UUID id, @Nullable RibView view) {\n      this.name = name;\n      this.id = id;\n      this.children = new ArrayList();\n      this.view = view;\n    }\n\n    public String getName() {\n      return name;\n    }\n\n    public UUID getId() {\n      return id;\n    }\n\n    public List<RibNode> getChildren() {\n      return children;\n    }\n\n    public void addChildren(RibNode childNode) {\n      children.add(childNode);\n    }\n\n    @Nullable\n    public RibView getView() {\n      return view;\n    }\n  }\n\n  public static class RibView {\n    private final UUID id;\n    private final String name;\n    private final String viewId;\n    private final String layoutId;\n    private final List<RibView> children;\n\n    public RibView(String name, UUID id, String viewId, String layoutId) {\n      this.name = name;\n      this.id = id;\n      this.viewId = viewId;\n      this.layoutId = layoutId;\n      this.children = new ArrayList();\n    }\n\n    public String getName() {\n      return name;\n    }\n\n    public UUID getId() {\n      return id;\n    }\n\n    public List<RibView> getChildren() {\n      return children;\n    }\n\n    public void addChildren(RibView childNode) {\n      children.add(childNode);\n    }\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/native/intellij-broadcast-rib/src/main/java/com/uber/debug/broadcast/rib/RibHierarchyUtils.java",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.debug.broadcast.rib;\n\nimport android.content.res.Resources;\nimport android.view.View;\n\n/** Utility class used by the RibHierarchy debug broadcast handler */\npublic class RibHierarchyUtils {\n\n  private RibHierarchyUtils() {}\n\n  /**\n   * Friendly formatting of resources ids.\n   *\n   * @param res the resources\n   * @param resourceId the resource Id\n   * @return the string representation of the resource Id\n   */\n  static String getFriendlyResourceId(Resources res, int resourceId) {\n    try {\n      return res.getResourceEntryName(resourceId);\n    } catch (Exception e) {\n      return \"\";\n    }\n  }\n\n  /**\n   * Check if given target is included in the view.\n   *\n   * @param view the view\n   * @param targetX the X target in window coordinates\n   * @param targetY the Y target in window coordinates\n   * @return whether view includes the target\n   */\n  static boolean viewIncludesTarget(View view, int targetX, int targetY) {\n    int[] location = new int[2];\n    view.getLocationInWindow(location);\n    int x = location[0];\n    int y = location[1];\n    return x <= targetX\n        && y <= targetY\n        && x + view.getWidth() >= targetX\n        && y + view.getHeight() >= targetY;\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/native/intellij-broadcast-rib/src/main/java/com/uber/debug/broadcast/rib/RibHierarchyWithSelectionPayload.java",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.debug.broadcast.rib;\n\n/** Response payload class for command letting user select a view on device. */\npublic class RibHierarchyWithSelectionPayload extends RibHierarchyPayload {\n\n  private final String selectedRibId;\n  private final String selectedViewId;\n\n  public RibHierarchyWithSelectionPayload(\n      String name, RibApplication application, String nodeId, String viewId) {\n    super(name, application);\n    this.selectedRibId = nodeId;\n    this.selectedViewId = viewId;\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/action/rib/GenerateAction.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.action.rib;\n\nimport com.google.common.base.Preconditions;\nimport com.intellij.ide.IdeView;\nimport com.intellij.lang.java.JavaLanguage;\nimport com.intellij.openapi.actionSystem.AnAction;\nimport com.intellij.openapi.actionSystem.AnActionEvent;\nimport com.intellij.openapi.actionSystem.CommonDataKeys;\nimport com.intellij.openapi.actionSystem.DataContext;\nimport com.intellij.openapi.actionSystem.LangDataKeys;\nimport com.intellij.openapi.actionSystem.Presentation;\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.openapi.command.CommandProcessor;\nimport com.intellij.openapi.module.Module;\nimport com.intellij.openapi.module.ModuleUtilCore;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.roots.ContentEntry;\nimport com.intellij.openapi.roots.JavaProjectRootsUtil;\nimport com.intellij.openapi.roots.ModuleRootManager;\nimport com.intellij.openapi.roots.ProjectFileIndex;\nimport com.intellij.openapi.roots.ProjectRootManager;\nimport com.intellij.openapi.roots.SourceFolder;\nimport com.intellij.openapi.util.text.StringUtil;\nimport com.intellij.psi.JavaDirectoryService;\nimport com.intellij.psi.PsiDirectory;\nimport com.intellij.psi.PsiFile;\nimport com.intellij.psi.PsiFileFactory;\nimport com.intellij.psi.PsiManager;\nimport com.intellij.psi.PsiNameHelper;\nimport com.intellij.psi.PsiPackage;\nimport com.intellij.refactoring.PackageWrapper;\nimport com.intellij.refactoring.util.RefactoringUtil;\nimport com.uber.presidio.intellij_plugin.generator.Generator;\nimport java.util.List;\nimport org.jetbrains.jps.model.java.JavaModuleSourceRootTypes;\nimport org.jetbrains.jps.model.java.JavaSourceRootType;\n\n/** Base action to generate Java source and test source files with {@link Generator}. */\npublic abstract class GenerateAction extends AnAction {\n\n  private DataContext dataContext;\n\n  private static SourceFolder suitableTestSourceFolders(Project project, Module module) {\n    ContentEntry[] contentEntries = ModuleRootManager.getInstance(module).getContentEntries();\n    for (ContentEntry contentEntry : contentEntries) {\n      List<SourceFolder> testSourceFolders =\n          contentEntry.getSourceFolders(JavaSourceRootType.TEST_SOURCE);\n      for (SourceFolder testSourceFolder : testSourceFolders) {\n        if (testSourceFolder.getFile() != null) {\n          if (!JavaProjectRootsUtil.isInGeneratedCode(testSourceFolder.getFile(), project)) {\n            return testSourceFolder;\n          }\n        }\n      }\n    }\n\n    return null;\n  }\n\n  /**\n   * Writes a source file generated by a {@link Generator} to disk.\n   *\n   * @param project to write file in.\n   * @param generator to generate file with.\n   * @param directory to write file to.\n   */\n  private static void createSourceFile(\n      Project project, Generator generator, PsiDirectory directory) {\n    PsiFile file =\n        PsiFileFactory.getInstance(project)\n            .createFileFromText(\n                String.format(\"%s\" + generator.getFileExtension(), generator.getClassName()),\n                JavaLanguage.INSTANCE,\n                generator.generate());\n    directory.add(file);\n  }\n\n  @Override\n  public final void update(AnActionEvent e) {\n    this.dataContext = e.getDataContext();\n    final Presentation presentation = e.getPresentation();\n\n    final boolean enabled = isAvailable(dataContext);\n\n    presentation.setVisible(enabled);\n    presentation.setEnabled(enabled);\n  }\n\n  /**\n   * Generates source with given generators.\n   *\n   * @param mainSourceGenerators generators to use to genertae source in the main source directory.\n   */\n  protected void generate(\n      final List<Generator> mainSourceGenerators, final List<Generator> testSourceGenerators) {\n    /** Preconditions have been validated by {@link GenerateRibAction#isAvailable(DataContext)}. */\n    final Project project = Preconditions.checkNotNull(CommonDataKeys.PROJECT.getData(dataContext));\n    final IdeView view = Preconditions.checkNotNull(LangDataKeys.IDE_VIEW.getData(dataContext));\n    final PsiDirectory directory = Preconditions.checkNotNull(view.getOrChooseDirectory());\n    final Module currentModule =\n        Preconditions.checkNotNull(ModuleUtilCore.findModuleForPsiElement(directory));\n    final SourceFolder testSourceFolder = suitableTestSourceFolders(project, currentModule);\n\n    final PackageWrapper targetPackage =\n        new PackageWrapper(PsiManager.getInstance(project), getPackageName());\n\n    ApplicationManager.getApplication()\n        .runWriteAction(\n            new Runnable() {\n              @Override\n              public void run() {\n                CommandProcessor.getInstance()\n                    .executeCommand(\n                        project,\n                        new Runnable() {\n                          @Override\n                          public void run() {\n                            for (Generator generator : mainSourceGenerators) {\n                              createSourceFile(project, generator, directory);\n                            }\n\n                            if (testSourceFolder != null) {\n                              PsiDirectory testDirectory =\n                                  RefactoringUtil.createPackageDirectoryInSourceRoot(\n                                      targetPackage, testSourceFolder.getFile());\n                              for (Generator generator : testSourceGenerators) {\n                                createSourceFile(project, generator, testDirectory);\n                              }\n                            }\n                          }\n                        },\n                        \"Generate new RIB\",\n                        null);\n              }\n            });\n  }\n\n  /**\n   * @return gets the current package name for an executing action.\n   */\n  protected final String getPackageName() {\n    /** Preconditions have been validated by {@link GenerateAction#isAvailable(DataContext)}. */\n    final Project project = Preconditions.checkNotNull(CommonDataKeys.PROJECT.getData(dataContext));\n    final IdeView view = Preconditions.checkNotNull(LangDataKeys.IDE_VIEW.getData(dataContext));\n    final PsiDirectory directory = Preconditions.checkNotNull(view.getOrChooseDirectory());\n    final PsiPackage psiPackage =\n        Preconditions.checkNotNull(JavaDirectoryService.getInstance().getPackage(directory));\n\n    return psiPackage.getQualifiedName();\n  }\n\n  /**\n   * Checked whether or not this action can be enabled.\n   *\n   * <p>\n   *\n   * <p>Requirements to be enabled: * User must be in a Java source folder.\n   *\n   * @param dataContext to figure out where the user is.\n   * @return {@code true} when the action is available, {@code false} when the action is not\n   *     available.\n   */\n  private boolean isAvailable(DataContext dataContext) {\n    final Project project = CommonDataKeys.PROJECT.getData(dataContext);\n    if (project == null) {\n      return false;\n    }\n\n    final IdeView view = LangDataKeys.IDE_VIEW.getData(dataContext);\n    if (view == null || view.getDirectories().length == 0) {\n      return false;\n    }\n\n    ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(project).getFileIndex();\n    for (PsiDirectory dir : view.getDirectories()) {\n      if (projectFileIndex.isUnderSourceRootOfType(\n              dir.getVirtualFile(), JavaModuleSourceRootTypes.SOURCES)\n          && checkPackageExists(dir)) {\n        return true;\n      }\n    }\n\n    return false;\n  }\n\n  /**\n   * Checks if a Java package exists for a directory.\n   *\n   * @param directory to check.\n   * @return {@code true} when a package exists, {@code false} when it does not.\n   */\n  private boolean checkPackageExists(PsiDirectory directory) {\n    PsiPackage pkg = JavaDirectoryService.getInstance().getPackage(directory);\n    if (pkg == null) {\n      return false;\n    }\n\n    String name = pkg.getQualifiedName();\n    return StringUtil.isEmpty(name)\n        || PsiNameHelper.getInstance(directory.getProject()).isQualifiedName(name);\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/action/rib/GenerateRibAction.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.action.rib;\n\nimport com.intellij.openapi.actionSystem.AnActionEvent;\nimport com.uber.presidio.intellij_plugin.generator.GeneratorPair;\n\n/**\n * This action will ask generate files required for a new Rib based on a name provided by the user.\n */\npublic class GenerateRibAction extends GenerateAction implements GenerateRibDialog.Listener {\n\n  @Override\n  public void actionPerformed(AnActionEvent anActionEvent) {\n    GenerateRibDialog dialog = new GenerateRibDialog(this);\n    dialog.show();\n  }\n\n  @Override\n  public void onGenerateClicked(\n      String ribName, boolean createPresenterAndView, boolean isKotlinSelected) {\n    final GeneratorPair generators =\n        createPresenterAndView\n            ? Generators.getGeneratorsForRibWithPresenterAndView(\n                getPackageName(), ribName, isKotlinSelected)\n            : Generators.getGeneratorsForRibWithoutPresenterAndView(\n                getPackageName(), ribName, isKotlinSelected);\n    generate(generators.getMainSourceSetGenerators(), generators.getTestSourceSetGenerators());\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/action/rib/GenerateRibDialog.form",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<form xmlns=\"http://www.intellij.com/uidesigner/form/\" version=\"1\" bind-to-class=\"com.uber.presidio.intellij_plugin.action.rib.GenerateRibDialog\">\n  <grid id=\"27dc6\" binding=\"contentPane\" layout-manager=\"GridLayoutManager\" row-count=\"4\" column-count=\"2\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"0\" vgap=\"0\">\n    <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n    <constraints>\n      <xy x=\"20\" y=\"20\" width=\"500\" height=\"400\"/>\n    </constraints>\n    <properties/>\n    <border type=\"none\"/>\n    <children>\n      <component id=\"a2374\" class=\"javax.swing.JTextField\" binding=\"ribNameTextField\">\n        <constraints>\n          <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\">\n            <preferred-size width=\"150\" height=\"-1\"/>\n          </grid>\n        </constraints>\n        <properties/>\n      </component>\n      <component id=\"ca6ba\" class=\"javax.swing.JLabel\">\n        <constraints>\n          <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties>\n          <text value=\"Rib Name\"/>\n        </properties>\n      </component>\n      <component id=\"67920\" class=\"javax.swing.JCheckBox\" binding=\"createPresenterAndViewCheckBox\" default-binding=\"true\">\n        <constraints>\n          <grid row=\"1\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties>\n          <text value=\"Create presenter and view.\"/>\n        </properties>\n      </component>\n      <component id=\"117d5\" class=\"javax.swing.JCheckBox\" binding=\"createKotlinCode\">\n        <constraints>\n          <grid row=\"2\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties>\n          <text value=\"Use Kotlin\"/>\n        </properties>\n      </component>\n    </children>\n  </grid>\n</form>\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/action/rib/GenerateRibDialog.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.action.rib;\n\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.ui.DialogWrapper;\nimport javax.swing.JCheckBox;\nimport javax.swing.JComponent;\nimport javax.swing.JPanel;\nimport javax.swing.JTextField;\nimport org.jetbrains.annotations.Nullable;\n\n/** Dialog that prompts the user for information required to generate a new rib. */\npublic class GenerateRibDialog extends DialogWrapper {\n\n  private final Listener listener;\n  private JPanel contentPane;\n  private JTextField ribNameTextField;\n  private JCheckBox createPresenterAndViewCheckBox;\n  private JCheckBox createKotlinCode;\n\n  public GenerateRibDialog(final Listener listener) {\n    super((Project) null);\n    this.listener = listener;\n    init();\n\n    createPresenterAndViewCheckBox.setSelected(true);\n    createKotlinCode.setSelected(true);\n  }\n\n  @Nullable\n  @Override\n  protected JComponent createCenterPanel() {\n    return contentPane;\n  }\n\n  @Override\n  protected void doOKAction() {\n    super.doOKAction();\n\n    this.listener.onGenerateClicked(\n        ribNameTextField.getText(),\n        createPresenterAndViewCheckBox.isSelected(),\n        createKotlinCode.isSelected());\n  }\n\n  /** Listener interface to be implemented by consumers of the dialog. */\n  public interface Listener {\n\n    /**\n     * Called when the user clicks OK on the generate dialog.\n     *\n     * @param ribName name for new rib.\n     * @param createPresenterAndView {@code true} when a presenter and a corresponding view should\n     *     be created, {@code false} otherwise.\n     */\n    void onGenerateClicked(\n        String ribName, boolean createPresenterAndView, boolean isKotlinSelected);\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/action/rib/Generators.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.action.rib;\n\nimport com.google.common.collect.ImmutableList;\nimport com.uber.presidio.intellij_plugin.generator.GeneratorPair;\nimport com.uber.presidio.intellij_plugin.generator.rib.BuilderGenerator;\nimport com.uber.presidio.intellij_plugin.generator.rib.InteractorWithEmptyPresenterGenerator;\nimport com.uber.presidio.intellij_plugin.generator.rib.InteractorWithEmptyPresenterTestGenerator;\nimport com.uber.presidio.intellij_plugin.generator.rib.InteractorWithPresenterGenerator;\nimport com.uber.presidio.intellij_plugin.generator.rib.InteractorWithPresenterTestGenerator;\nimport com.uber.presidio.intellij_plugin.generator.rib.RouterGenerator;\nimport com.uber.presidio.intellij_plugin.generator.rib.RouterTestGenerator;\nimport com.uber.presidio.intellij_plugin.generator.rib.ViewBuilderGenerator;\nimport com.uber.presidio.intellij_plugin.generator.rib.ViewGenerator;\nimport com.uber.presidio.intellij_plugin.generator.rib.ViewRouterGenerator;\nimport com.uber.presidio.intellij_plugin.generator.rib.ViewRouterTestGenerator;\n\n/** Utility methods for getting lists of generators in different configurations. */\npublic final class Generators {\n\n  private Generators() {}\n\n  /**\n   * @param packageName to use for generators.\n   * @param ribName to use for generators.\n   * @return a list of generators to use when generating a rib with a presenter and view.\n   */\n  public static GeneratorPair getGeneratorsForRibWithPresenterAndView(\n      String packageName, String ribName, boolean isKotlinSelected) {\n\n    InteractorWithPresenterGenerator interactorGenerator =\n        new InteractorWithPresenterGenerator(packageName, ribName, isKotlinSelected);\n    ViewBuilderGenerator viewBuilderGenerator =\n        new ViewBuilderGenerator(packageName, ribName, isKotlinSelected);\n    ViewGenerator viewGenerator = new ViewGenerator(packageName, ribName, isKotlinSelected);\n    ViewRouterGenerator viewRouterGenerator =\n        new ViewRouterGenerator(packageName, ribName, isKotlinSelected);\n\n    InteractorWithPresenterTestGenerator interactorWithPresenterTestGenerator =\n        new InteractorWithPresenterTestGenerator(packageName, ribName, isKotlinSelected);\n    ViewRouterTestGenerator viewRouterTestGenerator =\n        new ViewRouterTestGenerator(packageName, ribName, isKotlinSelected);\n\n    return new GeneratorPair(\n        ImmutableList.of(\n            interactorGenerator, viewBuilderGenerator, viewGenerator, viewRouterGenerator),\n        ImmutableList.of(interactorWithPresenterTestGenerator, viewRouterTestGenerator));\n  }\n\n  /**\n   * @param packageName to use for generators.\n   * @param ribName to use for generators.\n   * @return a list of generators to use when generating a rib without a presenter and view.\n   */\n  public static GeneratorPair getGeneratorsForRibWithoutPresenterAndView(\n      String packageName, String ribName, boolean isKotlinSelected) {\n    InteractorWithEmptyPresenterGenerator interactorGenerator =\n        new InteractorWithEmptyPresenterGenerator(packageName, ribName, isKotlinSelected);\n    BuilderGenerator builderGenerator =\n        new BuilderGenerator(packageName, ribName, isKotlinSelected);\n    RouterGenerator routerGenerator = new RouterGenerator(packageName, ribName, isKotlinSelected);\n\n    InteractorWithEmptyPresenterTestGenerator interactorWithEmptyPresenterTestGenerator =\n        new InteractorWithEmptyPresenterTestGenerator(packageName, ribName, isKotlinSelected);\n    RouterTestGenerator routerTestGenerator =\n        new RouterTestGenerator(packageName, ribName, isKotlinSelected);\n\n    return new GeneratorPair(\n        ImmutableList.of(interactorGenerator, builderGenerator, routerGenerator),\n        ImmutableList.of(interactorWithEmptyPresenterTestGenerator, routerTestGenerator));\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/generator/Generator.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.generator;\n\nimport com.google.common.base.Charsets;\nimport com.google.common.base.Preconditions;\nimport com.google.common.io.CharStreams;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.net.URLDecoder;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\nimport org.apache.commons.lang.text.StrSubstitutor;\n\n/**\n * Base class for generating rib classes.\n *\n * <p>\n *\n * <p>Templates are tokenized using a ${token_name} syntax. {@code package_name} and {@code\n * rib_name} token values are provided by the base generate - however subclasses can add custom\n * paramaters when needed using {@link Generator#getTemplateValuesMap()}.\n */\npublic abstract class Generator {\n\n  private static final String TEMPLATE_TOKEN_PACKAGE_NAME = \"package_name\";\n  private static final String TEMPLATE_TOKEN_RIBLET_NAME = \"rib_name\";\n  private static final String TEMPLATE_TOKEN_RIBLET_NAME_TO_LOWER = \"rib_name_to_lower\";\n\n  private final String packageName;\n  private final String ribName;\n  private final String templateString;\n  private final boolean isKotlin;\n  private final Map<String, String> templateValuesMap;\n\n  /**\n   * @param packageName rib package name.\n   * @param ribName rib name.\n   * @param templateName template to be used by this generate.\n   */\n  public Generator(String packageName, String ribName, boolean isKotlin, String templateName) {\n    this.packageName = packageName;\n    this.ribName = ribName;\n    this.isKotlin = isKotlin;\n\n    templateValuesMap = new HashMap<String, String>();\n    templateValuesMap.put(TEMPLATE_TOKEN_PACKAGE_NAME, packageName);\n    templateValuesMap.put(TEMPLATE_TOKEN_RIBLET_NAME, ribName);\n    templateValuesMap.put(TEMPLATE_TOKEN_RIBLET_NAME_TO_LOWER, ribName.toLowerCase());\n\n    try {\n      String[] resources = getResourceListing(this.getClass(), \"partials/\");\n      for (String resourceName : resources) {\n        if (resourceName == null || resourceName.length() == 0) {\n          continue;\n        }\n        InputStream resourceAsStream =\n            Generator.class.getResourceAsStream(\"/partials/\" + resourceName);\n        String resourceContents =\n            Preconditions.checkNotNull(\n                CharStreams.toString(new InputStreamReader(resourceAsStream, Charsets.UTF_8)));\n        templateValuesMap.put(String.format(\"partial: %s\", resourceName), resourceContents);\n      }\n    } catch (IOException e) {\n      throw new RuntimeException(e);\n    } catch (URISyntaxException e) {\n      throw new RuntimeException(e);\n    }\n    try {\n      // Need to use getResourceAsStream() since we may be reading resources from inside a jar.\n      // Class.getResource() doesn't work in this scenario.\n\n      String resource = \"/templates/java/\" + templateName + \".java.template\";\n\n      if (isKotlin) {\n        resource = \"/templates/kotlin/\" + templateName + \".kt.template\";\n      }\n\n      InputStream resourceAsStream1 = Generator.class.getResourceAsStream(resource);\n      templateString =\n          Preconditions.checkNotNull(\n              CharStreams.toString(new InputStreamReader(resourceAsStream1, Charsets.UTF_8)));\n    } catch (IOException e) {\n      throw new RuntimeException(e);\n    }\n  }\n\n  /**\n   * @return the class name for the generated file.\n   */\n  public abstract String getClassName();\n\n  /**\n   * @return the package name for the generated file.\n   */\n  public final String getPackageName() {\n    return packageName;\n  }\n\n  /**\n   * @return the rib name for the generator.\n   */\n  public final String getRibName() {\n    return ribName;\n  }\n\n  public final String getFileExtension() {\n\n    if (isKotlin) {\n      return \".kt\";\n    }\n\n    return \".java\";\n  }\n\n  /**\n   * @return the template values map, to add more template paramters.\n   */\n  protected final Map<String, String> getTemplateValuesMap() {\n    return templateValuesMap;\n  }\n\n  /**\n   * @return the source for the generated file.\n   */\n  public final String generate() {\n    StrSubstitutor substitutor = new StrSubstitutor(templateValuesMap);\n    String newFile = substitutor.replace(templateString);\n    System.out.println(newFile);\n    return newFile;\n  }\n\n  /**\n   * List directory contents for a resource folder. Not recursive. This is basically a brute-force\n   * implementation. Works for regular files and also JARs. Found at\n   * http://stackoverflow.com/questions/6247144/how-to-load-a-folder-from-a-jar.\n   *\n   * @param clazz Any java class that lives in the same place as the resources you want.\n   * @param path Should end with \"/\", but not start with one.\n   * @return Just the name of each member item, not the full paths.\n   * @throws URISyntaxException\n   * @throws IOException\n   */\n  String[] getResourceListing(Class clazz, String path) throws URISyntaxException, IOException {\n    URL dirURL = clazz.getClassLoader().getResource(path);\n    if (dirURL != null && dirURL.getProtocol().equals(\"file\")) {\n      // A file path: easy enough\n      return new File(dirURL.toURI()).list();\n    }\n\n    if (dirURL == null) {\n      // In case of a jar file, we can't actually find a directory. Have to assume the same jar as\n      // clazz.\n      String me = clazz.getName().replace(\".\", \"/\") + \".class\";\n      dirURL = clazz.getClassLoader().getResource(me);\n    }\n\n    if (dirURL.getProtocol().equals(\"jar\")) {\n      // Strip out only the JAR file.\n      String jarPath = dirURL.getPath().substring(5, dirURL.getPath().indexOf(\"!\"));\n\n      // Gives ALL entries in jar and avoids duplicates.\n      JarFile jar = new JarFile(URLDecoder.decode(jarPath, \"UTF-8\"));\n      Enumeration<JarEntry> entries = jar.entries();\n      Set<String> result = new HashSet<String>();\n      while (entries.hasMoreElements()) {\n        String name = entries.nextElement().getName();\n        if (name.startsWith(path)) { // filter according to the path\n          String entry = name.substring(path.length());\n          int checkSubdir = entry.indexOf(\"/\");\n          if (checkSubdir >= 0) {\n            // if it is a subdirectory, we just return the directory name\n            entry = entry.substring(0, checkSubdir);\n          }\n          result.add(entry);\n        }\n      }\n      return result.toArray(new String[result.size()]);\n    }\n    throw new UnsupportedOperationException(\"Cannot list files for URL \" + dirURL);\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/generator/GeneratorPair.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.generator;\n\nimport java.util.List;\n\npublic class GeneratorPair {\n\n  private final List<Generator> mainSourceSetGenerators;\n  private final List<Generator> testSourceSetGenerators;\n\n  public GeneratorPair(\n      List<Generator> mainSourceSetGenerators, List<Generator> testSourceSetGenerators) {\n    this.mainSourceSetGenerators = mainSourceSetGenerators;\n    this.testSourceSetGenerators = testSourceSetGenerators;\n  }\n\n  public List<Generator> getMainSourceSetGenerators() {\n    return mainSourceSetGenerators;\n  }\n\n  public List<Generator> getTestSourceSetGenerators() {\n    return testSourceSetGenerators;\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/generator/rib/BuilderGenerator.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.generator.rib;\n\nimport com.uber.presidio.intellij_plugin.generator.Generator;\n\npublic class BuilderGenerator extends Generator {\n\n  private static final String TEMPLATE_NAME = \"RibBuilder\";\n\n  public BuilderGenerator(String packageName, String ribName, boolean isKotlinSelected) {\n    super(packageName, ribName, isKotlinSelected, TEMPLATE_NAME);\n  }\n\n  @Override\n  public String getClassName() {\n    return String.format(\"%sBuilder\", getRibName());\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/generator/rib/InteractorWithEmptyPresenterGenerator.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.generator.rib;\n\nimport com.uber.presidio.intellij_plugin.generator.Generator;\n\npublic class InteractorWithEmptyPresenterGenerator extends Generator {\n\n  private static final String TEMPLATE_NAME = \"RibInteractorWithEmptyPresenter\";\n\n  public InteractorWithEmptyPresenterGenerator(\n      String packageName, String ribName, boolean isKotlinSelected) {\n    super(packageName, ribName, isKotlinSelected, TEMPLATE_NAME);\n  }\n\n  @Override\n  public String getClassName() {\n    return String.format(\"%sInteractor\", getRibName());\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/generator/rib/InteractorWithEmptyPresenterTestGenerator.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.generator.rib;\n\nimport com.uber.presidio.intellij_plugin.generator.Generator;\n\npublic class InteractorWithEmptyPresenterTestGenerator extends Generator {\n\n  private static final String TEMPLATE_NAME = \"RibInteractorWithEmptyPresenterTest\";\n\n  public InteractorWithEmptyPresenterTestGenerator(\n      String packageName, String ribName, boolean isKotlinSelected) {\n    super(packageName, ribName, isKotlinSelected, TEMPLATE_NAME);\n  }\n\n  @Override\n  public String getClassName() {\n    return String.format(\"%sInteractorTest\", getRibName());\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/generator/rib/InteractorWithPresenterGenerator.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.generator.rib;\n\nimport com.uber.presidio.intellij_plugin.generator.Generator;\n\npublic class InteractorWithPresenterGenerator extends Generator {\n\n  private static final String TEMPLATE_NAME = \"RibInteractorWithPresenter\";\n\n  public InteractorWithPresenterGenerator(\n      String packageName, String ribName, boolean isKotlinSelected) {\n\n    super(packageName, ribName, isKotlinSelected, TEMPLATE_NAME);\n  }\n\n  @Override\n  public String getClassName() {\n    return String.format(\"%sInteractor\", getRibName());\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/generator/rib/InteractorWithPresenterTestGenerator.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.generator.rib;\n\nimport com.uber.presidio.intellij_plugin.generator.Generator;\n\npublic class InteractorWithPresenterTestGenerator extends Generator {\n\n  private static final String TEMPLATE_NAME = \"RibInteractorWithPresenterTest\";\n\n  public InteractorWithPresenterTestGenerator(\n      String packageName, String ribName, boolean isKotlinSelected) {\n    super(packageName, ribName, isKotlinSelected, TEMPLATE_NAME);\n  }\n\n  @Override\n  public String getClassName() {\n    return String.format(\"%sInteractorTest\", getRibName());\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/generator/rib/RouterGenerator.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.generator.rib;\n\nimport com.uber.presidio.intellij_plugin.generator.Generator;\n\npublic class RouterGenerator extends Generator {\n\n  private static final String TEMPLATE_NAME = \"RibRouter\";\n\n  public RouterGenerator(String packageName, String ribName, boolean isKotlinSelected) {\n    super(packageName, ribName, isKotlinSelected, TEMPLATE_NAME);\n  }\n\n  @Override\n  public String getClassName() {\n    return String.format(\"%sRouter\", getRibName());\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/generator/rib/RouterTestGenerator.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.generator.rib;\n\nimport com.uber.presidio.intellij_plugin.generator.Generator;\n\npublic class RouterTestGenerator extends Generator {\n\n  private static final String TEMPLATE_NAME = \"RibRouterTest\";\n\n  public RouterTestGenerator(String packageName, String ribName, boolean isKotlinSelected) {\n    super(packageName, ribName, isKotlinSelected, TEMPLATE_NAME);\n  }\n\n  @Override\n  public String getClassName() {\n    return String.format(\"%sRouterTest\", getRibName());\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/generator/rib/ViewBuilderGenerator.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.generator.rib;\n\nimport com.uber.presidio.intellij_plugin.generator.Generator;\n\npublic class ViewBuilderGenerator extends Generator {\n\n  private static final String TEMPLATE_NAME = \"RibViewBuilder\";\n\n  public ViewBuilderGenerator(String packageName, String ribName, boolean isKotlinSelected) {\n    super(packageName, ribName, isKotlinSelected, TEMPLATE_NAME);\n  }\n\n  @Override\n  public String getClassName() {\n    return String.format(\"%sBuilder\", getRibName());\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/generator/rib/ViewGenerator.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.generator.rib;\n\nimport com.uber.presidio.intellij_plugin.generator.Generator;\n\n/** Generate subclass that uses a view template. */\npublic class ViewGenerator extends Generator {\n\n  private static final String TEMPLATE_NAME = \"RibView\";\n\n  public ViewGenerator(String packageName, String ribName, boolean isKotlinSelected) {\n    super(packageName, ribName, isKotlinSelected, TEMPLATE_NAME);\n  }\n\n  @Override\n  public String getClassName() {\n    return String.format(\"%sView\", getRibName());\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/generator/rib/ViewRouterGenerator.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.generator.rib;\n\nimport com.uber.presidio.intellij_plugin.generator.Generator;\n\npublic class ViewRouterGenerator extends Generator {\n\n  private static final String TEMPLATE_NAME = \"RibViewRouter\";\n\n  public ViewRouterGenerator(String packageName, String ribName, boolean isKotlinSelected) {\n    super(packageName, ribName, isKotlinSelected, TEMPLATE_NAME);\n  }\n\n  @Override\n  public String getClassName() {\n    return String.format(\"%sRouter\", getRibName());\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/java/com/uber/presidio/intellij_plugin/generator/rib/ViewRouterTestGenerator.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.generator.rib;\n\nimport com.uber.presidio.intellij_plugin.generator.Generator;\n\npublic class ViewRouterTestGenerator extends Generator {\n\n  private static final String TEMPLATE_NAME = \"RibViewRouterTest\";\n\n  public ViewRouterTestGenerator(String packageName, String ribName, boolean isKotlinSelected) {\n    super(packageName, ribName, isKotlinSelected, TEMPLATE_NAME);\n  }\n\n  @Override\n  public String getClassName() {\n    return String.format(\"%sRouterTest\", getRibName());\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/AndroidDeviceRepository.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib\n\nimport com.android.ddmlib.AndroidDebugBridge\nimport com.android.ddmlib.IDevice\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.components.Service.Level.PROJECT\nimport com.intellij.openapi.project.Project\nimport org.jetbrains.android.sdk.AndroidSdkUtils\n\n/** IntelliJ Project component responsible for exposing connected Android devices. */\n@Service(PROJECT)\npublic class AndroidDeviceRepository(public val project: Project) :\n  AndroidDebugBridge.IDeviceChangeListener, Disposable {\n\n  private val devices: ArrayList<IDevice> = arrayListOf()\n  private val listeners: ArrayList<Listener> = arrayListOf()\n\n  init {\n    ApplicationManager.getApplication().invokeLater {\n      AndroidSdkUtils.getDebugBridge(project)?.devices?.forEach { devices.add(it) }\n      AndroidDebugBridge.addDeviceChangeListener(this)\n    }\n  }\n\n  @Suppress(\"EmptyFunctionBlock\") override fun deviceChanged(device: IDevice, changeMask: Int) {}\n\n  override fun deviceConnected(device: IDevice) {\n    if (!devices.contains(device)) {\n      devices.add(device)\n      broadcastChanges()\n    } else {\n      throw IllegalArgumentException(\"Adding a device that already exists\")\n    }\n  }\n\n  override fun deviceDisconnected(device: IDevice) {\n    if (devices.contains(device)) {\n      devices.remove(device)\n      broadcastChanges()\n    } else {\n      throw IllegalArgumentException(\"Removing a device that does not exist\")\n    }\n  }\n\n  @Synchronized\n  public fun addListener(listener: Listener) {\n    listeners.add(listener)\n    if (devices.size > 0) {\n      listener.onAvailableDevicesChanged(devices)\n    }\n  }\n\n  @Synchronized\n  public fun removeListener(listener: Listener) {\n    if (listeners.contains(listener)) {\n      listeners.remove(listener)\n    }\n  }\n\n  @Synchronized\n  private fun broadcastChanges() {\n    listeners.forEach { it.onAvailableDevicesChanged(devices) }\n  }\n\n  public fun isBridgeConnected(): Boolean {\n    val debugBridge = AndroidDebugBridge.getBridge()\n    return debugBridge != null && with(debugBridge) { isConnected && hasInitialDeviceList() }\n  }\n\n  override fun dispose() {\n    AndroidDebugBridge.removeDeviceChangeListener(this)\n    devices.clear()\n  }\n\n  public interface Listener {\n\n    public fun onAvailableDevicesChanged(devices: List<IDevice>)\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/AttachRibProjectServiceActivity.kt",
    "content": "/*\n * Copyright (C) 2022. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib\n\nimport com.intellij.openapi.components.service\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.startup.ProjectActivity\n\npublic class AttachRibProjectServiceActivity : ProjectActivity {\n  override suspend fun execute(project: Project) {\n    project.service<RibProjectService>().attach()\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/CommandLineUtils.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib\n\nimport com.intellij.execution.ExecutionException\nimport com.intellij.execution.configurations.GeneralCommandLine\nimport com.intellij.openapi.project.Project\nimport java.io.BufferedReader\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.InputStreamReader\nimport java.nio.charset.Charset\nimport java.util.LinkedList\n\n/** Some command line utilities. */\npublic object CommandLineUtils {\n\n  /**\n   * Executes `which` for a command.\n   *\n   * @param project the project.\n   * @param command the command to find.\n   * @return the path of the command, or null if not found.\n   * @throws InterruptedException\n   * @throws ExecutionException\n   * @throws IOException\n   */\n  public fun which(project: Project, command: String): String {\n    return executeWithLineOutput(project, \"which\", command).output()[0]\n  }\n\n  /**\n   * Executes the given `command` appending the given `params` and returns the output.\n   *\n   * @param project the current project\n   * @param command to execute\n   * @param params to append\n   * @return the process output\n   * @throws InterruptedException\n   * @throws ExecutionException\n   * @throws IOException\n   */\n  public fun executeWithLineOutput(\n    project: Project,\n    command: String,\n    vararg params: String,\n  ): ProcessOutput {\n    val commandLine = GeneralCommandLine(command)\n    for (p in params) {\n      commandLine.addParameter(p)\n    }\n    commandLine.setWorkDirectory(project.basePath)\n\n    val process = commandLine.createProcess()\n    process.waitFor()\n\n    return ProcessOutput(\n      consumeInputStream(process.inputStream),\n      consumeInputStream(process.errorStream),\n    )\n  }\n\n  private fun consumeInputStream(inputStream: InputStream?): List<String> {\n    val lines = LinkedList<String>()\n\n    if (inputStream == null) {\n      return lines\n    }\n\n    val reader = BufferedReader(InputStreamReader(inputStream, Charset.forName(\"utf-8\")))\n\n    var line: String?\n    do {\n      line = reader.readLine()\n      line?.let { lines.add(line.trim { it <= ' ' }) }\n    } while (line != null)\n    reader.close()\n\n    return lines\n  }\n\n  /** Holder for process output and error stream. */\n  public class ProcessOutput(private val output: List<String>, private val error: List<String>) {\n\n    /** Returns the process std output */\n    public fun output(): List<String> {\n      return output\n    }\n\n    /** Returns the process error output */\n    public fun error(): List<String> {\n      return error\n    }\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibHierarchyBrowser.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib\n\nimport com.intellij.icons.AllIcons\nimport com.intellij.ide.BrowserUtil\nimport com.intellij.ide.IdeBundle\nimport com.intellij.ide.actions.ExportToTextFileToolbarAction\nimport com.intellij.ide.hierarchy.HierarchyNodeDescriptor\nimport com.intellij.ide.hierarchy.HierarchyTreeStructure\nimport com.intellij.ide.hierarchy.JavaHierarchyUtil\nimport com.intellij.ide.util.treeView.NodeDescriptor\nimport com.intellij.openapi.actionSystem.ActionPlaces\nimport com.intellij.openapi.actionSystem.AnAction\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.actionSystem.DefaultActionGroup\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.components.service\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.ui.MessageType\nimport com.intellij.psi.PsiClass\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.presentation.java.ClassPresentationUtil\nimport com.intellij.ui.awt.RelativePoint\nimport com.intellij.ui.content.tabs.PinToolwindowTabAction\nimport com.intellij.ui.treeStructure.Tree\nimport com.uber.intellij.plugin.android.rib.RibHierarchyUtils.Companion.EMPTY_UUID\nimport com.uber.intellij.plugin.android.rib.RibHierarchyUtils.Companion.displayPopup\nimport com.uber.intellij.plugin.android.rib.RibHierarchyUtils.Companion.getPsiClass\nimport com.uber.intellij.plugin.android.rib.RibHierarchyUtils.Companion.isRootElement\nimport com.uber.intellij.plugin.android.rib.io.RibHost\nimport com.uber.intellij.plugin.android.rib.ui.HierarchyBrowserBase\nimport com.uber.intellij.plugin.android.rib.ui.RibHierarchyNodeDescriptor\nimport com.uber.intellij.plugin.android.rib.ui.RibHierarchyRootNodeDescriptor\nimport com.uber.intellij.plugin.android.rib.ui.RibHierarchyTreeStructure\nimport java.text.MessageFormat\nimport java.util.UUID\nimport javax.swing.JPanel\nimport javax.swing.JTree\nimport javax.swing.tree.DefaultMutableTreeNode\n\n/** UI component used to render tree of Ribs. */\n@SuppressWarnings(\"TooManyFunctions\")\npublic class RibHierarchyBrowser(\n  project: Project,\n  initialModel: Model,\n  private val rootElement: PsiElement,\n  private val selectionListener: Listener?,\n) : HierarchyBrowserBase(project, rootElement) {\n\n  public companion object {\n    /** Go to previous Rib label */\n    public const val LABEL_GO_PREVIOUS_RIB: String = \"Go to previous Scope.\"\n\n    /** Go to next Rib label */\n    public const val LABEL_GO_NEXT_RIB: String = \"Go to next Scope\"\n\n    /** Type of the Rib hierarchy */\n    public const val TYPE_HIERARCHY_TYPE: String = \"Ribs\"\n\n    private const val ENABLE_LOCATE_MODE: String = \"Enable selecting RIB on device\"\n\n    private const val LOCATE_VIEW: String =\n      \"Please click on the UI element on your<br>\" + \"emulator/device locate it.\"\n  }\n\n  /** Enum used to represent the status of the component */\n  public enum class Status {\n    UNINITIALIZED,\n    INITIALIZING,\n    INITIALIZED,\n    REFRESHING,\n  }\n\n  /**\n   * Data class used to represent the host of the Rib application and possible selections\n   *\n   * @param host the model of the application host\n   * @param selectedRibId the RIB ID of the RIB selected by user (if any)\n   * @param selectedViewId the view ID of the view selected by user (if any)\n   */\n  public data class Model(\n    val host: RibHost,\n    val selectedRibId: String = \"\",\n    val selectedViewId: String = \"\",\n  )\n\n  private val ribProjectService: RibProjectService = project.service()\n\n  private var status: Status = Status.UNINITIALIZED\n\n  private var model: Model = initialModel\n\n  private var menuGroup: DefaultActionGroup? = null\n\n  private var refreshComplete: Boolean = false\n\n  private fun isUpdating(): Boolean {\n    return status == Status.INITIALIZING || status == Status.REFRESHING\n  }\n\n  override fun isApplicableElement(element: PsiElement): Boolean {\n    return element is PsiClass\n  }\n\n  override fun getActionPlace(): String {\n    return ActionPlaces.METHOD_HIERARCHY_VIEW_TOOLBAR\n  }\n\n  override fun getComparator(): Comparator<NodeDescriptor<*>> {\n    return JavaHierarchyUtil.getComparator(myProject)\n  }\n\n  override fun getElementFromDescriptor(descriptor: HierarchyNodeDescriptor): PsiElement? {\n    if (isRootElement(descriptor.psiElement)) {\n      return null\n    }\n    return descriptor.psiElement\n  }\n\n  override fun getPrevOccurenceActionNameImpl(): String {\n    return LABEL_GO_PREVIOUS_RIB\n  }\n\n  override fun createLegendPanel(): JPanel? {\n    return null\n  }\n\n  override fun getNextOccurenceActionNameImpl(): String {\n    return LABEL_GO_NEXT_RIB\n  }\n  override fun createTrees(trees: MutableMap<in String, in JTree>) {\n    trees[TYPE_HIERARCHY_TYPE] = createTree(true)\n  }\n\n  override fun getContentDisplayName(typeName: String, element: PsiElement): String? {\n    if (element !is PsiClass) {\n      return null\n    }\n    return MessageFormat.format(typeName, ClassPresentationUtil.getNameForClass(element, false))\n  }\n\n  override fun appendActions(actionGroup: DefaultActionGroup, helpID: String?) {\n    super.appendActions(actionGroup, helpID)\n    menuGroup = actionGroup\n\n    // replace original refresh action with custom one, so that we can gray it out\n    actionGroup.replaceAction(actionGroup.getChildren(null).first(), RefreshAction())\n\n    // remove export/pin action, and add locate, help and autodeploy actions\n    actionGroup.remove(actionGroup.getChildren(null).first { it is PinToolwindowTabAction })\n    actionGroup.remove(actionGroup.getChildren(null).first { it is ExportToTextFileToolbarAction })\n    actionGroup.add(LocateAction())\n    actionGroup.add(HelpAction())\n  }\n\n  override fun createHierarchyTreeStructure(\n    typeName: String,\n    psiElement: PsiElement,\n  ): HierarchyTreeStructure? {\n    if (psiElement == rootElement) {\n      val rootDescriptor =\n        RibHierarchyRootNodeDescriptor(\n          project,\n          getPsiClass(project, model.host.name),\n          model.host,\n          status,\n        )\n      return RibHierarchyTreeStructure(project, rootDescriptor)\n    }\n    return null\n  }\n\n  override fun configureTree(tree: Tree) {\n    super.configureTree(tree)\n    tree.addTreeSelectionListener {\n      val node: Any? = tree.lastSelectedPathComponent\n      if (node is DefaultMutableTreeNode) {\n        val descriptor = node.userObject\n        if (descriptor is RibHierarchyNodeDescriptor) {\n          selectionListener?.onSelectedRibChanged(UUID.fromString(descriptor.ribNode.id))\n        } else {\n          selectionListener?.onSelectedRibChanged(EMPTY_UUID)\n        }\n      }\n    }\n  }\n\n  override fun doRefresh(currentBuilderOnly: Boolean) {\n    when (status) {\n      Status.INITIALIZED -> {\n        status = Status.REFRESHING\n      }\n      Status.UNINITIALIZED -> {\n        status = Status.INITIALIZING\n        refresh()\n      }\n      else -> {}\n    }\n    ApplicationManager.getApplication().invokeLater { ribProjectService.refreshRibHierarchy() }\n  }\n\n  /** Request to update hierarchy with provided model */\n  public fun onModelUpdated(model: Model) {\n    this.status = Status.INITIALIZED\n    this.model = model\n    this.refreshComplete = false\n    refresh()\n  }\n\n  /** Callback invoked when refresh completed */\n  override fun onRefreshComplete() {\n    if (!refreshComplete) {\n      model.selectedRibId?.let { selectById(it) }\n      refreshComplete = true\n    }\n  }\n\n  /*\n   * Request view to refresh, which causes most recent rib tree to be used.\n   */\n  private fun refresh() {\n    super.doRefresh(true)\n  }\n\n  private inner class RefreshAction internal constructor() :\n    com.intellij.ide.actions.RefreshAction(\n      IdeBundle.message(\"action.refresh\"),\n      IdeBundle.message(\"action.refresh\"),\n      AllIcons.Actions.Refresh,\n    ) {\n\n    override fun actionPerformed(e: AnActionEvent) {\n      doRefresh(false)\n    }\n\n    override fun update(event: AnActionEvent) {\n      val presentation = event.presentation\n      val hasDevices = ribProjectService.hasSelectedDevice()\n      presentation.isEnabled = hasDevices && !isUpdating()\n    }\n  }\n\n  private inner class LocateAction internal constructor() :\n    AnAction(ENABLE_LOCATE_MODE, ENABLE_LOCATE_MODE, AllIcons.General.Locate) {\n\n    private var popupDisplayed = false\n\n    override fun actionPerformed(e: AnActionEvent) {\n      ribProjectService.enableLocateMode()\n\n      if (!popupDisplayed) {\n        displayPopup(\n          LOCATE_VIEW,\n          RelativePoint.getSouthOf(this@RibHierarchyBrowser),\n          MessageType.INFO,\n        )\n        popupDisplayed = true\n      }\n    }\n\n    override fun update(event: AnActionEvent) {\n      event.presentation.isEnabled = !ribProjectService.isLocating()\n    }\n  }\n\n  private inner class HelpAction internal constructor() :\n    AnAction(\n      IdeBundle.message(\"action.help\"),\n      IdeBundle.message(\"action.help\"),\n      AllIcons.General.TodoQuestion,\n    ) {\n\n    override fun actionPerformed(e: AnActionEvent) {\n      BrowserUtil.open(\n        \"https://github.com/uber/RIBs/wiki/Android-Tooling#ribs-intellij-plugin-for-android\",\n      )\n    }\n\n    override fun update(event: AnActionEvent) {\n      event.presentation.isEnabled = true\n    }\n  }\n\n  /**\n   * Interface used to notify that an new element was selected in {@ScopeHierarchyBrowser}\n   * component.\n   */\n  public interface Listener {\n\n    /** Callback indicating the selected Rib has changed. */\n    public fun onSelectedRibChanged(id: UUID)\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibHierarchyPanel.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib\n\nimport com.android.ddmlib.IDevice\nimport com.intellij.openapi.components.service\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.intellij.uiDesigner.core.GridConstraints\nimport com.intellij.uiDesigner.core.GridLayoutManager\nimport com.uber.intellij.plugin.android.rib.RibHierarchyBrowser.Model\nimport com.uber.intellij.plugin.android.rib.RibHierarchyUtils.Companion.findRibNodeRecursive\nimport com.uber.intellij.plugin.android.rib.RibHierarchyUtils.Companion.findRibViewRecursive\nimport com.uber.intellij.plugin.android.rib.RibViewBrowser.Model as ViewModel\nimport com.uber.intellij.plugin.android.rib.io.RibNode\nimport com.uber.intellij.plugin.android.rib.io.RibView\nimport java.awt.Insets\nimport java.awt.event.ActionEvent\nimport java.awt.event.ActionListener\nimport java.util.UUID\nimport javax.swing.DefaultComboBoxModel\nimport javax.swing.JComboBox\nimport javax.swing.JPanel\nimport javax.swing.JSplitPane\nimport javax.swing.JSplitPane.RIGHT\n\n/** UI Component representing the panel including rib hierarchy. */\npublic class RibHierarchyPanel(public val project: Project, private val initialModel: Model) :\n  JPanel(),\n  RibProjectService.Listener,\n  ActionListener,\n  RibHierarchyBrowser.Listener,\n  RibViewBrowser.Listener {\n\n  public companion object {\n    private val EMPTY_RIB_VIEW: RibView = RibView(\"\", \"\", \"\", \"\", emptyList())\n    private val EMPTY_RIB_NODE: RibNode = RibNode(\"\", \"\", emptyList(), EMPTY_RIB_VIEW)\n    private val EMPTY_VIEW_MODEL: ViewModel =\n      RibViewBrowser.Model(EMPTY_RIB_NODE, EMPTY_RIB_VIEW, EMPTY_RIB_NODE)\n  }\n\n  private val ribProjectService: RibProjectService = project.service()\n  private val comboBox: JComboBox<IDevice>\n  private val comboBoxModel: DefaultComboBoxModel<IDevice>\n  private val splitPane: JSplitPane\n  private val ribBrowser: RibHierarchyBrowser\n  private var viewBrowser: RibViewBrowser? = null\n  private var model: Model = initialModel\n  private var dividerSet: Boolean = false\n\n  init {\n    val rootElement: PsiElement = RibHierarchyUtils.buildRootElement(project)\n\n    // Build UI\n    layout = GridLayoutManager(2, 1, Insets(0, 0, 0, 0), -1, -1)\n\n    comboBoxModel = DefaultComboBoxModel()\n    comboBox = JComboBox(comboBoxModel)\n    comboBox.addActionListener(this)\n    add(\n      comboBox,\n      GridConstraints(\n        0,\n        0,\n        1,\n        1,\n        GridConstraints.ANCHOR_WEST,\n        GridConstraints.FILL_HORIZONTAL,\n        GridConstraints.SIZEPOLICY_CAN_GROW,\n        GridConstraints.SIZEPOLICY_FIXED,\n        null,\n        null,\n        null,\n        0,\n        false,\n      ),\n    )\n\n    ribBrowser = RibHierarchyBrowser(project, model, rootElement, this)\n    ribBrowser.changeView(RibHierarchyBrowser.TYPE_HIERARCHY_TYPE)\n    splitPane = JSplitPane(JSplitPane.VERTICAL_SPLIT, ribBrowser, null)\n    add(\n      splitPane,\n      GridConstraints(\n        1,\n        0,\n        1,\n        1,\n        GridConstraints.ANCHOR_CENTER,\n        GridConstraints.FILL_BOTH,\n        GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW,\n        GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW,\n        null,\n        null,\n        null,\n        0,\n        false,\n      ),\n    )\n  }\n\n  /** Requests to update the list of devices. */\n  public fun onAvailableDevicesChanged(devices: List<IDevice>) {\n    comboBoxModel.removeAllElements()\n    devices.forEach { comboBoxModel.addElement(it) }\n  }\n\n  /** Requests to update the selected device. */\n  public fun onSelectedDeviceChanged(selectedDevice: IDevice?) {\n    comboBoxModel.selectedItem = selectedDevice\n  }\n\n  /** Listbox callback. */\n  override fun actionPerformed(e: ActionEvent) {\n    if (e.actionCommand == \"comboBoxChanged\") {\n      ribProjectService.selectDevice(comboBox.selectedItem as IDevice?)\n    }\n  }\n\n  /** Updates the UI with provided model. */\n  override fun onModelUpdated(model: Model) {\n    this.model = model\n    ribBrowser.onModelUpdated(model)\n    splitPane.add(JPanel(), RIGHT)\n    viewBrowser = null\n  }\n\n  /** Notify panel that a new rib has been selected in rib hierarchy browser. */\n  override fun onSelectedRibChanged(id: UUID) {\n    if (model.selectedViewId.isEmpty()) {\n      ribProjectService.highlightRib(id)\n    }\n\n    val rootElement: PsiElement = RibHierarchyUtils.buildRootElement(project)\n    val rootRibNode: RibNode = this.model.host.application?.activities?.first()?.rootRib ?: return\n    val ribNode: RibNode? = findRibNodeRecursive(rootRibNode, id)\n    val ribView: RibView? =\n      if (ribNode?.view?.id?.isNotEmpty() == true) {\n        findRibViewRecursive(rootRibNode.view, UUID.fromString(ribNode?.view?.id))\n      } else {\n        null\n      }\n    val model =\n      if (ribNode != null && ribView != null) {\n        ViewModel(\n          ribNode,\n          ribView,\n          rootRibNode,\n          this.model.selectedRibId,\n          this.model.selectedViewId,\n        )\n      } else {\n        EMPTY_VIEW_MODEL\n      }\n\n    val previousDividerLocation = splitPane.dividerLocation\n    viewBrowser = RibViewBrowser(project, model, rootElement, this)\n    viewBrowser?.changeView(RibViewBrowser.TYPE_HIERARCHY_TYPE)\n\n    splitPane.add(viewBrowser, RIGHT)\n    if (ribView != null && !dividerSet) {\n      splitPane.dividerLocation = splitPane.height / 2\n      dividerSet = true\n    } else {\n      splitPane.dividerLocation = previousDividerLocation\n    }\n  }\n\n  /** Notify panel that a new view has been selected in rib hierarchy browser. */\n  override fun onSelectedViewChanged(ribView: RibView) {\n    ribProjectService.highlightView(UUID.fromString(ribView.id))\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibHierarchyUtils.kt",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib\n\n/*\n * Copyright (c) 2018-2019 Uber Technologies, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport com.intellij.ide.highlighter.XmlFileType\nimport com.intellij.notification.Notification\nimport com.intellij.notification.NotificationType\nimport com.intellij.notification.Notifications\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.ProjectRootManager\nimport com.intellij.openapi.ui.MessageType\nimport com.intellij.openapi.ui.popup.Balloon\nimport com.intellij.openapi.ui.popup.JBPopupFactory\nimport com.intellij.openapi.vfs.LocalFileSystem\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.JavaPsiFacade\nimport com.intellij.psi.PsiClass\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.ui.awt.RelativePoint\nimport com.uber.intellij.plugin.android.rib.io.RibNode\nimport com.uber.intellij.plugin.android.rib.io.RibView\nimport java.io.File\nimport java.nio.file.Path\nimport java.nio.file.Paths\nimport java.util.UUID\n\n/** Utility class used by the Rib hierarchy browser component. */\n@SuppressWarnings(\"TooManyFunctions\")\npublic class RibHierarchyUtils {\n\n  private constructor()\n\n  public companion object {\n\n    /** Constant used to represent empty UUID */\n    public val EMPTY_UUID: UUID = UUID(0, 0)\n\n    /** Time for balloon to fade out */\n    private const val BALLOON_FADE_OUT_TIME: Long = 3000\n\n    /** Name of layout folder */\n    private const val LAYOUT_FOLDER_NAME: String = \"layout\"\n\n    /** Build root element, used when class is not available. */\n    public fun buildRootElement(project: Project): PsiClass {\n      val psiClass: PsiClass? =\n        JavaPsiFacade.getInstance(project)\n          .findClass(Object::class.java.name, GlobalSearchScope.allScope(project))\n      checkNotNull(psiClass)\n      return psiClass!!\n    }\n\n    /** Return the psiClass corresponding to the given class name. */\n    public fun getPsiClass(project: Project, name: String): PsiClass {\n      val psiClass: PsiClass? =\n        JavaPsiFacade.getInstance(project).findClass(name, GlobalSearchScope.allScope(project))\n      return psiClass ?: buildRootElement(project)\n    }\n\n    /** Check if the element supplied is a root element. */\n    public fun isRootElement(element: PsiElement?): Boolean {\n      return element is PsiClass && element.qualifiedName == Object::class.java.name\n    }\n\n    /** Format fully qualified class name. */\n    public fun formatQualifiedName(qualifiedName: String): String {\n      val index: Int = qualifiedName.lastIndexOf(\".\")\n      return if (index > 0) qualifiedName.substring(0, index) else qualifiedName\n    }\n\n    /** Format fully qualified class name. */\n    public fun formatSimpleName(qualifiedName: String): String {\n      val index: Int = qualifiedName.lastIndexOf(\".\")\n      return if (index > 0) qualifiedName.substring(index + 1) else qualifiedName\n    }\n\n    /** Find node with the given ID in node hierarchy */\n    @SuppressWarnings(\"ReturnCount\")\n    public fun findRibNodeRecursive(ribNode: RibNode?, id: UUID): RibNode? {\n      if (ribNode == null) {\n        return null\n      }\n      if (ribNode.id == id.toString()) {\n        return ribNode\n      }\n      for (element in ribNode.children) {\n        val node: RibNode? = findRibNodeRecursive(element, id)\n        if (node != null) {\n          return node\n        }\n      }\n      return null\n    }\n\n    /** Find view with the given ID in view hierarchy */\n    @SuppressWarnings(\"ReturnCount\")\n    public fun findRibViewRecursive(ribView: RibView?, id: UUID): RibView? {\n      if (ribView == null) {\n        return null\n      }\n      if (ribView.id == id.toString()) {\n        return ribView\n      }\n      for (childView in ribView.children) {\n        val view: RibView? = findRibViewRecursive(childView, id)\n        if (view != null) {\n          return view\n        }\n      }\n      return null\n    }\n\n    /** Get a view tag value suffix */\n    public fun getTagValueSuffix(value: String?): String? {\n      if (value == null) {\n        return null\n      }\n      val index = value.indexOf(\"/\")\n      return if (index > 0) value.substring(index + 1) else value\n    }\n\n    /** Get virtual file from path */\n    @SuppressWarnings(\"MagicNumber\")\n    public fun getVirtualFile(filePath: String): VirtualFile? {\n      val actualPath: Path = Paths.get(filePath)\n      val pathFile: File = actualPath.toFile()\n      return LocalFileSystem.getInstance().findFileByIoFile(pathFile)\n    }\n\n    /** Returns whether virtual file belongs to project and appears to be a layout file */\n    public fun isProjectLayoutFile(project: Project, file: VirtualFile): Boolean {\n      return ProjectRootManager.getInstance(project).fileIndex.isInContent(file) &&\n        file.fileType is XmlFileType &&\n        file.path.contains(\"/$LAYOUT_FOLDER_NAME/\")\n    }\n\n    /** Display popup balloon. */\n    public fun displayPopup(\n      message: String,\n      location: RelativePoint,\n      type: MessageType = MessageType.WARNING,\n    ) {\n      JBPopupFactory.getInstance()\n        .createHtmlTextBalloonBuilder(message, type, null)\n        .setFadeoutTime(BALLOON_FADE_OUT_TIME)\n        .createBalloon()\n        .show(location, Balloon.Position.above)\n    }\n\n    /** Display notification bubble. */\n    public fun log(message: String) {\n      Notifications.Bus.notify(Notification(\"Rib\", \"Rib\", message, NotificationType.INFORMATION))\n    }\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibIcons.kt",
    "content": "/*\n * Copyright (C) 2023. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib\n\nimport com.intellij.openapi.util.IconLoader\nimport javax.swing.Icon\n\npublic object RibIcons {\n  @JvmField public val RibIcon: Icon = IconLoader.getIcon(\"/icons/rib.png\", javaClass)\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibProjectService.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib\n\nimport com.android.ddmlib.IDevice\nimport com.google.common.util.concurrent.FutureCallback\nimport com.google.common.util.concurrent.Futures\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.components.Service.Level.PROJECT\nimport com.intellij.openapi.components.service\nimport com.intellij.openapi.progress.ProgressIndicator\nimport com.intellij.openapi.progress.ProgressManager\nimport com.intellij.openapi.progress.Task\nimport com.intellij.openapi.project.DumbService\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.wm.ToolWindow\nimport com.intellij.openapi.wm.ToolWindowAnchor\nimport com.intellij.openapi.wm.ToolWindowManager\nimport com.intellij.ui.content.Content\nimport com.intellij.ui.content.ContentFactory\nimport com.uber.intellij.plugin.android.rib.io.EnableLocateModeRequest\nimport com.uber.intellij.plugin.android.rib.io.LogcatRequestProcessor\nimport com.uber.intellij.plugin.android.rib.io.Request\nimport com.uber.intellij.plugin.android.rib.io.RibHierarchyRequest\nimport com.uber.intellij.plugin.android.rib.io.RibHierarchyResponse\nimport com.uber.intellij.plugin.android.rib.io.RibHierarchyWithSelectionResponse\nimport com.uber.intellij.plugin.android.rib.io.RibHighlightRequest\nimport com.uber.intellij.plugin.android.rib.io.RibHost\nimport com.uber.intellij.plugin.android.rib.io.RibHostWithSelection\nimport java.util.UUID\nimport java.util.concurrent.Executor\nimport java.util.concurrent.Executors\n\n@Service(PROJECT)\npublic class RibProjectService(public val project: Project) :\n  AndroidDeviceRepository.Listener, Disposable {\n\n  public companion object {\n    private const val TOOL_WINDOW_ID: String = \"Ribs\"\n    private const val TOOL_WINDOW_TITLE: String = \"Ribs\"\n    private const val TAB_NAME_RIBS: String = \"Hierarchy\"\n    private const val LABEL_RIB_REFRESH: String = \"Refreshing Rib Hierarchy...\"\n    private const val LABEL_RIB_LOCATE: String = \"Waiting for RIB selection on Device...\"\n    private val EMPTY_MODEL: RibHierarchyBrowser.Model =\n      RibHierarchyBrowser.Model(RibHost(\"\", null), \"\", \"\")\n    private val executor: Executor = Executors.newSingleThreadExecutor()\n  }\n\n  private val androidDeviceRepository = project.service<AndroidDeviceRepository>()\n  private var ribPanel: RibHierarchyPanel? = null\n  private var ribContent: Content? = null\n  private var devices: List<IDevice> = arrayListOf()\n  private var selectedDevice: IDevice? = null\n  private var isRefreshing: Boolean = false\n  private var isLocating: Boolean = false\n\n  public fun attach() {\n    DumbService.getInstance(project).runWhenSmart {\n      ApplicationManager.getApplication().runReadAction {\n        androidDeviceRepository.addListener(this)\n        onModelUpdated(EMPTY_MODEL)\n      }\n    }\n  }\n\n  public fun refreshRibHierarchy() {\n    if (isRefreshing) {\n      return\n    }\n\n    if (selectedDevice == null) {\n      onModelUpdated(EMPTY_MODEL)\n    } else {\n      val device: IDevice = selectedDevice ?: return\n      isRefreshing = true\n      ProgressManager.getInstance()\n        .run(\n          object : Task.Backgroundable(project, LABEL_RIB_REFRESH) {\n            override fun run(indicator: ProgressIndicator) {\n              ApplicationManager.getApplication().invokeLater {\n                val request: Request<RibHierarchyResponse> = RibHierarchyRequest(device)\n                val future = LogcatRequestProcessor().execute(request)\n                Futures.addCallback(\n                  future,\n                  object : FutureCallback<RibHierarchyResponse> {\n                    override fun onSuccess(result: RibHierarchyResponse?) {\n                      val host: RibHost = result?.payload ?: return\n                      val model = RibHierarchyBrowser.Model(host)\n                      onModelUpdated(model)\n                      isRefreshing = false\n                    }\n\n                    override fun onFailure(throwable: Throwable) {\n                      onModelUpdated(EMPTY_MODEL)\n                      isRefreshing = false\n                    }\n                  },\n                  executor,\n                )\n              }\n            }\n          },\n        )\n    }\n  }\n\n  public fun highlightRib(id: UUID) {\n    val device: IDevice = selectedDevice ?: return\n    LogcatRequestProcessor().execute(RibHighlightRequest(device, id))\n  }\n\n  public fun highlightView(id: UUID) {\n    val device: IDevice = selectedDevice ?: return\n    LogcatRequestProcessor().execute(RibHighlightRequest(device, id))\n  }\n\n  public fun isLocating(): Boolean {\n    return isLocating\n  }\n\n  public fun enableLocateMode() {\n    if (isLocating) {\n      return\n    }\n\n    val device: IDevice = selectedDevice ?: return\n    ProgressManager.getInstance()\n      .run(\n        object : Task.Backgroundable(project, LABEL_RIB_LOCATE) {\n          override fun run(indicator: ProgressIndicator) {\n            ApplicationManager.getApplication().invokeLater {\n              isLocating = true\n              val request: Request<RibHierarchyWithSelectionResponse> =\n                EnableLocateModeRequest(device, true)\n              val future = LogcatRequestProcessor().execute(request)\n              Futures.addCallback(\n                future,\n                object : FutureCallback<RibHierarchyWithSelectionResponse> {\n                  override fun onSuccess(result: RibHierarchyWithSelectionResponse?) {\n                    isLocating = false\n                    val payload: RibHostWithSelection = result?.payload ?: return\n                    val model =\n                      RibHierarchyBrowser.Model(\n                        RibHost(payload.name, payload.application),\n                        payload.selectedRibId,\n                        payload.selectedViewId,\n                      )\n                    onModelUpdated(model)\n                  }\n\n                  override fun onFailure(throwable: Throwable) {\n                    isLocating = false\n                    LogcatRequestProcessor().execute(EnableLocateModeRequest(device, false))\n                  }\n                },\n                executor,\n              )\n            }\n          }\n        },\n      )\n  }\n\n  public fun selectDevice(device: IDevice?) {\n    if (selectedDevice == device) {\n      return\n    }\n    if (device != null && !devices.contains(device)) {\n      throw IllegalArgumentException(\"Selecting not connected device\")\n    }\n    selectedDevice = device\n\n    ribPanel?.onSelectedDeviceChanged(selectedDevice)\n\n    refreshRibHierarchy()\n  }\n\n  public fun hasSelectedDevice(): Boolean {\n    return selectedDevice != null\n  }\n\n  override fun onAvailableDevicesChanged(devices: List<IDevice>) {\n    this.devices = devices\n\n    ribPanel?.onAvailableDevicesChanged(devices)\n\n    if (selectedDevice !in devices) {\n      val fallbackDevice: IDevice? = if (devices.isNotEmpty()) devices[0] else null\n      selectDevice(fallbackDevice)\n    }\n  }\n\n  override fun dispose() {\n    androidDeviceRepository.removeListener(this)\n  }\n\n  private fun onModelUpdated(model: RibHierarchyBrowser.Model) {\n    ApplicationManager.getApplication().invokeLater {\n      val toolWindowManager: ToolWindowManager = ToolWindowManager.getInstance(project)\n      if (toolWindowManager.getToolWindow(TOOL_WINDOW_ID) == null) {\n        val toolWindow: ToolWindow =\n          toolWindowManager.registerToolWindow(TOOL_WINDOW_ID, true, ToolWindowAnchor.RIGHT)\n        toolWindow.setIcon(RibIcons.RibIcon)\n        toolWindow.title = TOOL_WINDOW_TITLE\n\n        ribPanel = RibHierarchyPanel(project, model)\n        if (devices.isNotEmpty()) {\n          ribPanel?.onAvailableDevicesChanged(devices)\n        }\n        ribContent = createRibContent(toolWindow)\n      } else {\n        ribPanel?.onModelUpdated(model)\n      }\n    }\n  }\n\n  private fun createRibContent(toolWindow: ToolWindow): Content {\n    val content = ContentFactory.getInstance().createContent(ribPanel, TAB_NAME_RIBS, true)\n    content.isCloseable = false\n    toolWindow.contentManager.addContent(content)\n    return content\n  }\n\n  public interface Listener {\n    public fun onModelUpdated(model: RibHierarchyBrowser.Model)\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibViewBrowser.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib\n\nimport com.intellij.ide.hierarchy.HierarchyNodeDescriptor\nimport com.intellij.ide.hierarchy.HierarchyTreeStructure\nimport com.intellij.ide.hierarchy.JavaHierarchyUtil\nimport com.intellij.ide.util.treeView.NodeDescriptor\nimport com.intellij.openapi.actionSystem.ActionPlaces\nimport com.intellij.openapi.actionSystem.DefaultActionGroup\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiClass\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.presentation.java.ClassPresentationUtil\nimport com.intellij.ui.treeStructure.Tree\nimport com.uber.intellij.plugin.android.rib.RibHierarchyUtils.Companion.isRootElement\nimport com.uber.intellij.plugin.android.rib.io.RibNode\nimport com.uber.intellij.plugin.android.rib.io.RibView\nimport com.uber.intellij.plugin.android.rib.ui.HierarchyBrowserBase\nimport com.uber.intellij.plugin.android.rib.ui.RibHierarchyTreeStructure\nimport com.uber.intellij.plugin.android.rib.ui.RibViewNodeDescriptor\nimport com.uber.intellij.plugin.android.rib.ui.RibViewRootNodeDescriptor\nimport java.awt.event.FocusEvent\nimport java.awt.event.FocusListener\nimport java.text.MessageFormat\nimport java.util.UUID\nimport javax.swing.JPanel\nimport javax.swing.JTree\nimport javax.swing.tree.DefaultMutableTreeNode\nimport kotlin.Comparator\n\n/** UI component used to render tree of Ribs. */\n@SuppressWarnings(\"TooManyFunctions\")\npublic class RibViewBrowser(\n  project: Project,\n  private val model: Model,\n  private val rootElement: PsiElement,\n  private val selectionListener: Listener?,\n) : HierarchyBrowserBase(project, rootElement) {\n\n  public companion object {\n    /** Go to previous Rib label */\n    public const val LABEL_GO_PREVIOUS_RIB: String = \"Go to previous Scope.\"\n\n    /** Go to next Rib label */\n    public const val LABEL_GO_NEXT_RIB: String = \"Go to next Scope\"\n\n    /** Type of the Rib hierarchy */\n    public const val TYPE_HIERARCHY_TYPE: String = \"Views\"\n  }\n\n  /**\n   * Data class used to represent the host of the Rib application\n   *\n   * @param ribNode the rib node corresponding to this model\n   * @param ribView the rib view corresponding to this model\n   * @param rootRib the root rib corresponding to this model\n   * @param selectedRibId the RIB ID of the RIB selected by user (if any)\n   * @param selectedViewId the view ID of the view selected by user (if any)\n   */\n  public data class Model(\n    val ribNode: RibNode,\n    val ribView: RibView,\n    val rootRib: RibNode,\n    val selectedRibId: String = \"\",\n    val selectedViewId: String = \"\",\n  )\n\n  private var hasFocus: Boolean = false\n\n  override fun isApplicableElement(element: PsiElement): Boolean {\n    return element is PsiClass\n  }\n\n  override fun getActionPlace(): String {\n    return ActionPlaces.METHOD_HIERARCHY_VIEW_TOOLBAR\n  }\n\n  override fun getComparator(): Comparator<NodeDescriptor<*>> {\n    return JavaHierarchyUtil.getComparator(myProject)\n  }\n\n  override fun getElementFromDescriptor(descriptor: HierarchyNodeDescriptor): PsiElement? {\n    if (isRootElement(descriptor.psiElement)) {\n      return null\n    }\n    return descriptor.psiElement\n  }\n\n  override fun getPrevOccurenceActionNameImpl(): String {\n    return LABEL_GO_PREVIOUS_RIB\n  }\n\n  override fun createLegendPanel(): JPanel? {\n    return null\n  }\n\n  override fun createTrees(trees: MutableMap<in String, in JTree>) {\n    trees[TYPE_HIERARCHY_TYPE] = createTree(true)\n  }\n\n  override fun getNextOccurenceActionNameImpl(): String {\n    return LABEL_GO_NEXT_RIB\n  }\n\n  override fun getContentDisplayName(typeName: String, element: PsiElement): String? {\n    if (element !is PsiClass) {\n      return null\n    }\n    return MessageFormat.format(typeName, ClassPresentationUtil.getNameForClass(element, false))\n  }\n\n  override fun appendActions(actionGroup: DefaultActionGroup, helpID: String?) {\n    // Do nothing instead of invoking parent method, in order to remove action bar\n  }\n\n  override fun createHierarchyTreeStructure(\n    typeName: String,\n    psiElement: PsiElement,\n  ): HierarchyTreeStructure? {\n    val rootDescriptor =\n      RibViewRootNodeDescriptor(project, psiElement, model.ribNode, model.ribView)\n    return RibHierarchyTreeStructure(project, rootDescriptor)\n  }\n\n  override fun configureTree(tree: Tree) {\n    super.configureTree(tree)\n\n    tree.addFocusListener(\n      object : FocusListener {\n        override fun focusLost(e: FocusEvent?) {\n          hasFocus = false\n        }\n\n        override fun focusGained(e: FocusEvent?) {\n          hasFocus = true\n          notifySelectedViewChanged()\n        }\n      },\n    )\n\n    tree.addTreeSelectionListener { notifySelectedViewChanged() }\n\n    // by default, expand the entire tree, and select item if needed\n    ApplicationManager.getApplication().invokeLater {\n      expandAll()\n      if (model.selectedViewId.isNotEmpty() && this.model.ribNode.id == this.model.selectedRibId) {\n        val ribView: RibView =\n          RibHierarchyUtils.findRibViewRecursive(\n            this.model.rootRib.view,\n            UUID.fromString(model.selectedViewId),\n          )\n            ?: return@invokeLater\n        selectionListener?.onSelectedViewChanged(ribView)\n\n        ApplicationManager.getApplication().invokeLater { selectById(model.selectedViewId) }\n      }\n    }\n  }\n\n  /** Notify that the currently selected view has changed. */\n  public fun notifySelectedViewChanged() {\n    val node: Any? = currentTree.lastSelectedPathComponent\n    if (node is DefaultMutableTreeNode && hasFocus) {\n      val descriptor = node.userObject\n      if (\n        descriptor is RibViewNodeDescriptor &&\n          descriptor.ribView != null &&\n          descriptor.ribView.id.isNotEmpty()\n      ) {\n        selectionListener?.onSelectedViewChanged(descriptor.ribView)\n      }\n    }\n  }\n\n  /**\n   * Interface used to notify that a new view was selected in {@ScopeHierarchyBrowser} component.\n   */\n  public interface Listener {\n\n    /** Callback indicating the selected View has changed. */\n    public fun onSelectedViewChanged(ribView: RibView)\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/AckRequest.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.io\n\nimport com.android.ddmlib.IDevice\n\n/** Ack response object. */\npublic class AckResponse : Response<String>()\n\n/** Ack request object. */\npublic class AckRequest(device: IDevice) :\n  Request<AckResponse>(device, \"ACK\", AckResponse::class.java)\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/LogcatMessageDecoder.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.io\n\n/**\n * Class responsible for collecting split messages from logcat, and concatenating them into full\n * message response. This is to work around 4000 char limit of logcat entries.\n */\npublic class LogcatMessageDecoder {\n\n  private inner class MessagePart(message: String) : Comparable<MessagePart> {\n    val partNumber: Int\n    val partCount: Int\n    private val part: String\n\n    init {\n      val slashIndex = message.indexOf('/')\n      val spaceIndex = message.indexOf(' ')\n      partNumber = Integer.parseInt(message.substring(0, slashIndex))\n      partCount = Integer.parseInt(message.substring(slashIndex + 1, spaceIndex))\n      part = message.substring(spaceIndex + 1)\n    }\n\n    override fun compareTo(other: MessagePart): Int {\n      return partNumber.compareTo(other.partNumber)\n    }\n\n    override fun toString(): String {\n      return part\n    }\n  }\n\n  private var partCount: Int = 0\n  private var parts: HashMap<Int, MessagePart> = HashMap()\n\n  /** Whether all parts were received to reconstruct message */\n  public val complete: Boolean\n    get() {\n      return partCount > 0 && parts.size == partCount\n    }\n\n  /** The full message. */\n  public val message: String\n    get() {\n      if (!complete) {\n        error(\"Message is not complete\")\n      }\n      val strings = arrayListOf<MessagePart>()\n      strings.addAll(parts.values)\n\n      strings.sort()\n      return strings.joinToString(\"\")\n    }\n\n  /** Method invoked when new part of message are received. */\n  public fun onMessagePartReceived(message: String) {\n    val part = MessagePart(message)\n    if (partCount == 0) {\n      partCount = part.partCount\n    }\n    if (part.partCount != partCount) {\n      error(\"Unexpected number of part received\")\n    }\n    parts[part.partNumber] = part\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/LogcatRequestProcessor.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.io\n\nimport com.android.ddmlib.IDevice\nimport com.android.ddmlib.MultiLineReceiver\nimport com.android.ddmlib.logcat.LogCatMessageParser\nimport com.google.common.util.concurrent.ListenableFuture\nimport com.google.common.util.concurrent.ListeningExecutorService\nimport com.google.common.util.concurrent.MoreExecutors\nimport com.google.gson.Gson\nimport java.util.concurrent.Callable\nimport java.util.concurrent.Executors\nimport java.util.concurrent.atomic.AtomicInteger\n\n/**\n * Implementation of the request processor interface, relying on emitting broadcast and parsing\n * logcat output.\n */\npublic class LogcatRequestProcessor : RequestProcessor {\n  public companion object {\n    private const val SHELL_COMMAND_TEMPLATE: String =\n      \"am broadcast -a com.uber.debug.intent.action.COMMAND --ei SEQ %d --es CMD %s\"\n    private const val LOGCAT_COMMAND_TEMPLATE: String =\n      \"logcat -s DebugBroadcastReceiver[%d] -b main -d -v long -t 100\"\n    private const val PARAM_TEMPLATE: String = \" --es %s %s\"\n    private const val ADB_BROADCAST_SUCCESS_MESSAGE: String = \"Broadcast completed: result=0\"\n    private const val SLEEP_INCREMENT: Long = 100\n    private const val MAX_SEQUENCE: Int = 1000000\n    private const val THREAD_COUNT: Int = 3\n\n    private val counter: AtomicInteger = AtomicInteger((Math.random() * MAX_SEQUENCE).toInt())\n    private val service: ListeningExecutorService =\n      MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(THREAD_COUNT))\n    private val jsonParser: Gson = Gson()\n  }\n\n  private var result: Any? = null\n  private var error: String? = null\n\n  override fun <T> execute(request: Request<T>): ListenableFuture<T> {\n    return service.submit(\n      Callable<T> {\n        val sequence: Int = counter.getAndIncrement() % MAX_SEQUENCE\n\n        // Send command to Android device via intent broadcast\n        var shellCommand: String = String.format(SHELL_COMMAND_TEMPLATE, sequence, request.command)\n        request.params.forEach {\n          shellCommand += String.format(PARAM_TEMPLATE, it.first, it.second)\n        }\n        val broadcastReceiver = StringMatchReceiver(ADB_BROADCAST_SUCCESS_MESSAGE)\n        request.device.executeShellCommand(shellCommand, broadcastReceiver)\n\n        if (!broadcastReceiver.matched) {\n          error(\"Failed to broadcast intent\")\n        }\n\n        // Parse logcat for response in separate thread\n        val receiver = LogCatOutputReceiver(request.device, request.clazz, LogCatMessageParser())\n        val logCatCommand = String.format(LOGCAT_COMMAND_TEMPLATE, sequence)\n        request.device.executeShellCommand(logCatCommand, receiver)\n\n        // .. and wait for result value to be set\n        var timeWaitingMs: Long = 0\n        var retryCount = 0\n        while (result == null) {\n          Thread.sleep(SLEEP_INCREMENT)\n\n          timeWaitingMs += SLEEP_INCREMENT\n          if (timeWaitingMs > request.timeoutMs) {\n            if (retryCount++ > request.numRetries) {\n              error(\"Timed out waiting for response to be output in logcat\")\n            } else {\n              request.device.executeShellCommand(logCatCommand, receiver)\n              timeWaitingMs = 0\n            }\n          }\n          if (error != null) {\n            error(\"Response could not be parsed: $error\")\n          }\n        }\n        val response: Response<*> = result as Response<*>\n        if (response.errorDescription?.isNotEmpty() == true) {\n          error(\"Command failed: ${response.errorDescription}\")\n        }\n        result as T\n      },\n    )\n  }\n\n  private inner class LogCatOutputReceiver<T>(\n    private val device: IDevice,\n    private val clazz: Class<T>,\n    private val parser: LogCatMessageParser,\n  ) : MultiLineReceiver() {\n\n    private val decoder: LogcatMessageDecoder = LogcatMessageDecoder()\n\n    init {\n      setTrimLine(false)\n    }\n\n    override fun isCancelled(): Boolean {\n      return false\n    }\n\n    @SuppressWarnings(\"TooGenericExceptionCaught\")\n    override fun processNewLines(lines: Array<String>) {\n      if (result != null) {\n        return\n      }\n      parser.processLogLines(lines, device).forEach {\n        try {\n          decoder.onMessagePartReceived(it.message)\n          if (decoder.complete) {\n            result = jsonParser.fromJson(decoder.message, clazz)\n          }\n        } catch (e: Exception) {\n          error = e.message\n        }\n      }\n    }\n  }\n\n  private inner class StringMatchReceiver(private val match: String) : MultiLineReceiver() {\n\n    var matched: Boolean = false\n\n    override fun processNewLines(lines: Array<String>) {\n      lines.forEach {\n        if (it.indexOf(match) >= 0) {\n          matched = true\n        }\n      }\n    }\n\n    override fun isCancelled(): Boolean {\n      return false\n    }\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/Request.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.io\n\nimport com.android.ddmlib.IDevice\n\n/**\n * Class representing a request.\n *\n * @param device the device to send the request to\n * @param command the command to send\n * @param clazz the class of the expected response\n * @param params the parameter to add to request\n * @param timeoutMs the timeout to add to request\n * @param numRetries the number of retires to use for this request\n */\npublic open class Request<T>(\n  public val device: IDevice,\n  public val command: String,\n  public val clazz: Class<T>,\n  public val params: List<Pair<String, Any>> = emptyList(),\n  public val timeoutMs: Int = 2000,\n  public val numRetries: Int = 1,\n)\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RequestProcessor.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.io\n\nimport com.google.common.util.concurrent.ListenableFuture\n\n/**\n * Interface used by classes capable of communicating to Android device, i.e send and receive\n * messages.\n */\npublic interface RequestProcessor {\n\n  /** Send a request to device and returns a future to access response. */\n  public fun <T> execute(request: Request<T>): ListenableFuture<T>\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/Response.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.io\n\n/** Class representing a response. */\npublic open class Response<T> {\n  /** Whether request was successful */\n  public var success: Boolean = false\n\n  /** Protocol version of the response message */\n  public var version: Int = 0\n\n  /** Description of the error (if any) */\n  public var errorDescription: String? = null\n\n  /** Payload of the response */\n  public var payload: T? = null\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RibHierarchyRequest.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.io\n\nimport com.android.ddmlib.IDevice\n\n/** Data class representing the host of a Rib application, i.e an android device. */\npublic data class RibHost(val name: String, val application: RibApplication?)\n\n/** Data class representing a Rib application. */\npublic data class RibApplication(val name: String, val activities: List<RibActivity>)\n\n/** Data class representing a Rib activity. */\npublic data class RibActivity(val name: String, val rootRib: RibNode)\n\n/**\n * Data class representing a Rib node.\n *\n * @param id the id of the rib node\n * @param name the name of the rib node\n * @param children the list of children for this node\n * @param view the view for this rib node\n */\npublic data class RibNode(\n  val id: String,\n  val name: String,\n  val children: List<RibNode>,\n  val view: RibView,\n)\n\n/**\n * Data class representing a Rib view.\n *\n * @param id the id of the rib view\n * @param name the name of the rib view\n * @param viewId the view id for thie view\n * @param layoutId the name of the layout this view was inflated from\n * @param children the list of children for this view\n */\npublic data class RibView(\n  val id: String,\n  val name: String,\n  val viewId: String,\n  val layoutId: String,\n  val children: List<RibView>,\n)\n\n/** Data class representing the response of the Rib hierarchy request. */\npublic data class RibHierarchyResponse(val host: RibHost) : Response<RibHost>()\n\n/** Data class representing the request for a Rib hierarchy. */\npublic class RibHierarchyRequest(device: IDevice) :\n  Request<RibHierarchyResponse>(device, \"RIB_HIERARCHY\", RibHierarchyResponse::class.java)\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RibHighlightRequest.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.io\n\nimport com.android.ddmlib.IDevice\nimport java.util.UUID\n\n/** Rib highlight response object. */\npublic class RibHighlightResponse : Response<Unit>()\n\n/** Rib highlight request object. */\npublic class RibHighlightRequest(device: IDevice, id: UUID) :\n  Request<RibHighlightResponse>(\n    device,\n    \"RIB_HIGHLIGHT\",\n    RibHighlightResponse::class.java,\n    listOf(Pair(\"ID\", id), Pair(\"VISIBLE\", true)),\n  )\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RibLocateRequest.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.io\n\nimport com.android.ddmlib.IDevice\n\n/**\n * Data class representing the host of a Rib application, i.e an android device.\n *\n * @param name the name of the host\n * @param application the application model\n * @param selectedRibId the RIB ID of the RIB selected by user\n * @param selectedViewId the view ID of the view selected by user\n */\npublic data class RibHostWithSelection(\n  val name: String,\n  val application: RibApplication?,\n  val selectedRibId: String,\n  val selectedViewId: String,\n)\n\n/**\n * Data class representing the response of the Rib hierarchy request.\n *\n * @param host the host\n */\npublic data class RibHierarchyWithSelectionResponse(val host: RibHostWithSelection) :\n  Response<RibHostWithSelection>()\n\n/** Rib locate request object. */\npublic class EnableLocateModeRequest(device: IDevice, enabled: Boolean) :\n  Request<RibHierarchyWithSelectionResponse>(\n    device,\n    \"RIB_LOCATE\",\n    RibHierarchyWithSelectionResponse::class.java,\n    listOf(Pair(\"VISIBLE\", enabled)),\n    TIMEOUT_MS,\n    NUM_RETRIES,\n  ) {\n  public companion object {\n    private const val TIMEOUT_MS = 1000\n    private const val NUM_RETRIES = 5\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/HierarchyBrowserBase.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.ui\n\nimport com.intellij.ide.hierarchy.HierarchyBrowserBaseEx\nimport com.intellij.ide.hierarchy.HierarchyBrowserManager\nimport com.intellij.ide.hierarchy.HierarchyNodeRenderer\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.intellij.ui.AutoScrollToSourceHandler\nimport com.intellij.ui.TreeSpeedSearch\nimport com.intellij.ui.tree.TreeVisitor\nimport com.intellij.ui.treeStructure.Tree\nimport com.intellij.util.ui.tree.TreeUtil\nimport javax.swing.tree.DefaultMutableTreeNode\nimport javax.swing.tree.TreePath\nimport javax.swing.tree.TreeSelectionModel\n\n/**\n * Base class for hierarchy browser UI components.\n *\n * It enables speed search configurations to search non-expanded nodes too. It is needed when\n * searching for given scope(s) in the entire graph hierarchy (which can be pretty large).\n */\npublic abstract class HierarchyBrowserBase(\n  public val project: Project,\n  private val rootElement: PsiElement,\n) : HierarchyBrowserBaseEx(project, rootElement) {\n\n  override fun doRefresh(currentBuilderOnly: Boolean) {\n    super.doRefresh(currentBuilderOnly)\n    ApplicationManager.getApplication().invokeLater {\n      expandAll()\n      onRefreshComplete()\n    }\n  }\n\n  override fun configureTree(tree: Tree) {\n    // Hack: we're copying code from parent class here, in order to override speed search behavior\n    tree.selectionModel.selectionMode = TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION\n    tree.toggleClickCount = -1\n    tree.cellRenderer = HierarchyNodeRenderer()\n    TreeSpeedSearch(tree, { path -> path.lastPathComponent.toString() }, true)\n    TreeUtil.installActions(tree)\n    object : AutoScrollToSourceHandler() {\n        override fun isAutoScrollMode(): Boolean {\n          return HierarchyBrowserManager.getSettings(myProject).IS_AUTOSCROLL_TO_SOURCE\n        }\n\n        override fun setAutoScrollMode(state: Boolean) {\n          HierarchyBrowserManager.getSettings(myProject).IS_AUTOSCROLL_TO_SOURCE = state\n        }\n      }\n      .install(tree)\n  }\n\n  /** Refresh completion callback. */\n  public open fun onRefreshComplete() {}\n\n  /** Expand entire hierarchy */\n  public fun expandAll() {\n    TreeUtil.expandAll(currentTree)\n  }\n\n  /** Select a given item in the hierarchy, based on provided id. */\n  public fun selectById(id: String) {\n    TreeUtil.promiseSelect(\n      currentTree,\n      object : TreeVisitor {\n        override fun visit(path: TreePath): TreeVisitor.Action {\n          if (path.lastPathComponent is DefaultMutableTreeNode) {\n            val node: DefaultMutableTreeNode = path.lastPathComponent as DefaultMutableTreeNode\n            if (node.userObject is RibHierarchyDescriptor) {\n              val descriptor = node.userObject as RibHierarchyDescriptor\n              if (descriptor.getUniqueId() == id) {\n                return TreeVisitor.Action.INTERRUPT\n              }\n            }\n          }\n          return TreeVisitor.Action.CONTINUE\n        }\n      },\n    )\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyActivityDescriptor.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.ui\n\nimport com.intellij.icons.AllIcons\nimport com.intellij.ide.hierarchy.HierarchyNodeDescriptor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.ui.util.CompositeAppearance\nimport com.intellij.psi.PsiClass\nimport com.intellij.psi.PsiElement\nimport com.uber.intellij.plugin.android.rib.RibHierarchyUtils.Companion.formatQualifiedName\nimport com.uber.intellij.plugin.android.rib.RibHierarchyUtils.Companion.formatSimpleName\nimport com.uber.intellij.plugin.android.rib.io.RibActivity\nimport javax.swing.Icon\n\n/** Node descriptor used to render a Rib activities. */\npublic class RibHierarchyActivityDescriptor(\n  project: Project,\n  parentDescriptor: HierarchyNodeDescriptor?,\n  private val clazz: PsiClass,\n  public val ribActivity: RibActivity,\n) : RibHierarchyDescriptor(project, parentDescriptor, clazz, false) {\n\n  override fun updateText(text: CompositeAppearance) {\n    text.ending.addText(formatSimpleName(ribActivity.name), getDefaultTextAttributes())\n    text.ending.addText(\" (${formatQualifiedName(ribActivity.name)})\", getPackageNameAttributes())\n  }\n\n  override fun getIcon(element: PsiElement): Icon? {\n    return AllIcons.Actions.Execute\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyApplicationDescriptor.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.ui\n\nimport com.intellij.icons.AllIcons\nimport com.intellij.ide.hierarchy.HierarchyNodeDescriptor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.ui.util.CompositeAppearance\nimport com.intellij.psi.PsiClass\nimport com.intellij.psi.PsiElement\nimport com.uber.intellij.plugin.android.rib.io.RibApplication\nimport javax.swing.Icon\n\n/** Node descriptor used to render a Rib Application. */\npublic class RibHierarchyApplicationDescriptor(\n  project: Project,\n  parentDescriptor: HierarchyNodeDescriptor?,\n  private val clazz: PsiClass,\n  public val ribApplication: RibApplication,\n) : RibHierarchyDescriptor(project, parentDescriptor, clazz, false) {\n\n  override fun updateText(text: CompositeAppearance) {\n    text.ending.addText(ribApplication.name, getDefaultTextAttributes())\n  }\n\n  override fun getIcon(element: PsiElement): Icon? {\n    return AllIcons.Nodes.Parameter\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyDescriptor.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.ui\n\nimport com.intellij.ide.hierarchy.HierarchyNodeDescriptor\nimport com.intellij.openapi.editor.markup.EffectType\nimport com.intellij.openapi.editor.markup.TextAttributes\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.ui.util.CompositeAppearance\nimport com.intellij.openapi.util.Comparing\nimport com.intellij.psi.PsiElement\nimport java.awt.Color\nimport java.awt.Font\nimport javax.swing.Icon\n\n/** Base class for all tree node descriptors used in Rib IntelliJ plugin. */\npublic open class RibHierarchyDescriptor(\n  project: Project,\n  public val parentDescriptor: HierarchyNodeDescriptor?,\n  public val element: PsiElement,\n  isBase: Boolean,\n) : HierarchyNodeDescriptor(project, parentDescriptor, element, isBase) {\n\n  /** Method to set text of the node entry. */\n  public open fun updateText(text: CompositeAppearance) {}\n\n  /** Method used to get the unique id of descriptor. Used for programmatic selection. */\n  public open fun getUniqueId(): String? {\n    return null\n  }\n\n  override fun update(): Boolean {\n    val changes = super.update()\n\n    if (psiElement == null) {\n      return invalidElement()\n    }\n\n    val oldText = myHighlightedText\n    myHighlightedText = CompositeAppearance()\n    updateText(myHighlightedText)\n\n    return changes || !myHighlightedText.compareTo(oldText)\n  }\n\n  /** Return default text attributes. */\n  public fun getDefaultTextAttributes(isError: Boolean = false): TextAttributes {\n    val font: Int = if (myIsBase) Font.BOLD else Font.PLAIN\n    return if (isError) {\n      TextAttributes(myColor, null, Color.red, EffectType.WAVE_UNDERSCORE, font)\n    } else {\n      TextAttributes(myColor, null, null, null, font)\n    }\n  }\n\n  /** Return icon to display. */\n  override fun getIcon(element: PsiElement): Icon? {\n    return null\n  }\n\n  /** Compare 2 instances of text appearance. */\n  public fun CompositeAppearance.compareTo(another: CompositeAppearance): Boolean {\n    return Comparing.equal(this, another)\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyNodeDescriptor.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.ui\n\nimport com.intellij.ide.hierarchy.HierarchyNodeDescriptor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.ui.util.CompositeAppearance\nimport com.intellij.psi.PsiClass\nimport com.uber.intellij.plugin.android.rib.RibHierarchyUtils.Companion.formatQualifiedName\nimport com.uber.intellij.plugin.android.rib.RibHierarchyUtils.Companion.formatSimpleName\nimport com.uber.intellij.plugin.android.rib.io.RibNode\n\n/** Node descriptor used to render a Rib. */\npublic class RibHierarchyNodeDescriptor(\n  project: Project,\n  parentDescriptor: HierarchyNodeDescriptor?,\n  private val clazz: PsiClass,\n  public val ribNode: RibNode,\n) : RibHierarchyDescriptor(project, parentDescriptor, clazz, false) {\n\n  override fun updateText(text: CompositeAppearance) {\n    text.ending.addText(formatSimpleName(ribNode.name), getDefaultTextAttributes())\n    text.ending.addText(\" (${formatQualifiedName(ribNode.name)})\", getPackageNameAttributes())\n  }\n\n  /** Method used to get the unique id of descriptor. Used for programmatic selection. */\n  override fun getUniqueId(): String? {\n    return ribNode.id\n  }\n\n  override fun toString(): String {\n    return ribNode.name\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyRootNodeDescriptor.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.ui\n\nimport com.intellij.icons.AllIcons\nimport com.intellij.openapi.components.service\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.ui.util.CompositeAppearance\nimport com.intellij.psi.PsiElement\nimport com.uber.intellij.plugin.android.rib.AndroidDeviceRepository\nimport com.uber.intellij.plugin.android.rib.RibHierarchyBrowser\nimport com.uber.intellij.plugin.android.rib.RibProjectService\nimport com.uber.intellij.plugin.android.rib.io.RibHost\nimport javax.swing.Icon\n\n/** Node descriptor used to render tree roots. */\npublic class RibHierarchyRootNodeDescriptor(\n  project: Project,\n  element: PsiElement,\n  public val ribHost: RibHost,\n  private val status: RibHierarchyBrowser.Status,\n) : RibHierarchyDescriptor(project, null, element, true) {\n\n  private val deviceRepository: AndroidDeviceRepository = project.service()\n  private val ribProjectService: RibProjectService = project.service()\n\n  public companion object {\n    /** Label used when android bridge is not connected */\n    public const val LABEL_NO_BRIDGE: String =\n      \"No Android bridge. Make sure Android SDK is configured for this project.\"\n\n    /** Label used when no device is connected. */\n    public const val LABEL_NO_DEVICE: String = \"No Android device connected...\"\n\n    /** Label used when device list is being refreshed. */\n    public const val LABEL_WAIT: String = \"Loading RIB info...\"\n\n    /** Label used when no no Rib info could be fetched from device. */\n    public const val LABEL_ERROR: String =\n      \"No RIB info available. Make sure RIB app is running in foreground, then refresh.\"\n  }\n\n  override fun updateText(text: CompositeAppearance) {\n    if (!deviceRepository.isBridgeConnected()) {\n      text.ending.addText(LABEL_NO_BRIDGE)\n      return\n    }\n\n    if (!ribProjectService.hasSelectedDevice()) {\n      text.ending.addText(LABEL_NO_DEVICE)\n      return\n    }\n\n    when (status) {\n      RibHierarchyBrowser.Status.UNINITIALIZED -> {\n        text.ending.addText(LABEL_NO_DEVICE)\n      }\n      RibHierarchyBrowser.Status.INITIALIZING -> {\n        text.ending.addText(LABEL_WAIT)\n      }\n      else -> {\n        val label: String = if (ribHost.name.isNotEmpty()) ribHost.name else LABEL_ERROR\n        text.ending.addText(label, getDefaultTextAttributes())\n      }\n    }\n  }\n\n  override fun getIcon(element: PsiElement): Icon? {\n    if (!ribProjectService.hasSelectedDevice()) {\n      return AllIcons.General.BalloonInformation\n    }\n\n    return when (status) {\n      RibHierarchyBrowser.Status.UNINITIALIZED -> {\n        AllIcons.General.BalloonInformation\n      }\n      RibHierarchyBrowser.Status.INITIALIZING -> {\n        AllIcons.Ide.UpDown\n      }\n      else -> {\n        AllIcons.Actions.Dump\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyTreeStructure.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.ui\n\nimport com.intellij.ide.hierarchy.HierarchyNodeDescriptor\nimport com.intellij.ide.hierarchy.HierarchyTreeStructure\nimport com.intellij.openapi.project.Project\nimport com.uber.intellij.plugin.android.rib.RibHierarchyUtils\n\n/** Tree structure used by Rib hierarchy */\npublic class RibHierarchyTreeStructure(\n  private val project: Project,\n  descriptor: HierarchyNodeDescriptor,\n) : HierarchyTreeStructure(project, descriptor) {\n\n  init {\n    setBaseElement(descriptor)\n  }\n\n  override fun buildChildren(descriptor: HierarchyNodeDescriptor): Array<Any> {\n    val descriptors: ArrayList<HierarchyNodeDescriptor> = ArrayList(1)\n    when (descriptor) {\n      is RibHierarchyRootNodeDescriptor -> {\n        descriptor.ribHost.application?.let {\n          descriptors.add(\n            RibHierarchyApplicationDescriptor(\n              myProject,\n              descriptor,\n              RibHierarchyUtils.getPsiClass(project, descriptor.ribHost.name),\n              it,\n            ),\n          )\n        }\n      }\n      is RibHierarchyApplicationDescriptor -> {\n        descriptor.ribApplication.activities.forEach { activity ->\n          descriptors.add(\n            RibHierarchyActivityDescriptor(\n              myProject,\n              descriptor,\n              RibHierarchyUtils.getPsiClass(project, activity.name),\n              activity,\n            ),\n          )\n        }\n      }\n      is RibHierarchyActivityDescriptor -> {\n        descriptors.add(\n          RibHierarchyNodeDescriptor(\n            myProject,\n            descriptor,\n            RibHierarchyUtils.getPsiClass(project, descriptor.ribActivity.name),\n            descriptor.ribActivity.rootRib,\n          ),\n        )\n      }\n      is RibHierarchyNodeDescriptor -> {\n        descriptor.ribNode.children.forEach { childRibNode ->\n          descriptors.add(\n            RibHierarchyNodeDescriptor(\n              myProject,\n              descriptor,\n              RibHierarchyUtils.getPsiClass(project, childRibNode.name),\n              childRibNode,\n            ),\n          )\n        }\n      }\n      is RibViewRootNodeDescriptor -> {\n        descriptor.ribView?.children?.forEach { view ->\n          descriptors.add(\n            RibViewNodeDescriptor(\n              myProject,\n              RibHierarchyUtils.getPsiClass(project, view.id),\n              descriptor.ribNode,\n              view,\n            ),\n          )\n        }\n      }\n      is RibViewNodeDescriptor -> {\n        descriptor.ribView?.children?.forEach { view ->\n          descriptors.add(\n            RibViewNodeDescriptor(\n              myProject,\n              RibHierarchyUtils.getPsiClass(project, view.id),\n              descriptor.ribNode,\n              view,\n            ),\n          )\n        }\n      }\n    }\n    return descriptors.toTypedArray()\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibViewNodeDescriptor.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.ui\n\nimport com.intellij.icons.AllIcons\nimport com.intellij.openapi.editor.markup.TextAttributes\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.ui.util.CompositeAppearance\nimport com.intellij.psi.PsiElement\nimport com.uber.intellij.plugin.android.rib.RibHierarchyUtils\nimport com.uber.intellij.plugin.android.rib.io.RibNode\nimport com.uber.intellij.plugin.android.rib.io.RibView\nimport java.awt.Font\nimport javax.swing.Icon\n\n/**\n * Node descriptor used to render view tree.\n *\n * @param project the current project\n * @param element the psi element corresponding to this descriptor\n * @param ribNode the rib node corresponding to this descriptor\n * @param ribView the rib view corresponding to this descriptor\n */\npublic open class RibViewNodeDescriptor(\n  project: Project,\n  element: PsiElement,\n  public val ribNode: RibNode,\n  public val ribView: RibView?,\n) : RibHierarchyDescriptor(project, null, element, false) {\n\n  override fun updateText(text: CompositeAppearance) {\n    if (ribView == null) {\n      return\n    }\n    text.ending.addText(RibHierarchyUtils.formatSimpleName(ribView.name))\n\n    val boldFont = TextAttributes(myColor, null, null, null, Font.BOLD)\n    if (ribView.layoutId.isNotEmpty()) {\n      text.ending.addText(\" [layout/${ribView.layoutId}.xml]\", boldFont)\n    } else if (ribView.viewId.isNotEmpty()) {\n      text.ending.addText(\" [id/${ribView.viewId}]\", boldFont)\n    }\n\n    if (ribView.name.isNotEmpty()) {\n      text.ending.addText(\n        \" (${RibHierarchyUtils.formatQualifiedName(ribView.name)})\",\n        getPackageNameAttributes(),\n      )\n    }\n  }\n\n  /** Method used to get the unique id of descriptor. Used for programmatic selection. */\n  override fun getUniqueId(): String? {\n    return ribView?.id ?: null\n  }\n\n  override fun toString(): String {\n    return ribView?.viewId ?: \"\"\n  }\n\n  @SuppressWarnings(\"ReturnCount\")\n  override fun getIcon(element: PsiElement): Icon? {\n    if (ribView?.name == null || ribView.name.isEmpty()) {\n      return AllIcons.General.BalloonWarning\n    } else if (hasLayoutId()) {\n      return AllIcons.General.CopyHovered\n    } else if (ribView.name.contains(\"Image\")) {\n      return AllIcons.General.LayoutPreviewOnly\n    } else if (ribView.name.contains(\"Text\")) {\n      return AllIcons.Actions.Highlighting\n    } else if (ribView.name.contains(\"Layout\")) {\n      return AllIcons.Graph.Grid\n    }\n    return AllIcons.General.InspectionsEye\n  }\n\n  private fun hasLayoutId(): Boolean {\n    return ribView != null && ribView.layoutId.isNotEmpty()\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibViewRootNodeDescriptor.kt",
    "content": "/*\n * Copyright (C) 2018-2019. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.intellij.plugin.android.rib.ui\n\nimport com.intellij.openapi.editor.markup.TextAttributes\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.ui.util.CompositeAppearance\nimport com.intellij.psi.PsiElement\nimport com.uber.intellij.plugin.android.rib.RibHierarchyUtils\nimport com.uber.intellij.plugin.android.rib.io.RibNode\nimport com.uber.intellij.plugin.android.rib.io.RibView\nimport java.awt.Font\n\n/**\n * Node descriptor used to render view tree.\n *\n * @param nonNullProject the current project\n * @param element the psi element corresponding to this descriptor\n * @param ribNode the rib node corresponding to this descriptor\n * @param ribView the rib view corresponding to this descriptor\n */\npublic class RibViewRootNodeDescriptor(\n  private val nonNullProject: Project,\n  element: PsiElement,\n  ribNode: RibNode,\n  ribView: RibView?,\n) : RibViewNodeDescriptor(nonNullProject, element, ribNode, ribView) {\n\n  override fun updateText(text: CompositeAppearance) {\n    if (ribView?.name?.isNotEmpty() == false) {\n      text.ending.addText(\"Please select a RIB\", getDefaultTextAttributes())\n      return\n    }\n    text.ending.addText(\n      RibHierarchyUtils.formatSimpleName(ribNode.name).replace(\"Router\", \"View\"),\n      getDefaultTextAttributes(),\n    )\n    if (ribView != null && ribView.layoutId.isNotEmpty()) {\n      val boldFont = TextAttributes(myColor, null, null, null, Font.BOLD)\n      text.ending.addText(\" [layout/${ribView.layoutId}.xml]\", boldFont)\n    }\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/META-INF/plugin.xml",
    "content": "<idea-plugin>\n    <id>com.uber.rib.intellij-plugin</id>\n    <name>RIBs</name>\n    <version>0.1.5</version>\n    <vendor>\n        Uber Mobile Platform Team\n    </vendor>\n\n    <description><![CDATA[\n      Uber RIBs<br /> <br />\n      <ul>\n        <li>Easily create new RIBs.</li>\n        <li>Visualize RIB hierarchy.</li>\n      </ul>\n    ]]></description>\n\n    <change-notes><![CDATA[\n      <strong>0.1.5</strong><br/>\n      <ul>\n        <li>IntelliJ 2023.2 compatibility</li>\n      </ul>\n      <strong>0.1.4</strong><br/>\n      <ul>\n        <li>IntelliJ 2022 compatibility</li>\n      </ul>\n      <strong>0.1.3</strong><br/>\n      <ul>\n        <li>Added Kotlin support</li>\n      </ul>\n      <strong>0.1.0</strong><br/>\n      <ul>\n        <li>Initial release</li>\n      </ul>\n    ]]>\n\n    </change-notes>\n\n    <depends>com.intellij.modules.platform</depends>\n    <depends>com.intellij.java</depends>\n    <depends>org.jetbrains.android</depends>\n\n    <idea-version since-build=\"223\"/>\n\n    <extensions defaultExtensionNs=\"com.intellij\">\n        <backgroundPostStartupActivity implementation=\"com.uber.intellij.plugin.android.rib.AttachRibProjectServiceActivity\"/>\n    </extensions>\n\n    <actions>\n        <group description=\"RIB architecture templates\" id=\"RibActionGroup\" text=\"RIB\">\n            <separator/>\n            <action\n                    class=\"com.uber.presidio.intellij_plugin.action.rib.GenerateRibAction\"\n                    description=\"Generates a new RIB.\"\n                    icon=\"/icons/generate_rib.png\"\n                    id=\"GenerateRib\"\n                    text=\"New RIB...\">\n                <add-to-group anchor=\"last\" group-id=\"NewGroup\" />\n            </action>\n        </group>\n    </actions>\n</idea-plugin>\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/partials/ExampleInteractorTest.java.partial",
    "content": "  /**\n   * TODO: Delete this example and add real tests.\n   */\n  @Test\n  public void anExampleTest_withSomeConditions_shouldPass() {\n    // Use InteractorHelper to drive your interactor's lifecycle.\n    InteractorHelper.attach(interactor, presenter, router, null);\n    InteractorHelper.detach(interactor);\n\n    throw new RuntimeException(\"Remove this test and add real tests.\");\n  }\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/partials/ExampleRouterTest.java.partial",
    "content": "  /**\n   * TODO: Delete this example and add real tests.\n   */\n  @Test\n  public void anExampleTest_withSomeConditions_shouldPass() {\n    // Use RouterHelper to drive your router's lifecycle.\n    RouterHelper.attach(router);\n    RouterHelper.detach(router);\n\n    throw new RuntimeException(\"Remove this test and add real tests.\");\n  }\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/partials/RibInteractorDidBecomeActive.java.partial",
    "content": "  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    // TODO: Add attachment logic here (RxSubscriptions, etc.).\n  }\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/partials/RibInteractorWillResignActive.java.partial",
    "content": "  @Override\n  protected void willResignActive() {\n    super.willResignActive();\n\n    // TODO: Perform any required clean up here, or delete this method entirely if not needed.\n  }\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/java/RibBuilder.java.template",
    "content": "package ${package_name};\n\nimport androidx.annotation.NonNull;\n\nimport com.uber.rib.core.Builder;\nimport com.uber.rib.core.EmptyPresenter;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport java.lang.annotation.Retention;\n\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\nimport dagger.Provides;\nimport dagger.BindsInstance;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\npublic class ${rib_name}Builder extends Builder<${rib_name}Router, ${rib_name}Builder.ParentComponent> {\n\n  public ${rib_name}Builder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link ${rib_name}Router}.\n   *\n   * @return a new {@link ${rib_name}Router}.\n   */\n  public ${rib_name}Router build() {\n    ${rib_name}Interactor interactor = new ${rib_name}Interactor();\n    Component component = Dagger${rib_name}Builder_Component.builder()\n        .parentComponent(getDependency())\n        .interactor(interactor)\n        .build();\n\n    return component.${rib_name_to_lower}Router();\n  }\n\n  public interface ParentComponent {\n    // TODO: Define dependencies required from your parent interactor here.\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @${rib_name}Scope\n    @Provides\n    static EmptyPresenter presenter() {\n      return new EmptyPresenter();\n    }\n\n    @${rib_name}Scope\n    @Provides\n    static ${rib_name}Router router(Component component, ${rib_name}Interactor interactor) {\n      return new ${rib_name}Router(interactor, component);\n    }\n\n    // TODO: Create provider methods for dependencies created by this Rib. These methods should be static.\n  }\n\n  @${rib_name}Scope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  public interface Component extends InteractorBaseComponent<${rib_name}Interactor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n      @BindsInstance\n      Builder interactor(${rib_name}Interactor interactor);\n      Builder parentComponent(ParentComponent component);\n      Component build();\n    }\n\n  }\n\n  interface BuilderComponent {\n    ${rib_name}Router ${rib_name_to_lower}Router();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface ${rib_name}Scope { }\n\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface ${rib_name}Internal { }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/java/RibInteractorWithEmptyPresenter.java.template",
    "content": "package ${package_name};\n\nimport androidx.annotation.Nullable;\n\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.EmptyPresenter;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.core.Router;\n\nimport javax.inject.Inject;\n\n/**\n * Coordinates Business Logic for {@link ${rib_name}Scope}.\n *\n * TODO describe the logic of this scope.\n */\n@RibInteractor\npublic class ${rib_name}Interactor extends Interactor<EmptyPresenter, ${rib_name}Router> {\n\n${partial: RibInteractorDidBecomeActive.java.partial}\n${partial: RibInteractorWillResignActive.java.partial}\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/java/RibInteractorWithEmptyPresenterTest.java.template",
    "content": "package ${package_name};\n\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport com.uber.rib.core.EmptyPresenter;\nimport com.uber.rib.core.InteractorHelper;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class ${rib_name}InteractorTest extends RibTestBasePlaceholder {\n\n  @Mock EmptyPresenter presenter;\n  @Mock ${rib_name}Router router;\n\n  private ${rib_name}Interactor interactor;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    interactor = Test${rib_name}Interactor.create();\n  }\n\n${partial: ExampleInteractorTest.java.partial}\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/java/RibInteractorWithPresenter.java.template",
    "content": "package ${package_name};\n\nimport androidx.annotation.Nullable;\n\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.core.Presenter;\nimport com.uber.rib.core.Router;\n\nimport javax.inject.Inject;\n\n/**\n * Coordinates Business Logic for {@link ${rib_name}Scope}.\n *\n * TODO describe the logic of this scope.\n */\n@RibInteractor\npublic class ${rib_name}Interactor\n    extends Interactor<${rib_name}Interactor.${rib_name}Presenter, ${rib_name}Router> {\n\n  @Inject ${rib_name}Presenter presenter;\n\n${partial: RibInteractorDidBecomeActive.java.partial}\n${partial: RibInteractorWillResignActive.java.partial}\n\n  /**\n   * Presenter interface implemented by this RIB's view.\n   */\n  interface ${rib_name}Presenter { }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/java/RibInteractorWithPresenterTest.java.template",
    "content": "package ${package_name};\n\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport com.uber.rib.core.InteractorHelper;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class ${rib_name}InteractorTest extends RibTestBasePlaceholder {\n\n  @Mock ${rib_name}Interactor.${rib_name}Presenter presenter;\n  @Mock ${rib_name}Router router;\n\n  private ${rib_name}Interactor interactor;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    interactor = Test${rib_name}Interactor.create(presenter);\n  }\n\n${partial: ExampleInteractorTest.java.partial}\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/java/RibRouter.java.template",
    "content": "package ${package_name};\n\nimport androidx.annotation.NonNull;\nimport android.view.View;\n\nimport com.uber.rib.core.Router;\n\n/**\n * Adds and removes children of {@link ${rib_name}Builder.${rib_name}Scope}.\n *\n * TODO describe the possible child configurations of this scope.\n */\npublic class ${rib_name}Router\n    extends Router<${rib_name}Interactor> {\n\n  public ${rib_name}Router(\n      ${rib_name}Interactor interactor,\n      ${rib_name}Builder.Component component) {\n    super(interactor, component);\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/java/RibRouterTest.java.template",
    "content": "package ${package_name};\n\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport com.uber.rib.core.RouterHelper;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class ${rib_name}RouterTest extends RibTestBasePlaceholder {\n\n  @Mock ${rib_name}Builder.Component component;\n  @Mock ${rib_name}Interactor interactor;\n\n  private ${rib_name}Router router;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    router = new ${rib_name}Router(interactor, component);\n  }\n\n${partial: ExampleRouterTest.java.partial}\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/java/RibView.java.template",
    "content": "package ${package_name};\n\nimport android.content.Context;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport android.util.AttributeSet;\nimport android.view.View;\n\n/**\n * Top level view for {@link ${rib_name}Builder.${rib_name}Scope}.\n */\nclass ${rib_name}View extends View implements ${rib_name}Interactor.${rib_name}Presenter {\n\n  public ${rib_name}View(Context context) {\n    this(context, null);\n  }\n\n  public ${rib_name}View(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public ${rib_name}View(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/java/RibViewBuilder.java.template",
    "content": "package ${package_name};\n\nimport androidx.annotation.NonNull;\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\n\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport java.lang.annotation.Retention;\n\nimport javax.inject.Scope;\nimport javax.inject.Qualifier;\n\nimport dagger.Provides;\nimport dagger.Binds;\nimport dagger.BindsInstance;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\n/**\n * Builder for the {@link ${rib_name}Scope}.\n *\n * TODO describe this scope's responsibility as a whole.\n */\npublic class ${rib_name}Builder\n    extends ViewBuilder<${rib_name}View, ${rib_name}Router, ${rib_name}Builder.ParentComponent> {\n\n  public ${rib_name}Builder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link ${rib_name}Router}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link ${rib_name}Router}.\n   */\n  public ${rib_name}Router build(ViewGroup parentViewGroup) {\n    ${rib_name}View view = createView(parentViewGroup);\n    ${rib_name}Interactor interactor = new ${rib_name}Interactor();\n    Component component = Dagger${rib_name}Builder_Component.builder()\n        .parentComponent(getDependency())\n        .view(view)\n        .interactor(interactor)\n        .build();\n    return component.${rib_name_to_lower}Router();\n  }\n\n  @Override\n  protected ${rib_name}View inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    // TODO: Inflate a new view using the provided inflater, or create a new view programatically using the\n    // provided context from the parentViewGroup.\n    return null;\n  }\n\n  public interface ParentComponent {\n    // TODO: Define dependencies required from your parent interactor here.\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @${rib_name}Scope\n    @Binds\n    abstract ${rib_name}Interactor.${rib_name}Presenter presenter(${rib_name}View view);\n\n    @${rib_name}Scope\n    @Provides\n    static ${rib_name}Router router(\n      Component component,\n      ${rib_name}View view,\n      ${rib_name}Interactor interactor) {\n      return new ${rib_name}Router(view, interactor, component);\n    }\n\n    // TODO: Create provider methods for dependencies created by this Rib. These should be static.\n  }\n\n  @${rib_name}Scope\n  @dagger.Component(modules = Module.class,\n       dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<${rib_name}Interactor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n      @BindsInstance\n      Builder interactor(${rib_name}Interactor interactor);\n      @BindsInstance\n      Builder view(${rib_name}View view);\n      Builder parentComponent(ParentComponent component);\n      Component build();\n    }\n  }\n\n  interface BuilderComponent  {\n    ${rib_name}Router ${rib_name_to_lower}Router();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface ${rib_name}Scope { }\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface ${rib_name}Internal { }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/java/RibViewRouter.java.template",
    "content": "package ${package_name};\n\nimport androidx.annotation.NonNull;\nimport android.view.View;\n\nimport com.uber.rib.core.ViewRouter;\n\n/**\n * Adds and removes children of {@link ${rib_name}Builder.${rib_name}Scope}.\n *\n * TODO describe the possible child configurations of this scope.\n */\npublic class ${rib_name}Router extends\n    ViewRouter<${rib_name}View, ${rib_name}Interactor, ${rib_name}Builder.Component> {\n\n  public ${rib_name}Router(\n      ${rib_name}View view,\n      ${rib_name}Interactor interactor,\n      ${rib_name}Builder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/java/RibViewRouterTest.java.template",
    "content": "package ${package_name};\n\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport com.uber.rib.core.RouterHelper;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class ${rib_name}RouterTest extends RibTestBasePlaceholder {\n\n  @Mock ${rib_name}Builder.Component component;\n  @Mock ${rib_name}Interactor interactor;\n  @Mock ${rib_name}View view;\n\n  private ${rib_name}Router router;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    router = new ${rib_name}Router(view, interactor, component);\n  }\n\n${partial: ExampleRouterTest.java.partial}\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/kotlin/RibBuilder.kt.template",
    "content": "package ${package_name}\n\nimport com.uber.rib.core.Builder\nimport com.uber.rib.core.EmptyPresenter\nimport com.uber.rib.core.InteractorBaseComponent\nimport java.lang.annotation.Retention\n\nimport javax.inject.Qualifier\nimport javax.inject.Scope\n\nimport dagger.Provides\nimport dagger.BindsInstance\n\nimport java.lang.annotation.RetentionPolicy.CLASS\n\nclass ${rib_name}Builder(dependency: ParentComponent) : Builder<${rib_name}Router, ${rib_name}Builder.ParentComponent>(dependency) {\n\n  /**\n   * Builds a new [${rib_name}Router].\n   *\n   * @return a new [${rib_name}Router].\n   */\n  fun build(): ${rib_name}Router {\n    val interactor = ${rib_name}Interactor()\n    val component = Dagger${rib_name}Builder_Component.builder()\n        .parentComponent(dependency)\n        .interactor(interactor)\n        .build()\n\n    return component.${rib_name_to_lower}Router()\n  }\n\n  interface ParentComponent {\n    // TODO: Define dependencies required from your parent interactor here.\n  }\n\n\n  @dagger.Module\n  abstract class Module {\n\n    @dagger.Module\n    companion object {\n\n      @${rib_name}Scope\n      @Provides\n      @JvmStatic\n      internal fun presenter(): EmptyPresenter {\n        return EmptyPresenter()\n      }\n\n      @${rib_name}Scope\n      @Provides\n      @JvmStatic\n      internal fun router(component: Component, interactor: ${rib_name}Interactor): ${rib_name}Router {\n        return ${rib_name}Router(interactor, component)\n      }\n\n      // TODO: Create provider methods for dependencies created by this Rib. These methods should be static.\n    }\n  }\n\n\n  @${rib_name}Scope\n  @dagger.Component(modules = arrayOf(Module::class), dependencies = arrayOf(ParentComponent::class))\n  interface Component : InteractorBaseComponent<${rib_name}Interactor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n      @BindsInstance\n      fun interactor(interactor: ${rib_name}Interactor): Builder\n\n      fun parentComponent(component: ParentComponent): Builder\n      fun build(): Component\n    }\n\n  }\n\n  interface BuilderComponent {\n    fun ${rib_name_to_lower}Router(): ${rib_name}Router\n  }\n\n  @Scope\n  @Retention(CLASS)\n  internal annotation class ${rib_name}Scope\n\n\n  @Qualifier\n  @Retention(CLASS)\n  internal annotation class ${rib_name}Internal\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/kotlin/RibInteractorWithEmptyPresenter.kt.template",
    "content": "package ${package_name}\n\nimport com.uber.rib.core.Bundle\nimport com.uber.rib.core.EmptyPresenter\nimport com.uber.rib.core.Interactor\nimport com.uber.rib.core.RibInteractor\nimport com.uber.rib.core.Router\n\nimport javax.inject.Inject\n\n/**\n * Coordinates Business Logic for [${rib_name}Scope].\n *\n * TODO describe the logic of this scope.\n */\n@RibInteractor\nclass ${rib_name}Interactor : Interactor<EmptyPresenter, ${rib_name}Router>() {\n\n  override fun didBecomeActive(savedInstanceState: Bundle?) {\n    super.didBecomeActive(savedInstanceState)\n\n    // TODO: Add attachment logic here (RxSubscriptions, etc.).\n  }\n\n  override fun willResignActive() {\n    super.willResignActive()\n\n    // TODO: Perform any required clean up here, or delete this method entirely if not needed.\n  }\n\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/kotlin/RibInteractorWithEmptyPresenterTest.kt.template",
    "content": "package ${package_name}\n\nimport com.uber.rib.core.RibTestBasePlaceholder\nimport com.uber.rib.core.EmptyPresenter\nimport com.uber.rib.core.InteractorHelper\n\nimport org.junit.Before\nimport org.junit.Test\nimport org.mockito.Mock\nimport org.mockito.MockitoAnnotations\n\nclass ${rib_name}InteractorTest : RibTestBasePlaceholder() {\n\n  @Mock internal lateinit var presenter: EmptyPresenter\n  @Mock internal lateinit var router: ${rib_name}Router\n\n  private var interactor: ${rib_name}Interactor? = null\n\n  @Before\n  fun setup() {\n    MockitoAnnotations.initMocks(this)\n\n    interactor = Test${rib_name}Interactor.create()\n  }\n\n  /**\n   * TODO: Delete this example and add real tests.\n   */\n  @Test\n  fun anExampleTest_withSomeConditions_shouldPass() {\n    // Use InteractorHelper to drive your interactor's lifecycle.\n    InteractorHelper.attach(interactor!!, presenter, router, null)\n    InteractorHelper.detach(interactor!!)\n\n    throw RuntimeException(\"Remove this test and add real tests.\")\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/kotlin/RibInteractorWithPresenter.kt.template",
    "content": "package ${package_name}\n\nimport com.uber.rib.core.Bundle\nimport com.uber.rib.core.Interactor\nimport com.uber.rib.core.RibInteractor\nimport javax.inject.Inject\n\n/**\n * Coordinates Business Logic for [${rib_name}Scope].\n *\n * TODO describe the logic of this scope.\n */\n@RibInteractor\nclass ${rib_name}Interactor : Interactor<${rib_name}Interactor.${rib_name}Presenter, ${rib_name}Router>() {\n\n  @Inject\n  lateinit var presenter: ${rib_name}Presenter\n\n  override fun didBecomeActive(savedInstanceState: Bundle?) {\n    super.didBecomeActive(savedInstanceState)\n\n    // TODO: Add attachment logic here (RxSubscriptions, etc.).\n  }\n\n  override fun willResignActive() {\n    super.willResignActive()\n\n    // TODO: Perform any required clean up here, or delete this method entirely if not needed.\n  }\n\n  /**\n   * Presenter interface implemented by this RIB's view.\n   */\n  interface ${rib_name}Presenter\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/kotlin/RibInteractorWithPresenterTest.kt.template",
    "content": "package ${package_name}\n\nimport com.uber.rib.core.RibTestBasePlaceholder\nimport com.uber.rib.core.InteractorHelper\n\nimport org.junit.Before\nimport org.junit.Test\nimport org.mockito.Mock\nimport org.mockito.MockitoAnnotations\n\nclass ${rib_name}InteractorTest : RibTestBasePlaceholder() {\n\n  @Mock internal lateinit var presenter: ${rib_name}Interactor.${rib_name}Presenter\n  @Mock internal lateinit var router: ${rib_name}Router\n\n  private var interactor: ${rib_name}Interactor? = null\n\n  @Before\n  fun setup() {\n    MockitoAnnotations.initMocks(this)\n\n    interactor = Test${rib_name}Interactor.create(presenter)\n  }\n\n  /**\n   * TODO: Delete this example and add real tests.\n   */\n  @Test\n  fun anExampleTest_withSomeConditions_shouldPass() {\n    // Use InteractorHelper to drive your interactor's lifecycle.\n    InteractorHelper.attach<${rib_name}Interactor.${rib_name}Presenter, ${rib_name}Router>(interactor!!, presenter, router, null)\n    InteractorHelper.detach(interactor!!)\n\n    throw RuntimeException(\"Remove this test and add real tests.\")\n  }\n}"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/kotlin/RibRouter.kt.template",
    "content": "package ${package_name}\n\nimport com.uber.rib.core.Router\n\n/**\n * Adds and removes children of {@link ${rib_name}Builder.${rib_name}Scope}.\n *\n * TODO describe the possible child configurations of this scope.\n */\nclass ${rib_name}Router(\n    interactor: ${rib_name}Interactor,\n    component: ${rib_name}Builder.Component) : Router<${rib_name}Interactor, ${rib_name}Builder.Component>(interactor, component)\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/kotlin/RibRouterTest.kt.template",
    "content": "package ${package_name}\n\nimport com.uber.rib.core.RibTestBasePlaceholder\nimport com.uber.rib.core.RouterHelper\n\nimport org.junit.Before\nimport org.junit.Test\nimport org.mockito.Mock\nimport org.mockito.MockitoAnnotations\n\nclass ${rib_name}RouterTest : RibTestBasePlaceholder() {\n\n  @Mock internal lateinit var component: ${rib_name}Builder.Component\n  @Mock internal lateinit var interactor: ${rib_name}Interactor\n\n  private var router: ${rib_name}Router? = null\n\n  @Before\n  fun setup() {\n    MockitoAnnotations.initMocks(this)\n\n    router = ${rib_name}Router(interactor, component)\n  }\n\n  /**\n   * TODO: Delete this example and add real tests.\n   */\n  @Test\n  fun anExampleTest_withSomeConditions_shouldPass() {\n    // Use RouterHelper to drive your router's lifecycle.\n    RouterHelper.attach(router!!)\n    RouterHelper.detach(router!!)\n\n    throw RuntimeException(\"Remove this test and add real tests.\")\n  }\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/kotlin/RibView.kt.template",
    "content": "package ${package_name}\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.View\n\n/**\n * Top level view for {@link ${rib_name}Builder.${rib_name}Scope}.\n */\nclass ${rib_name}View @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : View(context, attrs, defStyle), ${rib_name}Interactor.${rib_name}Presenter\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/kotlin/RibViewBuilder.kt.template",
    "content": "package ${package_name}\n\nimport android.view.LayoutInflater\nimport android.view.ViewGroup\nimport com.uber.rib.core.InteractorBaseComponent\nimport com.uber.rib.core.ViewBuilder\nimport dagger.Binds\nimport dagger.BindsInstance\nimport dagger.Provides\nimport java.lang.annotation.Retention\nimport java.lang.annotation.RetentionPolicy.CLASS\nimport javax.inject.Qualifier\nimport javax.inject.Scope\n\n/**\n * Builder for the {@link ${rib_name}Scope}.\n *\n * TODO describe this scope's responsibility as a whole.\n */\nclass ${rib_name}Builder(dependency: ParentComponent) : ViewBuilder<${rib_name}View, ${rib_name}Router, ${rib_name}Builder.ParentComponent>(dependency) {\n\n  /**\n   * Builds a new [${rib_name}Router].\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new [${rib_name}Router].\n   */\n  fun build(parentViewGroup: ViewGroup): ${rib_name}Router {\n    val view = createView(parentViewGroup)\n    val interactor = ${rib_name}Interactor()\n    val component = Dagger${rib_name}Builder_Component.builder()\n        .parentComponent(dependency)\n        .view(view)\n        .interactor(interactor)\n        .build()\n    return component.${rib_name_to_lower}Router()\n  }\n\n  override fun inflateView(inflater: LayoutInflater, parentViewGroup: ViewGroup): ${rib_name}View? {\n    // TODO: Inflate a new view using the provided inflater, or create a new view programatically using the\n    // provided context from the parentViewGroup.\n    return null\n  }\n\n  interface ParentComponent {\n    // TODO: Define dependencies required from your parent interactor here.\n  }\n\n  @dagger.Module\n  abstract class Module {\n\n    @${rib_name}Scope\n    @Binds\n    internal abstract fun presenter(view: ${rib_name}View): ${rib_name}Interactor.${rib_name}Presenter\n\n    @dagger.Module\n    companion object {\n\n      @${rib_name}Scope\n      @Provides\n      @JvmStatic\n      internal fun router(\n          component: Component,\n          view: ${rib_name}View,\n          interactor: ${rib_name}Interactor): ${rib_name}Router {\n        return ${rib_name}Router(view, interactor, component)\n      }\n    }\n\n    // TODO: Create provider methods for dependencies created by this Rib. These should be static.\n  }\n\n  @${rib_name}Scope\n  @dagger.Component(modules = arrayOf(Module::class), dependencies = arrayOf(ParentComponent::class))\n  interface Component : InteractorBaseComponent<${rib_name}Interactor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n      @BindsInstance\n      fun interactor(interactor: ${rib_name}Interactor): Builder\n\n      @BindsInstance\n      fun view(view: ${rib_name}View): Builder\n\n      fun parentComponent(component: ParentComponent): Builder\n      fun build(): Component\n    }\n  }\n\n  interface BuilderComponent {\n    fun ${rib_name_to_lower}Router(): ${rib_name}Router\n  }\n\n  @Scope\n  @Retention(CLASS)\n  internal annotation class ${rib_name}Scope\n\n  @Qualifier\n  @Retention(CLASS)\n  internal annotation class ${rib_name}Internal\n}\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/kotlin/RibViewRouter.kt.template",
    "content": "package ${package_name}\n\nimport android.view.View\n\nimport com.uber.rib.core.ViewRouter\n\n/**\n * Adds and removes children of {@link ${rib_name}Builder.${rib_name}Scope}.\n *\n * TODO describe the possible child configurations of this scope.\n */\nclass ${rib_name}Router(\n    view: ${rib_name}View,\n    interactor: ${rib_name}Interactor,\n    component: ${rib_name}Builder.Component) : ViewRouter<${rib_name}View, ${rib_name}Interactor, ${rib_name}Builder.Component>(view, interactor, component)\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/main/resources/templates/kotlin/RibViewRouterTest.kt.template",
    "content": "package ${package_name}\n\nimport com.uber.rib.core.RibTestBasePlaceholder\nimport com.uber.rib.core.RouterHelper\n\nimport org.junit.Before\nimport org.junit.Test\nimport org.mockito.Mock\nimport org.mockito.MockitoAnnotations\n\nclass ${rib_name}RouterTest : RibTestBasePlaceholder() {\n\n  @Mock internal lateinit var component: ${rib_name}Builder.Component\n  @Mock internal lateinit var interactor: ${rib_name}Interactor\n  @Mock internal lateinit var view: ${rib_name}View\n\n  private var router: ${rib_name}Router? = null\n\n  @Before\n  fun setup() {\n    MockitoAnnotations.initMocks(this)\n\n    router = ${rib_name}Router(view, interactor, component)\n  }\n\n  /**\n   * TODO: Delete this example and add real tests.\n   */\n  @Test\n  fun anExampleTest_withSomeConditions_shouldPass() {\n    // Use RouterHelper to drive your router's lifecycle.\n    RouterHelper.attach(router!!)\n    RouterHelper.detach(router!!)\n\n    throw RuntimeException(\"Remove this test and add real tests.\")\n  }\n\n}\n\n"
  },
  {
    "path": "tooling/rib-intellij-plugin/src/test/java/com/uber/presidio/intellij_plugin/action/rib/RibGeneratorsTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.presidio.intellij_plugin.action.rib;\n\nimport static com.google.common.truth.Truth.assert_;\nimport static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;\n\nimport androidx.annotation.NonNull;\nimport com.google.testing.compile.JavaFileObjects;\nimport com.uber.presidio.intellij_plugin.generator.Generator;\nimport com.uber.presidio.intellij_plugin.generator.GeneratorPair;\nimport com.uber.rib.compiler.RibTestProcessor;\nimport dagger.internal.codegen.ComponentProcessor;\nimport java.util.ArrayList;\nimport java.util.List;\nimport javax.tools.JavaFileObject;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\npublic class RibGeneratorsTest {\n\n  private static final String TEST_PACKAGE_NAME = \"com.test\";\n  private static final String TEST_RIBLET_NAME = \"TestRib\";\n\n  @Test\n  @Ignore(\"https://github.com/JetBrains/gradle-intellij-plugin/issues/1452\")\n  public void ribGenerators_shouldGenerateClassesThatCompiler() {\n    testWithGenerators(\n        Generators.getGeneratorsForRibWithoutPresenterAndView(\n            TEST_PACKAGE_NAME, TEST_RIBLET_NAME, false));\n  }\n\n  @Test\n  @Ignore(\n      \"This test only passes when using buck+okbuck. Gradle is unable to import the rib-android aar as a test \"\n          + \"dependency for this java module.\")\n  public void viewRibGenerators_shouldGenerateClassesThatCompile() {\n    testWithGenerators(\n        Generators.getGeneratorsForRibWithPresenterAndView(\n            TEST_PACKAGE_NAME, TEST_RIBLET_NAME, false));\n  }\n\n  private static void testWithGenerators(@NonNull GeneratorPair generators) {\n    List<JavaFileObject> javaFileObjects = new ArrayList<JavaFileObject>();\n\n    generateSourceFiles(javaFileObjects, generators.getMainSourceSetGenerators());\n    generateSourceFiles(javaFileObjects, generators.getTestSourceSetGenerators());\n\n    assert_()\n        .about(javaSources())\n        .that(javaFileObjects)\n        .processedWith(new ComponentProcessor(), new RibTestProcessor())\n        .compilesWithoutError();\n  }\n\n  private static void generateSourceFiles(\n      List<JavaFileObject> javaFileObjects, List<Generator> generators) {\n    for (Generator generator : generators) {\n      javaFileObjects.add(\n          JavaFileObjects.forSourceString(\n              String.format(\"%s.%s\", generator.getPackageName(), generator.getClassName()),\n              generator.generate()));\n    }\n  }\n}\n"
  },
  {
    "path": "tooling/utils/intellij-broadcast-core/README.md",
    "content": "# Intellij Broadcast Core Debugging tool\n\nThis Android library enables communicating between Intellij plugin(s) and applications running on Android device.\n\nIt does so via a custom broadcast intent receiver, which writes serialized chunked response to standard logcat in\nresponse to broadcast commands emitted by IntelliJ plugin. IntelliJ plugin also queries and aggregates logcat logs, to\nformat and deserialize device's response.\n\nPractically, this enables 2-way communication between Intellij plugin and Android device. We are using it, for example,\nto query RIBs hierarchy data from running application and display it in IntelliJ.\n"
  },
  {
    "path": "tooling/utils/intellij-broadcast-core/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nplugins {\n    id(\"ribs.android.library\")\n    alias(libs.plugins.maven.publish)\n}\n\nandroid {\n    namespace = \"com.uber.debug.broadcast.core\"\n}\n\ndependencies {\n    api(project(\":libraries:rib-android\"))\n    api(project(\":libraries:rib-android-core\"))\n    api(project(\":libraries:rib-base\"))\n    api(libs.rxkotlin)\n    api(libs.rxrelay2)\n    api(libs.rxjava2)\n    implementation(libs.androidx.annotation)\n    implementation(libs.androidx.appcompat)\n    implementation(libs.guava.android)\n    implementation(libs.gson)\n}\n"
  },
  {
    "path": "tooling/utils/intellij-broadcast-core/gradle.properties",
    "content": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nPOM_NAME=RIBs (IJ Plugin Native)\nPOM_ARTIFACT_ID=rib-ij-plugin-native\nPOM_PACKAGING=jar\n"
  },
  {
    "path": "tooling/utils/intellij-broadcast-core/src/main/java/com/uber/debug/broadcast/core/AckDebugBroadcastHandler.java",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.debug.broadcast.core;\n\nimport com.uber.debug.broadcast.core.DebugBroadcastReceiver.Handler;\n\n/*\n * Simple example handler for responding to 'ACK' command requests.\n */\npublic class AckDebugBroadcastHandler implements Handler<String> {\n\n  static final String COMMAND_ACK = \"ACK\";\n\n  public AckDebugBroadcastHandler() {}\n\n  @Override\n  public boolean canHandle(DebugBroadcastRequest request) {\n    return request.isCommand(COMMAND_ACK);\n  }\n\n  @Override\n  public void handle(DebugBroadcastRequest request) {\n    request.respond(\"Hello\");\n  }\n}\n"
  },
  {
    "path": "tooling/utils/intellij-broadcast-core/src/main/java/com/uber/debug/broadcast/core/DebugBroadcastReceiver.java",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.debug.broadcast.core;\n\nimport android.app.ActivityManager;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/*\n * Broadcast receiver used to debugging purpose only. It outputs in logcat the response of command\n * sent via Intent, using a unique tag (which is used as a filter).\n *\n * It is currently used for IntelliJ plugins to communicate with running application. This\n * implementation is likely to be replaced by socket-based communication in the future.\n */\npublic class DebugBroadcastReceiver extends BroadcastReceiver {\n\n  public static final String ACTION_DEBUG_COMMAND = \"com.uber.debug.intent.action.COMMAND\";\n\n  private static final IntentFilter intent = new IntentFilter(ACTION_DEBUG_COMMAND);\n  private static final List<Handler> handlers = new ArrayList<>();\n\n  public static void initWithDefaults(Context context, List<Handler> initialHandlers) {\n    handlers.add(new AckDebugBroadcastHandler());\n    for (Handler handler : initialHandlers) {\n      handlers.add(handler);\n    }\n    DebugBroadcastReceiver receiver = new DebugBroadcastReceiver();\n    context.registerReceiver(receiver, intent);\n  }\n\n  @Override\n  public void onReceive(Context context, Intent intent) {\n    // Only application is the foreground should respond to intent\n    if (!isAppInForeground(context)) {\n      return;\n    }\n    DebugBroadcastRequest request = DebugBroadcastRequest.from(intent);\n    if (!request.isValid()) {\n      request.error(\"Invalid request\");\n      return;\n    }\n    for (Handler handler : handlers) {\n      if (handler.canHandle(request)) {\n        try {\n          handler.handle(request);\n        } catch (Exception e) {\n          request.error(\"Command error: \" + e.toString());\n        }\n        return;\n      }\n    }\n    request.error(\"Unsupported command\");\n  }\n\n  private boolean isAppInForeground(Context context) {\n    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);\n    List<ActivityManager.RunningAppProcessInfo> runningProcesses = am.getRunningAppProcesses();\n    if (runningProcesses != null) {\n      for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {\n        if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {\n          for (String activeProcess : processInfo.pkgList) {\n            if (activeProcess.equals(context.getPackageName())) {\n              return true;\n            }\n          }\n        }\n      }\n    }\n    return false;\n  }\n\n  public interface Handler<T> {\n\n    boolean canHandle(DebugBroadcastRequest request);\n\n    void handle(DebugBroadcastRequest request);\n  }\n}\n"
  },
  {
    "path": "tooling/utils/intellij-broadcast-core/src/main/java/com/uber/debug/broadcast/core/DebugBroadcastRequest.java",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.debug.broadcast.core;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.util.Log;\nimport androidx.annotation.Nullable;\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\nimport java.util.Locale;\n\n/** Class representing incoming request to debug broadcast receiver. */\npublic class DebugBroadcastRequest {\n\n  public static final String INTENT_EXTRA_COMMAND = \"CMD\";\n  public static final String INTENT_EXTRA_SEQUENCE = \"SEQ\";\n\n  private static final int LOGCAT_MAX_LENGTH = 3900;\n  private static final String TAG_TEMPLATE = DebugBroadcastReceiver.class.getSimpleName();\n  private static final Gson GSON = new GsonBuilder().create();\n\n  private int sequence;\n  private String command;\n  private @Nullable Bundle extras;\n\n  protected DebugBroadcastRequest(int sequence, @Nullable String command, @Nullable Bundle extras) {\n    this.sequence = sequence;\n    if (command != null) {\n      this.command = command;\n    } else {\n      this.command = \"\";\n    }\n    this.extras = extras;\n  }\n\n  static DebugBroadcastRequest from(Intent intent) {\n    return new DebugBroadcastRequest(\n        intent.getIntExtra(INTENT_EXTRA_SEQUENCE, 0),\n        intent.getStringExtra(INTENT_EXTRA_COMMAND),\n        intent.getExtras());\n  }\n\n  /**\n   * Returns the command associated with this request.\n   *\n   * @return the command.\n   */\n  public String getCommand() {\n    return command;\n  }\n\n  /**\n   * Check if supplied command matches this request.\n   *\n   * @param cmd the provided command.\n   * @return whether command matches or not.\n   */\n  public boolean isCommand(String cmd) {\n    return command.equalsIgnoreCase(cmd);\n  }\n\n  /**\n   * Returns additional data for this request.\n   *\n   * @param name the name of the extra data.\n   * @return additional data for this request.\n   */\n  public String getStringExtra(String name) {\n    if (extras != null) {\n      return extras.getString(name, \"\");\n    }\n    return \"\";\n  }\n\n  /**\n   * Returns whether command is valid or not.\n   *\n   * @return whether request is valid or not.\n   */\n  public boolean isValid() {\n    return sequence > 0 && !command.isEmpty();\n  }\n\n  /**\n   * Provides response to this request.\n   *\n   * @param payload the response's payload\n   */\n  public void respond(Object payload) {\n    writeResponse(sequence, payload, null);\n  }\n\n  /**\n   * Provides error response to this request.\n   *\n   * @param description the error description\n   */\n  public void error(String description) {\n    writeResponse(sequence, new Object(), description);\n  }\n\n  /**\n   * Gets the tag string associated with this request's response.\n   *\n   * @param sequence the request response\n   * @return the tag\n   */\n  static String getTag(int sequence) {\n    return TAG_TEMPLATE + \"[\" + sequence + \"]\";\n  }\n\n  /**\n   * Write response to logcat, splitting message into multiple messages to accomodate for logcat\n   * message length limitation (4 * 1024).\n   *\n   * @param sequence the response sequence\n   * @param payload the response payload\n   * @param errorDescription friendly error description, in case request failed\n   */\n  @SuppressWarnings({\"LogCat\", \"LogConditional\"})\n  private static void writeResponse(\n      int sequence, Object payload, @Nullable String errorDescription) {\n    String tag = getTag(sequence);\n    DebugBroadcastResponse result = new DebugBroadcastResponse(payload);\n    if (errorDescription != null) {\n      result.setErrorDescription(errorDescription);\n    }\n    String response = GSON.toJson(result);\n    int partNumber = 0;\n    int partCount = (response.length() / LOGCAT_MAX_LENGTH) + 1;\n    while (partNumber++ <= partCount) {\n      int leftover = Math.min(response.length(), LOGCAT_MAX_LENGTH);\n      String part = response.substring(0, leftover);\n      response = response.substring(leftover);\n      String prefix = String.format(Locale.US, \"%d/%d\", partNumber, partCount);\n      Log.d(tag, prefix + \" \" + part);\n    }\n  }\n}\n"
  },
  {
    "path": "tooling/utils/intellij-broadcast-core/src/main/java/com/uber/debug/broadcast/core/DebugBroadcastResponse.java",
    "content": "/*\n * Copyright (C) 2021. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.debug.broadcast.core;\n\nimport androidx.annotation.Nullable;\n\n/*\n * Data class for broadcast receiver responses.\n */\npublic class DebugBroadcastResponse<T> {\n\n  public static int VERSION = 1;\n\n  Boolean success;\n  int version;\n  @Nullable private String errorDescription;\n  @Nullable private T payload;\n\n  public DebugBroadcastResponse(T payload) {\n    this.success = true;\n    this.version = VERSION;\n    this.errorDescription = null;\n    this.payload = payload;\n  }\n\n  public void setErrorDescription(String errorDescription) {\n    this.success = false;\n    this.errorDescription = errorDescription;\n  }\n\n  @Nullable\n  public T getPayload() {\n    return payload;\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial1/README.md",
    "content": "# RIB Tutorial 1: Create a RIB\n\nThis project is the starting point for [tutorial 1](https://github.com/uber/RIBs/wiki/Android-Tutorial-1) that will get you aquatinted with the RIBs architecture.\n\n\n### Getting started\nRun the code by\n\n```\n./gradlew :tutorials:tutorial1:installDebug\n```\n\nThen follow the steps described in [tutorial 1](https://github.com/uber/RIBs/wiki/Android-Tutorial-1) on the RIBs wiki.\n"
  },
  {
    "path": "tutorials/tutorial1/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nplugins {\n    id(\"ribs.android.application.errorprone\")\n    alias(libs.plugins.kotlin.kapt)\n}\n\nandroid {\n    namespace = \"com.uber.rib.tutorial1\"\n\n    defaultConfig {\n        applicationId = \"com.uber.tutorial1\"\n    }\n}\n\ndependencies {\n    kapt(project(\":libraries:rib-compiler-test\"))\n    ksp(libs.dagger.compiler)\n    implementation(project(\":libraries:rib-android\"))\n    implementation(libs.androidx.appcompat)\n    implementation(libs.dagger.library)\n    implementation(libs.rxbinding)\n    compileOnly(appLibs.jsr250)\n    testImplementation(project(\":libraries:rib-test\"))\n}\n"
  },
  {
    "path": "tutorials/tutorial1/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <uses-feature android:required=\"true\" android:glEsVersion='0x00020000'/>\n\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>\n\n    <application\n        android:name=\"com.uber.rib.SampleApplication\"\n        android:allowBackup=\"false\"\n        android:icon=\"@drawable/ub__ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:screenOrientation=\"portrait\"\n        android:theme=\"@style/Theme.AppCompat.Light\"\n        tools:replace=\"android:theme\">\n        <activity\n            android:name=\"com.uber.rib.RootActivity\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n    </application>\n</manifest>\n"
  },
  {
    "path": "tutorials/tutorial1/src/main/java/com/uber/rib/RootActivity.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib;\n\nimport android.view.ViewGroup;\nimport com.uber.rib.core.RibActivity;\nimport com.uber.rib.core.ViewRouter;\nimport com.uber.rib.root.RootBuilder;\n\n/** The sample app's single activity. */\npublic class RootActivity extends RibActivity {\n\n  @SuppressWarnings(\"unchecked\")\n  @Override\n  protected ViewRouter<?, ?> createRouter(ViewGroup parentViewGroup) {\n    RootBuilder rootBuilder = new RootBuilder(new RootBuilder.ParentComponent() {});\n    return rootBuilder.build(parentViewGroup);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial1/src/main/java/com/uber/rib/SampleApplication.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib;\n\nimport android.app.Application;\n\npublic class SampleApplication extends Application {}\n"
  },
  {
    "path": "tutorials/tutorial1/src/main/java/com/uber/rib/root/RootBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.tutorial1.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Scope;\n\n/** Builder for the {@link RootScope}. */\npublic class RootBuilder extends ViewBuilder<RootView, RootRouter, RootBuilder.ParentComponent> {\n\n  public RootBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link RootRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link RootRouter}.\n   */\n  public RootRouter build(ViewGroup parentViewGroup) {\n    RootView view = createView(parentViewGroup);\n    RootInteractor interactor = new RootInteractor();\n    Component component =\n        DaggerRootBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.rootRouter();\n  }\n\n  @Override\n  protected RootView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (RootView) inflater.inflate(R.layout.root_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n    // Define dependencies required from your parent interactor here.\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @RootScope\n    @Binds\n    abstract RootInteractor.RootPresenter presenter(RootView view);\n\n    @RootScope\n    @Provides\n    static RootRouter router(Component component, RootView view, RootInteractor interactor) {\n      return new RootRouter(view, interactor, component);\n    }\n  }\n\n  @RootScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<RootInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n      @BindsInstance\n      Builder interactor(RootInteractor interactor);\n\n      @BindsInstance\n      Builder view(RootView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n    RootRouter rootRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface RootScope {}\n}\n"
  },
  {
    "path": "tutorials/tutorial1/src/main/java/com/uber/rib/root/RootInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link RootBuilder.RootScope}. */\n@RibInteractor\npublic class RootInteractor extends Interactor<RootInteractor.RootPresenter, RootRouter> {\n\n  @Inject RootPresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    // Add attachment logic here (RxSubscriptions, etc.).\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface RootPresenter {}\n}\n"
  },
  {
    "path": "tutorials/tutorial1/src/main/java/com/uber/rib/root/RootRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link RootBuilder.RootScope}. */\npublic class RootRouter extends ViewRouter<RootView, RootInteractor> {\n\n  RootRouter(RootView view, RootInteractor interactor, RootBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial1/src/main/java/com/uber/rib/root/RootView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.FrameLayout;\nimport androidx.annotation.Nullable;\n\n/** Top level view for {@link RootBuilder.RootScope}. */\npublic class RootView extends FrameLayout implements RootInteractor.RootPresenter {\n\n  public RootView(Context context) {\n    this(context, null);\n  }\n\n  public RootView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public RootView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial1/src/main/res/layout/root_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.RootView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"/>\n"
  },
  {
    "path": "tutorials/tutorial1/src/main/res/values/ub__strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">RIB Tutorial 1</string>\n</resources>\n"
  },
  {
    "path": "tutorials/tutorial1/src/test/java/com/uber/rib/root/RootInteractorTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\n@SuppressWarnings(\"NullAway\")\npublic class RootInteractorTest {\n\n  @Mock RootInteractor.RootPresenter presenter;\n  @Mock RootRouter router;\n\n  private RootInteractor interactor;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    interactor = TestRootInteractor.create(presenter);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial1/src/test/java/com/uber/rib/root/RootRouterTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\n@SuppressWarnings(\"NullAway\")\npublic class RootRouterTest {\n\n  @Mock RootBuilder.Component component;\n  @Mock RootInteractor interactor;\n  @Mock RootView view;\n\n  private RootRouter router;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    router = new RootRouter(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial2/README.md",
    "content": "# RIB Tutorial 2: Composing RIBs to create features\n\nThis project is the completed code for [tutorial 1](https://github.com/uber/RIBs/wiki/Android-Tutorial-1) and the starting point for [tutorial 2](https://github.com/uber/RIBs/wiki/Android-Tutorial-2).\n\n### Getting started\nRun the code by\n\n```\n./gradlew :tutorials:tutorial2:installDebug\n```\n\nThen follow the steps described in [tutorial 2](https://github.com/uber/RIBs/wiki/Android-Tutorial-2) on the RIBs wiki.\n"
  },
  {
    "path": "tutorials/tutorial2/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport net.ltgt.gradle.errorprone.errorprone\n\nplugins {\n    id(\"ribs.android.application.errorprone\")\n    alias(libs.plugins.kotlin.kapt)\n}\n\nandroid {\n    namespace = \"com.uber.rib.tutorial1\"\n\n    defaultConfig {\n        applicationId = \"com.uber.tutorial2\"\n    }\n}\n\ndependencies {\n    ksp(libs.dagger.compiler)\n    kapt(project(\":libraries:rib-compiler-test\"))\n    implementation(project(\":libraries:rib-android\"))\n    implementation(libs.androidx.appcompat)\n    implementation(appLibs.percent)\n    implementation(libs.dagger.library)\n    implementation(libs.rxbinding)\n    compileOnly(libs.android.api)\n    compileOnly(appLibs.jsr250)\n    testImplementation(project(\":libraries:rib-test\"))\n}\n\ntasks.withType<JavaCompile>().configureEach {\n    // Disable error prone checks I don't want.\n    options.errorprone {\n        disable(\"CheckReturnValue\", \"AutoDispose\")\n    }\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <uses-feature android:required=\"true\" android:glEsVersion='0x00020000'/>\n\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>\n\n    <application\n        android:name=\"com.uber.rib.SampleApplication\"\n        android:allowBackup=\"false\"\n        android:icon=\"@drawable/ub__ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:screenOrientation=\"portrait\">\n        <activity\n            android:name=\"com.uber.rib.RootActivity\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n    </application>\n</manifest>\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/RootActivity.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib;\n\nimport android.view.ViewGroup;\nimport com.uber.rib.core.RibActivity;\nimport com.uber.rib.core.ViewRouter;\nimport com.uber.rib.root.RootBuilder;\n\n/** The sample app's single activity. */\npublic class RootActivity extends RibActivity {\n\n  @SuppressWarnings(\"unchecked\")\n  @Override\n  protected ViewRouter<?, ?> createRouter(ViewGroup parentViewGroup) {\n    RootBuilder rootBuilder = new RootBuilder(new RootBuilder.ParentComponent() {});\n    return rootBuilder.build(parentViewGroup);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/SampleApplication.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib;\n\nimport android.app.Application;\n\npublic class SampleApplication extends Application {}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/RootBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder;\nimport com.uber.rib.tutorial1.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Scope;\n\n/** Builder for the {@link RootScope}. */\npublic class RootBuilder extends ViewBuilder<RootView, RootRouter, RootBuilder.ParentComponent> {\n\n  public RootBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link RootRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link RootRouter}.\n   */\n  public RootRouter build(ViewGroup parentViewGroup) {\n    RootView view = createView(parentViewGroup);\n    RootInteractor interactor = new RootInteractor();\n    Component component =\n        DaggerRootBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.rootRouter();\n  }\n\n  @Override\n  protected RootView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (RootView) inflater.inflate(R.layout.root_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n    // Define dependencies required from your parent interactor here.\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @RootScope\n    @Binds\n    abstract RootInteractor.RootPresenter presenter(RootView view);\n\n    @RootScope\n    @Provides\n    static RootRouter router(Component component, RootView view, RootInteractor interactor) {\n      return new RootRouter(view, interactor, component, new LoggedOutBuilder(component));\n    }\n  }\n\n  @RootScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component\n      extends InteractorBaseComponent<RootInteractor>,\n          LoggedOutBuilder.ParentComponent,\n          BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n      @BindsInstance\n      Builder interactor(RootInteractor interactor);\n\n      @BindsInstance\n      Builder view(RootView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n    RootRouter rootRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface RootScope {}\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/RootInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link RootBuilder.RootScope}. */\n@RibInteractor\npublic class RootInteractor extends Interactor<RootInteractor.RootPresenter, RootRouter> {\n\n  @Inject RootPresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n    getRouter().attachLoggedOut();\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface RootPresenter {}\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/RootRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.ViewRouter;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutRouter;\n\n/** Adds and removes children of {@link RootBuilder.RootScope}. */\npublic class RootRouter extends ViewRouter<RootView, RootInteractor> {\n\n  private final LoggedOutBuilder loggedOutBuilder;\n\n  @Nullable private LoggedOutRouter loggedOutRouter;\n\n  RootRouter(\n      RootView view,\n      RootInteractor interactor,\n      RootBuilder.Component component,\n      LoggedOutBuilder loggedOutBuilder) {\n    super(view, interactor, component);\n    this.loggedOutBuilder = loggedOutBuilder;\n  }\n\n  void attachLoggedOut() {\n    loggedOutRouter = loggedOutBuilder.build(getView());\n    attachChild(loggedOutRouter);\n    getView().addView(loggedOutRouter.getView());\n  }\n\n  public void attachLoggedIn() {}\n\n  public void detachLoggedOut() {}\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/RootView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.FrameLayout;\nimport androidx.annotation.Nullable;\n\n/** Top level view for {@link RootBuilder.RootScope}. */\npublic class RootView extends FrameLayout implements RootInteractor.RootPresenter {\n\n  public RootView(Context context) {\n    this(context, null);\n  }\n\n  public RootView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public RootView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.tutorial1.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link OffGameScope}. */\npublic class OffGameBuilder\n    extends ViewBuilder<OffGameView, OffGameRouter, OffGameBuilder.ParentComponent> {\n\n  public OffGameBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link OffGameRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link OffGameRouter}.\n   */\n  public OffGameRouter build(ViewGroup parentViewGroup) {\n    OffGameView view = createView(parentViewGroup);\n    OffGameInteractor interactor = new OffGameInteractor();\n    Component component =\n        DaggerOffGameBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.offgameRouter();\n  }\n\n  @Override\n  protected OffGameView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (OffGameView) inflater.inflate(R.layout.off_game_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n\n    OffGameInteractor.Listener listener();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @OffGameScope\n    @Binds\n    abstract OffGameInteractor.OffGamePresenter presenter(OffGameView view);\n\n    @OffGameScope\n    @Provides\n    static OffGameRouter router(\n        Component component, OffGameView view, OffGameInteractor interactor) {\n      return new OffGameRouter(view, interactor, component);\n    }\n  }\n\n  @OffGameScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<OffGameInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(OffGameInteractor interactor);\n\n      @BindsInstance\n      Builder view(OffGameView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    OffGameRouter offgameRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface OffGameScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface OffGameInternal {}\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link OffGameScope}. */\n@RibInteractor\npublic class OffGameInteractor\n    extends Interactor<OffGameInteractor.OffGamePresenter, OffGameRouter> {\n\n  @Inject Listener listener;\n  @Inject OffGamePresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    presenter\n        .startGameRequest()\n        .subscribe(\n            new Consumer<Object>() {\n              @Override\n              public void accept(Object object) throws Exception {\n                listener.onStartGame();\n              }\n            });\n  }\n\n  public interface Listener {\n\n    void onStartGame();\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface OffGamePresenter {\n\n    Observable<Object> startGameRequest();\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link OffGameBuilder.OffGameScope}. */\npublic class OffGameRouter extends ViewRouter<OffGameView, OffGameInteractor> {\n\n  public OffGameRouter(\n      OffGameView view, OffGameInteractor interactor, OffGameBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.Button;\nimport android.widget.LinearLayout;\nimport androidx.annotation.Nullable;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.core.Initializer;\nimport com.uber.rib.tutorial1.R;\nimport io.reactivex.Observable;\n\n/** Top level view for {@link OffGameBuilder.OffGameScope}. */\npublic class OffGameView extends LinearLayout implements OffGameInteractor.OffGamePresenter {\n\n  private Button button;\n\n  public OffGameView(Context context) {\n    this(context, null);\n  }\n\n  public OffGameView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public OffGameView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Initializer\n  @Override\n  protected void onFinishInflate() {\n    super.onFinishInflate();\n    button = (Button) findViewById(R.id.start_game_button);\n  }\n\n  @Override\n  public Observable<Object> startGameRequest() {\n    return RxView.clicks(button);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/loggedin/tictactoe/Board.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport javax.inject.Inject;\n\nclass Board {\n\n  static final int ROWS = 3;\n  static final int COLS = 3;\n\n  MarkerType[][] cells;\n  int currentRow;\n  int currentCol;\n\n  @Inject\n  Board() {\n    cells = new MarkerType[ROWS][COLS];\n    for (int row = 0; row < ROWS; ++row) {\n      for (int col = 0; col < COLS; ++col) {\n        cells[row][col] = null;\n      }\n    }\n  }\n\n  /** Return true if it is a draw (i.e., no more EMPTY cell) */\n  boolean isDraw() {\n    for (int row = 0; row < ROWS; ++row) {\n      for (int col = 0; col < COLS; ++col) {\n        if (cells[row][col] == null) {\n          return false;\n        }\n      }\n    }\n    return !hasWon(MarkerType.CROSS) && !hasWon(MarkerType.NOUGHT);\n  }\n\n  /** Return true if the player with \"theSeed\" has won after placing at (currentRow, currentCol) */\n  boolean hasWon(MarkerType theSeed) {\n    return ((cells[currentRow][0] == theSeed\n            && cells[currentRow][1] == theSeed\n            && cells[currentRow][2] == theSeed)\n        || (cells[0][currentCol] == theSeed\n            && cells[1][currentCol] == theSeed\n            && cells[2][currentCol] == theSeed)\n        || (currentRow == currentCol\n            && cells[0][0] == theSeed\n            && cells[1][1] == theSeed\n            && cells[2][2] == theSeed)\n        || (currentRow + currentCol == 2\n            && cells[0][2] == theSeed\n            && cells[1][1] == theSeed\n            && cells[2][0] == theSeed));\n  }\n\n  enum MarkerType {\n    CROSS,\n    NOUGHT\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/loggedin/tictactoe/BoardCoordinate.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nclass BoardCoordinate {\n\n  private final int x;\n  private final int y;\n\n  BoardCoordinate(int x, int y) {\n    this.x = x;\n    this.y = y;\n  }\n\n  int getX() {\n    return x;\n  }\n\n  int getY() {\n    return y;\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.tutorial1.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link TicTacToeScope}. */\npublic class TicTacToeBuilder\n    extends ViewBuilder<TicTacToeView, TicTacToeRouter, TicTacToeBuilder.ParentComponent> {\n\n  public TicTacToeBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link TicTacToeRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link TicTacToeRouter}.\n   */\n  public TicTacToeRouter build(ViewGroup parentViewGroup) {\n    TicTacToeView view = createView(parentViewGroup);\n    TicTacToeInteractor interactor = new TicTacToeInteractor();\n    Component component =\n        DaggerTicTacToeBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.tictactoeRouter();\n  }\n\n  @Override\n  protected TicTacToeView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (TicTacToeView) inflater.inflate(R.layout.tic_tac_toe_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n    // TODO: Define dependencies required from your parent interactor here.\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @TicTacToeScope\n    @Binds\n    abstract TicTacToeInteractor.TicTacToePresenter presenter(TicTacToeView view);\n\n    @TicTacToeScope\n    @Provides\n    static TicTacToeRouter router(\n        Component component, TicTacToeView view, TicTacToeInteractor interactor) {\n      return new TicTacToeRouter(view, interactor, component);\n    }\n  }\n\n  @TicTacToeScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<TicTacToeInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(TicTacToeInteractor interactor);\n\n      @BindsInstance\n      Builder view(TicTacToeView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    TicTacToeRouter tictactoeRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface TicTacToeScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface TicTacToeInternal {}\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedin.tictactoe.Board.MarkerType;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link TicTacToeScope}. */\n@RibInteractor\npublic class TicTacToeInteractor\n    extends Interactor<TicTacToeInteractor.TicTacToePresenter, TicTacToeRouter> {\n\n  @Inject Board board;\n  @Inject TicTacToePresenter presenter;\n\n  private final String playerOne = \"Fake name 1\";\n  private final String playerTwo = \"Fake name 2\";\n\n  private MarkerType currentPlayer = MarkerType.CROSS;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    presenter\n        .squareClicks()\n        .subscribe(\n            new Consumer<BoardCoordinate>() {\n              @Override\n              public void accept(BoardCoordinate xy) throws Exception {\n                if (board.cells[xy.getX()][xy.getY()] == null) {\n                  if (currentPlayer == MarkerType.CROSS) {\n                    board.cells[xy.getX()][xy.getY()] = MarkerType.CROSS;\n                    board.currentRow = xy.getX();\n                    board.currentCol = xy.getY();\n                    presenter.addCross(xy);\n                    currentPlayer = MarkerType.NOUGHT;\n                  } else {\n                    board.cells[xy.getX()][xy.getY()] = MarkerType.NOUGHT;\n                    board.currentRow = xy.getX();\n                    board.currentCol = xy.getY();\n                    presenter.addNought(xy);\n                    currentPlayer = MarkerType.CROSS;\n                  }\n                }\n                if (board.hasWon(MarkerType.CROSS)) {\n                  presenter.setPlayerWon(playerOne);\n                } else if (board.hasWon(MarkerType.NOUGHT)) {\n                  presenter.setPlayerWon(playerTwo);\n                } else if (board.isDraw()) {\n                  presenter.setPlayerTie();\n                } else {\n                  updateCurrentPlayer();\n                }\n              }\n            });\n    updateCurrentPlayer();\n  }\n\n  private void updateCurrentPlayer() {\n    if (currentPlayer == MarkerType.CROSS) {\n      presenter.setCurrentPlayerName(playerOne);\n    } else {\n      presenter.setCurrentPlayerName(playerTwo);\n    }\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface TicTacToePresenter {\n    Observable<BoardCoordinate> squareClicks();\n\n    void setCurrentPlayerName(String currentPlayer);\n\n    void setPlayerWon(String playerName);\n\n    void setPlayerTie();\n\n    void addCross(BoardCoordinate xy);\n\n    void addNought(BoardCoordinate xy);\n  }\n\n  public interface Listener {}\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link TicTacToeBuilder.TicTacToeScope}. */\npublic class TicTacToeRouter extends ViewRouter<TicTacToeView, TicTacToeInteractor> {\n\n  public TicTacToeRouter(\n      TicTacToeView view, TicTacToeInteractor interactor, TicTacToeBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport androidx.percentlayout.widget.PercentRelativeLayout;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.core.Initializer;\nimport com.uber.rib.tutorial1.R;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Function;\nimport java.util.ArrayList;\n\n/** Top level view for {@link TicTacToeBuilder.TicTacToeScope}. */\npublic class TicTacToeView extends PercentRelativeLayout\n    implements TicTacToeInteractor.TicTacToePresenter {\n\n  private TextView[][] imageButtons;\n  private TextView titleView;\n\n  public TicTacToeView(Context context) {\n    this(context, null);\n  }\n\n  public TicTacToeView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public TicTacToeView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Initializer\n  @Override\n  protected void onFinishInflate() {\n    super.onFinishInflate();\n    imageButtons = new TextView[3][];\n    imageButtons[0] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button11),\n          (TextView) findViewById(R.id.button12),\n          (TextView) findViewById(R.id.button13)\n        };\n    imageButtons[1] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button21),\n          (TextView) findViewById(R.id.button22),\n          (TextView) findViewById(R.id.button23)\n        };\n    imageButtons[2] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button31),\n          (TextView) findViewById(R.id.button32),\n          (TextView) findViewById(R.id.button33)\n        };\n    titleView = (TextView) findViewById(R.id.title);\n  }\n\n  @Override\n  public Observable<BoardCoordinate> squareClicks() {\n    ArrayList<Observable<BoardCoordinate>> observables = new ArrayList<>();\n    for (int i = 0; i < 3; i++) {\n      for (int j = 0; j < 3; j++) {\n        final int finalI = i;\n        final int finalJ = j;\n        observables.add(\n            RxView.clicks(imageButtons[i][j])\n                .map(\n                    new Function<Object, BoardCoordinate>() {\n                      @Override\n                      public BoardCoordinate apply(Object irrelevant) throws Exception {\n                        return new BoardCoordinate(finalI, finalJ);\n                      }\n                    }));\n      }\n    }\n    return Observable.merge(observables);\n  }\n\n  @Override\n  public void addCross(BoardCoordinate xy) {\n    TextView textView = imageButtons[xy.getX()][xy.getY()];\n    textView.setText(\"x\");\n    textView.setClickable(false);\n  }\n\n  @Override\n  public void addNought(BoardCoordinate xy) {\n    TextView textView = imageButtons[xy.getX()][xy.getY()];\n    textView.setText(\"O\");\n    textView.setClickable(false);\n  }\n\n  @Override\n  public void setCurrentPlayerName(String currentPlayer) {\n    titleView.setText(\"Current Player: \" + currentPlayer);\n  }\n\n  @Override\n  public void setPlayerWon(String playerName) {\n    titleView.setText(\"Player won: \" + playerName + \"!!!\");\n  }\n\n  @Override\n  public void setPlayerTie() {\n    titleView.setText(\"Tie game!\");\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/loggedout/LoggedOutBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.tutorial1.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link LoggedOutScope}. */\npublic class LoggedOutBuilder\n    extends ViewBuilder<LoggedOutView, LoggedOutRouter, LoggedOutBuilder.ParentComponent> {\n\n  public LoggedOutBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link LoggedOutRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link LoggedOutRouter}.\n   */\n  public LoggedOutRouter build(ViewGroup parentViewGroup) {\n    LoggedOutView view = createView(parentViewGroup);\n    LoggedOutInteractor interactor = new LoggedOutInteractor();\n    Component component =\n        DaggerLoggedOutBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.loggedoutRouter();\n  }\n\n  @Override\n  protected LoggedOutView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (LoggedOutView) inflater.inflate(R.layout.logged_out_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n    // TODO: Define dependencies required from your parent interactor here.\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @LoggedOutScope\n    @Binds\n    abstract LoggedOutInteractor.LoggedOutPresenter presenter(LoggedOutView view);\n\n    @LoggedOutScope\n    @Provides\n    static LoggedOutRouter router(\n        Component component, LoggedOutView view, LoggedOutInteractor interactor) {\n      return new LoggedOutRouter(view, interactor, component);\n    }\n\n    // TODO: Create provider methods for dependencies created by this Rib. These should be static.\n  }\n\n  @LoggedOutScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<LoggedOutInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n      @BindsInstance\n      Builder interactor(LoggedOutInteractor interactor);\n\n      @BindsInstance\n      Builder view(LoggedOutView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n    LoggedOutRouter loggedoutRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface LoggedOutScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface LoggedOutInternal {}\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/loggedout/LoggedOutInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport android.util.Log;\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder.LoggedOutScope;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link LoggedOutScope}. */\n@RibInteractor\npublic class LoggedOutInteractor\n    extends Interactor<LoggedOutInteractor.LoggedOutPresenter, LoggedOutRouter> {\n\n  @Inject LoggedOutPresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n    presenter\n        .loginName()\n        .subscribe(\n            new Consumer<String>() {\n              @Override\n              public void accept(String name) throws Exception {\n                Log.d(\"MOO\", name);\n              }\n            });\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface LoggedOutPresenter {\n    Observable<String> loginName();\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/loggedout/LoggedOutRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link LoggedOutBuilder.LoggedOutScope}. */\npublic class LoggedOutRouter extends ViewRouter<LoggedOutView, LoggedOutInteractor> {\n\n  public LoggedOutRouter(\n      LoggedOutView view, LoggedOutInteractor interactor, LoggedOutBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/java/com/uber/rib/root/loggedout/LoggedOutView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.tutorial1.R;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Function;\n\n/** Top level view for {@link LoggedOutBuilder.LoggedOutScope}. */\npublic class LoggedOutView extends LinearLayout implements LoggedOutInteractor.LoggedOutPresenter {\n\n  public LoggedOutView(Context context) {\n    this(context, null);\n  }\n\n  public LoggedOutView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public LoggedOutView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Override\n  public Observable<String> loginName() {\n    return RxView.clicks(findViewById(R.id.login_button))\n        .map(\n            new Function<Object, String>() {\n              @Override\n              public String apply(Object o) throws Exception {\n                TextView textView = (TextView) findViewById(R.id.edit_text);\n                return textView.getText().toString();\n              }\n            });\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/res/layout/logged_out_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedout.LoggedOutView\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    <Space\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"/>\n    <EditText\n        android:id=\"@+id/edit_text\"\n        android:hint=\"Enter your name\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"/>\n    <Button\n        android:id=\"@+id/login_button\"\n        android:text=\"Login\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"/>\n</com.uber.rib.root.loggedout.LoggedOutView>"
  },
  {
    "path": "tutorials/tutorial2/src/main/res/layout/off_game_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedin.offgame.OffGameView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:gravity=\"bottom\"\n    android:orientation=\"vertical\">\n\n    <LinearLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:layout_gravity=\"center_horizontal\"\n      android:layout_margin=\"16dp\"\n      android:orientation=\"horizontal\">\n\n        <TextView\n          android:id=\"@+id/player_one_name\"\n          android:text=\"@string/player_one_name\"\n          android:layout_width=\"0dp\"\n          android:layout_height=\"wrap_content\"\n          android:layout_weight=\"3\"/>\n\n        <TextView\n          android:id=\"@+id/player_one_win_count\"\n          android:layout_width=\"0dp\"\n          android:layout_height=\"wrap_content\"\n          android:layout_weight=\"1\"\n          android:text=\"@string/win_count\"/>\n\n    </LinearLayout>\n\n    <LinearLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:orientation=\"horizontal\"\n      android:layout_gravity=\"center_horizontal\"\n      android:layout_margin=\"16dp\">\n\n        <TextView\n          android:id=\"@+id/player_two_name\"\n          android:text=\"@string/player_two_name\"\n          android:layout_width=\"0dp\"\n          android:layout_height=\"wrap_content\"\n          android:layout_weight=\"3\"/>\n\n        <TextView\n          android:id=\"@+id/player_two_win_count\"\n          android:layout_width=\"0dp\"\n          android:layout_height=\"wrap_content\"\n          android:layout_weight=\"1\"\n          android:text=\"@string/win_count\"/>\n\n    </LinearLayout>\n    <Button\n        android:id=\"@+id/start_game_button\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"\n        android:layout_gravity=\"center_horizontal\"\n        android:text=\"@string/start_game\"/>\n\n</com.uber.rib.root.loggedin.offgame.OffGameView>\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/res/layout/root_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.RootView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"/>\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/res/layout/tic_tac_toe_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedin.tictactoe.TicTacToeView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/tic_tac_toe\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <TextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"40dp\"\n        android:gravity=\"center_horizontal\"/>\n\n    <TextView\n        android:id=\"@+id/button11\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button12\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        android:layout_toRightOf=\"@id/button11\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button13\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        android:layout_toRightOf=\"@id/button12\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button21\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button11\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button22\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button11\"\n        android:layout_toRightOf=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button23\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button13\"\n        android:layout_toRightOf=\"@id/button22\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button31\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button32\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button22\"\n        android:layout_toRightOf=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button33\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button23\"\n        android:layout_toRightOf=\"@id/button22\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n</com.uber.rib.root.loggedin.tictactoe.TicTacToeView>\n"
  },
  {
    "path": "tutorials/tutorial2/src/main/res/values/ub__strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">RIB Tutorial 2</string>\n    <string name=\"player_one_name\">Player One Name</string>\n    <string name=\"player_two_name\">Player Two Name</string>\n    <string name=\"login\">Login</string>\n    <string name=\"start_game\">Start game</string>\n    <string name=\"welcome_to_off_game\">Welcome FakeName!</string>\n    <string name=\"win_count\">win count</string>\n</resources>\n"
  },
  {
    "path": "tutorials/tutorial2/src/test/java/com/uber/rib/root/RootInteractorTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\n@SuppressWarnings(\"NullAway\")\npublic class RootInteractorTest {\n\n  @Mock RootInteractor.RootPresenter presenter;\n  @Mock RootRouter router;\n\n  private RootInteractor interactor;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    interactor = TestRootInteractor.create(presenter);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/test/java/com/uber/rib/root/RootRouterTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport com.uber.rib.root.loggedout.LoggedOutBuilder;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\n@SuppressWarnings(\"NullAway\")\npublic class RootRouterTest {\n\n  @Mock RootBuilder.Component component;\n  @Mock RootInteractor interactor;\n  @Mock RootView view;\n\n  private RootRouter router;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    router = new RootRouter(view, interactor, component, new LoggedOutBuilder(component));\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/test/java/com/uber/rib/root/loggedout/LoggedOutInteractorTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\n@SuppressWarnings(\"NullAway\")\npublic class LoggedOutInteractorTest extends RibTestBasePlaceholder {\n\n  @Mock LoggedOutInteractor.LoggedOutPresenter presenter;\n  @Mock LoggedOutRouter router;\n\n  private LoggedOutInteractor interactor;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    interactor = TestLoggedOutInteractor.create(presenter);\n  }\n\n  /** TODO: Delete this example and add real tests. */\n  @Test\n  public void anExampleTest_withSomeConditions_shouldPass() {}\n}\n"
  },
  {
    "path": "tutorials/tutorial2/src/test/java/com/uber/rib/root/loggedout/LoggedOutRouterTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\n@SuppressWarnings(\"NullAway\")\npublic class LoggedOutRouterTest extends RibTestBasePlaceholder {\n\n  @Mock LoggedOutBuilder.Component component;\n  @Mock LoggedOutInteractor interactor;\n  @Mock LoggedOutView view;\n\n  private LoggedOutRouter router;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/README.md",
    "content": "# RIB Tutorial 3: RIB DI and communication\n\nThis project is the completed code for [tutorial 2](https://github.com/uber/RIBs/wiki/Android-Tutorial-2) and the starting point for [tutorial 3](https://github.com/uber/RIBs/wiki/Android-Tutorial-3).\n\n### Getting started\nRun the code by\n\n```\n./gradlew :tutorials:tutorial3:installDebug\n```\n\nThen follow the steps described in [tutorial 3](https://github.com/uber/RIBs/wiki/Android-Tutorial-3) on the RIBs wiki.\n"
  },
  {
    "path": "tutorials/tutorial3/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.android.application\")\n    alias(libs.plugins.kotlin.kapt)\n    alias(libs.plugins.kotlin.ksp)\n}\n\nandroid {\n    namespace = \"com.uber.rib.tutorial1\"\n\n    defaultConfig {\n        applicationId = \"com.uber.tutorial3\"\n    }\n}\n\ndependencies {\n    ksp(libs.dagger.compiler)\n    kapt(project(\":libraries:rib-compiler-test\"))\n    implementation(project(\":libraries:rib-android\"))\n    implementation(libs.androidx.appcompat)\n    implementation(libs.dagger.library)\n    implementation(libs.rxbinding)\n    implementation(appLibs.percent)\n    compileOnly(appLibs.jsr250)\n    testImplementation(project(\":libraries:rib-test\"))\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <uses-feature android:required=\"true\" android:glEsVersion='0x00020000'/>\n\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>\n\n    <application\n        android:name=\"com.uber.rib.SampleApplication\"\n        android:allowBackup=\"false\"\n        android:icon=\"@drawable/ub__ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:screenOrientation=\"portrait\">\n        <activity\n            android:name=\"com.uber.rib.RootActivity\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n    </application>\n</manifest>\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/RootActivity.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib;\n\nimport android.view.ViewGroup;\nimport com.uber.rib.core.RibActivity;\nimport com.uber.rib.core.ViewRouter;\nimport com.uber.rib.root.RootBuilder;\n\n/** The sample app's single activity. */\npublic class RootActivity extends RibActivity {\n\n  @SuppressWarnings(\"unchecked\")\n  @Override\n  protected ViewRouter<?, ?> createRouter(ViewGroup parentViewGroup) {\n    RootBuilder rootBuilder = new RootBuilder(new RootBuilder.ParentComponent() {});\n    return rootBuilder.build(parentViewGroup);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/SampleApplication.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib;\n\nimport android.app.Application;\n\npublic class SampleApplication extends Application {}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/RootBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.root.loggedin.LoggedInBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutInteractor;\nimport com.uber.rib.tutorial1.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Scope;\n\n/** Builder for the {@link RootScope}. */\npublic class RootBuilder extends ViewBuilder<RootView, RootRouter, RootBuilder.ParentComponent> {\n\n  public RootBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link RootRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link RootRouter}.\n   */\n  public RootRouter build(ViewGroup parentViewGroup) {\n    RootView view = createView(parentViewGroup);\n    RootInteractor interactor = new RootInteractor();\n    Component component =\n        DaggerRootBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.rootRouter();\n  }\n\n  @Override\n  protected RootView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (RootView) inflater.inflate(R.layout.root_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n    // Define dependencies required from your parent interactor here.\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @RootScope\n    @Provides\n    static LoggedOutInteractor.Listener loggedOutListener(RootInteractor rootInteractor) {\n      return rootInteractor.new LoggedOutListener();\n    }\n\n    @RootScope\n    @Binds\n    abstract RootInteractor.RootPresenter presenter(RootView view);\n\n    @RootScope\n    @Provides\n    static RootRouter router(Component component, RootView view, RootInteractor interactor) {\n      return new RootRouter(\n          view,\n          interactor,\n          component,\n          new LoggedOutBuilder(component),\n          new LoggedInBuilder(component));\n    }\n  }\n\n  @RootScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component\n      extends InteractorBaseComponent<RootInteractor>,\n          LoggedOutBuilder.ParentComponent,\n          LoggedInBuilder.ParentComponent,\n          BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(RootInteractor interactor);\n\n      @BindsInstance\n      Builder view(RootView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    RootRouter rootRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface RootScope {}\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/RootInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedout.LoggedOutInteractor;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link RootBuilder.RootScope}. */\n@RibInteractor\npublic class RootInteractor extends Interactor<RootInteractor.RootPresenter, RootRouter> {\n\n  @Inject RootPresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n    getRouter().attachLoggedOut();\n  }\n\n  class LoggedOutListener implements LoggedOutInteractor.Listener {\n\n    @Override\n    public void login(String userNameA, String userNameB) {\n      // Switch to logged in. Let’s just ignore userName for now.\n      getRouter().detachLoggedOut();\n      getRouter().attachLoggedIn();\n    }\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface RootPresenter {}\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/RootRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.ViewRouter;\nimport com.uber.rib.root.loggedin.LoggedInBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutRouter;\n\n/** Adds and removes children of {@link RootBuilder.RootScope}. */\npublic class RootRouter extends ViewRouter<RootView, RootInteractor> {\n\n  private final LoggedOutBuilder loggedOutBuilder;\n  private final LoggedInBuilder loggedInBuilder;\n  @Nullable private LoggedOutRouter loggedOutRouter;\n\n  RootRouter(\n      RootView view,\n      RootInteractor interactor,\n      RootBuilder.Component component,\n      LoggedOutBuilder loggedOutBuilder,\n      LoggedInBuilder loggedInBuilder) {\n    super(view, interactor, component);\n    this.loggedOutBuilder = loggedOutBuilder;\n    this.loggedInBuilder = loggedInBuilder;\n  }\n\n  void attachLoggedOut() {\n    loggedOutRouter = loggedOutBuilder.build(getView());\n    attachChild(loggedOutRouter);\n    getView().addView(loggedOutRouter.getView());\n  }\n\n  void detachLoggedOut() {\n    if (loggedOutRouter != null) {\n      detachChild(loggedOutRouter);\n      getView().removeView(loggedOutRouter.getView());\n      loggedOutRouter = null;\n    }\n  }\n\n  void attachLoggedIn() {\n    attachChild(loggedInBuilder.build());\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/RootView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.FrameLayout;\nimport androidx.annotation.Nullable;\n\n/** Top level view for {@link RootBuilder.RootScope}. */\npublic class RootView extends FrameLayout implements RootInteractor.RootPresenter {\n\n  public RootView(Context context) {\n    this(context, null);\n  }\n\n  public RootView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public RootView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/loggedin/LoggedInBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport com.uber.rib.core.Builder;\nimport com.uber.rib.core.EmptyPresenter;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.root.RootView;\nimport com.uber.rib.root.loggedin.offgame.OffGameBuilder;\nimport com.uber.rib.root.loggedin.offgame.OffGameInteractor;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeBuilder;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\npublic class LoggedInBuilder extends Builder<LoggedInRouter, LoggedInBuilder.ParentComponent> {\n\n  public LoggedInBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link LoggedInRouter}.\n   *\n   * @return a new {@link LoggedInRouter}.\n   */\n  public LoggedInRouter build() {\n    LoggedInInteractor interactor = new LoggedInInteractor();\n    Component component =\n        DaggerLoggedInBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .interactor(interactor)\n            .build();\n\n    return component.loggedinRouter();\n  }\n\n  public interface ParentComponent {\n\n    RootView rootView();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @LoggedInScope\n    @Provides\n    static EmptyPresenter presenter() {\n      return new EmptyPresenter();\n    }\n\n    @LoggedInScope\n    @Provides\n    static LoggedInRouter router(\n        Component component, LoggedInInteractor interactor, RootView rootView) {\n      return new LoggedInRouter(\n          interactor,\n          component,\n          rootView,\n          new OffGameBuilder(component),\n          new TicTacToeBuilder(component));\n    }\n\n    @LoggedInScope\n    @Provides\n    static OffGameInteractor.Listener listener(LoggedInInteractor interactor) {\n      return interactor.new OffGameListener();\n    }\n  }\n\n  @LoggedInScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  public interface Component\n      extends InteractorBaseComponent<LoggedInInteractor>,\n          BuilderComponent,\n          OffGameBuilder.ParentComponent,\n          TicTacToeBuilder.ParentComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(LoggedInInteractor interactor);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    LoggedInRouter loggedinRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface LoggedInScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface LoggedInInternal {}\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/loggedin/LoggedInInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.EmptyPresenter;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedin.offgame.OffGameInteractor;\n\n/** Coordinates Business Logic for {@link LoggedInScope}. */\n@RibInteractor\npublic class LoggedInInteractor extends Interactor<EmptyPresenter, LoggedInRouter> {\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    // when first logging in we should be in the OffGame state\n    getRouter().attachOffGame();\n  }\n\n  class OffGameListener implements OffGameInteractor.Listener {\n\n    @Override\n    public void onStartGame() {\n      getRouter().detachOffGame();\n      getRouter().attachTicTacToe();\n    }\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/loggedin/LoggedInRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport android.view.ViewGroup;\nimport com.uber.rib.core.Router;\nimport com.uber.rib.root.loggedin.offgame.OffGameBuilder;\nimport com.uber.rib.root.loggedin.offgame.OffGameRouter;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeBuilder;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeRouter;\n\n/** Adds and removes children of {@link LoggedInBuilder.LoggedInScope}. */\npublic class LoggedInRouter extends Router<LoggedInInteractor> {\n\n  private final ViewGroup parentView;\n  private final OffGameBuilder offGameBuilder;\n  private final TicTacToeBuilder ticTacToeBuilder;\n  private OffGameRouter offGameRouter;\n  private TicTacToeRouter ticTacToeRouter;\n\n  LoggedInRouter(\n      LoggedInInteractor interactor,\n      LoggedInBuilder.Component component,\n      ViewGroup parentView,\n      OffGameBuilder offGameBuilder,\n      TicTacToeBuilder ticTacToeBuilder) {\n    super(interactor, component);\n    this.parentView = parentView;\n    this.offGameBuilder = offGameBuilder;\n    this.ticTacToeBuilder = ticTacToeBuilder;\n  }\n\n  @Override\n  protected void willDetach() {\n    super.willDetach();\n    detachOffGame();\n    detachTicTacToe();\n  }\n\n  void attachOffGame() {\n    offGameRouter = offGameBuilder.build(parentView);\n    attachChild(offGameRouter);\n    parentView.addView(offGameRouter.getView());\n  }\n\n  void detachOffGame() {\n    if (offGameRouter != null) {\n      detachChild(offGameRouter);\n      parentView.removeView(offGameRouter.getView());\n      offGameRouter = null;\n    }\n  }\n\n  void attachTicTacToe() {\n    ticTacToeRouter = ticTacToeBuilder.build(parentView);\n    attachChild(ticTacToeRouter);\n    parentView.addView(ticTacToeRouter.getView());\n  }\n\n  void detachTicTacToe() {\n    if (ticTacToeRouter != null) {\n      detachChild(ticTacToeRouter);\n      parentView.removeView(ticTacToeRouter.getView());\n      ticTacToeRouter = null;\n    }\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.tutorial1.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link OffGameScope}. */\npublic class OffGameBuilder\n    extends ViewBuilder<OffGameView, OffGameRouter, OffGameBuilder.ParentComponent> {\n\n  public OffGameBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link OffGameRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link OffGameRouter}.\n   */\n  public OffGameRouter build(ViewGroup parentViewGroup) {\n    OffGameView view = createView(parentViewGroup);\n    OffGameInteractor interactor = new OffGameInteractor();\n    Component component =\n        DaggerOffGameBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.offgameRouter();\n  }\n\n  @Override\n  protected OffGameView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (OffGameView) inflater.inflate(R.layout.off_game_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n\n    OffGameInteractor.Listener listener();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @OffGameScope\n    @Binds\n    abstract OffGameInteractor.OffGamePresenter presenter(OffGameView view);\n\n    @OffGameScope\n    @Provides\n    static OffGameRouter router(\n        Component component, OffGameView view, OffGameInteractor interactor) {\n      return new OffGameRouter(view, interactor, component);\n    }\n  }\n\n  @OffGameScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<OffGameInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(OffGameInteractor interactor);\n\n      @BindsInstance\n      Builder view(OffGameView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    OffGameRouter offgameRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface OffGameScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface OffGameInternal {}\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link OffGameScope}. */\n@RibInteractor\npublic class OffGameInteractor\n    extends Interactor<OffGameInteractor.OffGamePresenter, OffGameRouter> {\n\n  @Inject Listener listener;\n  @Inject OffGamePresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    presenter\n        .startGameRequest()\n        .subscribe(\n            new Consumer<Object>() {\n              @Override\n              public void accept(Object object) throws Exception {\n                listener.onStartGame();\n              }\n            });\n  }\n\n  public interface Listener {\n\n    void onStartGame();\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface OffGamePresenter {\n\n    Observable<Object> startGameRequest();\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link OffGameBuilder.OffGameScope}. */\npublic class OffGameRouter extends ViewRouter<OffGameView, OffGameInteractor> {\n\n  public OffGameRouter(\n      OffGameView view, OffGameInteractor interactor, OffGameBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.Button;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.core.Initializer;\nimport com.uber.rib.tutorial1.R;\nimport io.reactivex.Observable;\n\n/** Top level view for {@link OffGameBuilder.OffGameScope}. */\npublic class OffGameView extends LinearLayout implements OffGameInteractor.OffGamePresenter {\n\n  private Button button;\n  private TextView playerOneName;\n  private TextView playerTwoName;\n  private TextView playerOneScore;\n  private TextView playerTwoScore;\n\n  public OffGameView(Context context) {\n    this(context, null);\n  }\n\n  public OffGameView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public OffGameView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Initializer\n  @Override\n  protected void onFinishInflate() {\n    super.onFinishInflate();\n    button = (Button) findViewById(R.id.start_game_button);\n    playerOneName = (TextView) findViewById(R.id.player_one_name);\n    playerTwoName = (TextView) findViewById(R.id.player_two_name);\n    playerOneScore = (TextView) findViewById(R.id.player_one_win_count);\n    playerTwoScore = (TextView) findViewById(R.id.player_two_win_count);\n  }\n\n  @Override\n  public Observable<Object> startGameRequest() {\n    return RxView.clicks(button);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/loggedin/tictactoe/Board.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport javax.inject.Inject;\n\nclass Board {\n\n  static final int ROWS = 3;\n  static final int COLS = 3;\n\n  MarkerType[][] cells;\n  int currentRow;\n  int currentCol;\n\n  @Inject\n  Board() {\n    cells = new MarkerType[ROWS][COLS];\n    for (int row = 0; row < ROWS; ++row) {\n      for (int col = 0; col < COLS; ++col) {\n        cells[row][col] = null;\n      }\n    }\n  }\n\n  /** Return true if it is a draw (i.e., no more EMPTY cell) */\n  boolean isDraw() {\n    for (int row = 0; row < ROWS; ++row) {\n      for (int col = 0; col < COLS; ++col) {\n        if (cells[row][col] == null) {\n          return false;\n        }\n      }\n    }\n    return !hasWon(MarkerType.CROSS) && !hasWon(MarkerType.NOUGHT);\n  }\n\n  /** Return true if the player with \"theSeed\" has won after placing at (currentRow, currentCol) */\n  boolean hasWon(MarkerType theSeed) {\n    return ((cells[currentRow][0] == theSeed\n            && cells[currentRow][1] == theSeed\n            && cells[currentRow][2] == theSeed)\n        || (cells[0][currentCol] == theSeed\n            && cells[1][currentCol] == theSeed\n            && cells[2][currentCol] == theSeed)\n        || (currentRow == currentCol\n            && cells[0][0] == theSeed\n            && cells[1][1] == theSeed\n            && cells[2][2] == theSeed)\n        || (currentRow + currentCol == 2\n            && cells[0][2] == theSeed\n            && cells[1][1] == theSeed\n            && cells[2][0] == theSeed));\n  }\n\n  enum MarkerType {\n    CROSS,\n    NOUGHT\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/loggedin/tictactoe/BoardCoordinate.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nclass BoardCoordinate {\n\n  private final int x;\n  private final int y;\n\n  BoardCoordinate(int x, int y) {\n    this.x = x;\n    this.y = y;\n  }\n\n  int getX() {\n    return x;\n  }\n\n  int getY() {\n    return y;\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.tutorial1.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link TicTacToeScope}. */\npublic class TicTacToeBuilder\n    extends ViewBuilder<TicTacToeView, TicTacToeRouter, TicTacToeBuilder.ParentComponent> {\n\n  public TicTacToeBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link TicTacToeRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link TicTacToeRouter}.\n   */\n  public TicTacToeRouter build(ViewGroup parentViewGroup) {\n    TicTacToeView view = createView(parentViewGroup);\n    TicTacToeInteractor interactor = new TicTacToeInteractor();\n    Component component =\n        DaggerTicTacToeBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.tictactoeRouter();\n  }\n\n  @Override\n  protected TicTacToeView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (TicTacToeView) inflater.inflate(R.layout.tic_tac_toe_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n    // TODO: Define dependencies required from your parent interactor here.\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @TicTacToeScope\n    @Binds\n    abstract TicTacToeInteractor.TicTacToePresenter presenter(TicTacToeView view);\n\n    @TicTacToeScope\n    @Provides\n    static TicTacToeRouter router(\n        Component component, TicTacToeView view, TicTacToeInteractor interactor) {\n      return new TicTacToeRouter(view, interactor, component);\n    }\n  }\n\n  @TicTacToeScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<TicTacToeInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(TicTacToeInteractor interactor);\n\n      @BindsInstance\n      Builder view(TicTacToeView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    TicTacToeRouter tictactoeRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface TicTacToeScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface TicTacToeInternal {}\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedin.tictactoe.Board.MarkerType;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link TicTacToeScope}. */\n@RibInteractor\npublic class TicTacToeInteractor\n    extends Interactor<TicTacToeInteractor.TicTacToePresenter, TicTacToeRouter> {\n\n  @Inject Board board;\n  @Inject TicTacToePresenter presenter;\n\n  private final String playerOne = \"Fake name 1\";\n  private final String playerTwo = \"Fake name 2\";\n\n  private MarkerType currentPlayer = MarkerType.CROSS;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    presenter\n        .squareClicks()\n        .subscribe(\n            new Consumer<BoardCoordinate>() {\n              @Override\n              public void accept(BoardCoordinate xy) throws Exception {\n                if (board.cells[xy.getX()][xy.getY()] == null) {\n                  if (currentPlayer == MarkerType.CROSS) {\n                    board.cells[xy.getX()][xy.getY()] = MarkerType.CROSS;\n                    board.currentRow = xy.getX();\n                    board.currentCol = xy.getY();\n                    presenter.addCross(xy);\n                    currentPlayer = MarkerType.NOUGHT;\n                  } else {\n                    board.cells[xy.getX()][xy.getY()] = MarkerType.NOUGHT;\n                    board.currentRow = xy.getX();\n                    board.currentCol = xy.getY();\n                    presenter.addNought(xy);\n                    currentPlayer = MarkerType.CROSS;\n                  }\n                }\n                if (board.hasWon(MarkerType.CROSS)) {\n                  presenter.setPlayerWon(playerOne);\n                } else if (board.hasWon(MarkerType.NOUGHT)) {\n                  presenter.setPlayerWon(playerTwo);\n                } else if (board.isDraw()) {\n                  presenter.setPlayerTie();\n                } else {\n                  updateCurrentPlayer();\n                }\n              }\n            });\n    updateCurrentPlayer();\n  }\n\n  private void updateCurrentPlayer() {\n    if (currentPlayer == MarkerType.CROSS) {\n      presenter.setCurrentPlayerName(playerOne);\n    } else {\n      presenter.setCurrentPlayerName(playerTwo);\n    }\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface TicTacToePresenter {\n    Observable<BoardCoordinate> squareClicks();\n\n    void setCurrentPlayerName(String currentPlayer);\n\n    void setPlayerWon(String playerName);\n\n    void setPlayerTie();\n\n    void addCross(BoardCoordinate xy);\n\n    void addNought(BoardCoordinate xy);\n  }\n\n  public interface Listener {}\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link TicTacToeBuilder.TicTacToeScope}. */\npublic class TicTacToeRouter extends ViewRouter<TicTacToeView, TicTacToeInteractor> {\n\n  public TicTacToeRouter(\n      TicTacToeView view, TicTacToeInteractor interactor, TicTacToeBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport androidx.percentlayout.widget.PercentRelativeLayout;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.core.Initializer;\nimport com.uber.rib.tutorial1.R;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Function;\nimport java.util.ArrayList;\n\n/** Top level view for {@link TicTacToeBuilder.TicTacToeScope}. */\npublic class TicTacToeView extends PercentRelativeLayout\n    implements TicTacToeInteractor.TicTacToePresenter {\n\n  private TextView[][] imageButtons;\n  private TextView titleView;\n\n  public TicTacToeView(Context context) {\n    this(context, null);\n  }\n\n  public TicTacToeView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public TicTacToeView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Initializer\n  @Override\n  protected void onFinishInflate() {\n    super.onFinishInflate();\n    imageButtons = new TextView[3][];\n    imageButtons[0] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button11),\n          (TextView) findViewById(R.id.button12),\n          (TextView) findViewById(R.id.button13)\n        };\n    imageButtons[1] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button21),\n          (TextView) findViewById(R.id.button22),\n          (TextView) findViewById(R.id.button23)\n        };\n    imageButtons[2] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button31),\n          (TextView) findViewById(R.id.button32),\n          (TextView) findViewById(R.id.button33)\n        };\n    titleView = (TextView) findViewById(R.id.title);\n  }\n\n  @Override\n  public Observable<BoardCoordinate> squareClicks() {\n    ArrayList<Observable<BoardCoordinate>> observables = new ArrayList<>();\n    for (int i = 0; i < 3; i++) {\n      for (int j = 0; j < 3; j++) {\n        final int finalI = i;\n        final int finalJ = j;\n        observables.add(\n            RxView.clicks(imageButtons[i][j])\n                .map(\n                    new Function<Object, BoardCoordinate>() {\n                      @Override\n                      public BoardCoordinate apply(Object irrelevant) throws Exception {\n                        return new BoardCoordinate(finalI, finalJ);\n                      }\n                    }));\n      }\n    }\n    return Observable.merge(observables);\n  }\n\n  @Override\n  public void addCross(BoardCoordinate xy) {\n    TextView textView = imageButtons[xy.getX()][xy.getY()];\n    textView.setText(\"x\");\n    textView.setClickable(false);\n  }\n\n  @Override\n  public void addNought(BoardCoordinate xy) {\n    TextView textView = imageButtons[xy.getX()][xy.getY()];\n    textView.setText(\"O\");\n    textView.setClickable(false);\n  }\n\n  @Override\n  public void setCurrentPlayerName(String currentPlayer) {\n    titleView.setText(\"Current Player: \" + currentPlayer);\n  }\n\n  @Override\n  public void setPlayerWon(String playerName) {\n    titleView.setText(\"Player won: \" + playerName + \"!!!\");\n  }\n\n  @Override\n  public void setPlayerTie() {\n    titleView.setText(\"Tie game!\");\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/loggedout/LoggedOutBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.tutorial1.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link LoggedOutScope}. */\npublic class LoggedOutBuilder\n    extends ViewBuilder<LoggedOutView, LoggedOutRouter, LoggedOutBuilder.ParentComponent> {\n\n  public LoggedOutBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link LoggedOutRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link LoggedOutRouter}.\n   */\n  public LoggedOutRouter build(ViewGroup parentViewGroup) {\n    LoggedOutView view = createView(parentViewGroup);\n    LoggedOutInteractor interactor = new LoggedOutInteractor();\n    Component component =\n        DaggerLoggedOutBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.loggedoutRouter();\n  }\n\n  @Override\n  protected LoggedOutView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (LoggedOutView) inflater.inflate(R.layout.logged_out_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n\n    LoggedOutInteractor.Listener listener();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @LoggedOutScope\n    @Binds\n    abstract LoggedOutInteractor.LoggedOutPresenter presenter(LoggedOutView view);\n\n    @LoggedOutScope\n    @Provides\n    static LoggedOutRouter router(\n        Component component, LoggedOutView view, LoggedOutInteractor interactor) {\n      return new LoggedOutRouter(view, interactor, component);\n    }\n\n    // TODO: Create provider methods for dependencies created by this Rib. These should be static.\n  }\n\n  @LoggedOutScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<LoggedOutInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(LoggedOutInteractor interactor);\n\n      @BindsInstance\n      Builder view(LoggedOutView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    LoggedOutRouter loggedoutRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface LoggedOutScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface LoggedOutInternal {}\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/loggedout/LoggedOutInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport androidx.annotation.Nullable;\nimport androidx.core.util.Pair;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder.LoggedOutScope;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link LoggedOutScope}. */\n@RibInteractor\npublic class LoggedOutInteractor\n    extends Interactor<LoggedOutInteractor.LoggedOutPresenter, LoggedOutRouter> {\n\n  @Inject Listener listener;\n  @Inject LoggedOutPresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n    presenter\n        .loginName()\n        .subscribe(\n            new Consumer<Pair<String, String>>() {\n              @Override\n              public void accept(Pair<String, String> names) throws Exception {\n                if (!isEmpty(names.first) && !isEmpty(names.second)) {\n                  listener.login(names.first, names.second);\n                }\n              }\n            });\n  }\n\n  private boolean isEmpty(@Nullable String string) {\n    return string == null || string.length() == 0;\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface LoggedOutPresenter {\n\n    Observable<Pair<String, String>> loginName();\n  }\n\n  public interface Listener {\n    void login(String userNameA, String userNameB);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/loggedout/LoggedOutRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link LoggedOutBuilder.LoggedOutScope}. */\npublic class LoggedOutRouter extends ViewRouter<LoggedOutView, LoggedOutInteractor> {\n\n  public LoggedOutRouter(\n      LoggedOutView view, LoggedOutInteractor interactor, LoggedOutBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/java/com/uber/rib/root/loggedout/LoggedOutView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport androidx.core.util.Pair;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.tutorial1.R;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Function;\n\n/** Top level view for {@link LoggedOutBuilder.LoggedOutScope}. */\npublic class LoggedOutView extends LinearLayout implements LoggedOutInteractor.LoggedOutPresenter {\n\n  public LoggedOutView(Context context) {\n    this(context, null);\n  }\n\n  public LoggedOutView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public LoggedOutView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Override\n  public Observable<Pair<String, String>> loginName() {\n    return RxView.clicks(findViewById(R.id.login_button))\n        .map(\n            new Function<Object, Pair<String, String>>() {\n              @Override\n              public Pair<String, String> apply(Object o) throws Exception {\n                TextView playerNameOne = (TextView) findViewById(R.id.player_name_1);\n                TextView playerNameTwo = (TextView) findViewById(R.id.player_name_2);\n                return Pair.create(\n                    playerNameOne.getText().toString(), playerNameTwo.getText().toString());\n              }\n            });\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/res/layout/logged_out_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedout.LoggedOutView\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    <Space\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"/>\n    <EditText\n        android:id=\"@+id/player_name_1\"\n        android:hint=\"Player Name 1\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"/>\n    <EditText\n        android:id=\"@+id/player_name_2\"\n        android:hint=\"Player Name 2\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"/>\n    <Button\n        android:id=\"@+id/login_button\"\n        android:text=\"Login\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"/>\n</com.uber.rib.root.loggedout.LoggedOutView>"
  },
  {
    "path": "tutorials/tutorial3/src/main/res/layout/off_game_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedin.offgame.OffGameView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:gravity=\"bottom\"\n    android:orientation=\"vertical\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:layout_margin=\"16dp\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/player_one_name\"\n            android:text=\"@string/player_one_name\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"3\"/>\n\n        <TextView\n            android:id=\"@+id/player_one_win_count\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"@string/win_count\"/>\n\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\"\n        android:layout_gravity=\"center_horizontal\"\n        android:layout_margin=\"16dp\">\n\n        <TextView\n            android:id=\"@+id/player_two_name\"\n            android:text=\"@string/player_two_name\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"3\"/>\n\n        <TextView\n            android:id=\"@+id/player_two_win_count\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"@string/win_count\"/>\n\n    </LinearLayout>\n    <Button\n        android:id=\"@+id/start_game_button\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"\n        android:layout_gravity=\"center_horizontal\"\n        android:text=\"@string/start_game\"/>\n\n</com.uber.rib.root.loggedin.offgame.OffGameView>\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/res/layout/root_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.RootView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"/>\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/res/layout/tic_tac_toe_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedin.tictactoe.TicTacToeView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/tic_tac_toe\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <TextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"40dp\"\n        android:gravity=\"center_horizontal\"/>\n\n    <TextView\n        android:id=\"@+id/button11\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button12\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        android:layout_toRightOf=\"@id/button11\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button13\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        android:layout_toRightOf=\"@id/button12\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button21\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button11\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button22\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button11\"\n        android:layout_toRightOf=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button23\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button13\"\n        android:layout_toRightOf=\"@id/button22\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button31\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button32\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button22\"\n        android:layout_toRightOf=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button33\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button23\"\n        android:layout_toRightOf=\"@id/button22\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n</com.uber.rib.root.loggedin.tictactoe.TicTacToeView>\n"
  },
  {
    "path": "tutorials/tutorial3/src/main/res/values/ub__strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">RIB Tutorial 3</string>\n    <string name=\"player_one_name\">Player One Name</string>\n    <string name=\"player_two_name\">Player Two Name</string>\n    <string name=\"login\">Login</string>\n    <string name=\"start_game\">Start game</string>\n    <string name=\"welcome_to_off_game\">Welcome FakeName!</string>\n    <string name=\"win_count\">win count</string>\n</resources>\n"
  },
  {
    "path": "tutorials/tutorial3/src/test/java/com/uber/rib/root/RootInteractorTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class RootInteractorTest {\n\n  @Mock RootInteractor.RootPresenter presenter;\n  @Mock RootRouter router;\n\n  private RootInteractor interactor;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    interactor = TestRootInteractor.create(presenter);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/test/java/com/uber/rib/root/RootRouterTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport com.uber.rib.root.loggedin.LoggedInBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class RootRouterTest {\n\n  @Mock RootBuilder.Component component;\n  @Mock RootInteractor interactor;\n  @Mock RootView view;\n\n  private RootRouter router;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    router =\n        new RootRouter(\n            view,\n            interactor,\n            component,\n            new LoggedOutBuilder(component),\n            new LoggedInBuilder(component));\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/test/java/com/uber/rib/root/loggedin/LoggedInInteractorTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport com.uber.rib.core.EmptyPresenter;\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class LoggedInInteractorTest extends RibTestBasePlaceholder {\n\n  @Mock EmptyPresenter presenter;\n  @Mock LoggedInRouter router;\n\n  private LoggedInInteractor interactor;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    interactor = TestLoggedInInteractor.create();\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/test/java/com/uber/rib/root/loggedin/LoggedInRouterTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport android.view.ViewGroup;\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport com.uber.rib.root.loggedin.offgame.OffGameBuilder;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeBuilder;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class LoggedInRouterTest extends RibTestBasePlaceholder {\n\n  @Mock LoggedInBuilder.Component component;\n  @Mock LoggedInInteractor interactor;\n  @Mock ViewGroup parentView;\n  @Mock OffGameBuilder offGameBuilder;\n  @Mock TicTacToeBuilder ticTacToeBuilder;\n\n  private LoggedInRouter router;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    router =\n        new LoggedInRouter(interactor, component, parentView, offGameBuilder, ticTacToeBuilder);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/test/java/com/uber/rib/root/loggedin/offgame/OffGameInteractorTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class OffGameInteractorTest extends RibTestBasePlaceholder {\n\n  @Mock OffGameInteractor.Listener listener;\n  @Mock OffGameInteractor.OffGamePresenter presenter;\n  @Mock OffGameRouter router;\n\n  private OffGameInteractor interactor;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    interactor = TestOffGameInteractor.create(listener, presenter);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/test/java/com/uber/rib/root/loggedin/offgame/OffGameRouterTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class OffGameRouterTest extends RibTestBasePlaceholder {\n\n  @Mock OffGameBuilder.Component component;\n  @Mock OffGameInteractor interactor;\n  @Mock OffGameView view;\n\n  private OffGameRouter router;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    router = new OffGameRouter(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/test/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeInteractorTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class TicTacToeInteractorTest extends RibTestBasePlaceholder {\n\n  @Mock TicTacToeInteractor.TicTacToePresenter presenter;\n  @Mock TicTacToeRouter router;\n  @Mock Board board;\n\n  private TicTacToeInteractor interactor;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    interactor = TestTicTacToeInteractor.create(board, presenter);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/test/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeRouterTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class TicTacToeRouterTest extends RibTestBasePlaceholder {\n\n  @Mock TicTacToeBuilder.Component component;\n  @Mock TicTacToeInteractor interactor;\n  @Mock TicTacToeView view;\n\n  private TicTacToeRouter router;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    router = new TicTacToeRouter(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/test/java/com/uber/rib/root/loggedout/LoggedOutInteractorTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport androidx.core.util.Pair;\nimport com.uber.rib.core.InteractorHelper;\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport io.reactivex.Observable;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class LoggedOutInteractorTest extends RibTestBasePlaceholder {\n\n  @Mock LoggedOutInteractor.Listener listener;\n  @Mock LoggedOutInteractor.LoggedOutPresenter presenter;\n  @Mock LoggedOutRouter router;\n\n  private LoggedOutInteractor interactor;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    interactor = TestLoggedOutInteractor.create(listener, presenter);\n  }\n\n  @Test\n  public void attach_whenViewEmitsName_shouldCallListener() {\n    when(presenter.loginName()).thenReturn(Observable.just(Pair.create(\"fakename\", \"fakename\")));\n\n    InteractorHelper.attach(interactor, presenter, router, null);\n\n    verify(listener).login(any(String.class), any(String.class));\n  }\n\n  @Test\n  public void attach_whenViewEmitsEmptyName_shouldNotCallListener() {\n    when(presenter.loginName()).thenReturn(Observable.just(Pair.create(\"\", \"\")));\n\n    InteractorHelper.attach(interactor, presenter, router, null);\n\n    verify(listener, never()).login(any(String.class), any(String.class));\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3/src/test/java/com/uber/rib/root/loggedout/LoggedOutRouterTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class LoggedOutRouterTest extends RibTestBasePlaceholder {\n\n  @Mock LoggedOutBuilder.Component component;\n  @Mock LoggedOutInteractor interactor;\n  @Mock LoggedOutView view;\n\n  private LoggedOutRouter router;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    router = new LoggedOutRouter(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/README.md",
    "content": "# RIB Tutorial 3: RIB DI and communication\n\nThis project is the completed code for [tutorial 3](https://github.com/uber/RIBs/wiki/Android-Tutorial-3).\n\n### Getting started\nRun the code by\n\n```\n./gradlew :tutorials:tutorial3-completed:installDebug\n```\n"
  },
  {
    "path": "tutorials/tutorial3-completed/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.android.application\")\n    alias(libs.plugins.kotlin.kapt)\n    alias(libs.plugins.kotlin.ksp)\n}\n\nandroid {\n    namespace = \"com.uber.rib.tutorial1\"\n\n    defaultConfig {\n        applicationId = \"com.uber.tutorial3\"\n    }\n}\n\ndependencies {\n    ksp(libs.dagger.compiler)\n    kapt(project(\":libraries:rib-compiler-test\"))\n    implementation(project(\":libraries:rib-android\"))\n    implementation(libs.androidx.appcompat)\n    implementation(libs.dagger.library)\n    implementation(libs.rxbinding)\n    implementation(appLibs.percent)\n    implementation(libs.guava.android)\n    compileOnly(appLibs.jsr250)\n    testImplementation(project(\":libraries:rib-test\"))\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <uses-feature android:required=\"true\" android:glEsVersion='0x00020000'/>\n\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>\n\n    <application\n        android:name=\"com.uber.rib.SampleApplication\"\n        android:allowBackup=\"false\"\n        android:icon=\"@drawable/ub__ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:screenOrientation=\"portrait\">\n        <activity\n            android:name=\"com.uber.rib.RootActivity\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n    </application>\n</manifest>\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/RootActivity.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib;\n\nimport android.view.ViewGroup;\nimport com.uber.rib.core.RibActivity;\nimport com.uber.rib.core.ViewRouter;\nimport com.uber.rib.root.RootBuilder;\n\n/** The sample app's single activity. */\npublic class RootActivity extends RibActivity {\n\n  @SuppressWarnings(\"unchecked\")\n  @Override\n  protected ViewRouter<?, ?> createRouter(ViewGroup parentViewGroup) {\n    RootBuilder rootBuilder = new RootBuilder(new RootBuilder.ParentComponent() {});\n    return rootBuilder.build(parentViewGroup);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/SampleApplication.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib;\n\nimport android.app.Application;\n\npublic class SampleApplication extends Application {}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/RootBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.root.loggedin.LoggedInBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutInteractor;\nimport com.uber.rib.tutorial1.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Scope;\n\n/** Builder for the {@link RootScope}. */\npublic class RootBuilder extends ViewBuilder<RootView, RootRouter, RootBuilder.ParentComponent> {\n\n  public RootBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link RootRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link RootRouter}.\n   */\n  public RootRouter build(ViewGroup parentViewGroup) {\n    RootView view = createView(parentViewGroup);\n    RootInteractor interactor = new RootInteractor();\n    Component component =\n        DaggerRootBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.rootRouter();\n  }\n\n  @Override\n  protected RootView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (RootView) inflater.inflate(R.layout.root_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n    // Define dependencies required from your parent interactor here.\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @RootScope\n    @Provides\n    static LoggedOutInteractor.Listener loggedOutListener(RootInteractor rootInteractor) {\n      return rootInteractor.new LoggedOutListener();\n    }\n\n    @RootScope\n    @Binds\n    abstract RootInteractor.RootPresenter presenter(RootView view);\n\n    @RootScope\n    @Provides\n    static RootRouter router(Component component, RootView view, RootInteractor interactor) {\n      return new RootRouter(\n          view,\n          interactor,\n          component,\n          new LoggedOutBuilder(component),\n          new LoggedInBuilder(component));\n    }\n  }\n\n  @RootScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component\n      extends InteractorBaseComponent<RootInteractor>,\n          LoggedOutBuilder.ParentComponent,\n          LoggedInBuilder.ParentComponent,\n          BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(RootInteractor interactor);\n\n      @BindsInstance\n      Builder view(RootView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    RootRouter rootRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface RootScope {}\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/RootInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedout.LoggedOutInteractor;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link RootBuilder.RootScope}. */\n@RibInteractor\npublic class RootInteractor extends Interactor<RootInteractor.RootPresenter, RootRouter> {\n\n  @Inject RootPresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n    getRouter().attachLoggedOut();\n  }\n\n  class LoggedOutListener implements LoggedOutInteractor.Listener {\n\n    @Override\n    public void requestLogin(String playerOne, String playerTwo) {\n      // Switch to logged in. Let’s just ignore userName for now.\n      getRouter().detachLoggedOut();\n      getRouter().attachLoggedIn(playerOne, playerTwo);\n    }\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface RootPresenter {}\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/RootRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.ViewRouter;\nimport com.uber.rib.root.loggedin.LoggedInBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutRouter;\n\n/** Adds and removes children of {@link RootBuilder.RootScope}. */\npublic class RootRouter extends ViewRouter<RootView, RootInteractor> {\n\n  private final LoggedOutBuilder loggedOutBuilder;\n  private final LoggedInBuilder loggedInBuilder;\n  @Nullable private LoggedOutRouter loggedOutRouter;\n\n  RootRouter(\n      RootView view,\n      RootInteractor interactor,\n      RootBuilder.Component component,\n      LoggedOutBuilder loggedOutBuilder,\n      LoggedInBuilder loggedInBuilder) {\n    super(view, interactor, component);\n    this.loggedOutBuilder = loggedOutBuilder;\n    this.loggedInBuilder = loggedInBuilder;\n  }\n\n  void attachLoggedOut() {\n    loggedOutRouter = loggedOutBuilder.build(getView());\n    attachChild(loggedOutRouter);\n    getView().addView(loggedOutRouter.getView());\n  }\n\n  void detachLoggedOut() {\n    if (loggedOutRouter != null) {\n      detachChild(loggedOutRouter);\n      getView().removeView(loggedOutRouter.getView());\n      loggedOutRouter = null;\n    }\n  }\n\n  void attachLoggedIn(String playerOne, String playerTwo) {\n    attachChild(loggedInBuilder.build(playerOne, playerTwo));\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/RootView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.FrameLayout;\nimport androidx.annotation.Nullable;\n\n/** Top level view for {@link RootBuilder.RootScope}. */\npublic class RootView extends FrameLayout implements RootInteractor.RootPresenter {\n\n  public RootView(Context context) {\n    this(context, null);\n  }\n\n  public RootView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public RootView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedin/LoggedInBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport com.uber.rib.core.Builder;\nimport com.uber.rib.core.EmptyPresenter;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.root.RootView;\nimport com.uber.rib.root.loggedin.offgame.OffGameBuilder;\nimport com.uber.rib.root.loggedin.offgame.OffGameInteractor;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeBuilder;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeInteractor;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Named;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\npublic class LoggedInBuilder extends Builder<LoggedInRouter, LoggedInBuilder.ParentComponent> {\n\n  public LoggedInBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link LoggedInRouter}.\n   *\n   * @return a new {@link LoggedInRouter}.\n   */\n  public LoggedInRouter build(String playerOne, String playerTwo) {\n    LoggedInInteractor interactor = new LoggedInInteractor();\n    Component component =\n        DaggerLoggedInBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .interactor(interactor)\n            .playerOne(playerOne)\n            .playerTwo(playerTwo)\n            .build();\n\n    return component.loggedinRouter();\n  }\n\n  public interface ParentComponent {\n\n    RootView rootView();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @LoggedInScope\n    @Provides\n    static EmptyPresenter presenter() {\n      return new EmptyPresenter();\n    }\n\n    @LoggedInScope\n    @Provides\n    static LoggedInRouter router(\n        Component component, LoggedInInteractor interactor, RootView rootView) {\n      return new LoggedInRouter(\n          interactor,\n          component,\n          rootView,\n          new OffGameBuilder(component),\n          new TicTacToeBuilder(component));\n    }\n\n    @LoggedInScope\n    @LoggedInInternal\n    @Provides\n    static MutableScoreStream mutableScoreStream(\n        @Named(\"player_one\") String playerOne, @Named(\"player_two\") String playerTwo) {\n      return new MutableScoreStream(playerOne, playerTwo);\n    }\n\n    @LoggedInScope\n    @Provides\n    static OffGameInteractor.Listener listener(LoggedInInteractor interactor) {\n      return interactor.new OffGameListener();\n    }\n\n    @LoggedInScope\n    @Provides\n    static TicTacToeInteractor.Listener ticTacToeListener(LoggedInInteractor interactor) {\n      return interactor.new TicTacToeListener();\n    }\n\n    @LoggedInScope\n    @Binds\n    abstract ScoreStream scoreStream(@LoggedInInternal MutableScoreStream mutableScoreStream);\n  }\n\n  @LoggedInScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  public interface Component\n      extends InteractorBaseComponent<LoggedInInteractor>,\n          BuilderComponent,\n          OffGameBuilder.ParentComponent,\n          TicTacToeBuilder.ParentComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(LoggedInInteractor interactor);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n\n      @BindsInstance\n      Builder playerOne(@Named(\"player_one\") String playerOne);\n\n      @BindsInstance\n      Builder playerTwo(@Named(\"player_two\") String playerTwo);\n    }\n  }\n\n  interface BuilderComponent {\n\n    LoggedInRouter loggedinRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface LoggedInScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface LoggedInInternal {}\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedin/LoggedInInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.EmptyPresenter;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedin.offgame.OffGameInteractor;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeInteractor;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link LoggedInScope}. */\n@RibInteractor\npublic class LoggedInInteractor extends Interactor<EmptyPresenter, LoggedInRouter> {\n\n  @Inject @LoggedInBuilder.LoggedInInternal MutableScoreStream scoreStream;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    // when first logging in we should be in the OffGame state\n    getRouter().attachOffGame();\n  }\n\n  @Override\n  protected void willResignActive() {\n    super.willResignActive();\n\n    // TODO: Perform any required clean up here, or delete this method entirely if not needed.\n  }\n\n  class OffGameListener implements OffGameInteractor.Listener {\n\n    @Override\n    public void onStartGame() {\n      getRouter().detachOffGame();\n      getRouter().attachTicTacToe();\n    }\n  }\n\n  class TicTacToeListener implements TicTacToeInteractor.Listener {\n\n    @Override\n    public void gameWon(@Nullable String winner) {\n      if (winner != null) {\n        scoreStream.addVictory(winner);\n      }\n\n      getRouter().detachTicTacToe();\n      getRouter().attachOffGame();\n    }\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedin/LoggedInRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport android.view.ViewGroup;\nimport com.uber.rib.core.Router;\nimport com.uber.rib.root.loggedin.offgame.OffGameBuilder;\nimport com.uber.rib.root.loggedin.offgame.OffGameRouter;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeBuilder;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeRouter;\n\n/** Adds and removes children of {@link LoggedInBuilder.LoggedInScope}. */\npublic class LoggedInRouter extends Router<LoggedInInteractor> {\n\n  private final ViewGroup parentView;\n  private final OffGameBuilder offGameBuilder;\n  private final TicTacToeBuilder ticTacToeBuilder;\n  private OffGameRouter offGameRouter;\n  private TicTacToeRouter ticTacToeRouter;\n\n  LoggedInRouter(\n      LoggedInInteractor interactor,\n      LoggedInBuilder.Component component,\n      ViewGroup parentView,\n      OffGameBuilder offGameBuilder,\n      TicTacToeBuilder ticTacToeBuilder) {\n    super(interactor, component);\n    this.parentView = parentView;\n    this.offGameBuilder = offGameBuilder;\n    this.ticTacToeBuilder = ticTacToeBuilder;\n  }\n\n  @Override\n  protected void willDetach() {\n    super.willDetach();\n    detachOffGame();\n    detachTicTacToe();\n  }\n\n  void attachOffGame() {\n    offGameRouter = offGameBuilder.build(parentView);\n    attachChild(offGameRouter);\n    parentView.addView(offGameRouter.getView());\n  }\n\n  void detachOffGame() {\n    if (offGameRouter != null) {\n      detachChild(offGameRouter);\n      parentView.removeView(offGameRouter.getView());\n      offGameRouter = null;\n    }\n  }\n\n  void attachTicTacToe() {\n    ticTacToeRouter = ticTacToeBuilder.build(parentView);\n    attachChild(ticTacToeRouter);\n    parentView.addView(ticTacToeRouter.getView());\n  }\n\n  void detachTicTacToe() {\n    if (ticTacToeRouter != null) {\n      detachChild(ticTacToeRouter);\n      parentView.removeView(ticTacToeRouter.getView());\n      ticTacToeRouter = null;\n    }\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedin/MutableScoreStream.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport com.google.common.collect.ImmutableMap;\nimport com.jakewharton.rxrelay2.BehaviorRelay;\nimport io.reactivex.Observable;\nimport java.util.Map;\n\nclass MutableScoreStream implements ScoreStream {\n\n  private final BehaviorRelay<ImmutableMap<String, Integer>> scoresRelay = BehaviorRelay.create();\n\n  MutableScoreStream(String playerOne, String playerTwo) {\n    scoresRelay.accept(ImmutableMap.of(playerOne, 0, playerTwo, 0));\n  }\n\n  void addVictory(String userName) {\n    ImmutableMap<String, Integer> currentScores = scoresRelay.getValue();\n\n    ImmutableMap.Builder<String, Integer> newScoreMapBuilder = new ImmutableMap.Builder<>();\n    for (Map.Entry<String, Integer> entry : currentScores.entrySet()) {\n      if (entry.getKey().equals(userName)) {\n        newScoreMapBuilder.put(entry.getKey(), entry.getValue() + 1);\n      } else {\n        newScoreMapBuilder.put(entry.getKey(), entry.getValue());\n      }\n    }\n\n    scoresRelay.accept(newScoreMapBuilder.build());\n  }\n\n  @Override\n  public Observable<ImmutableMap<String, Integer>> scores() {\n    return scoresRelay.hide();\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedin/ScoreStream.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport com.google.common.collect.ImmutableMap;\nimport io.reactivex.Observable;\n\npublic interface ScoreStream {\n  Observable<ImmutableMap<String, Integer>> scores();\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.root.loggedin.ScoreStream;\nimport com.uber.rib.tutorial1.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Named;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link OffGameScope}. */\npublic class OffGameBuilder\n    extends ViewBuilder<OffGameView, OffGameRouter, OffGameBuilder.ParentComponent> {\n\n  public OffGameBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link OffGameRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link OffGameRouter}.\n   */\n  public OffGameRouter build(ViewGroup parentViewGroup) {\n    OffGameView view = createView(parentViewGroup);\n    OffGameInteractor interactor = new OffGameInteractor();\n    Component component =\n        DaggerOffGameBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.offgameRouter();\n  }\n\n  @Override\n  protected OffGameView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (OffGameView) inflater.inflate(R.layout.off_game_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n\n    @Named(\"player_one\")\n    String playerOne();\n\n    @Named(\"player_two\")\n    String playerTwo();\n\n    OffGameInteractor.Listener listener();\n\n    ScoreStream scoreStream();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @OffGameScope\n    @Binds\n    abstract OffGameInteractor.OffGamePresenter presenter(OffGameView view);\n\n    @OffGameScope\n    @Provides\n    static OffGameRouter router(\n        Component component, OffGameView view, OffGameInteractor interactor) {\n      return new OffGameRouter(view, interactor, component);\n    }\n  }\n\n  @OffGameScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<OffGameInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(OffGameInteractor interactor);\n\n      @BindsInstance\n      Builder view(OffGameView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    OffGameRouter offgameRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface OffGameScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface OffGameInternal {}\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport androidx.annotation.Nullable;\nimport com.google.common.collect.ImmutableMap;\nimport com.uber.autodispose.AutoDispose;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedin.ScoreStream;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport javax.inject.Inject;\nimport javax.inject.Named;\n\n/** Coordinates Business Logic for {@link OffGameScope}. */\n@RibInteractor\npublic class OffGameInteractor\n    extends Interactor<OffGameInteractor.OffGamePresenter, OffGameRouter> {\n\n  @Inject\n  @Named(\"player_one\")\n  String playerOne;\n\n  @Inject\n  @Named(\"player_two\")\n  String playerTwo;\n\n  @Inject Listener listener;\n  @Inject OffGamePresenter presenter;\n  @Inject ScoreStream scoreStream;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    presenter.setPlayerNames(playerOne, playerTwo);\n    presenter\n        .startGameRequest()\n        .subscribe(\n            new Consumer<Object>() {\n              @Override\n              public void accept(Object object) throws Exception {\n                listener.onStartGame();\n              }\n            });\n\n    scoreStream\n        .scores()\n        .as(AutoDispose.<ImmutableMap<String, Integer>>autoDisposable(this))\n        .subscribe(\n            new Consumer<ImmutableMap<String, Integer>>() {\n              @Override\n              public void accept(ImmutableMap<String, Integer> scores) throws Exception {\n                Integer playerOneScore = scores.get(playerOne);\n                Integer playerTwoScore = scores.get(playerTwo);\n                presenter.setScores(playerOneScore, playerTwoScore);\n              }\n            });\n  }\n\n  public interface Listener {\n\n    void onStartGame();\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface OffGamePresenter {\n\n    void setPlayerNames(String playerOne, String playerTwo);\n\n    void setScores(Integer playerOneScore, Integer playerTwoScore);\n\n    Observable<Object> startGameRequest();\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link OffGameBuilder.OffGameScope}. */\npublic class OffGameRouter extends ViewRouter<OffGameView, OffGameInteractor> {\n\n  public OffGameRouter(\n      OffGameView view, OffGameInteractor interactor, OffGameBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.Button;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.core.Initializer;\nimport com.uber.rib.tutorial1.R;\nimport io.reactivex.Observable;\nimport java.util.Locale;\n\n/** Top level view for {@link OffGameBuilder.OffGameScope}. */\npublic class OffGameView extends LinearLayout implements OffGameInteractor.OffGamePresenter {\n\n  private Button button;\n  private TextView playerOneName;\n  private TextView playerTwoName;\n  private TextView playerOneScore;\n  private TextView playerTwoScore;\n\n  public OffGameView(Context context) {\n    this(context, null);\n  }\n\n  public OffGameView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public OffGameView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Initializer\n  @Override\n  protected void onFinishInflate() {\n    super.onFinishInflate();\n    button = (Button) findViewById(R.id.start_game_button);\n    playerOneName = (TextView) findViewById(R.id.player_one_name);\n    playerTwoName = (TextView) findViewById(R.id.player_two_name);\n    playerOneScore = (TextView) findViewById(R.id.player_one_win_count);\n    playerTwoScore = (TextView) findViewById(R.id.player_two_win_count);\n  }\n\n  @Override\n  public void setPlayerNames(String playerOne, String playerTwo) {\n    playerOneName.setText(playerOne);\n    playerTwoName.setText(playerTwo);\n  }\n\n  @Override\n  public void setScores(Integer playerOneScore, Integer playerTwoScore) {\n    this.playerOneScore.setText(\n        String.format(Locale.getDefault(), \"Win Count: %d\", playerOneScore));\n    this.playerTwoScore.setText(\n        String.format(Locale.getDefault(), \"Win Count: %d\", playerTwoScore));\n  }\n\n  @Override\n  public Observable<Object> startGameRequest() {\n    return RxView.clicks(button);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedin/tictactoe/Board.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport javax.inject.Inject;\n\nclass Board {\n\n  static final int ROWS = 3;\n  static final int COLS = 3;\n\n  MarkerType[][] cells;\n  int currentRow;\n  int currentCol;\n\n  @Inject\n  Board() {\n    cells = new MarkerType[ROWS][COLS];\n    for (int row = 0; row < ROWS; ++row) {\n      for (int col = 0; col < COLS; ++col) {\n        cells[row][col] = null;\n      }\n    }\n  }\n\n  /** Return true if it is a draw (i.e., no more EMPTY cell) */\n  boolean isDraw() {\n    for (int row = 0; row < ROWS; ++row) {\n      for (int col = 0; col < COLS; ++col) {\n        if (cells[row][col] == null) {\n          return false;\n        }\n      }\n    }\n    return !hasWon(MarkerType.CROSS) && !hasWon(MarkerType.NOUGHT);\n  }\n\n  /** Return true if the player with \"theSeed\" has won after placing at (currentRow, currentCol) */\n  boolean hasWon(MarkerType theSeed) {\n    return ((cells[currentRow][0] == theSeed\n            && cells[currentRow][1] == theSeed\n            && cells[currentRow][2] == theSeed)\n        || (cells[0][currentCol] == theSeed\n            && cells[1][currentCol] == theSeed\n            && cells[2][currentCol] == theSeed)\n        || (currentRow == currentCol\n            && cells[0][0] == theSeed\n            && cells[1][1] == theSeed\n            && cells[2][2] == theSeed)\n        || (currentRow + currentCol == 2\n            && cells[0][2] == theSeed\n            && cells[1][1] == theSeed\n            && cells[2][0] == theSeed));\n  }\n\n  enum MarkerType {\n    CROSS,\n    NOUGHT\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedin/tictactoe/BoardCoordinate.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nclass BoardCoordinate {\n\n  private final int x;\n  private final int y;\n\n  BoardCoordinate(int x, int y) {\n    this.x = x;\n    this.y = y;\n  }\n\n  int getX() {\n    return x;\n  }\n\n  int getY() {\n    return y;\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.tutorial1.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Named;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link TicTacToeScope}. */\npublic class TicTacToeBuilder\n    extends ViewBuilder<TicTacToeView, TicTacToeRouter, TicTacToeBuilder.ParentComponent> {\n\n  public TicTacToeBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link TicTacToeRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link TicTacToeRouter}.\n   */\n  public TicTacToeRouter build(ViewGroup parentViewGroup) {\n    TicTacToeView view = createView(parentViewGroup);\n    TicTacToeInteractor interactor = new TicTacToeInteractor();\n    Component component =\n        DaggerTicTacToeBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.tictactoeRouter();\n  }\n\n  @Override\n  protected TicTacToeView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (TicTacToeView) inflater.inflate(R.layout.tic_tac_toe_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n\n    TicTacToeInteractor.Listener ticTacToeListener();\n\n    @Named(\"player_one\")\n    String playerOne();\n\n    @Named(\"player_two\")\n    String playerTwo();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @TicTacToeScope\n    @Binds\n    abstract TicTacToeInteractor.TicTacToePresenter presenter(TicTacToeView view);\n\n    @TicTacToeScope\n    @Provides\n    static TicTacToeRouter router(\n        Component component, TicTacToeView view, TicTacToeInteractor interactor) {\n      return new TicTacToeRouter(view, interactor, component);\n    }\n  }\n\n  @TicTacToeScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<TicTacToeInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(TicTacToeInteractor interactor);\n\n      @BindsInstance\n      Builder view(TicTacToeView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    TicTacToeRouter tictactoeRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface TicTacToeScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface TicTacToeInternal {}\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedin.tictactoe.Board.MarkerType;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport javax.inject.Inject;\nimport javax.inject.Named;\n\n/** Coordinates Business Logic for {@link TicTacToeScope}. */\n@RibInteractor\npublic class TicTacToeInteractor\n    extends Interactor<TicTacToeInteractor.TicTacToePresenter, TicTacToeRouter> {\n\n  @Inject Board board;\n  @Inject Listener listener;\n  @Inject TicTacToePresenter presenter;\n\n  @Inject\n  @Named(\"player_one\")\n  String playerOne;\n\n  @Inject\n  @Named(\"player_two\")\n  String playerTwo;\n\n  private MarkerType currentPlayer = MarkerType.CROSS;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    presenter\n        .squareClicks()\n        .subscribe(\n            new Consumer<BoardCoordinate>() {\n              @Override\n              public void accept(BoardCoordinate xy) throws Exception {\n                if (board.cells[xy.getX()][xy.getY()] == null) {\n                  if (currentPlayer == MarkerType.CROSS) {\n                    board.cells[xy.getX()][xy.getY()] = MarkerType.CROSS;\n                    board.currentRow = xy.getX();\n                    board.currentCol = xy.getY();\n                    presenter.addCross(xy);\n                    currentPlayer = MarkerType.NOUGHT;\n                  } else {\n                    board.cells[xy.getX()][xy.getY()] = MarkerType.NOUGHT;\n                    board.currentRow = xy.getX();\n                    board.currentCol = xy.getY();\n                    presenter.addNought(xy);\n                    currentPlayer = MarkerType.CROSS;\n                  }\n                }\n                if (board.hasWon(MarkerType.CROSS)) {\n                  presenter.setPlayerWon(playerOne);\n                  listener.gameWon(playerOne);\n                } else if (board.hasWon(MarkerType.NOUGHT)) {\n                  presenter.setPlayerWon(playerTwo);\n                  listener.gameWon(playerTwo);\n                } else if (board.isDraw()) {\n                  presenter.setPlayerTie();\n                  listener.gameWon(null);\n                } else {\n                  updateCurrentPlayer();\n                }\n              }\n            });\n    updateCurrentPlayer();\n  }\n\n  private void updateCurrentPlayer() {\n    if (currentPlayer == MarkerType.CROSS) {\n      presenter.setCurrentPlayerName(playerOne);\n    } else {\n      presenter.setCurrentPlayerName(playerTwo);\n    }\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface TicTacToePresenter {\n    Observable<BoardCoordinate> squareClicks();\n\n    void setCurrentPlayerName(String currentPlayer);\n\n    void setPlayerWon(String playerName);\n\n    void setPlayerTie();\n\n    void addCross(BoardCoordinate xy);\n\n    void addNought(BoardCoordinate xy);\n  }\n\n  public interface Listener {\n\n    /**\n     * Called when the game is over.\n     *\n     * @param winner player that won, or null if it's a tie.\n     */\n    void gameWon(@Nullable String winner);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link TicTacToeBuilder.TicTacToeScope}. */\npublic class TicTacToeRouter extends ViewRouter<TicTacToeView, TicTacToeInteractor> {\n\n  public TicTacToeRouter(\n      TicTacToeView view, TicTacToeInteractor interactor, TicTacToeBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport androidx.percentlayout.widget.PercentRelativeLayout;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.core.Initializer;\nimport com.uber.rib.tutorial1.R;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Function;\nimport java.util.ArrayList;\n\n/** Top level view for {@link TicTacToeBuilder.TicTacToeScope}. */\npublic class TicTacToeView extends PercentRelativeLayout\n    implements TicTacToeInteractor.TicTacToePresenter {\n\n  private TextView[][] imageButtons;\n  private TextView titleView;\n\n  public TicTacToeView(Context context) {\n    this(context, null);\n  }\n\n  public TicTacToeView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public TicTacToeView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Initializer\n  @Override\n  protected void onFinishInflate() {\n    super.onFinishInflate();\n    imageButtons = new TextView[3][];\n    imageButtons[0] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button11),\n          (TextView) findViewById(R.id.button12),\n          (TextView) findViewById(R.id.button13)\n        };\n    imageButtons[1] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button21),\n          (TextView) findViewById(R.id.button22),\n          (TextView) findViewById(R.id.button23)\n        };\n    imageButtons[2] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button31),\n          (TextView) findViewById(R.id.button32),\n          (TextView) findViewById(R.id.button33)\n        };\n    titleView = (TextView) findViewById(R.id.title);\n  }\n\n  @Override\n  public Observable<BoardCoordinate> squareClicks() {\n    ArrayList<Observable<BoardCoordinate>> observables = new ArrayList<>();\n    for (int i = 0; i < 3; i++) {\n      for (int j = 0; j < 3; j++) {\n        final int finalI = i;\n        final int finalJ = j;\n        observables.add(\n            RxView.clicks(imageButtons[i][j])\n                .map(\n                    new Function<Object, BoardCoordinate>() {\n                      @Override\n                      public BoardCoordinate apply(Object irrelevant) throws Exception {\n                        return new BoardCoordinate(finalI, finalJ);\n                      }\n                    }));\n      }\n    }\n    return Observable.merge(observables);\n  }\n\n  @Override\n  public void addCross(BoardCoordinate xy) {\n    TextView textView = imageButtons[xy.getX()][xy.getY()];\n    textView.setText(\"x\");\n    textView.setClickable(false);\n  }\n\n  @Override\n  public void addNought(BoardCoordinate xy) {\n    TextView textView = imageButtons[xy.getX()][xy.getY()];\n    textView.setText(\"O\");\n    textView.setClickable(false);\n  }\n\n  @Override\n  public void setCurrentPlayerName(String currentPlayer) {\n    titleView.setText(\"Current Player: \" + currentPlayer);\n  }\n\n  @Override\n  public void setPlayerWon(String playerName) {\n    titleView.setText(\"Player won: \" + playerName + \"!!!\");\n  }\n\n  @Override\n  public void setPlayerTie() {\n    titleView.setText(\"Tie game!\");\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedout/LoggedOutBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.tutorial1.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link LoggedOutScope}. */\npublic class LoggedOutBuilder\n    extends ViewBuilder<LoggedOutView, LoggedOutRouter, LoggedOutBuilder.ParentComponent> {\n\n  public LoggedOutBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link LoggedOutRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link LoggedOutRouter}.\n   */\n  public LoggedOutRouter build(ViewGroup parentViewGroup) {\n    LoggedOutView view = createView(parentViewGroup);\n    LoggedOutInteractor interactor = new LoggedOutInteractor();\n    Component component =\n        DaggerLoggedOutBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.loggedoutRouter();\n  }\n\n  @Override\n  protected LoggedOutView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (LoggedOutView) inflater.inflate(R.layout.logged_out_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n\n    LoggedOutInteractor.Listener listener();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @LoggedOutScope\n    @Binds\n    abstract LoggedOutInteractor.LoggedOutPresenter presenter(LoggedOutView view);\n\n    @LoggedOutScope\n    @Provides\n    static LoggedOutRouter router(\n        Component component, LoggedOutView view, LoggedOutInteractor interactor) {\n      return new LoggedOutRouter(view, interactor, component);\n    }\n\n    // TODO: Create provider methods for dependencies created by this Rib. These should be static.\n  }\n\n  @LoggedOutScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<LoggedOutInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(LoggedOutInteractor interactor);\n\n      @BindsInstance\n      Builder view(LoggedOutView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    LoggedOutRouter loggedoutRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface LoggedOutScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface LoggedOutInternal {}\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedout/LoggedOutInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport androidx.annotation.Nullable;\nimport androidx.core.util.Pair;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder.LoggedOutScope;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link LoggedOutScope}. */\n@RibInteractor\npublic class LoggedOutInteractor\n    extends Interactor<LoggedOutInteractor.LoggedOutPresenter, LoggedOutRouter> {\n\n  @Inject Listener listener;\n  @Inject LoggedOutPresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n    presenter\n        .playerNames()\n        .subscribe(\n            new Consumer<Pair<String, String>>() {\n              @Override\n              public void accept(Pair<String, String> names) throws Exception {\n                if (!isEmpty(names.first) && !isEmpty(names.second)) {\n                  listener.requestLogin(names.first, names.second);\n                }\n              }\n            });\n  }\n\n  private boolean isEmpty(@Nullable String string) {\n    return string == null || string.length() == 0;\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface LoggedOutPresenter {\n\n    Observable<Pair<String, String>> playerNames();\n  }\n\n  public interface Listener {\n\n    void requestLogin(String playerOne, String playerTwo);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedout/LoggedOutRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link LoggedOutBuilder.LoggedOutScope}. */\npublic class LoggedOutRouter extends ViewRouter<LoggedOutView, LoggedOutInteractor> {\n\n  public LoggedOutRouter(\n      LoggedOutView view, LoggedOutInteractor interactor, LoggedOutBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/java/com/uber/rib/root/loggedout/LoggedOutView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.LinearLayout;\nimport androidx.annotation.Nullable;\nimport androidx.core.util.Pair;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.core.Initializer;\nimport com.uber.rib.tutorial1.R;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Function;\n\n/** Top level view for {@link LoggedOutBuilder.LoggedOutScope}. */\npublic class LoggedOutView extends LinearLayout implements LoggedOutInteractor.LoggedOutPresenter {\n\n  private Button loginButton;\n  private EditText playerOneEditText;\n  private EditText playerTwoEditText;\n\n  public LoggedOutView(Context context) {\n    this(context, null);\n  }\n\n  public LoggedOutView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public LoggedOutView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Initializer\n  @Override\n  protected void onFinishInflate() {\n    super.onFinishInflate();\n    playerOneEditText = (EditText) findViewById(R.id.player_one_name);\n    playerTwoEditText = (EditText) findViewById(R.id.player_two_name);\n    loginButton = (Button) findViewById(R.id.login_button);\n  }\n\n  @Override\n  public Observable<Pair<String, String>> playerNames() {\n    return RxView.clicks(findViewById(R.id.login_button))\n        .map(\n            new Function<Object, Pair<String, String>>() {\n              @Override\n              public Pair<String, String> apply(Object irrelevant) throws Exception {\n                return new Pair<>(\n                    playerOneEditText.getText().toString(), playerTwoEditText.getText().toString());\n              }\n            });\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/res/layout/logged_out_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedout.LoggedOutView\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    <Space\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"/>\n\n    <EditText\n      android:id=\"@+id/player_one_name\"\n      android:hint=\"@string/player_one_name\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:maxLines=\"1\"\n      android:layout_margin=\"16dp\"/>\n\n    <EditText\n      android:id=\"@+id/player_two_name\"\n      android:hint=\"@string/player_two_name\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:maxLines=\"1\"\n      android:layout_margin=\"16dp\"/>\n\n    <Button\n        android:id=\"@+id/login_button\"\n        android:text=\"Login\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"/>\n</com.uber.rib.root.loggedout.LoggedOutView>"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/res/layout/off_game_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedin.offgame.OffGameView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:gravity=\"bottom\"\n    android:orientation=\"vertical\">\n\n    <LinearLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:layout_gravity=\"center_horizontal\"\n      android:layout_margin=\"16dp\"\n      android:orientation=\"horizontal\">\n\n        <TextView\n          android:id=\"@+id/player_one_name\"\n          android:text=\"@string/player_one_name\"\n          android:layout_width=\"0dp\"\n          android:layout_height=\"wrap_content\"\n          android:layout_weight=\"3\"/>\n\n        <TextView\n          android:id=\"@+id/player_one_win_count\"\n          android:layout_width=\"0dp\"\n          android:layout_height=\"wrap_content\"\n          android:layout_weight=\"1\"\n          android:text=\"@string/win_count\"/>\n\n    </LinearLayout>\n\n    <LinearLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:orientation=\"horizontal\"\n      android:layout_gravity=\"center_horizontal\"\n      android:layout_margin=\"16dp\">\n\n        <TextView\n          android:id=\"@+id/player_two_name\"\n          android:text=\"@string/player_two_name\"\n          android:layout_width=\"0dp\"\n          android:layout_height=\"wrap_content\"\n          android:layout_weight=\"3\"/>\n\n        <TextView\n          android:id=\"@+id/player_two_win_count\"\n          android:layout_width=\"0dp\"\n          android:layout_height=\"wrap_content\"\n          android:layout_weight=\"1\"\n          android:text=\"@string/win_count\"/>\n\n    </LinearLayout>\n    <Button\n        android:id=\"@+id/start_game_button\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"\n        android:layout_gravity=\"center_horizontal\"\n        android:text=\"@string/start_game\"/>\n\n</com.uber.rib.root.loggedin.offgame.OffGameView>\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/res/layout/root_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.RootView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"/>\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/res/layout/tic_tac_toe_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedin.tictactoe.TicTacToeView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/tic_tac_toe\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <TextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"40dp\"\n        android:gravity=\"center_horizontal\"/>\n\n    <TextView\n        android:id=\"@+id/button11\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button12\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        android:layout_toRightOf=\"@id/button11\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button13\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        android:layout_toRightOf=\"@id/button12\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button21\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button11\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button22\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button11\"\n        android:layout_toRightOf=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button23\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button13\"\n        android:layout_toRightOf=\"@id/button22\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button31\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button32\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button22\"\n        android:layout_toRightOf=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button33\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button23\"\n        android:layout_toRightOf=\"@id/button22\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n</com.uber.rib.root.loggedin.tictactoe.TicTacToeView>\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/main/res/values/ub__strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">RIB Tutorial 3</string>\n    <string name=\"player_one_name\">Player One Name</string>\n    <string name=\"player_two_name\">Player Two Name</string>\n    <string name=\"login\">Login</string>\n    <string name=\"start_game\">Start game</string>\n    <string name=\"welcome_to_off_game\">Welcome FakeName!</string>\n    <string name=\"win_count\">win count</string>\n</resources>\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/test/java/com/uber/rib/root/RootInteractorTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class RootInteractorTest {\n\n  @Mock RootInteractor.RootPresenter presenter;\n  @Mock RootRouter router;\n\n  private RootInteractor interactor;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    interactor = TestRootInteractor.create(presenter);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/test/java/com/uber/rib/root/RootRouterTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport com.uber.rib.root.loggedin.LoggedInBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class RootRouterTest {\n\n  @Mock RootBuilder.Component component;\n  @Mock RootInteractor interactor;\n  @Mock RootView view;\n\n  private RootRouter router;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    router =\n        new RootRouter(\n            view,\n            interactor,\n            component,\n            new LoggedOutBuilder(component),\n            new LoggedInBuilder(component));\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/test/java/com/uber/rib/root/loggedin/LoggedInInteractorTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport com.uber.rib.core.EmptyPresenter;\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class LoggedInInteractorTest extends RibTestBasePlaceholder {\n\n  @Mock EmptyPresenter presenter;\n  @Mock LoggedInRouter router;\n  @Mock MutableScoreStream scoreStream;\n\n  private LoggedInInteractor interactor;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    interactor = TestLoggedInInteractor.create(scoreStream);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/test/java/com/uber/rib/root/loggedin/LoggedInRouterTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport android.view.ViewGroup;\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport com.uber.rib.root.loggedin.offgame.OffGameBuilder;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeBuilder;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class LoggedInRouterTest extends RibTestBasePlaceholder {\n\n  @Mock LoggedInBuilder.Component component;\n  @Mock LoggedInInteractor interactor;\n  @Mock ViewGroup parentView;\n  @Mock OffGameBuilder offGameBuilder;\n  @Mock TicTacToeBuilder ticTacToeBuilder;\n\n  private LoggedInRouter router;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    router =\n        new LoggedInRouter(interactor, component, parentView, offGameBuilder, ticTacToeBuilder);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/test/java/com/uber/rib/root/loggedin/offgame/OffGameInteractorTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport com.uber.rib.root.loggedin.ScoreStream;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class OffGameInteractorTest extends RibTestBasePlaceholder {\n\n  private final String playerOne = \"playerOne\";\n  private final String playerTwo = \"playerTwo\";\n\n  @Mock OffGameInteractor.Listener listener;\n  @Mock OffGameInteractor.OffGamePresenter presenter;\n  @Mock OffGameRouter router;\n  @Mock ScoreStream scoreStream;\n\n  private OffGameInteractor interactor;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    interactor =\n        TestOffGameInteractor.create(playerOne, playerTwo, listener, presenter, scoreStream);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/test/java/com/uber/rib/root/loggedin/offgame/OffGameRouterTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class OffGameRouterTest extends RibTestBasePlaceholder {\n\n  @Mock OffGameBuilder.Component component;\n  @Mock OffGameInteractor interactor;\n  @Mock OffGameView view;\n\n  private OffGameRouter router;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    router = new OffGameRouter(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/test/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeInteractorTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class TicTacToeInteractorTest extends RibTestBasePlaceholder {\n\n  private final String playerOne = \"playerOne\";\n  private final String playerTwo = \"playerTwo\";\n\n  @Mock Board board;\n  @Mock TicTacToeInteractor.Listener listener;\n  @Mock TicTacToeInteractor.TicTacToePresenter presenter;\n  @Mock TicTacToeRouter router;\n\n  private TicTacToeInteractor interactor;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    interactor = TestTicTacToeInteractor.create(board, listener, presenter, playerOne, playerTwo);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/test/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeRouterTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class TicTacToeRouterTest extends RibTestBasePlaceholder {\n\n  @Mock TicTacToeBuilder.Component component;\n  @Mock TicTacToeInteractor interactor;\n  @Mock TicTacToeView view;\n\n  private TicTacToeRouter router;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    router = new TicTacToeRouter(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/test/java/com/uber/rib/root/loggedout/LoggedOutInteractorTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport androidx.core.util.Pair;\nimport com.uber.rib.core.InteractorHelper;\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport io.reactivex.Observable;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class LoggedOutInteractorTest extends RibTestBasePlaceholder {\n\n  @Mock LoggedOutInteractor.Listener listener;\n  @Mock LoggedOutInteractor.LoggedOutPresenter presenter;\n  @Mock LoggedOutRouter router;\n\n  private LoggedOutInteractor interactor;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    interactor = TestLoggedOutInteractor.create(listener, presenter);\n  }\n\n  @Test\n  public void attach_whenViewEmitsName_shouldCallListener() {\n    String fakeName1 = \"1\";\n    String fakeName2 = \"2\";\n    when(presenter.playerNames()).thenReturn(Observable.just(new Pair<>(fakeName1, fakeName2)));\n\n    InteractorHelper.attach(interactor, presenter, router, null);\n\n    verify(listener).requestLogin(any(String.class), any(String.class));\n  }\n\n  @Test\n  public void attach_whenViewEmitsEmptyName_shouldNotCallListener() {\n    when(presenter.playerNames()).thenReturn(Observable.just(new Pair<String, String>(\"\", \"\")));\n\n    InteractorHelper.attach(interactor, presenter, router, null);\n\n    verify(listener, never()).requestLogin(any(String.class), any(String.class));\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial3-completed/src/test/java/com/uber/rib/root/loggedout/LoggedOutRouterTest.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport com.uber.rib.core.RibTestBasePlaceholder;\nimport org.junit.Before;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\npublic class LoggedOutRouterTest extends RibTestBasePlaceholder {\n\n  @Mock LoggedOutBuilder.Component component;\n  @Mock LoggedOutInteractor interactor;\n  @Mock LoggedOutView view;\n\n  private LoggedOutRouter router;\n\n  @Before\n  public void setup() {\n    MockitoAnnotations.initMocks(this);\n\n    router = new LoggedOutRouter(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/README.md",
    "content": "# RIB Tutorial 4: Deep Linking Workflows\n\nThis project is the starting point for [tutorial 4](https://github.com/uber/RIBs/wiki/Android-Tutorial-4).\n\n### Getting started\nRun the code by\n\n```\n./gradlew :tutorials:tutorial4:installDebug\n```\n\nThen follow the steps described in [tutorial 4](https://github.com/uber/RIBs/wiki/Android-Tutorial-4) on the RIBs wiki.\n"
  },
  {
    "path": "tutorials/tutorial4/build.gradle.kts",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id(\"ribs.android.application\")\n    alias(libs.plugins.kotlin.kapt)\n    alias(libs.plugins.kotlin.ksp)\n}\n\nandroid {\n    namespace = \"com.uber.rib.tutorial4\"\n\n    defaultConfig {\n        applicationId = \"com.uber.tutorial3\"\n    }\n}\n\ndependencies {\n    ksp(libs.dagger.compiler)\n    kapt(project(\":libraries:rib-compiler-test\"))\n    implementation(project(\":libraries:rib-android\"))\n    implementation(project(\":libraries:rib-workflow\"))\n    implementation(libs.androidx.annotation)\n    implementation(libs.androidx.appcompat)\n    implementation(libs.dagger.library)\n    implementation(libs.rxbinding)\n    implementation(appLibs.percent)\n    implementation(libs.guava.android)\n    compileOnly(appLibs.jsr250)\n    testImplementation(project(\":libraries:rib-test\"))\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <uses-feature android:required=\"true\" android:glEsVersion='0x00020000'/>\n\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>\n\n    <application\n        android:name=\"com.uber.rib.SampleApplication\"\n        android:allowBackup=\"false\"\n        android:icon=\"@drawable/ub__ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:screenOrientation=\"portrait\"\n        android:theme=\"@style/Theme.AppCompat.Light\"\n        tools:replace=\"android:theme\">\n        <activity\n            android:name=\"com.uber.rib.RootActivity\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\"/>\n                <category android:name=\"android.intent.category.DEFAULT\"/>\n                <category android:name=\"android.intent.category.BROWSABLE\"/>\n                <data android:scheme=\"rib-tutorials\"/>\n            </intent-filter>\n        </activity>\n    </application>\n</manifest>\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/RootActivity.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.view.ViewGroup;\nimport androidx.annotation.Nullable;\nimport com.google.common.base.Optional;\nimport com.uber.autodispose.AutoDispose;\nimport com.uber.rib.core.RibActivity;\nimport com.uber.rib.core.ViewRouter;\nimport com.uber.rib.root.RootBuilder;\nimport com.uber.rib.root.RootInteractor;\nimport com.uber.rib.root.RootRouter;\nimport com.uber.rib.root.RootWorkflow;\nimport com.uber.rib.root.WorkflowFactory;\nimport io.reactivex.functions.Consumer;\n\n/** The sample app's single activity. */\npublic class RootActivity extends RibActivity {\n\n  private RootInteractor rootInteractor;\n\n  @SuppressWarnings(\"unchecked\")\n  @Override\n  protected ViewRouter<?, ?> createRouter(ViewGroup parentViewGroup) {\n    RootBuilder rootBuilder = new RootBuilder(new RootBuilder.ParentComponent() {});\n    RootRouter router = rootBuilder.build(parentViewGroup);\n    rootInteractor = router.getInteractor();\n    return router;\n  }\n\n  @Override\n  protected void onCreate(@Nullable Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    if (getIntent() != null) {\n      handleDeepLink(getIntent());\n    }\n  }\n\n  private void handleDeepLink(Intent intent) {\n    RootWorkflow<RootReturnValue, ?> rootWorkflow = new WorkflowFactory().getWorkflow(intent);\n    if (rootWorkflow != null) {\n\n      rootWorkflow\n          .createSingle(rootInteractor)\n          .as(AutoDispose.<Optional<RootReturnValue>>autoDisposable(this))\n          .subscribe(\n              new Consumer<Optional<?>>() {\n                @Override\n                public void accept(Optional<?> optional) throws Exception {}\n              });\n    }\n  }\n\n  private class RootReturnValue {}\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/SampleApplication.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib;\n\nimport android.app.Application;\n\npublic class SampleApplication extends Application {}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/RootActionableItem.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport com.uber.rib.workflow.core.ActionableItem;\n\npublic interface RootActionableItem extends ActionableItem {}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/RootBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.root.loggedin.LoggedInBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutInteractor;\nimport com.uber.rib.tutorial4.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Scope;\n\n/** Builder for the {@link RootScope}. */\npublic class RootBuilder extends ViewBuilder<RootView, RootRouter, RootBuilder.ParentComponent> {\n\n  public RootBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link RootRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link RootRouter}.\n   */\n  public RootRouter build(ViewGroup parentViewGroup) {\n    RootView view = createView(parentViewGroup);\n    RootInteractor interactor = new RootInteractor();\n    Component component =\n        DaggerRootBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.rootRouter();\n  }\n\n  @Override\n  protected RootView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (RootView) inflater.inflate(R.layout.root_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n    // Define dependencies required from your parent interactor here.\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @RootScope\n    @Provides\n    static LoggedOutInteractor.Listener loggedOutListener(RootInteractor rootInteractor) {\n      return rootInteractor.new LoggedOutListener();\n    }\n\n    @RootScope\n    @Binds\n    abstract RootInteractor.RootPresenter presenter(RootView view);\n\n    @RootScope\n    @Provides\n    static RootRouter router(Component component, RootView view, RootInteractor interactor) {\n      return new RootRouter(\n          view,\n          interactor,\n          component,\n          new LoggedOutBuilder(component),\n          new LoggedInBuilder(component));\n    }\n  }\n\n  @RootScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component\n      extends InteractorBaseComponent<RootInteractor>,\n          LoggedOutBuilder.ParentComponent,\n          LoggedInBuilder.ParentComponent,\n          BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(RootInteractor interactor);\n\n      @BindsInstance\n      Builder view(RootView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    RootRouter rootRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface RootScope {}\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/RootInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.loggedout.LoggedOutInteractor;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link RootBuilder.RootScope}. */\n@RibInteractor\npublic class RootInteractor extends Interactor<RootInteractor.RootPresenter, RootRouter>\n    implements RootActionableItem {\n\n  @Inject RootPresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n    getRouter().attachLoggedOut();\n  }\n\n  class LoggedOutListener implements LoggedOutInteractor.Listener {\n\n    @Override\n    public void requestLogin(UserName playerOne, UserName playerTwo) {\n      // Switch to logged in. Let’s just ignore userName for now.\n      getRouter().detachLoggedOut();\n      getRouter().attachLoggedIn(playerOne, playerTwo);\n    }\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface RootPresenter {}\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/RootRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport androidx.annotation.Nullable;\nimport com.google.common.base.Optional;\nimport com.jakewharton.rxrelay2.BehaviorRelay;\nimport com.uber.rib.core.ViewRouter;\nimport com.uber.rib.root.loggedin.LoggedInActionableItem;\nimport com.uber.rib.root.loggedin.LoggedInBuilder;\nimport com.uber.rib.root.loggedin.LoggedInRouter;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder;\nimport com.uber.rib.root.loggedout.LoggedOutRouter;\n\n/** Adds and removes children of {@link RootBuilder.RootScope}. */\npublic class RootRouter extends ViewRouter<RootView, RootInteractor> {\n\n  private final LoggedOutBuilder loggedOutBuilder;\n  private final LoggedInBuilder loggedInBuilder;\n  @Nullable private LoggedOutRouter loggedOutRouter;\n\n  private final BehaviorRelay<Optional<LoggedInActionableItem>> loggedInActionableItemRelay =\n      BehaviorRelay.create();\n\n  RootRouter(\n      RootView view,\n      RootInteractor interactor,\n      RootBuilder.Component component,\n      LoggedOutBuilder loggedOutBuilder,\n      LoggedInBuilder loggedInBuilder) {\n    super(view, interactor, component);\n    this.loggedOutBuilder = loggedOutBuilder;\n    this.loggedInBuilder = loggedInBuilder;\n  }\n\n  void attachLoggedOut() {\n    loggedOutRouter = loggedOutBuilder.build(getView());\n    attachChild(loggedOutRouter);\n    getView().addView(loggedOutRouter.getView());\n  }\n\n  void detachLoggedOut() {\n    if (loggedOutRouter != null) {\n      detachChild(loggedOutRouter);\n      getView().removeView(loggedOutRouter.getView());\n      loggedOutRouter = null;\n    }\n  }\n\n  LoggedInActionableItem attachLoggedIn(UserName playerOne, UserName playerTwo) {\n    // No need to attach views in any way.\n    LoggedInRouter loggedInRouter = loggedInBuilder.build(playerOne, playerTwo);\n    attachChild(loggedInRouter);\n    loggedInActionableItemRelay.accept(\n        Optional.<LoggedInActionableItem>of(loggedInRouter.getInteractor()));\n    return loggedInRouter.getInteractor();\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/RootView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.FrameLayout;\nimport androidx.annotation.Nullable;\n\n/** Top level view for {@link RootBuilder.RootScope}. */\npublic class RootView extends FrameLayout implements RootInteractor.RootPresenter {\n\n  public RootView(Context context) {\n    this(context, null);\n  }\n\n  public RootView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public RootView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/RootWorkflow.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport android.content.Intent;\nimport com.uber.rib.workflow.core.Step;\nimport com.uber.rib.workflow.core.Workflow;\nimport java.io.Serializable;\n\n/**\n * Represents a workflow that is launched starting at {@link RootActionableItem}.\n *\n * @param <TWorkflowReturnType> the type of value returned by the workflow (if any).\n * @param <TDeepLinkModel> the type of deep link model used by this workflow.\n */\npublic abstract class RootWorkflow<TWorkflowReturnType, TDeepLinkModel extends RootWorkflowModel>\n    extends Workflow<TWorkflowReturnType, RootActionableItem> implements Serializable {\n\n  private final TDeepLinkModel deepLinkModel;\n\n  /**\n   * Constructor to create a {@link RootWorkflow} from an {@link Intent}.\n   *\n   * @param deepLinkIntent the raw deep link {@link Intent} to parse for the workflow.\n   */\n  public RootWorkflow(Intent deepLinkIntent) {\n    this.deepLinkModel = parseDeepLinkIntent(deepLinkIntent);\n  }\n\n  /**\n   * Constructor to create a {@link RootWorkflow} from a deep link model.\n   *\n   * @param deepLinkModel\n   */\n  public RootWorkflow(TDeepLinkModel deepLinkModel) {\n    this.deepLinkModel = deepLinkModel;\n  }\n\n  @Override\n  protected final Step<TWorkflowReturnType, ?> getSteps(RootActionableItem rootActionableItem) {\n    return getSteps(rootActionableItem, deepLinkModel);\n  }\n\n  /**\n   * @return the model for the workflow.\n   */\n  public TDeepLinkModel getDeepLinkModel() {\n    return deepLinkModel;\n  }\n\n  /**\n   * @param rootActionableItem to create steps with.\n   * @param deepLinkModel to create deep link data from.\n   * @return steps to be performed for this workflow.\n   */\n  protected abstract Step<TWorkflowReturnType, ?> getSteps(\n      RootActionableItem rootActionableItem, TDeepLinkModel deepLinkModel);\n\n  /**\n   * Responsible for turning a raw URI into a deep link model with a rigid schema.\n   *\n   * @param deepLinkIntent the raw deep link {@link Intent}.\n   * @return the deep link model.\n   */\n  protected abstract TDeepLinkModel parseDeepLinkIntent(Intent deepLinkIntent);\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/RootWorkflowModel.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport java.io.Serializable;\n\n/** Represents a parsed deep link model. */\npublic interface RootWorkflowModel extends Serializable {}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/UserName.kt",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root\n\ndata class UserName(val userName: String)\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/WorkflowFactory.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root;\n\nimport android.content.Intent;\nimport androidx.annotation.Nullable;\n\npublic class WorkflowFactory {\n  @Nullable\n  public RootWorkflow getWorkflow(Intent intent) {\n    // If this was a real app you would likely write a pattern for each workflow object to\n    // independently declare which intent it applied to. Then you would pick the first match.\n    // Instead lets just do some simple if-else branches here.\n    if (intent != null && intent.getData() != null) {\n      // TODO: return a workflow here\n      return null;\n    }\n    return null;\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/GameKey.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\npublic interface GameKey {\n  String gameName();\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/GameProvider.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport android.view.ViewGroup;\nimport com.uber.rib.core.ViewRouter;\n\npublic interface GameProvider extends GameKey {\n  String gameName();\n\n  ViewRouter viewRouter(ViewGroup viewGroup);\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/LoggedInActionableItem.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport com.uber.rib.workflow.core.ActionableItem;\n\npublic interface LoggedInActionableItem extends ActionableItem {}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/LoggedInBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.ViewGroup;\nimport com.google.common.collect.Lists;\nimport com.uber.rib.core.Builder;\nimport com.uber.rib.core.EmptyPresenter;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewRouter;\nimport com.uber.rib.root.RootView;\nimport com.uber.rib.root.UserName;\nimport com.uber.rib.root.loggedin.offgame.OffGameBuilder;\nimport com.uber.rib.root.loggedin.offgame.OffGameInteractor;\nimport com.uber.rib.root.loggedin.randomWinner.RandomWinnerBuilder;\nimport com.uber.rib.root.loggedin.randomWinner.RandomWinnerInteractor;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeBuilder;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeInteractor;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport java.util.List;\nimport javax.inject.Named;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\npublic class LoggedInBuilder extends Builder<LoggedInRouter, LoggedInBuilder.ParentComponent> {\n\n  public LoggedInBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link LoggedInRouter}.\n   *\n   * @return a new {@link LoggedInRouter}.\n   */\n  public LoggedInRouter build(UserName playerOne, UserName playerTwo) {\n    LoggedInInteractor interactor = new LoggedInInteractor();\n    Component component =\n        DaggerLoggedInBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .interactor(interactor)\n            .playerOne(playerOne)\n            .playerTwo(playerTwo)\n            .build();\n\n    return component.loggedinRouter();\n  }\n\n  public interface ParentComponent {\n\n    RootView rootView();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @LoggedInScope\n    @Provides\n    static EmptyPresenter presenter() {\n      return new EmptyPresenter();\n    }\n\n    @LoggedInScope\n    @Provides\n    static LoggedInRouter router(\n        Component component, LoggedInInteractor interactor, RootView rootView) {\n      return new LoggedInRouter(\n          interactor,\n          component,\n          rootView,\n          new OffGameBuilder(component),\n          new TicTacToeBuilder(component));\n    }\n\n    @LoggedInScope\n    @LoggedInInternal\n    @Provides\n    static MutableScoreStream mutableScoreStream(\n        @Named(\"player_one\") UserName playerOne, @Named(\"player_two\") UserName playerTwo) {\n      return new MutableScoreStream(playerOne, playerTwo);\n    }\n\n    @LoggedInScope\n    @Provides\n    static OffGameInteractor.Listener listener(LoggedInInteractor interactor) {\n      return interactor.new OffGameListener();\n    }\n\n    @LoggedInScope\n    @Binds\n    abstract RandomWinnerInteractor.Listener randomWinnerListener(\n        LoggedInInteractor.GameListener listener);\n\n    @LoggedInScope\n    @Binds\n    abstract TicTacToeInteractor.Listener ticTacToeGameListener(\n        LoggedInInteractor.GameListener listener);\n\n    @LoggedInScope\n    @Provides\n    static LoggedInInteractor.GameListener ticTacToeListener(LoggedInInteractor interactor) {\n      return interactor.new GameListener();\n    }\n\n    @LoggedInScope\n    @Binds\n    abstract ScoreStream scoreStream(@LoggedInInternal MutableScoreStream mutableScoreStream);\n\n    @LoggedInInternal\n    @Provides\n    static List<GameProvider> gameProviders(final Component component) {\n      // Decorate the game builders with a \"name\" key so we can treat them generically elsewhere.\n      GameProvider ticTacToeGame =\n          new GameProvider() {\n            @Override\n            public String gameName() {\n              return \"TicTacToe\";\n            }\n\n            @Override\n            public ViewRouter viewRouter(ViewGroup viewGroup) {\n              return new TicTacToeBuilder(component).build(viewGroup);\n            }\n          };\n      GameProvider randomWinnerGame =\n          new GameProvider() {\n            @Override\n            public String gameName() {\n              return \"RandomWinner\";\n            }\n\n            @Override\n            public ViewRouter viewRouter(ViewGroup viewGroup) {\n              return new RandomWinnerBuilder(component).build(viewGroup);\n            }\n          };\n      return Lists.newArrayList(ticTacToeGame, randomWinnerGame);\n    }\n\n    @LoggedInScope\n    @Binds\n    abstract List<? extends GameKey> gameKeys(@LoggedInInternal List<GameProvider> gameProviders);\n  }\n\n  @LoggedInScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  public interface Component\n      extends InteractorBaseComponent<LoggedInInteractor>,\n          BuilderComponent,\n          OffGameBuilder.ParentComponent,\n          TicTacToeBuilder.ParentComponent,\n          RandomWinnerBuilder.ParentComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(LoggedInInteractor interactor);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n\n      @BindsInstance\n      Builder playerOne(@Named(\"player_one\") UserName playerOne);\n\n      @BindsInstance\n      Builder playerTwo(@Named(\"player_two\") UserName playerTwo);\n    }\n  }\n\n  interface BuilderComponent {\n    LoggedInRouter loggedinRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface LoggedInScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface LoggedInInternal {}\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/LoggedInInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.EmptyPresenter;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.UserName;\nimport com.uber.rib.root.loggedin.LoggedInBuilder.LoggedInInternal;\nimport com.uber.rib.root.loggedin.offgame.OffGameInteractor;\nimport com.uber.rib.root.loggedin.randomWinner.RandomWinnerInteractor;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeInteractor;\nimport java.util.List;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link LoggedInScope}. */\n@RibInteractor\npublic class LoggedInInteractor extends Interactor<EmptyPresenter, LoggedInRouter>\n    implements LoggedInActionableItem {\n\n  @Inject @LoggedInInternal MutableScoreStream scoreStream;\n  @Inject @LoggedInInternal List<GameProvider> gameProviders;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    // when first logging in we should be in the OffGame state\n    getRouter().attachOffGame();\n  }\n\n  class OffGameListener implements OffGameInteractor.Listener {\n\n    @Override\n    public void onStartGame(GameKey gameKey) {\n      getRouter().detachOffGame();\n      for (GameProvider gameProvider : gameProviders) {\n        if (gameProvider.gameName().equals(gameKey.gameName())) {\n          getRouter().attachGame(gameProvider);\n        }\n      }\n    }\n  }\n\n  class GameListener implements TicTacToeInteractor.Listener, RandomWinnerInteractor.Listener {\n\n    @Override\n    public void gameWon(UserName winner) {\n      if (winner != null) {\n        scoreStream.addVictory(winner);\n      }\n\n      getRouter().detachGame();\n      getRouter().attachOffGame();\n    }\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/LoggedInRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport android.view.ViewGroup;\nimport com.uber.rib.core.Router;\nimport com.uber.rib.core.ViewRouter;\nimport com.uber.rib.root.loggedin.offgame.OffGameBuilder;\nimport com.uber.rib.root.loggedin.offgame.OffGameRouter;\nimport com.uber.rib.root.loggedin.tictactoe.TicTacToeBuilder;\n\n/** Adds and removes children of {@link LoggedInBuilder.LoggedInScope}. */\npublic class LoggedInRouter extends Router<LoggedInInteractor> {\n\n  private final ViewGroup parentView;\n  private final OffGameBuilder offGameBuilder;\n  private final TicTacToeBuilder ticTacToeBuilder;\n  private OffGameRouter offGameRouter;\n  private ViewRouter gameRouter;\n\n  LoggedInRouter(\n      LoggedInInteractor interactor,\n      LoggedInBuilder.Component component,\n      ViewGroup parentView,\n      OffGameBuilder offGameBuilder,\n      TicTacToeBuilder ticTacToeBuilder) {\n    super(interactor, component);\n    this.parentView = parentView;\n    this.offGameBuilder = offGameBuilder;\n    this.ticTacToeBuilder = ticTacToeBuilder;\n  }\n\n  @Override\n  protected void willDetach() {\n    super.willDetach();\n    detachOffGame();\n    detachGame();\n  }\n\n  void attachOffGame() {\n    offGameRouter = offGameBuilder.build(parentView);\n    attachChild(offGameRouter);\n    parentView.addView(offGameRouter.getView());\n  }\n\n  void detachOffGame() {\n    if (offGameRouter != null) {\n      detachChild(offGameRouter);\n      parentView.removeView(offGameRouter.getView());\n      offGameRouter = null;\n    }\n  }\n\n  void attachGame(GameProvider gameProvider) {\n    gameRouter = gameProvider.viewRouter(parentView);\n    parentView.addView(gameRouter.getView());\n    attachChild(gameRouter);\n  }\n\n  void detachGame() {\n    if (gameRouter != null) {\n      detachChild(gameRouter);\n      parentView.removeView(gameRouter.getView());\n      gameRouter = null;\n    }\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/MutableScoreStream.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport com.google.common.collect.ImmutableMap;\nimport com.jakewharton.rxrelay2.BehaviorRelay;\nimport com.uber.rib.root.UserName;\nimport io.reactivex.Observable;\nimport java.util.Map;\n\nclass MutableScoreStream implements ScoreStream {\n\n  private final BehaviorRelay<ImmutableMap<UserName, Integer>> scoresRelay = BehaviorRelay.create();\n\n  MutableScoreStream(UserName playerOne, UserName playerTwo) {\n    scoresRelay.accept(ImmutableMap.of(playerOne, 0, playerTwo, 0));\n  }\n\n  void addVictory(UserName userName) {\n    ImmutableMap<UserName, Integer> currentScores = scoresRelay.getValue();\n\n    ImmutableMap.Builder<UserName, Integer> newScoreMapBuilder = new ImmutableMap.Builder<>();\n    for (Map.Entry<UserName, Integer> entry : currentScores.entrySet()) {\n      if (entry.getKey().equals(userName)) {\n        newScoreMapBuilder.put(entry.getKey(), entry.getValue() + 1);\n      } else {\n        newScoreMapBuilder.put(entry.getKey(), entry.getValue());\n      }\n    }\n\n    scoresRelay.accept(newScoreMapBuilder.build());\n  }\n\n  @Override\n  public Observable<ImmutableMap<UserName, Integer>> scores() {\n    return scoresRelay.hide();\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/ScoreStream.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin;\n\nimport com.google.common.collect.ImmutableMap;\nimport com.uber.rib.root.UserName;\nimport io.reactivex.Observable;\n\npublic interface ScoreStream {\n  Observable<ImmutableMap<UserName, Integer>> scores();\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.root.UserName;\nimport com.uber.rib.root.loggedin.GameKey;\nimport com.uber.rib.root.loggedin.ScoreStream;\nimport com.uber.rib.tutorial4.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport java.util.List;\nimport javax.inject.Named;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link OffGameScope}. */\npublic class OffGameBuilder\n    extends ViewBuilder<OffGameView, OffGameRouter, OffGameBuilder.ParentComponent> {\n\n  public OffGameBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link OffGameRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link OffGameRouter}.\n   */\n  public OffGameRouter build(ViewGroup parentViewGroup) {\n    OffGameView view = createView(parentViewGroup);\n    OffGameInteractor interactor = new OffGameInteractor();\n    Component component =\n        DaggerOffGameBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.offgameRouter();\n  }\n\n  @Override\n  protected OffGameView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (OffGameView) inflater.inflate(R.layout.off_game_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n    @Named(\"player_one\")\n    UserName playerOne();\n\n    @Named(\"player_two\")\n    UserName playerTwo();\n\n    OffGameInteractor.Listener listener();\n\n    ScoreStream scoreStream();\n\n    List<? extends GameKey> gameKeys();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @OffGameScope\n    @Binds\n    abstract OffGameInteractor.OffGamePresenter presenter(OffGameView view);\n\n    @OffGameScope\n    @Provides\n    static OffGameRouter router(\n        Component component, OffGameView view, OffGameInteractor interactor) {\n      return new OffGameRouter(view, interactor, component);\n    }\n  }\n\n  @OffGameScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<OffGameInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(OffGameInteractor interactor);\n\n      @BindsInstance\n      Builder view(OffGameView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n    OffGameRouter offgameRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface OffGameScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface OffGameInternal {}\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport androidx.annotation.Nullable;\nimport com.google.common.collect.ImmutableMap;\nimport com.uber.autodispose.AutoDispose;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.UserName;\nimport com.uber.rib.root.loggedin.GameKey;\nimport com.uber.rib.root.loggedin.ScoreStream;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport java.util.List;\nimport javax.inject.Inject;\nimport javax.inject.Named;\n\n@RibInteractor\npublic class OffGameInteractor\n    extends Interactor<OffGameInteractor.OffGamePresenter, OffGameRouter> {\n\n  @Inject\n  @Named(\"player_one\")\n  UserName playerOne;\n\n  @Inject\n  @Named(\"player_two\")\n  UserName playerTwo;\n\n  @Inject Listener listener;\n  @Inject OffGamePresenter presenter;\n  @Inject ScoreStream scoreStream;\n  @Inject List<? extends GameKey> gameNames;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    presenter.setPlayerNames(playerOne.getUserName(), playerTwo.getUserName());\n    presenter\n        .startGameRequest(gameNames)\n        .subscribe(\n            new Consumer<GameKey>() {\n              @Override\n              public void accept(GameKey gameKey) throws Exception {\n                listener.onStartGame(gameKey);\n              }\n            });\n\n    scoreStream\n        .scores()\n        .as(AutoDispose.<ImmutableMap<UserName, Integer>>autoDisposable(this))\n        .subscribe(\n            new Consumer<ImmutableMap<UserName, Integer>>() {\n              @Override\n              public void accept(ImmutableMap<UserName, Integer> scores) throws Exception {\n                Integer playerOneScore = scores.get(playerOne);\n                Integer playerTwoScore = scores.get(playerTwo);\n                presenter.setScores(playerOneScore, playerTwoScore);\n              }\n            });\n  }\n\n  public interface Listener {\n\n    void onStartGame(GameKey gameKey);\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface OffGamePresenter {\n\n    void setPlayerNames(String playerOne, String playerTwo);\n\n    void setScores(Integer playerOneScore, Integer playerTwoScore);\n\n    Observable<GameKey> startGameRequest(List<? extends GameKey> gameKeys);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link OffGameBuilder.OffGameScope}. */\npublic class OffGameRouter extends ViewRouter<OffGameView, OffGameInteractor> {\n\n  public OffGameRouter(\n      OffGameView view, OffGameInteractor interactor, OffGameBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/offgame/OffGameView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.offgame;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.LayoutInflater;\nimport android.widget.Button;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.core.Initializer;\nimport com.uber.rib.root.loggedin.GameKey;\nimport com.uber.rib.tutorial4.R;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Function;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\n\n/** Top level view for {@link OffGameBuilder.OffGameScope}. */\npublic class OffGameView extends LinearLayout implements OffGameInteractor.OffGamePresenter {\n\n  private TextView playerOneName;\n  private TextView playerTwoName;\n  private TextView playerOneScore;\n  private TextView playerTwoScore;\n\n  public OffGameView(Context context) {\n    this(context, null);\n  }\n\n  public OffGameView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public OffGameView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Initializer\n  @Override\n  protected void onFinishInflate() {\n    super.onFinishInflate();\n    playerOneName = (TextView) findViewById(R.id.player_one_name);\n    playerTwoName = (TextView) findViewById(R.id.player_two_name);\n    playerOneScore = (TextView) findViewById(R.id.player_one_win_count);\n    playerTwoScore = (TextView) findViewById(R.id.player_two_win_count);\n  }\n\n  @Override\n  public void setPlayerNames(String playerOne, String playerTwo) {\n    playerOneName.setText(playerOne);\n    playerTwoName.setText(playerTwo);\n  }\n\n  @Override\n  public void setScores(Integer playerOneScore, Integer playerTwoScore) {\n    this.playerOneScore.setText(\n        String.format(Locale.getDefault(), \"Win Count: %d\", playerOneScore));\n    this.playerTwoScore.setText(\n        String.format(Locale.getDefault(), \"Win Count: %d\", playerTwoScore));\n  }\n\n  @Override\n  public Observable<GameKey> startGameRequest(List<? extends GameKey> gameKeys) {\n    List<Observable<GameKey>> observables = new ArrayList<>();\n    for (final GameKey gameKey : gameKeys) {\n      Button button =\n          (Button) LayoutInflater.from(getContext()).inflate(R.layout.game_button, this, false);\n      button.setText(gameKey.gameName());\n      Observable<GameKey> observable =\n          RxView.clicks(button)\n              .map(\n                  new Function<Object, GameKey>() {\n                    @Override\n                    public GameKey apply(Object o) throws Exception {\n                      return gameKey;\n                    }\n                  });\n      observables.add(observable);\n      addView(button);\n    }\n    return Observable.merge(observables);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/randomWinner/RandomWinnerBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.randomWinner;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.root.UserName;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Named;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/**\n * Builder for the {@link RandomWinnerScope}. Not a real game. This just picks a random winner than\n * exits.\n */\npublic class RandomWinnerBuilder\n    extends ViewBuilder<RandomWinnerView, RandomWinnerRouter, RandomWinnerBuilder.ParentComponent> {\n\n  public RandomWinnerBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link RandomWinnerRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link RandomWinnerRouter}.\n   */\n  public RandomWinnerRouter build(ViewGroup parentViewGroup) {\n    RandomWinnerView view = createView(parentViewGroup);\n    RandomWinnerInteractor interactor = new RandomWinnerInteractor();\n    Component component =\n        DaggerRandomWinnerBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.randomwinnerRouter();\n  }\n\n  @Override\n  protected RandomWinnerView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    // Just inflate a silly useless view that does nothing.\n    return new RandomWinnerView(parentViewGroup.getContext());\n  }\n\n  public interface ParentComponent {\n    RandomWinnerInteractor.Listener randomWinnerListener();\n\n    @Named(\"player_one\")\n    UserName playerOne();\n\n    @Named(\"player_two\")\n    UserName playerTwo();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @RandomWinnerScope\n    @Binds\n    abstract RandomWinnerInteractor.RandomWinnerPresenter presenter(RandomWinnerView view);\n\n    @RandomWinnerScope\n    @Provides\n    static RandomWinnerRouter router(\n        Component component, RandomWinnerView view, RandomWinnerInteractor interactor) {\n      return new RandomWinnerRouter(view, interactor, component);\n    }\n  }\n\n  @RandomWinnerScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<RandomWinnerInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n      @BindsInstance\n      Builder interactor(RandomWinnerInteractor interactor);\n\n      @BindsInstance\n      Builder view(RandomWinnerView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n    RandomWinnerRouter randomwinnerRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface RandomWinnerScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface RandomWinnerInternal {}\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/randomWinner/RandomWinnerInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.randomWinner;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.UserName;\nimport java.util.Random;\nimport javax.inject.Inject;\nimport javax.inject.Named;\n\n@RibInteractor\npublic class RandomWinnerInteractor\n    extends Interactor<RandomWinnerInteractor.RandomWinnerPresenter, RandomWinnerRouter> {\n\n  @Inject Listener listener;\n\n  @Inject\n  @Named(\"player_one\")\n  UserName playerOne;\n\n  @Inject\n  @Named(\"player_two\")\n  UserName playerTwo;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n    if (new Random(System.currentTimeMillis()).nextBoolean()) {\n      listener.gameWon(playerOne);\n    } else {\n      listener.gameWon(playerTwo);\n    }\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface RandomWinnerPresenter {}\n\n  public interface Listener {\n\n    /**\n     * Called when the game is over.\n     *\n     * @param winner player that won, or null if it's a tie.\n     */\n    void gameWon(UserName winner);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/randomWinner/RandomWinnerRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.randomWinner;\n\nimport com.uber.rib.core.ViewRouter;\n\n/**\n * Adds and removes children of {@link RandomWinnerBuilder.RandomWinnerScope}.\n *\n * <p>TODO describe the possible child configurations of this scope.\n */\npublic class RandomWinnerRouter extends ViewRouter<RandomWinnerView, RandomWinnerInteractor> {\n\n  public RandomWinnerRouter(\n      RandomWinnerView view,\n      RandomWinnerInteractor interactor,\n      RandomWinnerBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/randomWinner/RandomWinnerView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.randomWinner;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.FrameLayout;\nimport androidx.annotation.Nullable;\n\n/** Top level view for {@link RandomWinnerBuilder.RandomWinnerScope}. */\nclass RandomWinnerView extends FrameLayout implements RandomWinnerInteractor.RandomWinnerPresenter {\n\n  public RandomWinnerView(Context context) {\n    this(context, null);\n  }\n\n  public RandomWinnerView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public RandomWinnerView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/tictactoe/Board.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport javax.inject.Inject;\n\nclass Board {\n\n  static final int ROWS = 3;\n  static final int COLS = 3;\n\n  MarkerType[][] cells;\n  int currentRow;\n  int currentCol;\n\n  @Inject\n  Board() {\n    cells = new MarkerType[ROWS][COLS];\n    for (int row = 0; row < ROWS; ++row) {\n      for (int col = 0; col < COLS; ++col) {\n        cells[row][col] = null;\n      }\n    }\n  }\n\n  /** Return true if it is a draw (i.e., no more EMPTY cell) */\n  boolean isDraw() {\n    for (int row = 0; row < ROWS; ++row) {\n      for (int col = 0; col < COLS; ++col) {\n        if (cells[row][col] == null) {\n          return false;\n        }\n      }\n    }\n    return !hasWon(MarkerType.CROSS) && !hasWon(MarkerType.NOUGHT);\n  }\n\n  /** Return true if the player with \"theSeed\" has won after placing at (currentRow, currentCol) */\n  boolean hasWon(MarkerType theSeed) {\n    return ((cells[currentRow][0] == theSeed\n            && cells[currentRow][1] == theSeed\n            && cells[currentRow][2] == theSeed)\n        || (cells[0][currentCol] == theSeed\n            && cells[1][currentCol] == theSeed\n            && cells[2][currentCol] == theSeed)\n        || (currentRow == currentCol\n            && cells[0][0] == theSeed\n            && cells[1][1] == theSeed\n            && cells[2][2] == theSeed)\n        || (currentRow + currentCol == 2\n            && cells[0][2] == theSeed\n            && cells[1][1] == theSeed\n            && cells[2][0] == theSeed));\n  }\n\n  enum MarkerType {\n    CROSS,\n    NOUGHT\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/tictactoe/BoardCoordinate.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nclass BoardCoordinate {\n\n  private final int x;\n  private final int y;\n\n  BoardCoordinate(int x, int y) {\n    this.x = x;\n    this.y = y;\n  }\n\n  int getX() {\n    return x;\n  }\n\n  int getY() {\n    return y;\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.root.UserName;\nimport com.uber.rib.tutorial4.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Named;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link TicTacToeScope}. */\npublic class TicTacToeBuilder\n    extends ViewBuilder<TicTacToeView, TicTacToeRouter, TicTacToeBuilder.ParentComponent> {\n\n  public TicTacToeBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link TicTacToeRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link TicTacToeRouter}.\n   */\n  public TicTacToeRouter build(ViewGroup parentViewGroup) {\n    TicTacToeView view = createView(parentViewGroup);\n    TicTacToeInteractor interactor = new TicTacToeInteractor();\n    Component component =\n        DaggerTicTacToeBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.tictactoeRouter();\n  }\n\n  @Override\n  protected TicTacToeView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (TicTacToeView) inflater.inflate(R.layout.tic_tac_toe_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n    TicTacToeInteractor.Listener ticTacToeListener();\n\n    @Named(\"player_one\")\n    UserName playerOne();\n\n    @Named(\"player_two\")\n    UserName playerTwo();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @TicTacToeScope\n    @Binds\n    abstract TicTacToeInteractor.TicTacToePresenter presenter(TicTacToeView view);\n\n    @TicTacToeScope\n    @Provides\n    static TicTacToeRouter router(\n        Component component, TicTacToeView view, TicTacToeInteractor interactor) {\n      return new TicTacToeRouter(view, interactor, component);\n    }\n  }\n\n  @TicTacToeScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<TicTacToeInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(TicTacToeInteractor interactor);\n\n      @BindsInstance\n      Builder view(TicTacToeView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    TicTacToeRouter tictactoeRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface TicTacToeScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface TicTacToeInternal {}\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.UserName;\nimport com.uber.rib.root.loggedin.tictactoe.Board.MarkerType;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport javax.inject.Inject;\nimport javax.inject.Named;\n\n/** Coordinates Business Logic for {@link TicTacToeScope}. */\n@RibInteractor\npublic class TicTacToeInteractor\n    extends Interactor<TicTacToeInteractor.TicTacToePresenter, TicTacToeRouter> {\n\n  @Inject Board board;\n  @Inject Listener listener;\n  @Inject TicTacToePresenter presenter;\n\n  @Inject\n  @Named(\"player_one\")\n  UserName playerOne;\n\n  @Inject\n  @Named(\"player_two\")\n  UserName playerTwo;\n\n  private MarkerType currentPlayer = MarkerType.CROSS;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n\n    presenter\n        .squareClicks()\n        .subscribe(\n            new Consumer<BoardCoordinate>() {\n              @Override\n              public void accept(BoardCoordinate xy) throws Exception {\n                if (board.cells[xy.getX()][xy.getY()] == null) {\n                  if (currentPlayer == MarkerType.CROSS) {\n                    board.cells[xy.getX()][xy.getY()] = MarkerType.CROSS;\n                    board.currentRow = xy.getX();\n                    board.currentCol = xy.getY();\n                    presenter.addCross(xy);\n                    currentPlayer = MarkerType.NOUGHT;\n                  } else {\n                    board.cells[xy.getX()][xy.getY()] = MarkerType.NOUGHT;\n                    board.currentRow = xy.getX();\n                    board.currentCol = xy.getY();\n                    presenter.addNought(xy);\n                    currentPlayer = MarkerType.CROSS;\n                  }\n                }\n                if (board.hasWon(MarkerType.CROSS)) {\n                  presenter.setPlayerWon(playerOne.getUserName());\n                  listener.gameWon(playerOne);\n                } else if (board.hasWon(MarkerType.NOUGHT)) {\n                  presenter.setPlayerWon(playerTwo.getUserName());\n                  listener.gameWon(playerTwo);\n                } else if (board.isDraw()) {\n                  presenter.setPlayerTie();\n                  listener.gameWon(null);\n                } else {\n                  updateCurrentPlayer();\n                }\n              }\n            });\n    updateCurrentPlayer();\n  }\n\n  private void updateCurrentPlayer() {\n    if (currentPlayer == MarkerType.CROSS) {\n      presenter.setCurrentPlayerName(playerOne.getUserName());\n    } else {\n      presenter.setCurrentPlayerName(playerTwo.getUserName());\n    }\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface TicTacToePresenter {\n    Observable<BoardCoordinate> squareClicks();\n\n    void setCurrentPlayerName(String currentPlayer);\n\n    void setPlayerWon(String playerName);\n\n    void setPlayerTie();\n\n    void addCross(BoardCoordinate xy);\n\n    void addNought(BoardCoordinate xy);\n  }\n\n  public interface Listener {\n\n    /**\n     * Called when the game is over.\n     *\n     * @param winner player that won, or null if it's a tie.\n     */\n    void gameWon(UserName winner);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link TicTacToeBuilder.TicTacToeScope}. */\npublic class TicTacToeRouter extends ViewRouter<TicTacToeView, TicTacToeInteractor> {\n\n  public TicTacToeRouter(\n      TicTacToeView view, TicTacToeInteractor interactor, TicTacToeBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedin/tictactoe/TicTacToeView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedin.tictactoe;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport androidx.percentlayout.widget.PercentRelativeLayout;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.core.Initializer;\nimport com.uber.rib.tutorial4.R;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Function;\nimport java.util.ArrayList;\n\n/** Top level view for {@link TicTacToeBuilder.TicTacToeScope}. */\npublic class TicTacToeView extends PercentRelativeLayout\n    implements TicTacToeInteractor.TicTacToePresenter {\n\n  private TextView[][] imageButtons;\n  private TextView titleView;\n\n  public TicTacToeView(Context context) {\n    this(context, null);\n  }\n\n  public TicTacToeView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public TicTacToeView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Initializer\n  @Override\n  protected void onFinishInflate() {\n    super.onFinishInflate();\n    imageButtons = new TextView[3][];\n    imageButtons[0] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button11),\n          (TextView) findViewById(R.id.button12),\n          (TextView) findViewById(R.id.button13)\n        };\n    imageButtons[1] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button21),\n          (TextView) findViewById(R.id.button22),\n          (TextView) findViewById(R.id.button23)\n        };\n    imageButtons[2] =\n        new TextView[] {\n          (TextView) findViewById(R.id.button31),\n          (TextView) findViewById(R.id.button32),\n          (TextView) findViewById(R.id.button33)\n        };\n    titleView = (TextView) findViewById(R.id.title);\n  }\n\n  @Override\n  public Observable<BoardCoordinate> squareClicks() {\n    ArrayList<Observable<BoardCoordinate>> observables = new ArrayList<>();\n    for (int i = 0; i < 3; i++) {\n      for (int j = 0; j < 3; j++) {\n        final int finalI = i;\n        final int finalJ = j;\n        observables.add(\n            RxView.clicks(imageButtons[i][j])\n                .map(\n                    new Function<Object, BoardCoordinate>() {\n                      @Override\n                      public BoardCoordinate apply(Object irrelevant) throws Exception {\n                        return new BoardCoordinate(finalI, finalJ);\n                      }\n                    }));\n      }\n    }\n    return Observable.merge(observables);\n  }\n\n  @Override\n  public void addCross(BoardCoordinate xy) {\n    TextView textView = imageButtons[xy.getX()][xy.getY()];\n    textView.setText(\"x\");\n    textView.setClickable(false);\n  }\n\n  @Override\n  public void addNought(BoardCoordinate xy) {\n    TextView textView = imageButtons[xy.getX()][xy.getY()];\n    textView.setText(\"O\");\n    textView.setClickable(false);\n  }\n\n  @Override\n  public void setCurrentPlayerName(String currentPlayer) {\n    titleView.setText(\"Current Player: \" + currentPlayer);\n  }\n\n  @Override\n  public void setPlayerWon(String playerName) {\n    titleView.setText(\"Player won: \" + playerName + \"!!!\");\n  }\n\n  @Override\n  public void setPlayerTie() {\n    titleView.setText(\"Tie game!\");\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedout/LoggedOutBuilder.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport static java.lang.annotation.RetentionPolicy.CLASS;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport com.uber.rib.core.InteractorBaseComponent;\nimport com.uber.rib.core.ViewBuilder;\nimport com.uber.rib.tutorial4.R;\nimport dagger.Binds;\nimport dagger.BindsInstance;\nimport dagger.Provides;\nimport java.lang.annotation.Retention;\nimport javax.inject.Qualifier;\nimport javax.inject.Scope;\n\n/** Builder for the {@link LoggedOutScope}. */\npublic class LoggedOutBuilder\n    extends ViewBuilder<LoggedOutView, LoggedOutRouter, LoggedOutBuilder.ParentComponent> {\n\n  public LoggedOutBuilder(ParentComponent dependency) {\n    super(dependency);\n  }\n\n  /**\n   * Builds a new {@link LoggedOutRouter}.\n   *\n   * @param parentViewGroup parent view group that this router's view will be added to.\n   * @return a new {@link LoggedOutRouter}.\n   */\n  public LoggedOutRouter build(ViewGroup parentViewGroup) {\n    LoggedOutView view = createView(parentViewGroup);\n    LoggedOutInteractor interactor = new LoggedOutInteractor();\n    Component component =\n        DaggerLoggedOutBuilder_Component.builder()\n            .parentComponent(getDependency())\n            .view(view)\n            .interactor(interactor)\n            .build();\n    return component.loggedoutRouter();\n  }\n\n  @Override\n  protected LoggedOutView inflateView(LayoutInflater inflater, ViewGroup parentViewGroup) {\n    return (LoggedOutView) inflater.inflate(R.layout.logged_out_rib, parentViewGroup, false);\n  }\n\n  public interface ParentComponent {\n\n    LoggedOutInteractor.Listener listener();\n  }\n\n  @dagger.Module\n  public abstract static class Module {\n\n    @LoggedOutScope\n    @Binds\n    abstract LoggedOutInteractor.LoggedOutPresenter presenter(LoggedOutView view);\n\n    @LoggedOutScope\n    @Provides\n    static LoggedOutRouter router(\n        Component component, LoggedOutView view, LoggedOutInteractor interactor) {\n      return new LoggedOutRouter(view, interactor, component);\n    }\n\n    // TODO: Create provider methods for dependencies created by this Rib. These should be static.\n  }\n\n  @LoggedOutScope\n  @dagger.Component(modules = Module.class, dependencies = ParentComponent.class)\n  interface Component extends InteractorBaseComponent<LoggedOutInteractor>, BuilderComponent {\n\n    @dagger.Component.Builder\n    interface Builder {\n\n      @BindsInstance\n      Builder interactor(LoggedOutInteractor interactor);\n\n      @BindsInstance\n      Builder view(LoggedOutView view);\n\n      Builder parentComponent(ParentComponent component);\n\n      Component build();\n    }\n  }\n\n  interface BuilderComponent {\n\n    LoggedOutRouter loggedoutRouter();\n  }\n\n  @Scope\n  @Retention(CLASS)\n  @interface LoggedOutScope {}\n\n  @Qualifier\n  @Retention(CLASS)\n  @interface LoggedOutInternal {}\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedout/LoggedOutInteractor.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport android.util.Pair;\nimport androidx.annotation.Nullable;\nimport com.uber.rib.core.Bundle;\nimport com.uber.rib.core.Interactor;\nimport com.uber.rib.core.RibInteractor;\nimport com.uber.rib.root.UserName;\nimport com.uber.rib.root.loggedout.LoggedOutBuilder.LoggedOutScope;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Consumer;\nimport javax.inject.Inject;\n\n/** Coordinates Business Logic for {@link LoggedOutScope}. */\n@RibInteractor\npublic class LoggedOutInteractor\n    extends Interactor<LoggedOutInteractor.LoggedOutPresenter, LoggedOutRouter> {\n\n  @Inject Listener listener;\n  @Inject LoggedOutPresenter presenter;\n\n  @Override\n  protected void didBecomeActive(@Nullable Bundle savedInstanceState) {\n    super.didBecomeActive(savedInstanceState);\n    presenter\n        .playerNames()\n        .subscribe(\n            new Consumer<Pair<String, String>>() {\n              @Override\n              public void accept(Pair<String, String> names) throws Exception {\n                if (!isEmpty(names.first) && !isEmpty(names.second)) {\n                  listener.requestLogin(new UserName(names.first), new UserName(names.second));\n                }\n              }\n            });\n  }\n\n  private boolean isEmpty(@Nullable String string) {\n    return string == null || string.length() == 0;\n  }\n\n  /** Presenter interface implemented by this RIB's view. */\n  interface LoggedOutPresenter {\n\n    Observable<Pair<String, String>> playerNames();\n  }\n\n  public interface Listener {\n\n    void requestLogin(UserName playerOne, UserName playerTwo);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedout/LoggedOutRouter.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport com.uber.rib.core.ViewRouter;\n\n/** Adds and removes children of {@link LoggedOutBuilder.LoggedOutScope}. */\npublic class LoggedOutRouter extends ViewRouter<LoggedOutView, LoggedOutInteractor> {\n\n  public LoggedOutRouter(\n      LoggedOutView view, LoggedOutInteractor interactor, LoggedOutBuilder.Component component) {\n    super(view, interactor, component);\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/java/com/uber/rib/root/loggedout/LoggedOutView.java",
    "content": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.uber.rib.root.loggedout;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.util.Pair;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.LinearLayout;\nimport androidx.annotation.Nullable;\nimport com.jakewharton.rxbinding2.view.RxView;\nimport com.uber.rib.core.Initializer;\nimport com.uber.rib.tutorial4.R;\nimport io.reactivex.Observable;\nimport io.reactivex.functions.Function;\n\n/** Top level view for {@link LoggedOutBuilder.LoggedOutScope}. */\npublic class LoggedOutView extends LinearLayout implements LoggedOutInteractor.LoggedOutPresenter {\n\n  private Button loginButton;\n  private EditText playerOneEditText;\n  private EditText playerTwoEditText;\n\n  public LoggedOutView(Context context) {\n    this(context, null);\n  }\n\n  public LoggedOutView(Context context, @Nullable AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public LoggedOutView(Context context, @Nullable AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Initializer\n  @Override\n  protected void onFinishInflate() {\n    super.onFinishInflate();\n    playerOneEditText = (EditText) findViewById(R.id.player_one_name);\n    playerTwoEditText = (EditText) findViewById(R.id.player_two_name);\n    loginButton = (Button) findViewById(R.id.login_button);\n  }\n\n  @Override\n  public Observable<Pair<String, String>> playerNames() {\n    return RxView.clicks(findViewById(R.id.login_button))\n        .map(\n            new Function<Object, Pair<String, String>>() {\n              @Override\n              public Pair<String, String> apply(Object irrelevant) throws Exception {\n                return new Pair<>(\n                    playerOneEditText.getText().toString(), playerTwoEditText.getText().toString());\n              }\n            });\n  }\n}\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/res/layout/game_button.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Button xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/start_game_button\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_margin=\"16dp\"\n    android:layout_gravity=\"center_horizontal\"\n    android:text=\"@string/start_game\"/>"
  },
  {
    "path": "tutorials/tutorial4/src/main/res/layout/logged_out_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedout.LoggedOutView\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    <Space\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"/>\n\n    <EditText\n      android:id=\"@+id/player_one_name\"\n      android:hint=\"@string/player_one_name\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:maxLines=\"1\"\n      android:layout_margin=\"16dp\"/>\n\n    <EditText\n      android:id=\"@+id/player_two_name\"\n      android:hint=\"@string/player_two_name\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:maxLines=\"1\"\n      android:layout_margin=\"16dp\"/>\n\n    <Button\n        android:id=\"@+id/login_button\"\n        android:text=\"Login\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"/>\n</com.uber.rib.root.loggedout.LoggedOutView>"
  },
  {
    "path": "tutorials/tutorial4/src/main/res/layout/off_game_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedin.offgame.OffGameView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:gravity=\"bottom\"\n    android:orientation=\"vertical\">\n\n    <LinearLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:layout_gravity=\"center_horizontal\"\n      android:layout_margin=\"16dp\"\n      android:orientation=\"horizontal\">\n\n        <TextView\n          android:id=\"@+id/player_one_name\"\n          android:text=\"@string/player_one_name\"\n          android:layout_width=\"0dp\"\n          android:layout_height=\"wrap_content\"\n          android:layout_weight=\"3\"/>\n\n        <TextView\n          android:id=\"@+id/player_one_win_count\"\n          android:layout_width=\"0dp\"\n          android:layout_height=\"wrap_content\"\n          android:layout_weight=\"1\"\n          android:text=\"@string/win_count\"/>\n\n    </LinearLayout>\n\n    <LinearLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:orientation=\"horizontal\"\n      android:layout_gravity=\"center_horizontal\"\n      android:layout_margin=\"16dp\">\n\n        <TextView\n          android:id=\"@+id/player_two_name\"\n          android:text=\"@string/player_two_name\"\n          android:layout_width=\"0dp\"\n          android:layout_height=\"wrap_content\"\n          android:layout_weight=\"3\"/>\n\n        <TextView\n          android:id=\"@+id/player_two_win_count\"\n          android:layout_width=\"0dp\"\n          android:layout_height=\"wrap_content\"\n          android:layout_weight=\"1\"\n          android:text=\"@string/win_count\"/>\n\n    </LinearLayout>\n\n</com.uber.rib.root.loggedin.offgame.OffGameView>\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/res/layout/root_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.RootView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"/>\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/res/layout/tic_tac_toe_rib.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.uber.rib.root.loggedin.tictactoe.TicTacToeView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/tic_tac_toe\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <TextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"40dp\"\n        android:gravity=\"center_horizontal\"/>\n\n    <TextView\n        android:id=\"@+id/button11\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button12\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        android:layout_toRightOf=\"@id/button11\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button13\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@+id/title\"\n        android:layout_toRightOf=\"@id/button12\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button21\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button11\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button22\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button11\"\n        android:layout_toRightOf=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button23\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button13\"\n        android:layout_toRightOf=\"@id/button22\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button31\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button32\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button22\"\n        android:layout_toRightOf=\"@id/button21\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n    <TextView\n        android:id=\"@+id/button33\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_margin=\"8dp\"\n        android:layout_below=\"@id/button23\"\n        android:layout_toRightOf=\"@id/button22\"\n        app:layout_aspectRatio=\"100%\"\n        app:layout_widthPercent=\"30%\"\n        android:background=\"#808080\"/>\n\n</com.uber.rib.root.loggedin.tictactoe.TicTacToeView>\n"
  },
  {
    "path": "tutorials/tutorial4/src/main/res/values/ub__strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">RIB Tutorial 4</string>\n    <string name=\"player_one_name\">Player One Name</string>\n    <string name=\"player_two_name\">Player Two Name</string>\n    <string name=\"login\">Login</string>\n    <string name=\"start_game\">Start game</string>\n    <string name=\"welcome_to_off_game\">Welcome FakeName!</string>\n    <string name=\"win_count\">win count</string>\n</resources>\n"
  }
]