[
  {
    "path": ".gitignore",
    "content": "*.iml\n.idea\n.gradle\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n/.idea/navEditor.xml\n/.idea/assetWizardSettings.xml\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n.cxx\nlocal.properties\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\nAll notable changes to this project will be documented in this file.\n\n## [1.2.0] - 2022-11-19\n### Support Compose\n\n## [1.1.0] - 2022-08-17\n### Support fragment navigation\n\n## [1.0.0] - 2022-03-01\n### First version\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "![](Butterfly.png)\n\n[![](https://jitpack.io/v/ssseasonnn/Butterfly.svg)](https://jitpack.io/#ssseasonnn/Butterfly)\n\n# Butterfly\n\n*Read this in other languages: [中文](README.zh.md), [English](README.md), [Change Log](CHANGELOG.md)*\n\nButterfly is a versatile component-based navigation framework based on `Coroutine + Annotation + Ksp`.\nNot only does it support navigation for Activity, Fragment, and DialogFragment, but it also supports navigation for Compose pages.\n\nButterfly supports configuring startup modes for Fragment and Composite navigation, including Standard mode navigation\nClearTop clears stack top mode navigation and SingleTop reuse mode navigation.\n\nButterfly provides a unified fallback stack for managing all types of page navigation, using standard fallback APIs to reduce developer workload.\n\nButterfly also provides a powerful framework for inter component communication, allowing for true component decoupling without any dependencies between components.\n\n\n## Feature List\n\nThe butterfly provides these features：\n\n✅ Support navigation to Activity <br>\n✅ Support navigation to Fragment <br>\n✅ Support navigation to DialogFragment <br>\n✅ Support navigation to Compose <br>\n✅ Support navigation to Action <br>\n✅ Support parameter transfer and parse <br>\n✅ Support interceptor <br>\n✅ Support backstack <br>\n✅ Support group manage <br>\n✅ Support launch mode，such as SingleTop、ClearTop <br>\n✅ Support component communicate <br>\n\n## Installation\n\n```kotlin\n// Add jitpack repository\nrepositories {\n  maven { url(\"https://jitpack.io\") }\n}\n```\n\n```kotlin\n// First, declare the KSP plugin in your top level build.gradle.kts file.\nplugins {\n    id(\"com.google.devtools.ksp\") version \"1.8.10-1.0.9\" apply false\n}\n\n// Then, enable KSP in your module-level build.gradle.kts file:\nplugins {\n    id(\"com.google.devtools.ksp\")\n}\n\n\n// Then, add Butterfly dependencies.\ndependencies {\n  implementation(\"com.github.ssseasonnn.Butterfly:butterfly:1.3.0\")\n  ksp(\"com.github.ssseasonnn.Butterfly:compiler:1.3.0\")\n\n  //for compose\n  implementation(\"com.github.ssseasonnn.Butterfly:butterfly-compose:1.3.0\")\n}\n```\n\n## Basic Usage\n\n### Navigation\n\nButterfly support navigation for Activity、Fragment and DialogFragment and Compose component.\n\n```kotlin\n@Destination(\"test/activity\")\nclass TestActivity : AppCompatActivity()\n\n@Destination(\"test/fragment\")\nclass TestFragment : Fragment()\n\n@Destination(\"test/dialog\")\nclass TestDialogFragment : DialogFragment()\n\n@Destination(\"test/compose\")\n@Composable\nfun HomeScreen() {}\n\n//Navigation\nButterfly.of(context).navigate(\"test/xxx\")\n\n//Navigation and get result\nButterfly.of(context).navigate(\"test/xxx\") {\n    val result = it.getStringExtra(\"result\")\n    binding.tvResult.text = result\n}\n```\n\n### Communication\n\nButterfly implements component-based communication through `Evade` and `EvadeImpl` \nannotations. And the communication between components without any dependence \nis supported by dynamic agent technology.\n\nFor example, two components, `Home` and `Dashboard`, component `Dashboard` need to call the method in the component `Home`:\n\n```mermaid\nflowchart TD\n\tA(Main App) --> B(Component Home)\n\tA(Main App) --> C(Component Dashboard)\n```\n\n```kotlin\n//Define the Api that needs to access the Home in the component Dashboard and add the @ Evade annotation\n@Evade\ninterface DashboardCallHomeApi {\n    fun showHome(fragmentManager: FragmentManager, container: Int)\n}\n\n\n//Implement the corresponding Api in the component Home and add the @ EvadeImpl annotation\n@EvadeImpl\nclass DashboardCallHomeApiImpl {  //The implementation class name must end with Impl\n    val TAG = \"home_tag\"\n\n    //For the implementation of HomeApi, the method name and parameters must be the same\n    fun showHome(fragmentManager: FragmentManager, container: Int) {\n        val homeFragment = HomeFragment()\n        fragmentManager.beginTransaction()\n            .replace(container, homeFragment, TAG)\n            .commit()\n    }\n}\n\n//You can then invoke the interface in the component Home in the component Dashboard:\nval dashboardCallHomeApi = Butterfly.evade<DashboardCallHomeApi>()\ndashboardCallHomeApi.showHome(supportFragmentManager, R.id.container)\n```\n\n## Documentation\n\nSee examples and browse complete documentation at the Butterfly Wiki: [wiki](https://github.com/ssseasonnn/Butterfly/wiki)\n\nIf you still have questions, feel free to create a new issue.\n\n\n## License\n\n> ```\n> Copyright 2022 Season.Zlc\n>\n> Licensed under the Apache License, Version 2.0 (the \"License\");\n> you may not use this file except in compliance with the License.\n> You may obtain a copy of the License at\n>\n>    http://www.apache.org/licenses/LICENSE-2.0\n>\n> Unless required by applicable law or agreed to in writing, software\n> distributed under the License is distributed on an \"AS IS\" BASIS,\n> WITHOUT WARRANTIES OR CONDITIONS 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"
  },
  {
    "path": "README.zh.md",
    "content": "![](Butterfly.png)\n\n[![](https://jitpack.io/v/ssseasonnn/Butterfly.svg)](https://jitpack.io/#ssseasonnn/Butterfly)\n\n# Butterfly\n\n*Read this in other languages: [中文](README.zh.md), [English](README.md), [Change Log](CHANGELOG.md)*\n\nButterfly是一个基于`Coroutine + Annotation注解 + Ksp注解处理器`打造的全能的组件化路由框架。\n不仅支持Activity、Fragment、DialogFragment的导航，还支持Compose页面的导航。\n\nButterfly支持为Fragment和Compose的导航配置启动模式， 包括Standard标准模式导航、\nClearTop清除栈顶模式导航以及SingleTop栈顶复用模式导航。\n\nButterfly提供统一的回退栈对所有类型的页面导航进行管理，使用标准的回退Api，减轻开发者工作负担。\n\nButterfly还提供强大的组件间通信框架，可在组件间没有任何依赖情况下通信，实现真正的组件解耦。\n\n\n## 功能列表\n\nButterfly提供以下功能：\n\n✅ 支持导航Activity <br>\n✅ 支持导航Fragment <br>\n✅ 支持导航DialogFragment <br>\n✅ 支持导航Compose <br>\n✅ 支持导航Action <br>\n✅ 支持导航参数传递和解析 <br>\n✅ 支持拦截器 <br>\n✅ 支持回退栈 <br>\n✅ 支持组管理 <br>\n✅ 支持启动模式，如SingleTop、ClearTop <br>\n✅ 支持组件化通信 <br>\n\n## 安装\n\n```kotlin\n// 添加jitpack仓库\nrepositories {\n  maven { url(\"https://jitpack.io\") }\n}\n```\n\n```kotlin\n// 首先，在顶级build.gradle.kts文件中声明ksp插件\nplugins {\n    id(\"com.google.devtools.ksp\") version \"1.8.10-1.0.9\" apply false\n}\n\n// 然后，在模块级build.gradle.kts文件中启用ksp：\nplugins {\n    id(\"com.google.devtools.ksp\")\n}\n\n// 然后，添加 Butterfly 依赖项\ndependencies {\n    implementation(\"com.github.ssseasonnn.Butterfly:butterfly:1.3.0\")\n    ksp(\"com.github.ssseasonnn.Butterfly:compiler:1.3.0\")\n\n    //for compose\n    implementation(\"com.github.ssseasonnn.Butterfly:butterfly-compose:1.3.0\")\n}\n```\n\n\n## 基本用法\n\n### 导航\n\nButterfly支持Activity、Fragment、DialogFragment和Compose组件的导航。\n\n```kotlin\n@Destination(\"test/activity\")\nclass TestActivity : AppCompatActivity()\n\n@Destination(\"test/fragment\")\nclass TestFragment : Fragment()\n\n@Destination(\"test/dialog\")\nclass TestDialogFragment : DialogFragment()\n\n@Destination(\"test/compose\")\n@Composable\nfun HomeScreen() {}\n\n//导航\nButterfly.of(context).navigate(\"test/xxx\")\n\n//导航并获取结果\nButterfly.of(context).navigate(\"test/xxx\") {\n    val result = it.getStringExtra(\"result\")\n    binding.tvResult.text = result\n}\n```\n\n### 组件间通信\n\nButterfly通过`Evade`和`EvadeImpl`注解实现组件化通信。并且通过动态代理技术支持组件间在无任何依赖的情况下的通信。\n\n### 1. 在没有依赖的组件之间通信\n\n例如两个组件`Home`和组件`Dashboard`，组件`Dashboard`需要调用组件`Home`中的方法:\n\n```mermaid\nflowchart TD\n\tA(Main App) --> B(Component Home)\n\tA(Main App) --> C(Component Dashboard)\n```\n\n```kotlin\n//在组件Dashboard中定义需要访问Home对应的Api，并添加@Evade注解\n@Evade\ninterface DashboardCallHomeApi {\n    fun showHome(fragmentManager: FragmentManager, container: Int)\n}\n\n\n//在组件Home中实现对应的Api，并添加@EvadeImpl注解\n@EvadeImpl\nclass DashboardCallHomeApiImpl {  //实现类名必须以Impl结尾\n    val TAG = \"home_tag\"\n\n    //HomeApi的实现，方法名和方法参数必须相同\n    fun showHome(fragmentManager: FragmentManager, container: Int) {\n        val homeFragment = HomeFragment()\n        fragmentManager.beginTransaction()\n            .replace(container, homeFragment, TAG)\n            .commit()\n    }\n}\n\n//然后就可以在组件Dashboard中调用组件Home中的接口：\nval dashboardCallHomeApi = Butterfly.evade<DashboardCallHomeApi>()\ndashboardCallHomeApi.showHome(supportFragmentManager, R.id.container)\n```\n\n## 详细文档\n\n查看示例并浏览完整的文档，请访问Butterfly Wiki：[wiki](https://github.com/ssseasonnn/Butterfly/wiki)\n\n如果您仍有疑问，请随意创建新的issue。\n\n\n## License\n\n> ```\n> Copyright 2022 Season.Zlc\n>\n> Licensed under the Apache License, Version 2.0 (the \"License\");\n> you may not use this file except in compliance with the License.\n> You may obtain a copy of the License at\n>\n>    http://www.apache.org/licenses/LICENSE-2.0\n>\n> Unless required by applicable law or agreed to in writing, software\n> distributed under the License is distributed on an \"AS IS\" BASIS,\n> WITHOUT WARRANTIES OR CONDITIONS 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"
  },
  {
    "path": "annotation/.gitignore",
    "content": "/build"
  },
  {
    "path": "annotation/build.gradle.kts",
    "content": "plugins {\n    id(\"kotlin\")\n}\n\ngroup = \"com.github.ssseasonnn\""
  },
  {
    "path": "annotation/src/main/java/zlc/season/butterfly/annotation/Annotations.kt",
    "content": "package zlc.season.butterfly.annotation\n\n@Retention(AnnotationRetention.BINARY)\n@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)\nannotation class Destination(\n    val route: String\n)\n\n@Retention(AnnotationRetention.BINARY)\n@Target(AnnotationTarget.CLASS)\nannotation class Evade(\n    val identity: String = \"\"\n)\n\n@Retention(AnnotationRetention.BINARY)\n@Target(AnnotationTarget.CLASS)\nannotation class EvadeImpl(\n    val singleton: Boolean = true,\n    val identity: String = \"\"\n)"
  },
  {
    "path": "annotation/src/main/java/zlc/season/butterfly/annotation/EvadeData.kt",
    "content": "package zlc.season.butterfly.annotation\n\ndata class EvadeData(val cls: Class<*>, val singleton: Boolean)"
  },
  {
    "path": "annotation/src/main/java/zlc/season/butterfly/module/Module.kt",
    "content": "package zlc.season.butterfly.module\n\nimport zlc.season.butterfly.annotation.EvadeData\n\ninterface Module {\n    fun getDestination(): Map<String, Class<*>>\n    fun getEvade(): Map<String, Class<*>>\n    fun getEvadeImpl(): Map<String, EvadeData>\n}"
  },
  {
    "path": "build.gradle.kts",
    "content": "@file:Suppress(\"DSL_SCOPE_VIOLATION\", \"UnstableApiUsage\")\n\nplugins {\n    id(\"build.logic\")\n    alias(libs.plugins.application) apply false\n    alias(libs.plugins.library) apply false\n    alias(libs.plugins.kotlin) apply false\n    alias(libs.plugins.kotlin.jvm) apply false\n    alias(libs.plugins.kotlin.serialization) apply false\n    alias(libs.plugins.kotlin.parcelize) apply false\n    alias(libs.plugins.kotlin.kover) apply false\n    alias(libs.plugins.ksp) apply false\n    alias(libs.plugins.hilt) apply false\n    alias(libs.plugins.butterfly) apply false\n}\n\ntasks.register(\"clean\", Delete::class) {\n    delete(rootProject.buildDir)\n}"
  },
  {
    "path": "buildLogic/.gitignore",
    "content": "/build"
  },
  {
    "path": "buildLogic/build.gradle.kts",
    "content": "plugins {\n    `kotlin-dsl`\n}\n\njava {\n    sourceCompatibility = JavaVersion.VERSION_17\n    targetCompatibility = JavaVersion.VERSION_17\n}\n\ndependencies {\n    implementation(\"com.android.tools.build:gradle:8.0.2\")\n    implementation(\"org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10\")\n}\n\ngradlePlugin {\n    plugins.register(\"buildLogicPlugin\") {\n        id = \"build.logic\"\n        implementationClass = \"BuildLogicPlugin\"\n    }\n}\n"
  },
  {
    "path": "buildLogic/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.0-bin.zip\nnetworkTimeout=10000\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "buildLogic/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original 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#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS 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##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\nAPP_HOME=$( cd \"${APP_HOME:-./}\" && pwd -P ) || exit\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='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\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\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC3045 \n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC3045 \n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n# Collect all arguments for the java command;\n#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of\n#     shell script including quotes and variable substitutions, so put them in\n#     double quotes to make sure that they get re-expanded; and\n#   * put everything else in single quotes, so that it's not re-expanded.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        org.gradle.wrapper.GradleWrapperMain \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "buildLogic/gradlew.bat",
    "content": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\n@rem you may not use this file except in compliance with the License.\n@rem You may obtain a copy of the License at\n@rem\n@rem      https://www.apache.org/licenses/LICENSE-2.0\n@rem\n@rem Unless required by applicable law or agreed to in writing, software\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n@rem See the License for the specific language governing permissions and\n@rem limitations under the License.\n@rem\n\n@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\nset DIRNAME=%~dp0\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\n@rem This is normally unused\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\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=\"-Xmx64m\" \"-Xms64m\"\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% equ 0 goto execute\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 execute\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:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\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 %*\n\n:end\n@rem End local scope for the variables with windows NT shell\nif %ERRORLEVEL% equ 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!\nset EXIT_CODE=%ERRORLEVEL%\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\nexit /b %EXIT_CODE%\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "buildLogic/settings.gradle.kts",
    "content": "@file:Suppress(\"DSL_SCOPE_VIOLATION\", \"UnstableApiUsage\")\n\nenableFeaturePreview(\"TYPESAFE_PROJECT_ACCESSORS\")\n\ndependencyResolutionManagement {\n    repositories {\n        google()\n        mavenCentral()\n        mavenLocal()\n    }\n}\n"
  },
  {
    "path": "buildLogic/src/main/kotlin/BuildLogicPlugin.kt",
    "content": "import org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport zlc.season.buildlogic.base.setupMaven\n\nclass BuildLogicPlugin : Plugin<Project> {\n    override fun apply(project: Project) {\n        project.subprojects {\n            setupMaven()\n        }\n    }\n}\n"
  },
  {
    "path": "buildLogic/src/main/kotlin/zlc/season/buildlogic/base/AndroidExtensions.kt",
    "content": "@file:Suppress(\"UnstableApiUsage\")\n\npackage zlc.season.buildlogic.base\n\nimport com.android.build.api.dsl.CommonExtension\nimport com.android.build.gradle.BaseExtension\nimport com.android.build.gradle.LibraryExtension\nimport com.android.build.gradle.internal.dsl.BaseAppModuleExtension\nimport org.gradle.api.JavaVersion\nimport org.gradle.api.Project\n\nfun Project.androidApplication(block: BaseAppModuleExtension.() -> Unit = {}) {\n    configBase().applyAs<BaseAppModuleExtension> {\n        block()\n    }\n    configPackaging()\n}\n\nfun Project.androidLibrary(block: LibraryExtension.() -> Unit = {}) {\n    configBase().applyAs<LibraryExtension> {\n        block()\n    }\n    configPackaging()\n}\n\nprivate fun Project.configBase(): BaseExtension {\n    return android.apply {\n        val libs = getLibs()\n        setCompileSdkVersion(libs.getVersion(\"sdk-compile-version\"))\n\n        defaultConfig {\n            minSdk = libs.getVersion(\"sdk-min-version\")\n            targetSdk = libs.getVersion(\"sdk-target-version\")\n            vectorDrawables { useSupportLibrary = true }\n\n            testInstrumentationRunner = \"androidx.test.runner.AndroidJUnitRunner\"\n        }\n\n        compileOptions {\n            sourceCompatibility = JavaVersion.VERSION_17\n            targetCompatibility = JavaVersion.VERSION_17\n        }\n\n        kotlinOptions {\n            jvmTarget = JavaVersion.VERSION_17.toString()\n        }\n\n        testOptions {\n            unitTests {\n                isReturnDefaultValues = true\n                isIncludeAndroidResources = true\n            }\n        }\n    }\n}\n\nprivate fun Project.configPackaging() {\n    android.applyAs<CommonExtension<*, *, *, *>> {\n        packaging {\n            resources.excludes.add(\"META-INF/LICENSE.md\")\n            resources.excludes.add(\"META-INF/LICENSE-notice.md\")\n        }\n    }\n}\n\nfun Project.enableCompose() {\n    val libs = getLibs()\n\n    android.apply {\n        buildFeatures.compose = true\n\n        composeOptions {\n            kotlinCompilerExtensionVersion = libs.getVersionStr(\"compose-compiler\")\n        }\n    }\n}\n\nfun Project.addDefaultConstant(vararg constantPair: Pair<String, String>) {\n    android.apply {\n        defaultConfig {\n            val addDefaultConstantLambda = createAddDefaultConstantLambda()\n            constantPair.forEach {\n                addDefaultConstantLambda(it.first, it.second)\n            }\n        }\n    }\n}"
  },
  {
    "path": "buildLogic/src/main/kotlin/zlc/season/buildlogic/base/BaseExtensions.kt",
    "content": "@file:Suppress(\"TooManyFunctions\", \"UnstableApiUsage\")\n\npackage zlc.season.buildlogic.base\n\nimport com.android.build.gradle.BaseExtension\nimport com.android.build.gradle.LibraryExtension\nimport com.android.build.gradle.internal.dsl.BaseAppModuleExtension\nimport com.android.build.gradle.internal.dsl.BuildType\nimport com.android.build.gradle.internal.dsl.DefaultConfig\nimport com.android.build.gradle.internal.dsl.ProductFlavor\nimport org.gradle.api.Project\nimport org.gradle.api.artifacts.Dependency\nimport org.gradle.api.artifacts.VersionCatalog\nimport org.gradle.api.artifacts.VersionCatalogsExtension\nimport org.gradle.api.artifacts.dsl.DependencyHandler\nimport org.gradle.api.plugins.ExtensionAware\nimport org.gradle.api.provider.Provider\nimport org.gradle.kotlin.dsl.getByType\nimport org.gradle.plugin.use.PluginDependency\nimport org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions\nimport java.util.Properties\n\nfun Project.getApp(): BaseAppModuleExtension {\n    return extensions.getByType()\n}\n\nfun Project.getLibrary(): LibraryExtension {\n    return extensions.getByType()\n}\n\nfun Project.loadProperties(fileName: String): Properties {\n    return Properties().apply {\n        load(rootProject.file(fileName).reader())\n    }\n}\n\nfun Project.getLibs(): VersionCatalog {\n    return extensions.getByType<VersionCatalogsExtension>().named(\"libs\")\n}\n\nfun VersionCatalog.getVersion(name: String): Int {\n    return findVersion(name).get().toString().toInt()\n}\n\nfun VersionCatalog.getVersionStr(name: String): String {\n    return findVersion(name).get().toString()\n}\n\nfun VersionCatalog.getBundle(name: String): Any {\n    return findBundle(name).get()\n}\n\nfun VersionCatalog.getPlugin(name: String): String {\n    return (findPlugin(name).get() as Provider<PluginDependency>).get().pluginId\n}\n\ninternal val Project.android: BaseExtension\n    get() = extensions.findByName(\"android\") as? BaseExtension\n        ?: error(\"Project '$name' is not an Android module\")\n\ninternal fun BaseExtension.kotlinOptions(block: KotlinJvmOptions.() -> Unit) {\n    (this as ExtensionAware).extensions.configure(\"kotlinOptions\", block)\n}\n\ninternal fun DependencyHandler.implementation(dependencyNotation: Any): Dependency? =\n    add(\"implementation\", dependencyNotation)\n\n@Suppress(\"UNCHECKED_CAST\")\ninternal fun <T> Any.applyAs(block: T.() -> Unit) {\n    (this as T).block()\n}\n\ninternal fun createAddFlavorConstantLambda(): (ProductFlavor, String, String) -> Unit {\n    return { productFlavor, constantName, constantValue ->\n        productFlavor.manifestPlaceholders[constantName] = constantValue\n        productFlavor.buildConfigField(\"String\", constantName, \"\\\"${constantValue}\\\"\")\n    }\n}\n\ninternal fun createAddBuildTypeConstantLambda(): (BuildType, String, String) -> Unit {\n    return { buildType, constantName, constantValue ->\n        buildType.manifestPlaceholders[constantName] = constantValue\n        buildType.buildConfigField(\"String\", constantName, \"\\\"${constantValue}\\\"\")\n    }\n}\n\ninternal fun DefaultConfig.createAddDefaultConstantLambda(): (String, String) -> Unit {\n    return { constantName, constantValue ->\n        manifestPlaceholders[constantName] = constantValue\n        buildConfigField(\"String\", constantName, \"\\\"${constantValue}\\\"\")\n    }\n}\n"
  },
  {
    "path": "buildLogic/src/main/kotlin/zlc/season/buildlogic/base/MavenExtensions.kt",
    "content": "package zlc.season.buildlogic.base\n\nimport org.gradle.api.Project\nimport org.gradle.api.publish.PublishingExtension\nimport org.gradle.api.publish.maven.MavenPublication\nimport org.gradle.kotlin.dsl.apply\nimport org.gradle.kotlin.dsl.configure\nimport org.gradle.kotlin.dsl.create\n\nfun Project.setupMaven() {\n    afterEvaluate {\n        plugins.withId(\"com.android.library\") {\n            configPublish(\"release\")\n        }\n        plugins.withId(\"java\") {\n            configPublish(\"java\")\n        }\n    }\n}\n\nprivate fun Project.configPublish(componentName: String) {\n    apply(plugin = \"maven-publish\")\n    configure<PublishingExtension> {\n        repositories {\n            mavenLocal()\n        }\n        publications {\n            create<MavenPublication>(\"maven\") {\n                afterEvaluate {\n                    from(components.getByName(componentName))\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "butterfly/.gitignore",
    "content": "/build"
  },
  {
    "path": "butterfly/build.gradle.kts",
    "content": "import zlc.season.buildlogic.base.androidLibrary\n\n@Suppress(\"DSL_SCOPE_VIOLATION\")\nplugins {\n    alias(libs.plugins.library)\n    alias(libs.plugins.kotlin)\n    alias(libs.plugins.kotlin.parcelize)\n}\n\nandroidLibrary {\n    namespace = \"zlc.season.butterfly\"\n}\n\ndependencies {\n    api(project(\":annotation\"))\n    api(libs.clarity)\n    api(libs.kotlin.coroutines)\n    api(libs.lifecycle.runtime.ktx)\n    api(libs.fragment.ktx)\n    api(libs.core.ktx)\n\n    implementation(libs.bundles.unit.test)\n}"
  },
  {
    "path": "butterfly/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "butterfly/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.kts.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "butterfly/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"zlc.season.butterfly\">\n\n    <application />\n</manifest>"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/Butterfly.kt",
    "content": "package zlc.season.butterfly\n\nimport android.content.Context\nimport androidx.core.os.bundleOf\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.internal.ButterflyHelper\n\nobject Butterfly {\n    fun of(context: Context = ButterflyHelper.context): DestinationHandler {\n        return DestinationHandler(context)\n    }\n\n    // avoid inline\n    val EVADE_LAMBDA: (String, Class<*>) -> Any = { identity, cls ->\n        val real = identity.ifEmpty { cls.simpleName }\n        var request = ButterflyCore.queryEvade(real)\n        if (request.className.isEmpty()) {\n            request = request.copy(className = cls.name)\n        }\n        ButterflyCore.dispatchEvade(request)\n    }\n\n    inline fun <reified T> evade(\n        identity: String = \"\",\n        noinline func: (String, Class<*>) -> Any = EVADE_LAMBDA\n    ): T {\n        return func(identity, T::class.java) as T\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/ButterflyCore.kt",
    "content": "package zlc.season.butterfly\n\nimport android.content.Context\nimport android.os.Bundle\nimport zlc.season.butterfly.core.EvadeManager\nimport zlc.season.butterfly.core.InterceptorManager\nimport zlc.season.butterfly.core.ModuleManager\nimport zlc.season.butterfly.core.NavigatorManager\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.entities.EvadeData\nimport zlc.season.butterfly.interceptor.Interceptor\nimport zlc.season.butterfly.module.Module\n\nobject ButterflyCore {\n    private val moduleManager = ModuleManager()\n    private val interceptorManager = InterceptorManager()\n    private val navigatorManager = NavigatorManager()\n    private val evadeManager = EvadeManager()\n\n    fun getNavigatorManager() = navigatorManager\n\n    fun addModuleName(moduleName: String) {\n        try {\n            val cls = Class.forName(moduleName)\n            if (Module::class.java.isAssignableFrom(cls)) {\n                val module = cls.getDeclaredConstructor().newInstance() as Module\n                addModule(module)\n            }\n        } catch (ignore: Exception) {\n            //ignore\n        }\n    }\n\n    fun addModule(module: Module) = moduleManager.addModule(module)\n\n    fun removeModule(module: Module) = moduleManager.removeModule(module)\n\n    fun addInterceptor(interceptor: Interceptor) = interceptorManager.addInterceptor(interceptor)\n\n    fun removeInterceptor(interceptor: Interceptor) =\n        interceptorManager.removeInterceptor(interceptor)\n\n    fun queryDestination(route: String): String = moduleManager.queryDestination(route)\n\n    fun queryEvade(identity: String): EvadeData = moduleManager.queryEvade(identity)\n\n    suspend fun dispatchNavigate(\n        context: Context,\n        destinationData: DestinationData,\n        interceptorManager: InterceptorManager\n    ): Result<Bundle> {\n        // Use global interceptor before\n        var tempDestinationData = if (destinationData.enableGlobalInterceptor) {\n            this.interceptorManager.intercept(context, destinationData)\n        } else {\n            destinationData\n        }\n\n        // Then use current navigation interceptor\n        tempDestinationData = interceptorManager.intercept(context, tempDestinationData)\n\n        return navigatorManager.navigate(context, tempDestinationData)\n    }\n\n    fun popBack(context: Context, bundle: Bundle): DestinationData? {\n        return navigatorManager.popBack(context, bundle)\n    }\n\n    fun dispatchEvade(evadeData: EvadeData): Any {\n        return evadeManager.dispatch(evadeData)\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/DestinationHandler.kt",
    "content": "package zlc.season.butterfly\n\nimport android.content.Context\nimport android.os.Bundle\nimport androidx.core.os.bundleOf\nimport androidx.lifecycle.LifecycleOwner\nimport androidx.lifecycle.lifecycleScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport zlc.season.butterfly.core.InterceptorManager\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.interceptor.DefaultInterceptor\nimport zlc.season.butterfly.interceptor.Interceptor\nimport zlc.season.butterfly.internal.ButterflyHelper.findActivity\nimport zlc.season.butterfly.internal.key\nimport zlc.season.butterfly.internal.logd\nimport zlc.season.butterfly.internal.parseRoute\nimport zlc.season.butterfly.internal.parseRouteParams\nimport zlc.season.butterfly.launcher.DestinationLauncher\nimport zlc.season.butterfly.launcher.DestinationLauncherManager\n\nclass DestinationHandler(\n    private val context: Context,\n    private var destinationData: DestinationData = DestinationData(),\n    private val interceptorManager: InterceptorManager = InterceptorManager()\n) {\n    companion object {\n        private val EMPTY_CALLBACK: (Result<Bundle>) -> Unit = {}\n    }\n\n    /**\n     * Set params for destination\n     */\n    fun params(vararg pair: Pair<String, Any?>): DestinationHandler {\n        return apply {\n            destinationData.bundle.putAll(bundleOf(*pair))\n        }\n    }\n\n    fun params(bundle: Bundle): DestinationHandler {\n        return apply {\n            destinationData.bundle.putAll(bundle)\n        }\n    }\n\n    /**\n     * Skip global interceptor for current navigation\n     */\n    fun skipGlobalInterceptor(): DestinationHandler {\n        return configDestinationData {\n            copy(enableGlobalInterceptor = false)\n        }\n    }\n\n    /**\n     * Add interceptor for current navigation\n     */\n    fun addInterceptor(interceptor: Interceptor): DestinationHandler {\n        return apply { interceptorManager.addInterceptor(interceptor) }\n    }\n\n    fun addInterceptor(interceptor: suspend (Context, DestinationData) -> DestinationData): DestinationHandler {\n        return apply {\n            interceptorManager.addInterceptor(DefaultInterceptor(interceptor))\n        }\n    }\n\n    fun container(containerViewId: Int): DestinationHandler {\n        return configDestinationData {\n            copy(containerViewId = containerViewId)\n        }\n    }\n\n    fun container(containerViewTag: String): DestinationHandler {\n        return configDestinationData {\n            copy(containerViewTag = containerViewTag)\n        }\n    }\n\n    fun tag(uniqueTag: String): DestinationHandler {\n        return configDestinationData {\n            copy(uniqueTag = uniqueTag)\n        }\n    }\n\n    fun group(groupId: String): DestinationHandler {\n        return configDestinationData {\n            copy(groupId = groupId)\n        }\n    }\n\n    fun clearTop(): DestinationHandler {\n        return configDestinationData {\n            copy(clearTop = true)\n        }\n    }\n\n    fun singleTop(): DestinationHandler {\n        return configDestinationData {\n            copy(singleTop = true)\n        }\n    }\n\n    fun asRoot(): DestinationHandler {\n        return configDestinationData {\n            copy(isRoot = true)\n        }\n    }\n\n    fun disableBackStack(): DestinationHandler {\n        return configDestinationData {\n            copy(enableBackStack = false)\n        }\n    }\n\n    fun addFlag(flag: Int): DestinationHandler {\n        return configDestinationData {\n            copy(flags = flags or flag)\n        }\n    }\n\n    fun enterAnim(enterAnim: Int): DestinationHandler {\n        return configDestinationData {\n            copy(enterAnim = enterAnim)\n        }\n    }\n\n    fun exitAnim(exitAnim: Int): DestinationHandler {\n        return configDestinationData {\n            copy(exitAnim = exitAnim)\n        }\n    }\n\n    fun navigate(\n        route: String,\n        onResult: (Result<Bundle>) -> Unit = EMPTY_CALLBACK\n    ) {\n        navigate(route, bundleOf(), onResult)\n    }\n\n    fun navigate(\n        route: String,\n        vararg params: Pair<String, Any?>,\n        onResult: (Result<Bundle>) -> Unit = EMPTY_CALLBACK\n    ) {\n        navigate(route, bundleOf(*params), onResult)\n    }\n\n    fun navigate(\n        route: String,\n        params: Bundle,\n        onResult: (Result<Bundle>) -> Unit = EMPTY_CALLBACK\n    ) {\n        if (context is LifecycleOwner) {\n            context.lifecycleScope.launch {\n                if (onResult != EMPTY_CALLBACK) {\n                    onResult(awaitNavigateResult(route, params))\n                } else {\n                    awaitNavigate(route, params)\n                }\n            }\n        } else {\n            \"Navigate failed, context is not LifecycleOwner!\".logd()\n        }\n    }\n\n    suspend fun awaitNavigate(route: String, params: Bundle) {\n        setupRouteAndParams(route, false, params)\n\n        ButterflyCore.dispatchNavigate(context, destinationData, interceptorManager)\n    }\n\n    suspend fun awaitNavigateResult(route: String, params: Bundle): Result<Bundle> {\n        setupRouteAndParams(route, true, params)\n\n        return ButterflyCore.dispatchNavigate(context, destinationData, interceptorManager)\n    }\n\n    fun popBack(vararg result: Pair<String, Any?>): DestinationData? {\n        return popBack(bundleOf(*result))\n    }\n\n    fun popBack(result: Bundle): DestinationData? {\n        return ButterflyCore.popBack(context, result)\n    }\n\n    private suspend fun setupRouteAndParams(\n        route: String,\n        needResult: Boolean,\n        extraParams: Bundle\n    ) {\n        withContext(Dispatchers.Default) {\n            val parsedRoute = parseRoute(route)\n            val destinationClassName = ButterflyCore.queryDestination(parsedRoute)\n            val params = parseRouteParams(parsedRoute)\n\n            configDestinationData {\n                bundle.putAll(bundleOf(*params))\n                bundle.putAll(extraParams)\n\n                copy(\n                    route = parsedRoute,\n                    className = destinationClassName,\n                    needResult = needResult\n                )\n            }\n        }\n    }\n\n    fun getLauncher(context: Context): DestinationLauncher {\n        val agileHandler = configDestinationData { copy(needResult = true) }\n\n        val activity = context.findActivity()\n            ?: throw IllegalStateException(\"No Activity founded!\")\n\n        val key = activity.key()\n        var launcher = DestinationLauncherManager.getLauncher(key, destinationData.route)\n        if (launcher == null) {\n            launcher = DestinationLauncher(\n                context,\n                agileHandler.destinationData,\n                agileHandler.interceptorManager\n            )\n            DestinationLauncherManager.addLauncher(key, launcher)\n        }\n\n        return launcher\n    }\n\n    private fun configDestinationData(block: DestinationData.() -> DestinationData): DestinationHandler {\n        return apply {\n            destinationData = destinationData.block()\n        }\n    }\n}\n\n"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/action/Action.kt",
    "content": "package zlc.season.butterfly.action\n\nimport android.content.Context\nimport android.os.Bundle\n\ninterface Action {\n    fun doAction(context: Context, route: String, data: Bundle = Bundle())\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/core/BackStackEntryManager.kt",
    "content": "package zlc.season.butterfly.core\n\nimport android.app.Activity\nimport android.os.Bundle\nimport androidx.fragment.app.DialogFragment\nimport androidx.fragment.app.FragmentActivity\nimport zlc.season.butterfly.entities.BackStackEntry\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.internal.ButterflyHelper\nimport zlc.season.butterfly.internal.ButterflyHelper.KEY_DESTINATION_DATA\nimport zlc.season.butterfly.internal.ButterflyHelper.getDestinationData\nimport zlc.season.butterfly.internal.key\nimport zlc.season.butterfly.navigator.fragment.observeFragmentDestroy\nimport zlc.season.claritypotion.ActivityLifecycleCallbacksAdapter\n\nclass BackStackEntryManager {\n    companion object {\n        private const val KEY_SAVE_STATE = \"butterfly_back_stack_state\"\n    }\n\n    /**\n     * Activity as the key to save the BackStackEntry corresponding to each activity.\n     *\n     * like:\n     * {\n     *   {ActivityA} -> [BackStackEntryA1, BackStackEntryA2],\n     *   {ActivityB} -> [BackStackEntryB1, BackStackEntryB2]\n     * }\n     */\n    private val backStackEntryMap = mutableMapOf<String, MutableList<BackStackEntry>>()\n\n    init {\n        ButterflyHelper.application.registerActivityLifecycleCallbacks(object :\n            ActivityLifecycleCallbacksAdapter() {\n            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {\n                // If activity recreated, then restore current activity's entry list.\n                if (savedInstanceState != null) {\n                    restoreEntryList(activity, savedInstanceState)\n                } else {\n                    // If activity is started by Butterfly, then add it into back stack list.\n                    val data = activity.getDestinationData()\n                    if (data != null) {\n                        addEntry(activity, BackStackEntry(data))\n                    }\n                }\n\n                // Observe fragment's destroy event to remove FragmentEntry.\n                if (activity is FragmentActivity) {\n                    activity.observeFragmentDestroy {\n                        val uniqueTag = it.tag\n                        if (!uniqueTag.isNullOrEmpty()) {\n                            removeEntry(activity, uniqueTag)\n                        }\n                    }\n                }\n            }\n\n            override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) =\n                saveEntryList(activity, outState)\n\n            override fun onActivityDestroyed(activity: Activity) = destroyEntryList(activity)\n        })\n    }\n\n    @Suppress(\"deprecation\")\n    @Synchronized\n    private fun restoreEntryList(activity: Activity, savedState: Bundle) {\n        val data = savedState.getParcelableArrayList<DestinationData>(KEY_SAVE_STATE)\n        if (data != null) {\n            val entryList = data.map { BackStackEntry(it) }\n            getEntryList(activity).addAll(entryList)\n        }\n    }\n\n    @Synchronized\n    private fun saveEntryList(activity: Activity, outState: Bundle) {\n        val list = backStackEntryMap[activity.key()]\n        if (!list.isNullOrEmpty()) {\n            val savedData = list.mapTo(ArrayList()) { it.destinationData }\n            outState.putParcelableArrayList(KEY_SAVE_STATE, savedData)\n        }\n    }\n\n    @Synchronized\n    private fun destroyEntryList(activity: Activity) {\n        backStackEntryMap.remove(activity.key())\n    }\n\n    @Synchronized\n    private fun removeEntry(activity: Activity, uniqueTag: String) {\n        val find = getEntryList(activity).find { it.destinationData.uniqueTag == uniqueTag }\n        if (find != null) {\n            getEntryList(activity).remove(find)\n        }\n    }\n\n    @Synchronized\n    fun removeTopEntry(activity: Activity): BackStackEntry? {\n        return getEntryList(activity).removeLastOrNull()\n    }\n\n    @Synchronized\n    fun removeEntryList(activity: Activity, entryList: List<BackStackEntry>) {\n        getEntryList(activity).removeAll(entryList)\n    }\n\n    @Synchronized\n    fun addEntry(activity: Activity, entry: BackStackEntry) {\n        val list = getEntryList(activity)\n        list.add(entry)\n\n//        if (isDialogEntry(entry)) {\n//            list.add(entry)\n//        } else {\n//            val dialogEntry = list.firstOrNull { isDialogEntry(it) }\n//            if (dialogEntry != null) {\n//                val index = list.indexOf(dialogEntry)\n//                list.add(index, entry)\n//            } else {\n//                list.add(entry)\n//            }\n//        }\n    }\n\n    @Synchronized\n    fun getTopEntry(activity: Activity): BackStackEntry? {\n        val entryList = getEntryList(activity)\n\n        return if (entryList.isEmpty()) {\n            null\n        } else {\n            return entryList.lastOrNull()\n        }\n    }\n\n    @Synchronized\n    fun getTopEntryList(activity: Activity, data: DestinationData): MutableList<BackStackEntry> {\n        val result = mutableListOf<BackStackEntry>()\n\n        val backStackList = getEntryList(activity)\n        val index = backStackList.indexOfLast { it.destinationData.className == data.className }\n        if (index != -1) {\n            for (i in index until backStackList.size) {\n                val entry = backStackList[i]\n                result.add(entry)\n            }\n        }\n        return result\n    }\n\n    @Synchronized\n    fun getEntryList(activity: Activity): MutableList<BackStackEntry> {\n        var backStackList = backStackEntryMap[activity.key()]\n        if (backStackList == null) {\n            backStackList = mutableListOf()\n            backStackEntryMap[activity.key()] = backStackList\n        }\n\n        return backStackList\n    }\n\n    private fun isDialogEntry(entry: BackStackEntry): Boolean {\n        val cls = Class.forName(entry.destinationData.className)\n        return DialogFragment::class.java.isAssignableFrom(cls)\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/core/EvadeManager.kt",
    "content": "package zlc.season.butterfly.core\n\nimport zlc.season.butterfly.entities.EvadeData\nimport zlc.season.butterfly.internal.logw\nimport java.lang.reflect.Method\nimport java.lang.reflect.Proxy\nimport java.util.Objects\n\nclass EvadeManager {\n    private val implObjMap = mutableMapOf<String, Any>()\n\n    fun dispatch(request: EvadeData): Any {\n        val evadeClass = Class.forName(request.className)\n\n        if (!check(request)) {\n            \"Evade -> $request not found!\".logw()\n            return createProxyObj(evadeClass)\n        }\n\n        val implClass = Class.forName(request.implClassName)\n        val implObj = implClass.getImplObj(request)\n\n        // check extend\n        if (evadeClass.isAssignableFrom(implClass)) {\n            return implObj\n        }\n\n        return createProxyObj(evadeClass, implObj) { method, args ->\n            if (args == null) {\n                val findMethod = implClass.getDeclaredMethod(method.name)\n                findMethod.invoke(implObj)\n            } else {\n                val findMethod = implClass.getDeclaredMethod(method.name, *method.parameterTypes)\n                findMethod.invoke(implObj, *args)\n            }\n        }\n    }\n\n    private fun check(request: EvadeData): Boolean {\n        if (request.implClassName.isEmpty()) {\n            return false\n        }\n        return true\n    }\n\n    private fun Class<*>.getImplObj(request: EvadeData): Any {\n        return if (request.isSingleton) {\n            realGetImplObj(request)\n        } else {\n            getDeclaredConstructor().newInstance()\n        }\n    }\n\n    @Synchronized\n    private fun Class<*>.realGetImplObj(request: EvadeData): Any {\n        var find = implObjMap[request.implClassName]\n        if (find == null) {\n            val implObj = getDeclaredConstructor().newInstance()\n            implObjMap[request.implClassName] = implObj\n            find = implObj\n        }\n        return find!!\n    }\n\n    private fun createProxyObj(\n        cls: Class<*>,\n        implObj: Any = Any(),\n        block: (Method, Array<Any>?) -> Any? = { _, _ -> }\n    ): Any {\n        return Proxy.newProxyInstance(\n            Thread.currentThread().contextClassLoader,\n            arrayOf(cls)\n        ) { _, method, args ->\n            try {\n                if (method.name == \"hashCode\") {\n                    Objects.hashCode(implObj)\n                } else if (method.name == \"toString\") {\n                    Objects.toString(implObj)\n                } else if (method.name == \"equals\") {\n                    val other = args[0]\n                    if (Objects.hashCode(implObj) == other.hashCode()) {\n                        true\n                    } else {\n                        Objects.equals(implObj, other)\n                    }\n                } else {\n                    block(method, args)\n                }\n            } catch (e: Exception) {\n                if (e is NoSuchMethodException) {\n                    \"Evade -> Method ${e.message} not found!\".logw()\n                } else {\n                    e.logw()\n                }\n                Unit\n            }\n        }\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/core/GroupEntryManager.kt",
    "content": "package zlc.season.butterfly.core\n\nimport android.app.Activity\nimport android.os.Bundle\nimport androidx.fragment.app.FragmentActivity\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.entities.GroupEntry\nimport zlc.season.butterfly.internal.key\nimport zlc.season.butterfly.internal.logd\nimport zlc.season.butterfly.navigator.fragment.observeFragmentDestroy\nimport zlc.season.claritypotion.ActivityLifecycleCallbacksAdapter\nimport zlc.season.claritypotion.ClarityPotion.application\n\n@Suppress(\"DEPRECATION\")\nclass GroupEntryManager {\n    private val groupEntryMap = mutableMapOf<String, MutableList<GroupEntry>>()\n\n    companion object {\n        private const val KEY_SAVE_STATE = \"butterfly_group_state\"\n    }\n\n    init {\n        application.registerActivityLifecycleCallbacks(object :\n            ActivityLifecycleCallbacksAdapter() {\n            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {\n                // If activity recreated, then restore current activity's entry list.\n                if (savedInstanceState != null) {\n                    restoreEntryList(activity, savedInstanceState)\n                }\n\n                // Observe fragment's destroy event to remove FragmentEntry.\n                if (activity is FragmentActivity) {\n                    activity.observeFragmentDestroy {\n                        val uniqueTag = it.tag\n                        if (!uniqueTag.isNullOrEmpty()) {\n                            removeEntry(activity, uniqueTag)\n                        }\n                    }\n                }\n            }\n\n            override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {\n                saveEntryList(activity, outState)\n            }\n\n            override fun onActivityDestroyed(activity: Activity) {\n                destroyEntryList(activity)\n            }\n        })\n    }\n\n    @Synchronized\n    private fun restoreEntryList(activity: Activity, savedState: Bundle) {\n        val data = savedState.getParcelableArrayList<DestinationData>(KEY_SAVE_STATE)\n        if (data != null) {\n            val entryList = data.map { GroupEntry(it) }\n            getEntryList(activity).addAll(entryList)\n        }\n    }\n\n    @Synchronized\n    private fun saveEntryList(activity: Activity, outState: Bundle) {\n        val entryList = groupEntryMap[activity.key()]\n        if (!entryList.isNullOrEmpty()) {\n            val savedData = entryList.mapTo(ArrayList()) { it.destinationData }\n            outState.putParcelableArrayList(KEY_SAVE_STATE, savedData)\n        }\n    }\n\n    @Synchronized\n    private fun destroyEntryList(activity: Activity) {\n        groupEntryMap.remove(activity.key())\n    }\n\n    @Synchronized\n    private fun removeEntry(activity: Activity, uniqueTag: String) {\n        val entryList = getEntryList(activity)\n        val find = entryList.find { it.destinationData.uniqueTag == uniqueTag }\n        if (find != null) {\n            entryList.remove(find)\n        }\n    }\n\n    @Synchronized\n    fun addEntry(activity: Activity, groupEntry: GroupEntry) {\n        val entryList = getEntryList(activity)\n        entryList.add(groupEntry)\n    }\n\n    @Synchronized\n    fun getGroupList(activity: Activity, groupId: String): List<GroupEntry> {\n        val result = mutableListOf<GroupEntry>()\n        val list = getEntryList(activity)\n        list.forEach {\n            if (it.destinationData.groupId == groupId) {\n                result.add(it)\n            }\n        }\n        return result\n    }\n\n    @Synchronized\n    private fun getEntryList(activity: Activity): MutableList<GroupEntry> {\n        var groupList = groupEntryMap[activity.key()]\n        if (groupList == null) {\n            groupList = mutableListOf()\n            groupEntryMap[activity.key()] = groupList\n        }\n        return groupList\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/core/InterceptorManager.kt",
    "content": "package zlc.season.butterfly.core\n\nimport android.content.Context\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.interceptor.Interceptor\n\nclass InterceptorManager {\n    private val interceptorList = mutableListOf<Interceptor>()\n\n    fun addInterceptor(interceptor: Interceptor) {\n        interceptorList.add(interceptor)\n    }\n\n    fun removeInterceptor(interceptor: Interceptor) {\n        interceptorList.remove(interceptor)\n    }\n\n    suspend fun intercept(context: Context, destinationData: DestinationData): DestinationData {\n        val temp = mutableListOf<Interceptor>()\n        temp.addAll(interceptorList)\n\n        var tempData = destinationData\n        temp.forEach {\n            if (it.shouldIntercept(context, tempData)) {\n                tempData = it.intercept(context, tempData)\n            }\n        }\n\n        return tempData\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/core/ModuleManager.kt",
    "content": "package zlc.season.butterfly.core\n\nimport zlc.season.butterfly.entities.EvadeData\nimport zlc.season.butterfly.module.Module\n\nclass ModuleManager {\n    private val modules = mutableListOf<Module>()\n\n    fun addModule(vararg module: Module) {\n        module.forEach {\n            modules.add(it)\n        }\n    }\n\n    fun removeModule(module: Module) {\n        modules.remove(module)\n    }\n\n    fun queryDestination(route: String): String {\n        var result = \"\"\n        modules.forEach {\n            val find = it.getDestination()[route]\n            if (find != null) {\n                result = find.name\n                return@forEach\n            }\n        }\n        return result\n    }\n\n    fun queryEvade(identity: String): EvadeData {\n        var className = \"\"\n        var implClassName = \"\"\n        var isSingleton = true\n\n        modules.forEach {\n            if (className.isEmpty()) {\n                val evadeMap = it.getEvade()\n                val temp = evadeMap[identity]\n                if (temp != null) {\n                    className = temp.name\n                }\n            }\n            if (implClassName.isEmpty()) {\n                val implMap = it.getEvadeImpl()\n                val temp = implMap[identity]\n                if (temp != null) {\n                    implClassName = temp.cls.name\n                    isSingleton = temp.singleton\n                }\n            }\n\n            if (className.isNotEmpty() && implClassName.isNotEmpty()) {\n                return@forEach\n            }\n        }\n        return EvadeData(identity, className, implClassName, isSingleton)\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/core/NavigatorManager.kt",
    "content": "package zlc.season.butterfly.core\n\nimport android.app.Activity\nimport android.content.Context\nimport android.os.Bundle\nimport androidx.fragment.app.DialogFragment\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentActivity\nimport zlc.season.butterfly.action.Action\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.internal.logd\nimport zlc.season.butterfly.internal.logw\nimport zlc.season.butterfly.navigator.ActionNavigator\nimport zlc.season.butterfly.navigator.ActivityNavigator\nimport zlc.season.butterfly.navigator.fragment.DialogFragmentNavigator\nimport zlc.season.butterfly.navigator.ErrorNavigator\nimport zlc.season.butterfly.navigator.fragment.FragmentNavigator\nimport zlc.season.butterfly.navigator.Navigator\n\nclass NavigatorManager {\n    companion object {\n        private const val COMPOSE_DESTINATION_CLASS =\n            \"zlc.season.butterfly.compose.ComposeDestination\"\n        private const val COMPOSE_NAVIGATOR_CLASS = \"zlc.season.butterfly.compose.ComposeNavigator\"\n    }\n\n    private val navigatorMaps = LinkedHashMap<Class<*>, Navigator>()\n\n    private val backStackEntryManager = BackStackEntryManager()\n    private val groupEntryManager = GroupEntryManager()\n\n    fun getBackStackEntryManager() = backStackEntryManager\n\n    fun getGroupEntryManager() = groupEntryManager\n\n    init {\n        navigatorMaps.apply {\n            putAll(\n                listOf(\n                    Action::class.java to ActionNavigator,\n                    FragmentActivity::class.java to ActivityNavigator(backStackEntryManager),\n                    DialogFragment::class.java to DialogFragmentNavigator(backStackEntryManager),\n                    Fragment::class.java to FragmentNavigator(\n                        backStackEntryManager,\n                        groupEntryManager\n                    )\n                )\n            )\n            try {\n                val composeDestinationCls = Class.forName(COMPOSE_DESTINATION_CLASS)\n                val composeNavigatorCls = Class.forName(COMPOSE_NAVIGATOR_CLASS)\n                val composeNavigator = composeNavigatorCls.getConstructor(\n                    BackStackEntryManager::class.java,\n                    GroupEntryManager::class.java\n                ).newInstance(backStackEntryManager, groupEntryManager) as Navigator\n                put(composeDestinationCls, composeNavigator)\n            } catch (e: Exception) {\n                e.logw()\n            }\n\n            put(Any::class.java, ErrorNavigator)\n        }\n    }\n\n    suspend fun navigate(context: Context, data: DestinationData): Result<Bundle> {\n        if (data.className.isEmpty()) {\n            \"Navigate failed! Could not find destination class: destination=$data\".logd()\n            return Result.failure(IllegalStateException(\"Destination class not found!\"))\n        }\n\n        return findNavigator(data).navigate(context, data)\n    }\n\n    fun popBack(context: Context, result: Bundle): DestinationData? {\n        val currentActivity = if (context is Activity) {\n            context\n        } else {\n            \"Pop back failed! Need an Activity context: currentContext=$context\".logd()\n            return null\n        }\n\n        val topEntry = backStackEntryManager.removeTopEntry(currentActivity)\n        if (topEntry == null) {\n            \"Pop back failed! Current activity's backstack can not find any entry!\".logd()\n            return null\n        }\n\n        findNavigator(topEntry.destinationData).popBack(currentActivity, topEntry, result)\n\n        return topEntry.destinationData\n    }\n\n    private fun findNavigator(data: DestinationData): Navigator {\n        val cls = Class.forName(data.className)\n        return navigatorMaps[navigatorMaps.keys.find { it.isAssignableFrom(cls) }]!!\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/entities/BackStackEntry.kt",
    "content": "package zlc.season.butterfly.entities\n\ndata class BackStackEntry(val destinationData: DestinationData)"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/entities/DestinationData.kt",
    "content": "package zlc.season.butterfly.entities\n\nimport android.os.Bundle\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parcelize\nimport zlc.season.butterfly.internal.createDestinationDataTag\n\n@Parcelize\ndata class DestinationData(\n    val route: String = \"\",\n    val className: String = \"\",\n    val bundle: Bundle = Bundle(),\n\n    val enterAnim: Int = 0,\n    val exitAnim: Int = 0,\n    val flags: Int = 0,\n    val containerViewId: Int = 0,\n    val containerViewTag: String = \"\",\n\n    val needResult: Boolean = false,\n    val enableBackStack: Boolean = true,\n    val enableGlobalInterceptor: Boolean = true,\n\n    val isRoot: Boolean = false,\n    val clearTop: Boolean = false,\n    val singleTop: Boolean = false,\n    val useReplace: Boolean = false,\n\n    val groupId: String = \"\",\n    val uniqueTag: String = createDestinationDataTag()\n) : Parcelable"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/entities/EvadeData.kt",
    "content": "package zlc.season.butterfly.entities\n\ndata class EvadeData(\n    val identity: String,\n    val className: String,\n    val implClassName: String,\n    val isSingleton: Boolean\n) {\n    override fun toString(): String {\n        return \"\"\"[identity=\"$identity\", className=\"$className\", implClassName=\"$implClassName\", isSingleton=\"$isSingleton\"]\"\"\"\n    }\n}\n"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/entities/GroupEntry.kt",
    "content": "package zlc.season.butterfly.entities\n\ndata class GroupEntry(val destinationData: DestinationData)"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/interceptor/DefaultInterceptor.kt",
    "content": "package zlc.season.butterfly.interceptor\n\nimport android.content.Context\nimport zlc.season.butterfly.entities.DestinationData\n\nclass DefaultInterceptor(\n    private val interceptor: suspend (Context, DestinationData) -> DestinationData\n) : Interceptor {\n    override suspend fun shouldIntercept(\n        context: Context,\n        destinationData: DestinationData\n    ): Boolean {\n        return true\n    }\n\n    override suspend fun intercept(\n        context: Context,\n        destinationData: DestinationData\n    ): DestinationData {\n        return interceptor(context, destinationData)\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/interceptor/Interceptor.kt",
    "content": "package zlc.season.butterfly.interceptor\n\nimport android.content.Context\nimport zlc.season.butterfly.entities.DestinationData\n\ninterface Interceptor {\n    suspend fun shouldIntercept(context: Context, destinationData: DestinationData): Boolean\n    suspend fun intercept(context: Context, destinationData: DestinationData): DestinationData\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/internal/ButterflyFragment.kt",
    "content": "package zlc.season.butterfly.internal\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.os.Bundle\nimport androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentActivity\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.ViewModelProvider\nimport kotlinx.coroutines.suspendCancellableCoroutine\nimport zlc.season.butterfly.launcher.DestinationLauncherManager\nimport zlc.season.butterfly.navigator.fragment.addFragment\nimport zlc.season.butterfly.navigator.fragment.awaitFragmentResume\nimport zlc.season.butterfly.navigator.fragment.removeFragment\n\nclass ButterflyFragment : Fragment() {\n    companion object {\n        private const val KEY_ROUTE = \"key_destination_route\"\n        private const val KEY_TAG = \"key_destination_tag\"\n\n        suspend fun FragmentActivity.startActivityAndAwaitResult(\n            route: String,\n            intent: Intent\n        ): Result<Bundle> {\n            \"Await activity result: currentActivity=$this, route=$route\".logd()\n            val fragment = ButterflyFragment().apply {\n                arguments = Bundle().apply {\n                    putString(KEY_ROUTE, route)\n                }\n            }\n\n            \"Await activity result: add fragment $fragment...\".logd()\n            addFragment(fragment)\n\n            \"Await activity result: waiting fragment resume...\".logd()\n            awaitFragmentResume(fragment)\n\n            \"Await activity result: waiting activity result...\".logd()\n            val result = fragment.startActivityAndAwaitResult(intent)\n\n            \"Await activity result: result=$result.\".logd()\n            return Result.success(result)\n        }\n\n        suspend fun FragmentActivity.awaitFragmentResult(\n            route: String,\n            uniqueTag: String\n        ): Result<Bundle> {\n            \"Await fragment result: currentActivity=$this, route=$route, uniqueTag=$uniqueTag\".logd()\n            val fragment = ButterflyFragment().apply {\n                arguments = Bundle().apply {\n                    putString(KEY_ROUTE, route)\n                    putString(KEY_TAG, uniqueTag)\n                }\n            }\n\n            \"Await fragment result: add fragment $fragment...\".logd()\n            addFragment(fragment)\n\n            \"Await fragment result: waiting fragment resume...\".logd()\n            awaitFragmentResume(fragment)\n\n            \"Await fragment result: waiting fragment result...\".logd()\n            val result = fragment.waitFragmentResult()\n\n            \"Await fragment result: result=$result.\".logd()\n            return Result.success(result)\n        }\n    }\n\n    private val route by lazy { arguments?.getString(KEY_ROUTE) ?: \"\" }\n    private val uniqueTag by lazy { arguments?.getString(KEY_TAG) ?: \"\" }\n\n    private val viewModel by lazy { ViewModelProvider(this)[ButterflyViewModel::class.java] }\n    private val launcher = registerForActivityResult(StartActivityForResult()) {\n        val result = if (it.resultCode == Activity.RESULT_OK) {\n            it.data?.extras ?: Bundle()\n        } else {\n            Bundle()\n        }\n\n        \"ButterflyFragment[$this] onActivityResult: result=$result\".logd()\n        //set result for callback\n        viewModel.callback.invoke(result)\n\n        //set result for launcher\n        val launcher = DestinationLauncherManager.getLauncher(requireActivity().key(), route)\n        launcher?.flow?.tryEmit(Result.success(result))\n\n        //clear current fragment\n        activity?.removeFragment(this)\n    }\n\n    /**\n     * Launch activity and wait result.\n     */\n    suspend fun startActivityAndAwaitResult(intent: Intent): Bundle = suspendCancellableCoroutine {\n        viewModel.callback = { result ->\n            it.resumeWith(Result.success(result))\n            viewModel.callback = {}\n        }\n\n        \"ButterflyFragment[$this] launch activity and wait result\".logd()\n        launcher.launch(intent)\n\n        it.invokeOnCancellation {\n            viewModel.callback = {}\n        }\n    }\n\n    suspend fun waitFragmentResult(): Bundle = suspendCancellableCoroutine {\n        viewModel.callback = { result ->\n            it.resumeWith(Result.success(result))\n            viewModel.callback = {}\n        }\n\n        \"ButterflyFragment[$this] wait fragment result\".logd()\n\n        it.invokeOnCancellation {\n            viewModel.callback = {}\n        }\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        \"ButterflyFragment[$this] onCreate\".logd()\n\n        if (uniqueTag.isNotEmpty()) {\n            parentFragmentManager.setFragmentResultListener(uniqueTag, this) { requestKey, result ->\n                if (uniqueTag == requestKey) {\n                    \"ButterflyFragment[$this] onFragmentResult: result=$result\".logd()\n\n                    //set result for callback\n                    viewModel.callback.invoke(result)\n\n                    //set result for launcher\n                    val launcher =\n                        DestinationLauncherManager.getLauncher(requireActivity().key(), route)\n                    launcher?.flow?.tryEmit(Result.success(result))\n\n                    parentFragmentManager.clearFragmentResultListener(requestKey)\n\n                    //clear current fragment\n                    activity?.removeFragment(this)\n                }\n            }\n        }\n    }\n\n    override fun onResume() {\n        super.onResume()\n        \"ButterflyFragment[$this] onResume\".logd()\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        \"ButterflyFragment[$this] onDestroy\".logd()\n\n        viewModel.callback = {}\n\n        if (uniqueTag.isNotEmpty()) {\n            parentFragmentManager.clearFragmentResultListener(uniqueTag)\n        }\n    }\n\n    class ButterflyViewModel : ViewModel() {\n        var callback: ((Bundle) -> Unit) = {}\n    }\n}\n"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/internal/ButterflyHelper.kt",
    "content": "package zlc.season.butterfly.internal\n\nimport android.app.Activity\nimport android.app.Activity.RESULT_OK\nimport android.app.Application\nimport android.content.Context\nimport android.content.ContextWrapper\nimport android.content.Intent\nimport android.os.Bundle\nimport android.view.ViewGroup\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.claritypotion.ClarityPotion\n\nobject ButterflyHelper {\n    internal const val KEY_DESTINATION_DATA = \"key_butterfly_destination_data\"\n\n    val context: Context\n        get() = activity ?: application\n\n    val application: Application\n        get() = ClarityPotion.application\n\n    val activity: Activity?\n        get() = ClarityPotion.activity\n\n    fun Activity.setActivityResult(bundle: Bundle) {\n        if (bundle.isEmpty) return\n        setResult(RESULT_OK, Intent().apply { putExtras(bundle) })\n    }\n\n    fun Activity.contentView(): ViewGroup {\n        return findViewById(android.R.id.content)\n    }\n\n    fun Context.findActivity(): Activity? {\n        var context = this\n        while (context is ContextWrapper) {\n            if (context is Activity) return context\n            context = context.baseContext\n        }\n        return null\n    }\n\n    @Suppress(\"deprecation\")\n    fun Activity.getDestinationData(): DestinationData? {\n        return intent.getParcelableExtra(KEY_DESTINATION_DATA)\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/internal/LogUtil.kt",
    "content": "package zlc.season.butterfly.internal\n\nimport android.util.Log\n\nvar enableLog = true\nval TAG = \"Butterfly\"\n\nfun <T> T.logd(tag: String = \"\"): T {\n    if (enableLog) {\n        val realTag = tag.ifEmpty { TAG }\n        if (this is Throwable) {\n            Log.d(realTag, this.message ?: \"\", this)\n        } else {\n            Log.d(realTag, this.toString())\n        }\n    }\n    return this\n}\n\nfun <T> T.logw(tag: String = \"\"): T {\n    if (enableLog) {\n        val realTag = tag.ifEmpty { TAG }\n        if (this is Throwable) {\n            Log.w(realTag, this.message ?: \"\", this)\n        } else {\n            Log.w(realTag, this.toString())\n        }\n    }\n    return this\n}\n"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/internal/Util.kt",
    "content": "package zlc.season.butterfly.internal\n\nimport android.app.Activity\nimport androidx.core.net.toUri\nimport java.util.*\n\ninternal fun parseRoute(route: String): String {\n    val index = route.indexOfFirst { it == '?' }\n    return if (index > 0) {\n        route.substring(0, index)\n    } else {\n        route\n    }\n}\n\ninternal fun parseRouteParams(route: String): Array<Pair<String, String?>> {\n    val uri = route.toUri()\n    val query = uri.query\n\n    if (query != null) {\n        val result = mutableListOf<Pair<String, String?>>()\n        uri.queryParameterNames.forEach {\n            val value = uri.getQueryParameter(it)\n            result.add(it to value)\n        }\n        return result.toTypedArray()\n    }\n    return arrayOf()\n}\n\ninternal fun createDestinationDataTag(): String {\n    return UUID.randomUUID().toString().replace(\"-\", \"\").uppercase(Locale.getDefault())\n}\n\ninternal fun Activity.key(): String {\n    return \"${javaClass.canonicalName}@${hashCode()}\"\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/launcher/DestinationLauncher.kt",
    "content": "package zlc.season.butterfly.launcher\n\nimport android.content.Context\nimport android.os.Bundle\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport zlc.season.butterfly.ButterflyCore\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.core.InterceptorManager\n\nclass DestinationLauncher(\n    val context: Context,\n    val destinationData: DestinationData,\n    val interceptorManager: InterceptorManager\n) {\n    val flow = MutableSharedFlow<Result<Bundle>>(extraBufferCapacity = 1)\n\n    fun result(): Flow<Result<Bundle>> {\n        return flow\n    }\n\n    suspend fun launch() {\n        ButterflyCore.dispatchNavigate(context, destinationData, interceptorManager)\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/launcher/DestinationLauncherManager.kt",
    "content": "package zlc.season.butterfly.launcher\n\nimport android.app.Activity\nimport android.os.Bundle\nimport zlc.season.butterfly.internal.ButterflyHelper.application\nimport zlc.season.butterfly.internal.key\nimport zlc.season.claritypotion.ActivityLifecycleCallbacksAdapter\n\nobject DestinationLauncherManager {\n    private const val OLD_ACTIVITY_KEY = \"old_activity_key\"\n\n    private val launcherMap = mutableMapOf<String, MutableList<DestinationLauncher>>()\n    private val saveInstanceStateMap = mutableMapOf<String, Boolean>()\n\n    init {\n        application.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacksAdapter() {\n            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {\n                if (savedInstanceState != null) {\n                    val oldKey = savedInstanceState.getString(OLD_ACTIVITY_KEY)\n                    if (!oldKey.isNullOrEmpty()) {\n                        updateKey(oldKey, activity.key())\n                    }\n                }\n            }\n\n            override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {\n                super.onActivitySaveInstanceState(activity, outState)\n                val key = activity.key()\n                if (containsKey(key)) {\n                    outState.putString(OLD_ACTIVITY_KEY, key)\n                }\n                updateSaveInstanceState(key)\n            }\n\n            override fun onActivityDestroyed(activity: Activity) {\n                super.onActivityDestroyed(activity)\n                handleActivityDestroy(activity.key())\n            }\n        })\n    }\n\n    @Synchronized\n    private fun updateSaveInstanceState(key: String) {\n        saveInstanceStateMap[key] = true\n    }\n\n    @Synchronized\n    private fun handleActivityDestroy(key: String) {\n        if (saveInstanceStateMap[key] == null) {\n            launcherMap.remove(key)\n        }\n        saveInstanceStateMap.remove(key)\n    }\n\n    @Synchronized\n    private fun updateKey(oldKey: String, newKey: String) {\n        val oldLauncher = launcherMap.remove(oldKey)\n        oldLauncher?.let {\n            launcherMap[newKey] = oldLauncher\n        }\n    }\n\n    @Synchronized\n    fun containsKey(key: String): Boolean {\n        return launcherMap[key] != null\n    }\n\n    @Synchronized\n    fun addLauncher(key: String, launcher: DestinationLauncher) {\n        val list = launcherMap.getOrPut(key) { mutableListOf() }\n        if (list.find { it.destinationData.route == launcher.destinationData.route } == null) {\n            list.add(launcher)\n        }\n    }\n\n    @Synchronized\n    fun getLauncher(key: String, route: String): DestinationLauncher? {\n        return launcherMap[key]?.find { it.destinationData.route == route }\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/navigator/ActionNavigator.kt",
    "content": "package zlc.season.butterfly.navigator\n\nimport android.content.Context\nimport android.os.Bundle\nimport zlc.season.butterfly.action.Action\nimport zlc.season.butterfly.entities.DestinationData\n\nobject ActionNavigator : Navigator {\n    override suspend fun navigate(context: Context, data: DestinationData): Result<Bundle> {\n        return handleAction(context, data)\n    }\n\n    private fun handleAction(context: Context, request: DestinationData): Result<Bundle> {\n        val action = createAction(request)\n        action.doAction(context, request.route, request.bundle)\n        return Result.success(Bundle.EMPTY)\n    }\n\n    private fun createAction(request: DestinationData): Action {\n        val cls = Class.forName(request.className)\n        return cls.getDeclaredConstructor().newInstance() as Action\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/navigator/ActivityNavigator.kt",
    "content": "package zlc.season.butterfly.navigator\n\nimport android.app.Activity\nimport android.app.Application\nimport android.content.Context\nimport android.content.Intent\nimport android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP\nimport android.content.Intent.FLAG_ACTIVITY_NEW_TASK\nimport android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP\nimport android.os.Bundle\nimport androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult.Companion.EXTRA_ACTIVITY_OPTIONS_BUNDLE\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.core.app.ActivityOptionsCompat.makeCustomAnimation\nimport androidx.fragment.app.FragmentActivity\nimport kotlinx.coroutines.suspendCancellableCoroutine\nimport zlc.season.butterfly.entities.BackStackEntry\nimport zlc.season.butterfly.core.BackStackEntryManager\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.internal.ButterflyFragment.Companion.startActivityAndAwaitResult\nimport zlc.season.butterfly.internal.ButterflyHelper.KEY_DESTINATION_DATA\nimport zlc.season.butterfly.internal.ButterflyHelper.getDestinationData\nimport zlc.season.butterfly.internal.ButterflyHelper.setActivityResult\nimport zlc.season.claritypotion.ActivityLifecycleCallbacksAdapter\n\nclass ActivityNavigator(val backStackEntryManager: BackStackEntryManager) : Navigator {\n\n    override suspend fun navigate(context: Context, data: DestinationData): Result<Bundle> {\n        if (context is FragmentActivity) {\n            return navigate(context, data)\n        }\n\n        val intent = createIntent(context, data)\n        context.startActivity(intent, createActivityOptions(context, data)?.toBundle())\n        context.awaitActivityCreated(data)\n        return Result.success(Bundle.EMPTY)\n    }\n\n    private suspend fun navigate(\n        activity: FragmentActivity,\n        data: DestinationData\n    ): Result<Bundle> {\n        return if (!data.needResult) {\n            val intent = createIntent(activity, data)\n            activity.startActivity(intent, createActivityOptions(activity, data)?.toBundle())\n            activity.awaitActivityCreated(data)\n            Result.success(Bundle.EMPTY)\n        } else {\n            val intent = createIntent(activity, data)\n            createActivityOptions(activity, data)?.toBundle()?.let {\n                intent.putExtra(EXTRA_ACTIVITY_OPTIONS_BUNDLE, it)\n            }\n            activity.startActivityAndAwaitResult(data.route, intent)\n        }\n    }\n\n    override fun popBack(activity: Activity, topEntry: BackStackEntry, bundle: Bundle) {\n        with(activity) {\n            if (topEntry.destinationData.needResult) {\n                setActivityResult(bundle)\n            }\n            finish()\n        }\n    }\n\n    private fun createIntent(context: Context, data: DestinationData): Intent {\n        val intent = Intent().apply {\n            putExtra(KEY_DESTINATION_DATA, data)\n            setClassName(context.packageName, data.className)\n            putExtras(data.bundle)\n\n            if (data.clearTop) {\n                addFlags(FLAG_ACTIVITY_CLEAR_TOP)\n            } else if (data.singleTop) {\n                addFlags(FLAG_ACTIVITY_SINGLE_TOP)\n            }\n            if (data.flags != 0) {\n                addFlags(data.flags)\n            }\n            if (context !is Activity) {\n                addFlags(FLAG_ACTIVITY_NEW_TASK)\n            }\n        }\n\n        return intent\n    }\n\n    private fun createActivityOptions(\n        context: Context, data: DestinationData\n    ): ActivityOptionsCompat? {\n        return if (data.enterAnim != 0 || data.exitAnim != 0) {\n            makeCustomAnimation(context, data.enterAnim, data.exitAnim)\n        } else {\n            null\n        }\n    }\n\n    private suspend fun Context.awaitActivityCreated(data: DestinationData) =\n        suspendCancellableCoroutine {\n            val application = this.applicationContext as Application\n            val callback = object : ActivityLifecycleCallbacksAdapter() {\n                override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {\n                    val destinationData = activity.getDestinationData()\n                    if (destinationData?.uniqueTag == data.uniqueTag) {\n                        application.unregisterActivityLifecycleCallbacks(this)\n                        if (it.isActive) {\n                            it.resumeWith(Result.success(Unit))\n                        }\n                    }\n                }\n            }\n\n            application.registerActivityLifecycleCallbacks(callback)\n\n            it.invokeOnCancellation {\n                application.unregisterActivityLifecycleCallbacks(callback)\n            }\n        }\n\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/navigator/ErrorNavigator.kt",
    "content": "package zlc.season.butterfly.navigator\n\nimport android.content.Context\nimport android.os.Bundle\nimport zlc.season.butterfly.entities.DestinationData\n\nobject ErrorNavigator : Navigator {\n    override suspend fun navigate(\n        context: Context,\n        data: DestinationData\n    ): Result<Bundle> {\n        return Result.failure(IllegalStateException(\"Invalid destination data: $data\"))\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/navigator/Navigator.kt",
    "content": "package zlc.season.butterfly.navigator\n\nimport android.app.Activity\nimport android.content.Context\nimport android.os.Bundle\nimport zlc.season.butterfly.entities.BackStackEntry\nimport zlc.season.butterfly.entities.DestinationData\n\ninterface Navigator {\n    suspend fun navigate(context: Context, data: DestinationData): Result<Bundle>\n\n    fun popBack(activity: Activity, topEntry: BackStackEntry, bundle: Bundle) {}\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/navigator/fragment/DialogFragmentNavigator.kt",
    "content": "package zlc.season.butterfly.navigator.fragment\n\nimport android.app.Activity\nimport android.content.Context\nimport android.os.Bundle\nimport androidx.fragment.app.FragmentActivity\nimport zlc.season.butterfly.core.BackStackEntryManager\nimport zlc.season.butterfly.entities.BackStackEntry\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.internal.ButterflyFragment.Companion.awaitFragmentResult\nimport zlc.season.butterfly.navigator.Navigator\n\nclass DialogFragmentNavigator(val backStackEntryManager: BackStackEntryManager) : Navigator {\n\n    override suspend fun navigate(context: Context, data: DestinationData): Result<Bundle> {\n        return if (context is FragmentActivity) {\n            navigate(context, data)\n        } else {\n            Result.success(Bundle.EMPTY)\n        }\n    }\n\n    private suspend fun navigate(\n        activity: FragmentActivity,\n        destinationData: DestinationData\n    ): Result<Bundle> {\n        if (destinationData.enableBackStack) {\n            backStackEntryManager.addEntry(activity, BackStackEntry(destinationData))\n        }\n\n        activity.showDialogFragment(destinationData)\n\n        return if (destinationData.needResult) {\n            activity.awaitFragmentResult(destinationData.route, destinationData.uniqueTag)\n        } else {\n            Result.success(Bundle.EMPTY)\n        }\n    }\n\n    override fun popBack(activity: Activity, topEntry: BackStackEntry, bundle: Bundle) {\n        if (activity !is FragmentActivity) return\n        with(activity) {\n            val find = findDialogFragment(topEntry.destinationData) ?: return\n            if (topEntry.destinationData.needResult) {\n                setFragmentResult(topEntry.destinationData.uniqueTag, bundle)\n            }\n            find.dismissAllowingStateLoss()\n        }\n    }\n}\n\n\n"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/navigator/fragment/FragmentHelper.kt",
    "content": "package zlc.season.butterfly.navigator.fragment\n\nimport android.os.Bundle\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.fragment.app.DialogFragment\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentActivity\nimport androidx.fragment.app.FragmentManager\nimport androidx.fragment.app.FragmentResultListener\nimport androidx.fragment.app.FragmentTransaction\nimport androidx.lifecycle.DefaultLifecycleObserver\nimport androidx.lifecycle.LifecycleOwner\nimport kotlinx.coroutines.channels.awaitClose\nimport kotlinx.coroutines.flow.callbackFlow\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.suspendCancellableCoroutine\nimport zlc.season.butterfly.entities.DestinationData\n\nprivate fun FragmentActivity.createFragment(data: DestinationData): Fragment {\n    val fragment = supportFragmentManager.fragmentFactory.instantiate(\n        classLoader, data.className\n    )\n    fragment.arguments = data.bundle\n    return fragment\n}\n\ninternal fun FragmentActivity.createAndShowFragment(data: DestinationData): Fragment {\n    val fragment = createFragment(data)\n    with(supportFragmentManager.beginTransaction()) {\n        setCustomAnimations(data.enterAnim, data.exitAnim, 0, 0)\n        if (data.useReplace) {\n            replace(findContainerViewId(data), fragment, data.uniqueTag)\n        } else {\n            add(findContainerViewId(data), fragment, data.uniqueTag)\n        }\n\n        commitAllowingStateLoss()\n    }\n    return fragment\n}\n\n/**\n * Finds the target container view id for a destination within a FragmentActivity.\n *\n * @param data Information about the destination, including potential container view id or tag.\n * @return The id of the container view to use, or [android.R.id.content] if no specific container is found.\n */\nprivate fun FragmentActivity.findContainerViewId(data: DestinationData): Int {\n    var result = 0\n    if (data.containerViewId != 0) {\n        result = data.containerViewId\n    } else if (data.containerViewTag.isNotEmpty()) {\n        val containerView = window.decorView.findViewWithTag<ViewGroup>(data.containerViewTag)\n        if (containerView != null && containerView.id != View.NO_ID) {\n            result = containerView.id\n        }\n    }\n\n    return if (result != 0) result else android.R.id.content\n}\n\ninternal fun FragmentActivity.createDialogFragment(request: DestinationData): DialogFragment {\n    return createFragment(request) as DialogFragment\n}\n\ninternal fun FragmentActivity.showDialogFragment(request: DestinationData): DialogFragment {\n    val dialogFragment = createDialogFragment(request)\n    dialogFragment.show(supportFragmentManager, request.uniqueTag)\n    return dialogFragment\n}\n\ninternal fun FragmentActivity.findFragment(data: DestinationData): Fragment? {\n    return supportFragmentManager.findFragmentByTag(data.uniqueTag)\n}\n\ninternal fun FragmentActivity.findDialogFragment(request: DestinationData): DialogFragment? {\n    return supportFragmentManager.findFragmentByTag(request.uniqueTag) as? DialogFragment\n}\n\ninternal fun FragmentActivity.addFragment(fragment: Fragment) {\n    commit { add(fragment, fragment.javaClass.name) }\n}\n\ninternal fun FragmentActivity.removeFragment(fragment: Fragment) {\n    commit { remove(fragment) }\n}\n\ninternal fun FragmentActivity.removeFragment(tag: String) {\n    val find = supportFragmentManager.findFragmentByTag(tag)\n    if (find != null) {\n        commit { remove(find) }\n    }\n}\n\ninternal fun FragmentActivity.hideFragment(fragment: Fragment) {\n    commit { hide(fragment) }\n}\n\ninternal fun FragmentActivity.showFragment(fragment: Fragment) {\n    commit { show(fragment) }\n}\n\nprivate fun FragmentActivity.commit(block: FragmentTransaction.() -> Unit) {\n    val transaction = supportFragmentManager.beginTransaction()\n    transaction.block()\n    transaction.commitAllowingStateLoss()\n}\n\ninternal fun FragmentActivity.observeFragmentDestroy(block: (Fragment) -> Unit) {\n    val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {\n        override fun onFragmentDestroyed(fm: FragmentManager, f: Fragment) {\n            block(f)\n        }\n    }\n    supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false)\n    lifecycle.addObserver(object : DefaultLifecycleObserver {\n        override fun onDestroy(owner: LifecycleOwner) {\n            supportFragmentManager.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallbacks)\n            lifecycle.removeObserver(this)\n        }\n    })\n}\n\nsuspend fun FragmentActivity.awaitFragmentResume(fragment: Fragment) =\n    suspendCancellableCoroutine {\n        if (fragment.isResumed) {\n            it.resumeWith(Result.success(Unit))\n        } else {\n            val callback = object : FragmentManager.FragmentLifecycleCallbacks() {\n                override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {\n                    if (fragment === f) {\n                        supportFragmentManager.unregisterFragmentLifecycleCallbacks(this)\n                        if (it.isActive) {\n                            it.resumeWith(Result.success(Unit))\n                        }\n                    }\n                }\n            }\n\n            val lifecycleObserver = object : DefaultLifecycleObserver {\n                override fun onDestroy(owner: LifecycleOwner) {\n                    supportFragmentManager.unregisterFragmentLifecycleCallbacks(callback)\n                    lifecycle.removeObserver(this)\n                }\n            }\n\n            lifecycle.addObserver(lifecycleObserver)\n            supportFragmentManager.registerFragmentLifecycleCallbacks(callback, false)\n\n            it.invokeOnCancellation {\n                supportFragmentManager.unregisterFragmentLifecycleCallbacks(callback)\n                lifecycle.removeObserver(lifecycleObserver)\n            }\n        }\n    }\n\ninternal fun FragmentActivity.awaitFragmentResult(fragment: Fragment, requestKey: String) =\n    callbackFlow {\n        val listener = FragmentResultListener { key, result ->\n            if (requestKey == key) {\n                trySend(Result.success(result))\n                close()\n                supportFragmentManager.clearFragmentResultListener(requestKey)\n            }\n        }\n        if (!isDestroyed && isActive) {\n            supportFragmentManager.setFragmentResultListener(\n                requestKey,\n                fragment,\n                listener\n            )\n        }\n        awaitClose {\n            supportFragmentManager.clearFragmentResultListener(requestKey)\n        }\n    }\n\ninternal fun FragmentActivity.setFragmentResult(requestKey: String, bundle: Bundle) {\n    if (bundle.isEmpty) return\n    supportFragmentManager.setFragmentResult(requestKey, bundle)\n}\n"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/navigator/fragment/FragmentNavigator.kt",
    "content": "package zlc.season.butterfly.navigator.fragment\n\nimport android.app.Activity\nimport android.content.Context\nimport android.os.Bundle\nimport androidx.fragment.app.FragmentActivity\nimport zlc.season.butterfly.core.BackStackEntryManager\nimport zlc.season.butterfly.core.GroupEntryManager\nimport zlc.season.butterfly.entities.BackStackEntry\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.internal.ButterflyFragment.Companion.awaitFragmentResult\nimport zlc.season.butterfly.internal.ButterflyHelper.setActivityResult\nimport zlc.season.butterfly.navigator.Navigator\n\nclass FragmentNavigator(\n    private val backStackEntryManager: BackStackEntryManager,\n    private val groupEntryManager: GroupEntryManager\n) : Navigator {\n\n    private val navigatorContext = NavigatorContext(backStackEntryManager, groupEntryManager)\n\n    override suspend fun navigate(context: Context, data: DestinationData): Result<Bundle> {\n        return if (context is FragmentActivity) {\n            navigate(context, data)\n        } else {\n            Result.success(Bundle.EMPTY)\n        }\n    }\n\n    private suspend fun navigate(\n        activity: FragmentActivity,\n        request: DestinationData\n    ): Result<Bundle> {\n        navigatorContext.navigate(activity, request)\n\n        return if (request.needResult) {\n            activity.awaitFragmentResult(request.route, request.uniqueTag)\n        } else {\n            Result.success(Bundle.EMPTY)\n        }\n    }\n\n    override fun popBack(activity: Activity, topEntry: BackStackEntry, bundle: Bundle) {\n        if (activity !is FragmentActivity) return\n        activity.apply {\n            findFragment(topEntry.destinationData)?.let {\n                if (topEntry.destinationData.needResult) {\n                    setFragmentResult(topEntry.destinationData.uniqueTag, bundle)\n                }\n                removeFragment(it)\n            }\n        }\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/navigator/fragment/FragmentParamUpdatable.kt",
    "content": "package zlc.season.butterfly.navigator.fragment\n\nimport android.os.Bundle\n\ninterface FragmentParamUpdatable {\n    fun onParamsUpdate(newParams: Bundle)\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/navigator/fragment/NavigatorContext.kt",
    "content": "package zlc.season.butterfly.navigator.fragment\n\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentActivity\nimport zlc.season.butterfly.core.BackStackEntryManager\nimport zlc.season.butterfly.core.GroupEntryManager\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.navigator.fragment.backstack.ClearTopBackstackNavigator\nimport zlc.season.butterfly.navigator.fragment.backstack.SingleTopBackstackNavigator\nimport zlc.season.butterfly.navigator.fragment.backstack.StandardBackstackNavigator\nimport zlc.season.butterfly.navigator.fragment.group.GroupNavigator\n\nclass NavigatorContext(\n    backStackEntryManager: BackStackEntryManager,\n    groupEntryManager: GroupEntryManager\n) {\n    private val standardBackstackNavigator = StandardBackstackNavigator(backStackEntryManager)\n    private val clearTopBackstackNavigator = ClearTopBackstackNavigator(backStackEntryManager)\n    private val singleTopBackstackNavigator = SingleTopBackstackNavigator(backStackEntryManager)\n\n    private val groupNavigator = GroupNavigator(groupEntryManager)\n\n    suspend fun navigate(\n        activity: FragmentActivity,\n        request: DestinationData\n    ): Fragment {\n        return if (request.groupId.isNotEmpty()) {\n            groupNavigate(activity, request)\n        } else {\n            backstackNavigate(activity, request)\n        }\n    }\n\n    private suspend fun backstackNavigate(\n        activity: FragmentActivity,\n        request: DestinationData\n    ): Fragment {\n        return if (request.clearTop) {\n            clearTopBackstackNavigator.navigate(activity, request)\n        } else if (request.singleTop) {\n            singleTopBackstackNavigator.navigate(activity, request)\n        } else {\n            standardBackstackNavigator.navigate(activity, request)\n        }\n    }\n\n    private fun groupNavigate(\n        activity: FragmentActivity,\n        request: DestinationData\n    ): Fragment {\n        return groupNavigator.navigate(activity, request)\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/navigator/fragment/backstack/BackstackNavigator.kt",
    "content": "package zlc.season.butterfly.navigator.fragment.backstack\n\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentActivity\nimport zlc.season.butterfly.core.BackStackEntryManager\nimport zlc.season.butterfly.entities.DestinationData\n\ninterface BackstackNavigator {\n    suspend fun navigate(\n        activity: FragmentActivity,\n        destinationData: DestinationData\n    ): Fragment\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/navigator/fragment/backstack/BackstackNavigatorHelper.kt",
    "content": "package zlc.season.butterfly.navigator.fragment.backstack\n\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentActivity\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.navigator.fragment.findFragment\nimport zlc.season.butterfly.navigator.fragment.showFragment\nimport zlc.season.butterfly.navigator.fragment.FragmentParamUpdatable\nimport zlc.season.butterfly.navigator.fragment.awaitFragmentResume\nimport zlc.season.butterfly.navigator.fragment.createAndShowFragment\n\nclass BackstackNavigatorHelper {\n\n    suspend fun createAndShowFragment(\n        activity: FragmentActivity,\n        destinationData: DestinationData\n    ): Fragment {\n        val fragment = activity.createAndShowFragment(destinationData)\n        activity.awaitFragmentResume(fragment)\n        return fragment\n    }\n\n    suspend fun showFragmentAndUpdateArguments(\n        activity: FragmentActivity,\n        oldData: DestinationData,\n        newData: DestinationData\n    ): Fragment {\n        val target = activity.findFragment(oldData)\n        return if (target == null) {\n            createAndShowFragment(activity, newData)\n        } else {\n            if (target is FragmentParamUpdatable) {\n                target.onParamsUpdate(newData.bundle)\n            }\n            activity.showFragment(target)\n            activity.awaitFragmentResume(target)\n            target\n        }\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/navigator/fragment/backstack/ClearTopBackstackNavigator.kt",
    "content": "package zlc.season.butterfly.navigator.fragment.backstack\n\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentActivity\nimport zlc.season.butterfly.core.BackStackEntryManager\nimport zlc.season.butterfly.entities.BackStackEntry\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.navigator.fragment.removeFragment\n\nclass ClearTopBackstackNavigator(\n    val backStackEntryManager: BackStackEntryManager\n) : BackstackNavigator {\n    private val backstackNavigatorHelper = BackstackNavigatorHelper()\n\n    override suspend fun navigate(\n        activity: FragmentActivity,\n        destinationData: DestinationData\n    ): Fragment {\n        // Get the entry list above on the target Destination, including the target Destination.\n        val topEntryList = backStackEntryManager.getTopEntryList(activity, destinationData)\n\n        // If the list is empty, create and show a new Fragment.\n        return if (topEntryList.isEmpty()) {\n            val fragment = backstackNavigatorHelper.createAndShowFragment(\n                activity,\n                destinationData\n            )\n\n            // Add fragment to back stack.\n            if (destinationData.enableBackStack) {\n                backStackEntryManager.addEntry(activity, BackStackEntry(destinationData))\n            }\n            fragment\n        } else {\n            // Remove all the Fragment above the target Destination.\n            val targetEntry = topEntryList.removeFirst()\n            topEntryList.forEach {\n                activity.removeFragment(it.destinationData.uniqueTag)\n            }\n            backStackEntryManager.removeEntryList(activity, topEntryList)\n\n            // Show the target Fragment and update its arguments.\n            backstackNavigatorHelper.showFragmentAndUpdateArguments(\n                activity,\n                targetEntry.destinationData,\n                destinationData\n            )\n        }\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/navigator/fragment/backstack/SingleTopBackstackNavigator.kt",
    "content": "package zlc.season.butterfly.navigator.fragment.backstack\n\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentActivity\nimport zlc.season.butterfly.core.BackStackEntryManager\nimport zlc.season.butterfly.entities.BackStackEntry\nimport zlc.season.butterfly.entities.DestinationData\n\nclass SingleTopBackstackNavigator(\n    val backStackEntryManager: BackStackEntryManager\n) : BackstackNavigator {\n    private val backstackNavigatorHelper = BackstackNavigatorHelper()\n\n    override suspend fun navigate(\n        activity: FragmentActivity,\n        destinationData: DestinationData\n    ): Fragment {\n        val topEntry = backStackEntryManager.getTopEntry(activity)\n\n        // If topEntry is not null and the target Destination is the same as the topEntry,\n        // show the topEntry's fragment and update its arguments.\n        return if (\n            topEntry != null &&\n            topEntry.destinationData.className == destinationData.className\n        ) {\n            backstackNavigatorHelper.showFragmentAndUpdateArguments(\n                activity,\n                topEntry.destinationData,\n                destinationData\n            )\n        } else {\n            // Create and show a new Fragment.\n            val fragment = backstackNavigatorHelper.createAndShowFragment(\n                activity,\n                destinationData\n            )\n\n            // Add fragment to back stack.\n            if (destinationData.enableBackStack) {\n                backStackEntryManager.addEntry(activity, BackStackEntry(destinationData))\n            }\n\n            fragment\n        }\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/navigator/fragment/backstack/StandardBackstackNavigator.kt",
    "content": "package zlc.season.butterfly.navigator.fragment.backstack\n\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentActivity\nimport zlc.season.butterfly.core.BackStackEntryManager\nimport zlc.season.butterfly.entities.BackStackEntry\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.navigator.fragment.awaitFragmentResume\nimport zlc.season.butterfly.navigator.fragment.createAndShowFragment\n\nclass StandardBackstackNavigator(\n    val backStackEntryManager: BackStackEntryManager\n) : BackstackNavigator {\n    override suspend fun navigate(\n        activity: FragmentActivity,\n        destinationData: DestinationData\n    ): Fragment {\n        val fragment = activity.createAndShowFragment(destinationData)\n        activity.awaitFragmentResume(fragment)\n\n        if (destinationData.enableBackStack) {\n            backStackEntryManager.addEntry(activity, BackStackEntry(destinationData))\n        }\n\n        return fragment\n    }\n}"
  },
  {
    "path": "butterfly/src/main/java/zlc/season/butterfly/navigator/fragment/group/GroupNavigator.kt",
    "content": "package zlc.season.butterfly.navigator.fragment.group\n\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentActivity\nimport zlc.season.butterfly.core.GroupEntryManager\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.entities.GroupEntry\nimport zlc.season.butterfly.navigator.fragment.FragmentParamUpdatable\nimport zlc.season.butterfly.navigator.fragment.createAndShowFragment\nimport zlc.season.butterfly.navigator.fragment.findFragment\nimport zlc.season.butterfly.navigator.fragment.hideFragment\nimport zlc.season.butterfly.navigator.fragment.showFragment\n\nclass GroupNavigator(private val groupEntryManager: GroupEntryManager) {\n\n    fun navigate(\n        activity: FragmentActivity,\n        request: DestinationData\n    ): Fragment {\n        val list = groupEntryManager.getGroupList(activity, request.groupId)\n        list.forEach { entity ->\n            activity.findFragment(entity.destinationData)?.also {\n                activity.hideFragment(it)\n            }\n        }\n\n        val targetEntry = list.find { it.destinationData.className == request.className }\n        val targetFragment = targetEntry?.run {\n            activity.findFragment(targetEntry.destinationData)\n        }\n\n        return if (targetFragment == null) {\n            if (targetEntry == null) {\n                groupEntryManager.addEntry(activity, GroupEntry(request))\n            }\n            activity.createAndShowFragment(request)\n        } else {\n            // pass new arguments to fragment\n            if (targetFragment is FragmentParamUpdatable) {\n                targetFragment.onParamsUpdate(request.bundle)\n            }\n\n            activity.showFragment(targetFragment)\n            targetFragment\n        }\n    }\n}"
  },
  {
    "path": "butterfly/src/test/java/zlc/season/butterfly/ExampleUnitTest.kt",
    "content": "package zlc.season.butterfly\n\nimport org.junit.Test\n\nimport org.junit.Assert.*\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n    @Test\n    fun addition_isCorrect() {\n        assertEquals(4, 2 + 2)\n    }\n}"
  },
  {
    "path": "butterfly-compose/.gitignore",
    "content": "/build"
  },
  {
    "path": "butterfly-compose/build.gradle.kts",
    "content": "import zlc.season.buildlogic.base.androidLibrary\nimport zlc.season.buildlogic.base.enableCompose\n\n@Suppress(\"DSL_SCOPE_VIOLATION\")\nplugins {\n    alias(libs.plugins.library)\n    alias(libs.plugins.kotlin)\n    alias(libs.plugins.kotlin.parcelize)\n}\n\nandroidLibrary {\n    namespace = \"zlc.season.butterfly.compose\"\n\n    enableCompose()\n}\n\ndependencies {\n    api(project(\":butterfly\"))\n    api(libs.compose.ui)\n    api(libs.compose.runtime)\n    api(libs.compose.viewmodel)\n\n    implementation(libs.bundles.unit.test)\n}"
  },
  {
    "path": "butterfly-compose/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "butterfly-compose/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.kts.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "butterfly-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</manifest>"
  },
  {
    "path": "butterfly-compose/src/main/java/zlc/season/butterfly/compose/ComposeDestination.kt",
    "content": "package zlc.season.butterfly.compose\n\nimport android.os.Bundle\nimport androidx.compose.runtime.Composable\n\nopen class ComposeDestination(\n    open val composable: (@Composable () -> Unit)? = null,\n    open val paramsComposable: (@Composable (Bundle) -> Unit)? = null,\n    open val viewModelComposable: (@Composable (Any) -> Unit)? = null,\n    open val paramsViewModelComposable: (@Composable (Bundle, Any) -> Unit)? = null,\n    open val viewModelClass: String = \"\"\n) {\n    constructor() : this(\n        null,\n        null,\n        null,\n        null,\n        \"\"\n    )\n}"
  },
  {
    "path": "butterfly-compose/src/main/java/zlc/season/butterfly/compose/ComposeNavigator.kt",
    "content": "package zlc.season.butterfly.compose\n\nimport android.app.Activity\nimport android.content.Context\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport zlc.season.butterfly.compose.Utils.isComposeEntry\nimport zlc.season.butterfly.compose.navigator.ComposeBackStackNavigator\nimport zlc.season.butterfly.compose.navigator.ComposeGroupNavigator\nimport zlc.season.butterfly.compose.navigator.ComposeNavigatorHelper\nimport zlc.season.butterfly.core.BackStackEntryManager\nimport zlc.season.butterfly.core.GroupEntryManager\nimport zlc.season.butterfly.entities.BackStackEntry\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.internal.ButterflyHelper.application\nimport zlc.season.butterfly.internal.ButterflyHelper.contentView\nimport zlc.season.butterfly.internal.logd\nimport zlc.season.butterfly.navigator.Navigator\nimport zlc.season.claritypotion.ActivityLifecycleCallbacksAdapter\n\nclass ComposeNavigator(\n    private val backStackEntryManager: BackStackEntryManager,\n    private val groupEntryManager: GroupEntryManager\n) : Navigator {\n\n    private val composeGroupNavigator = ComposeGroupNavigator(groupEntryManager)\n    private val composeBackStackNavigator = ComposeBackStackNavigator(backStackEntryManager)\n\n    init {\n        application.registerActivityLifecycleCallbacks(object :\n            ActivityLifecycleCallbacksAdapter() {\n            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {\n                super.onActivityCreated(activity, savedInstanceState)\n                if (savedInstanceState != null && activity is ComponentActivity) {\n                    activity.contentView().post {\n                        val composeViewModel = ComposeViewModel.getInstance(activity.viewModelStore)\n                        composeViewModel.getAllDestinationDataFlows().forEach {\n                            it.value?.let {\n                                navigateDirectly(activity, it)\n                            }\n                        }\n                    }\n                }\n            }\n        })\n    }\n\n    override fun popBack(activity: Activity, topEntry: BackStackEntry, bundle: Bundle) {\n        if (activity !is ComponentActivity) {\n            \"Compose pop back failed! Need ComponentActivity, current activity is: $activity\".logd()\n            return\n        }\n\n        // Clear the ViewModel for the pop back page\n        ComposeViewModel.getInstance(activity.viewModelStore)\n            .clear(topEntry.destinationData.uniqueTag)\n\n        // Clear the view content for the pop back page\n        ComposeNavigatorHelper.clearComposeView(activity, topEntry.destinationData)\n\n        with(activity) {\n            val newTopEntry = backStackEntryManager.getTopEntry(this)\n\n            if (newTopEntry != null && isComposeEntry(newTopEntry)) {\n                // If new top entry is compose entry, launch new top entry directly\n                navigateDirectly(activity, newTopEntry.destinationData)\n            }\n        }\n    }\n\n    override suspend fun navigate(context: Context, data: DestinationData): Result<Bundle> {\n        return if (context is ComponentActivity) {\n            navigate(context, data)\n            Result.success(Bundle.EMPTY)\n        } else {\n            \"Compose navigation failed! Need ComponentActivity, current activity is: $context\".logd()\n            Result.failure(IllegalStateException(\"Invalid activity!\"))\n        }\n    }\n\n    private fun navigateDirectly(activity: ComponentActivity, data: DestinationData) {\n        ComposeNavigatorHelper.navigate(activity, data)\n    }\n\n    private fun navigate(activity: ComponentActivity, data: DestinationData) {\n        if (data.groupId.isNotEmpty()) {\n            composeGroupNavigator.navigate(activity, data)\n        } else {\n            composeBackStackNavigator.navigate(activity, data)\n        }\n    }\n}"
  },
  {
    "path": "butterfly-compose/src/main/java/zlc/season/butterfly/compose/ComposeViewModel.kt",
    "content": "package zlc.season.butterfly.compose\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.ViewModelStore\nimport androidx.lifecycle.get\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport zlc.season.butterfly.entities.DestinationData\n\n/**\n * This ViewModel is used to help Compose Destination Page in creating a ViewModel\n * based on the fallback stack lifecycle.\n */\nclass ComposeViewModel : ViewModel() {\n    /**\n     * Save [ComposeViewId : Flow]\n     */\n    private val destinationDataFlows = mutableMapOf<Int, MutableStateFlow<DestinationData?>>()\n\n    /**\n     * Save [destinationDataTag : ViewModelStore]\n     */\n    private val viewModelStores = mutableMapOf<String, ViewModelStore>()\n\n    fun getAllDestinationDataFlows(): List<MutableStateFlow<DestinationData?>> {\n        return destinationDataFlows.values.toList()\n    }\n\n    fun getDestinationDataFlow(\n        viewId: Int,\n        data: DestinationData? = null\n    ): MutableStateFlow<DestinationData?> {\n        var flow = destinationDataFlows[viewId]\n        if (flow == null) {\n            flow = MutableStateFlow(data)\n            destinationDataFlows[viewId] = flow\n        }\n        return flow\n    }\n\n    fun getViewModelStore(destinationDataTag: String): ViewModelStore {\n        var viewModelStore = viewModelStores[destinationDataTag]\n        if (viewModelStore == null) {\n            viewModelStore = ViewModelStore()\n            viewModelStores[destinationDataTag] = viewModelStore\n        }\n        return viewModelStore\n    }\n\n    fun clear(destinationDataTag: String) {\n        val viewModelStore = viewModelStores.remove(destinationDataTag)\n        viewModelStore?.clear()\n    }\n\n    override fun onCleared() {\n        // clear flow\n        destinationDataFlows.values.forEach {\n            it.value = null\n        }\n        destinationDataFlows.clear()\n\n        // clear viewModelStore\n        viewModelStores.values.forEach {\n            it.clear()\n        }\n        viewModelStores.clear()\n    }\n\n    override fun toString(): String {\n        val sb = StringBuilder(\"ComposeViewModel{\")\n        sb.append(Integer.toHexString(System.identityHashCode(this)))\n        sb.append(\"} ViewModelStores (\")\n        val viewModelStoreIterator: Iterator<String> = viewModelStores.keys.iterator()\n        while (viewModelStoreIterator.hasNext()) {\n            sb.append(viewModelStoreIterator.next())\n            if (viewModelStoreIterator.hasNext()) {\n                sb.append(\", \")\n            }\n        }\n        sb.append(')')\n        return sb.toString()\n    }\n\n    companion object {\n        private val FACTORY: ViewModelProvider.Factory = object : ViewModelProvider.Factory {\n            @Suppress(\"UNCHECKED_CAST\")\n            override fun <T : ViewModel> create(modelClass: Class<T>): T {\n                return ComposeViewModel() as T\n            }\n        }\n\n        fun getInstance(viewModelStore: ViewModelStore): ComposeViewModel {\n            return ViewModelProvider(viewModelStore, FACTORY).get()\n        }\n    }\n}\n"
  },
  {
    "path": "butterfly-compose/src/main/java/zlc/season/butterfly/compose/DestinationViewModelStoreOwner.kt",
    "content": "package zlc.season.butterfly.compose\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.ViewModelStore\nimport androidx.lifecycle.ViewModelStoreOwner\nimport zlc.season.butterfly.entities.DestinationData\n\nclass DestinationViewModelStoreOwner(\n    private val composeViewModel: ComposeViewModel,\n    private val destinationData: DestinationData\n) : ViewModelStoreOwner {\n    override val viewModelStore: ViewModelStore\n        get() = composeViewModel.getViewModelStore(destinationData.uniqueTag)\n\n    @Suppress(\"unchecked_cast\")\n    fun getViewModel(composable: ComposeDestination): ViewModel {\n        val viewModelClass = Class.forName(composable.viewModelClass) as Class<ViewModel>\n        return ViewModelProvider(this)[viewModelClass]\n    }\n}"
  },
  {
    "path": "butterfly-compose/src/main/java/zlc/season/butterfly/compose/Utils.kt",
    "content": "package zlc.season.butterfly.compose\n\nimport android.app.Activity\nimport android.view.ViewGroup\nimport androidx.compose.ui.platform.ComposeView\nimport zlc.season.butterfly.entities.BackStackEntry\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.entities.GroupEntry\nimport zlc.season.butterfly.internal.ButterflyHelper.contentView\n\nobject Utils {\n    const val COMPOSE_VIEW_TAG = \"Butterfly_Compose_View_Tag\"\n\n    fun isComposeEntry(entry: BackStackEntry): Boolean {\n        val cls = Class.forName(entry.destinationData.className)\n        return ComposeDestination::class.java.isAssignableFrom(cls)\n    }\n\n    fun isComposeEntry(entry: GroupEntry): Boolean {\n        val cls = Class.forName(entry.destinationData.className)\n        return ComposeDestination::class.java.isAssignableFrom(cls)\n    }\n\n    fun isActivityEntry(entry: BackStackEntry): Boolean {\n        val cls = Class.forName(entry.destinationData.className)\n        return Activity::class.java.isAssignableFrom(cls)\n    }\n\n    fun Activity.findContainerView(data: DestinationData): ViewGroup {\n        var result: ViewGroup? = null\n        if (data.containerViewId != 0) {\n            result = findViewById(data.containerViewId)\n        } else if (data.containerViewTag.isNotEmpty()) {\n            result = window.decorView.findViewWithTag(data.containerViewTag)\n        }\n        if (result == null) {\n            result = contentView()\n        }\n        return result\n    }\n\n    fun Activity.getComposeView(data: DestinationData): ComposeView? {\n        val containerView = findContainerView(data)\n        return containerView.findViewWithTag(COMPOSE_VIEW_TAG)\n    }\n\n    fun Activity.clearContainerView(data: DestinationData) {\n        val composeView = getComposeView(data)\n        composeView?.setContent { }\n    }\n\n    fun BackStackEntry.hasCustomContainer() =\n        destinationData.containerViewId != 0 || destinationData.containerViewTag.isNotEmpty()\n\n    fun BackStackEntry.hasSameCustomContainer(other: BackStackEntry) =\n        destinationData.containerViewId == other.destinationData.containerViewId &&\n                destinationData.containerViewTag == other.destinationData.containerViewTag\n}"
  },
  {
    "path": "butterfly-compose/src/main/java/zlc/season/butterfly/compose/navigator/ComposeBackStackNavigator.kt",
    "content": "package zlc.season.butterfly.compose.navigator\n\nimport androidx.activity.ComponentActivity\nimport zlc.season.butterfly.compose.ComposeViewModel\nimport zlc.season.butterfly.core.BackStackEntryManager\nimport zlc.season.butterfly.entities.BackStackEntry\nimport zlc.season.butterfly.entities.DestinationData\n\nclass ComposeBackStackNavigator(private val backStackEntryManager: BackStackEntryManager) {\n\n    fun navigate(activity: ComponentActivity, data: DestinationData) {\n        if (data.clearTop) {\n            clearTopNavigate(activity, data)\n        } else if (data.singleTop) {\n            singleTopNavigate(activity, data)\n        } else {\n            standardNavigate(activity, data)\n        }\n    }\n\n    private fun standardNavigate(activity: ComponentActivity, data: DestinationData) {\n        if (data.enableBackStack) {\n            backStackEntryManager.addEntry(activity, BackStackEntry(data))\n        }\n\n        ComposeNavigatorHelper.navigate(activity, data)\n    }\n\n    private fun clearTopNavigate(activity: ComponentActivity, data: DestinationData) {\n        val topEntryList = backStackEntryManager.getTopEntryList(activity, data)\n        return if (topEntryList.isEmpty()) {\n            standardNavigate(activity, data)\n        } else {\n            // do not remove target entry.\n            val topEntry = topEntryList.removeFirst()\n            backStackEntryManager.removeEntryList(activity, topEntryList)\n\n            // Clear removed entry resources.\n            topEntryList.forEach { each ->\n                // Clear ViewModel\n                ComposeViewModel.getInstance(activity.viewModelStore)\n                    .clear(each.destinationData.uniqueTag)\n\n                // Clear view content\n                ComposeNavigatorHelper.clearComposeView(activity, each.destinationData)\n            }\n\n            // update old destination's bundle data\n            val newData = topEntry.destinationData.copy(bundle = data.bundle)\n            ComposeNavigatorHelper.navigate(activity, newData)\n        }\n    }\n\n    private fun singleTopNavigate(activity: ComponentActivity, data: DestinationData) {\n        val topEntry = backStackEntryManager.getTopEntry(activity)\n        return if (topEntry != null && topEntry.destinationData.className == data.className) {\n            // update old destination's bundle data\n            val newData = topEntry.destinationData.copy(bundle = data.bundle)\n            ComposeNavigatorHelper.navigate(activity, newData)\n        } else {\n            standardNavigate(activity, data)\n        }\n    }\n}"
  },
  {
    "path": "butterfly-compose/src/main/java/zlc/season/butterfly/compose/navigator/ComposeGroupNavigator.kt",
    "content": "package zlc.season.butterfly.compose.navigator\n\nimport androidx.activity.ComponentActivity\nimport zlc.season.butterfly.core.GroupEntryManager\nimport zlc.season.butterfly.entities.DestinationData\nimport zlc.season.butterfly.entities.GroupEntry\n\nclass ComposeGroupNavigator(private val groupEntryManager: GroupEntryManager) {\n\n    fun navigate(activity: ComponentActivity, data: DestinationData) {\n        val groupEntryList = groupEntryManager.getGroupList(activity, data.groupId)\n        val find = groupEntryList.find { it.destinationData.className == data.className }\n        if (find == null) {\n            groupEntryManager.addEntry(activity, GroupEntry(data))\n        }\n\n        ComposeNavigatorHelper.navigate(activity, data)\n    }\n}"
  },
  {
    "path": "butterfly-compose/src/main/java/zlc/season/butterfly/compose/navigator/ComposeNavigatorHelper.kt",
    "content": "package zlc.season.butterfly.compose.navigator\n\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.activity.ComponentActivity\nimport androidx.compose.runtime.CompositionLocalProvider\nimport androidx.compose.runtime.collectAsState\nimport androidx.compose.runtime.getValue\nimport androidx.compose.ui.platform.ComposeView\nimport androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner\nimport zlc.season.butterfly.compose.ComposeDestination\nimport zlc.season.butterfly.compose.ComposeViewModel\nimport zlc.season.butterfly.compose.DestinationViewModelStoreOwner\nimport zlc.season.butterfly.compose.Utils\nimport zlc.season.butterfly.compose.Utils.findContainerView\nimport zlc.season.butterfly.entities.DestinationData\n\nobject ComposeNavigatorHelper {\n\n    fun clearComposeView(activity: ComponentActivity, data: DestinationData) {\n        val containerView = activity.findContainerView(data)\n        val composeView = containerView.findViewWithTag<ComposeView>(Utils.COMPOSE_VIEW_TAG)\n        if (composeView != null) {\n            val composeViewModel = ComposeViewModel.getInstance(activity.viewModelStore)\n            val destinationDataFlow = composeViewModel.getDestinationDataFlow(composeView.id)\n            destinationDataFlow.value = null\n        }\n    }\n\n    fun navigate(activity: ComponentActivity, data: DestinationData) {\n        val containerView = activity.findContainerView(data)\n        var composeView = containerView.findViewWithTag<ComposeView>(Utils.COMPOSE_VIEW_TAG)\n        if (composeView == null) {\n            composeView = createComposeView(containerView)\n\n            val composeViewModel = ComposeViewModel.getInstance(activity.viewModelStore)\n            val destinationDataFlow = composeViewModel.getDestinationDataFlow(composeView.id)\n            destinationDataFlow.value = data\n\n            composeView.setContent {\n                val destinationData by destinationDataFlow.collectAsState()\n                destinationData?.let {\n                    val viewModelStoreOwner = DestinationViewModelStoreOwner(composeViewModel, it)\n\n                    val composeDestination = createComposeDestination(it)\n\n                    CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) {\n                        when {\n                            composeDestination.paramsViewModelComposable != null -> {\n                                val viewModel = viewModelStoreOwner.getViewModel(composeDestination)\n                                composeDestination.paramsViewModelComposable?.invoke(\n                                    data.bundle,\n                                    viewModel\n                                )\n                            }\n\n                            composeDestination.viewModelComposable != null -> {\n                                val viewModel = viewModelStoreOwner.getViewModel(composeDestination)\n                                composeDestination.viewModelComposable?.invoke(viewModel)\n                            }\n\n                            composeDestination.paramsComposable != null -> {\n                                composeDestination.paramsComposable?.invoke(data.bundle)\n                            }\n\n                            else -> {\n                                composeDestination.composable?.invoke()\n                            }\n                        }\n                    }\n                }\n            }\n        } else {\n            val composeViewModel = ComposeViewModel.getInstance(activity.viewModelStore)\n            val destinationDataFlow = composeViewModel.getDestinationDataFlow(composeView.id)\n            destinationDataFlow.value = data\n        }\n    }\n\n    private fun createComposeView(containerView: ViewGroup): ComposeView {\n        val composeView = ComposeView(containerView.context).apply {\n            id = View.generateViewId()\n            tag = Utils.COMPOSE_VIEW_TAG\n        }\n        containerView.addView(\n            composeView,\n            ViewGroup.LayoutParams(\n                ViewGroup.LayoutParams.MATCH_PARENT,\n                ViewGroup.LayoutParams.MATCH_PARENT\n            )\n        )\n        return composeView\n    }\n\n    private fun createComposeDestination(data: DestinationData): ComposeDestination {\n        return Class.forName(data.className)\n            .getDeclaredConstructor()\n            .newInstance() as ComposeDestination\n    }\n}"
  },
  {
    "path": "butterfly-compose/src/test/java/zlc/season/butterfly/compose/ExampleUnitTest.kt",
    "content": "package zlc.season.butterfly.compose\n\nimport org.junit.Test\n\nimport org.junit.Assert.*\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n    @Test\n    fun addition_isCorrect() {\n        assertEquals(4, 2 + 2)\n    }\n}"
  },
  {
    "path": "compiler/.gitignore",
    "content": "/build"
  },
  {
    "path": "compiler/build.gradle.kts",
    "content": "@file:Suppress(\"DSL_SCOPE_VIOLATION\", \"UnstableApiUsage\")\n\nplugins {\n    kotlin(\"jvm\")\n}\n\ngroup = \"com.github.ssseasonnn\"\n\ndependencies {\n    implementation(project(\":annotation\"))\n    implementation(libs.kotlin.poet)\n    implementation(\"com.google.devtools.ksp:symbol-processing-api:1.9.20-1.0.14\")\n}"
  },
  {
    "path": "compiler/src/main/java/zlc/season/butterfly/compiler/Entities.kt",
    "content": "package zlc.season.butterfly.compiler\n\ndata class EvadeImplInfo(val className: String, val singleton: Boolean)\n\ndata class ComposeDestinationInfo(\n    val packageName: String,\n    val methodName: String,\n    val hasBundle: Boolean,\n    val viewModelName: String,\n)"
  },
  {
    "path": "compiler/src/main/java/zlc/season/butterfly/compiler/ProcessorProvider.kt",
    "content": "package zlc.season.butterfly.compiler\n\nimport com.google.devtools.ksp.processing.Dependencies\nimport com.google.devtools.ksp.processing.Resolver\nimport com.google.devtools.ksp.processing.SymbolProcessor\nimport com.google.devtools.ksp.processing.SymbolProcessorEnvironment\nimport com.google.devtools.ksp.processing.SymbolProcessorProvider\nimport com.google.devtools.ksp.symbol.KSAnnotated\nimport com.google.devtools.ksp.symbol.KSFile\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.butterfly.annotation.Evade\nimport zlc.season.butterfly.annotation.EvadeImpl\nimport zlc.season.butterfly.compiler.generator.ComposableGenerator\nimport zlc.season.butterfly.compiler.generator.ModuleClassGenerator\nimport zlc.season.butterfly.compiler.utils.BUTTERFLY_LOG_ENABLE\nimport zlc.season.butterfly.compiler.utils.DEFAULT_GENERATE_COMPOSABLE_PACKAGE_NAME\nimport zlc.season.butterfly.compiler.utils.DEFAULT_GENERATE_MODULE_PACKAGE\nimport zlc.season.butterfly.compiler.utils.TEMP_FILE_NAME\nimport zlc.season.butterfly.compiler.utils.composeDestinationClassName\nimport zlc.season.butterfly.compiler.utils.getGenerateModuleClassName\nimport zlc.season.butterfly.compiler.visitor.DestinationAnnotationVisitor\nimport zlc.season.butterfly.compiler.visitor.EvadeAnnotationVisitor\nimport zlc.season.butterfly.compiler.visitor.EvadeImplAnnotationVisitor\nimport java.io.File\n\nclass ProcessorProvider : SymbolProcessorProvider {\n    override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {\n        environment.options.forEach {\n            environment.logc(\"options: $it\")\n        }\n        return ButterflySymbolProcessor(environment)\n    }\n}\n\nprivate class ButterflySymbolProcessor(private val environment: SymbolProcessorEnvironment) : SymbolProcessor {\n    private val destinationMap = mutableMapOf<String, String>()\n    private val evadeMap = mutableMapOf<String, String>()\n    private val evadeImplMap = mutableMapOf<String, EvadeImplInfo>()\n\n    private val composableList = mutableListOf<ComposeDestinationInfo>()\n\n    private val sourceFileList = mutableListOf<KSFile>()\n\n    private var packageName = DEFAULT_GENERATE_MODULE_PACKAGE\n\n    override fun process(resolver: Resolver): List<KSAnnotated> {\n        processDestinationSymbols(resolver, sourceFileList)\n        processEvadeSymbols(resolver, sourceFileList)\n        processEvadeImplSymbols(resolver, sourceFileList)\n\n        return emptyList()\n    }\n\n    private fun processDestinationSymbols(\n        resolver: Resolver,\n        sourcesFile: MutableList<KSFile>\n    ) {\n        environment.logt(\"Start process Destination symbols...\")\n        val destinationSymbols = resolver.getSymbolsWithAnnotation(Destination::class.qualifiedName!!)\n        environment.logc(\"find destination list: ${destinationSymbols.toList()}\")\n\n        val visitor = DestinationAnnotationVisitor(environment, resolver, destinationMap, composableList, sourcesFile)\n        destinationSymbols.toList().forEach {\n            it.accept(visitor, Unit)\n        }\n        environment.logc(\"process Destination symbols end.\")\n    }\n\n    private fun processEvadeSymbols(\n        resolver: Resolver,\n        sourcesFile: MutableList<KSFile>\n    ) {\n        environment.logt(\"Start process Evade symbols...\")\n        val evadeSymbols = resolver.getSymbolsWithAnnotation(Evade::class.qualifiedName!!)\n        environment.logc(\"find evade list: ${evadeSymbols.toList()}\")\n\n        val visitor = EvadeAnnotationVisitor(environment, evadeMap, sourcesFile)\n        evadeSymbols.toList().forEach {\n            it.accept(visitor, Unit)\n        }\n        environment.logc(\"process Evade symbols end.\")\n    }\n\n    private fun processEvadeImplSymbols(\n        resolver: Resolver,\n        sourcesFile: MutableList<KSFile>\n    ) {\n        environment.logt(\"Start process EvadeImpl symbols...\")\n        val evadeImplSymbols = resolver.getSymbolsWithAnnotation(EvadeImpl::class.qualifiedName!!)\n        environment.logc(\"find evade impl list: ${evadeImplSymbols.toList()}\")\n\n        val visitor = EvadeImplAnnotationVisitor(environment, evadeImplMap, sourcesFile)\n        evadeImplSymbols.toList().forEach {\n            it.accept(visitor, Unit)\n        }\n        environment.logc(\"process EvadeImpl symbols end.\")\n    }\n\n    override fun finish() {\n        // create an empty temp file to get current module name\n        val tempOutputFile = environment.codeGenerator.createNewFile(\n            Dependencies(true),\n            packageName = packageName,\n            fileName = TEMP_FILE_NAME,\n        )\n        tempOutputFile.close()\n\n        val tempFile = environment.codeGenerator.generatedFile.find { it.name.startsWith(TEMP_FILE_NAME) }\n        tempFile?.let {\n            // generate composable class file first.\n            if (composableList.isNotEmpty()) {\n                generateComposeDestinationClass()\n            }\n\n            // generate module class file.\n            generateModuleClass(it)\n        }\n    }\n\n    private fun generateComposeDestinationClass() {\n        environment.logt(\"Generate compose destination classes...\")\n        val composableGenerator = ComposableGenerator()\n        composableList.forEach { composableInfo ->\n            val composableClassName = composeDestinationClassName(composableInfo.methodName)\n            environment.logc(\"generate compose destination class file: $composableClassName\")\n\n            val composableClassFile = environment.codeGenerator.createNewFile(\n                Dependencies(true),\n                packageName = DEFAULT_GENERATE_COMPOSABLE_PACKAGE_NAME,\n                fileName = composeDestinationClassName(composableInfo.methodName)\n            )\n            val composableClassContent = composableGenerator.createFileSpec(composableInfo).toString()\n            composableClassFile.write(composableClassContent.toByteArray())\n            composableClassFile.close()\n        }\n    }\n\n    private fun generateModuleClass(tempFile: File) {\n        val moduleClassName = getGenerateModuleClassName(tempFile.absolutePath)\n        environment.logt(\"Generate module class file: $moduleClassName\")\n\n        val moduleClassFile = environment.codeGenerator.createNewFile(\n            Dependencies(true, *sourceFileList.toTypedArray()),\n            packageName = packageName,\n            fileName = moduleClassName\n        )\n        val moduleClassGenerator = ModuleClassGenerator(packageName, moduleClassName, destinationMap, evadeMap, evadeImplMap)\n        val moduleClassContent = moduleClassGenerator.generate().toString()\n        moduleClassFile.write(moduleClassContent.toByteArray())\n        moduleClassFile.close()\n    }\n}\n\ninternal fun SymbolProcessorEnvironment.logt(log: String) {\n    if (isEnableLog()) {\n        logger.warn(\"==== $log\")\n    }\n}\n\ninternal fun SymbolProcessorEnvironment.logc(log: String) {\n    if (isEnableLog()) {\n        logger.warn(\"---- $log\")\n    }\n}\n\ninternal fun SymbolProcessorEnvironment.loge(log: String) {\n    logger.error(log)\n}\n\nprivate fun SymbolProcessorEnvironment.isEnableLog(): Boolean {\n    val value = options[BUTTERFLY_LOG_ENABLE]\n    return value == \"true\"\n}"
  },
  {
    "path": "compiler/src/main/java/zlc/season/butterfly/compiler/generator/ComposableGenerator.kt",
    "content": "package zlc.season.butterfly.compiler.generator\n\nimport com.squareup.kotlinpoet.AnnotationSpec\nimport com.squareup.kotlinpoet.ClassName\nimport com.squareup.kotlinpoet.FileSpec\nimport com.squareup.kotlinpoet.KModifier\nimport com.squareup.kotlinpoet.LambdaTypeName.Companion.get\nimport com.squareup.kotlinpoet.ParameterSpec\nimport com.squareup.kotlinpoet.PropertySpec\nimport com.squareup.kotlinpoet.TypeSpec\nimport com.squareup.kotlinpoet.asTypeName\nimport zlc.season.butterfly.compiler.ComposeDestinationInfo\nimport zlc.season.butterfly.compiler.utils.DEFAULT_GENERATE_COMPOSABLE_PACKAGE_NAME\nimport zlc.season.butterfly.compiler.utils.composeDestinationClassName\nimport zlc.season.butterfly.compiler.generator.ComposableHelper.composableLambdaType\nimport zlc.season.butterfly.compiler.generator.ComposableHelper.paramsComposableLambdaType\nimport zlc.season.butterfly.compiler.generator.ComposableHelper.paramsViewModelComposableLambdaType\nimport zlc.season.butterfly.compiler.generator.ComposableHelper.viewModelComposableLambdaType\nimport zlc.season.butterfly.compiler.utils.COMPOSE_DESTINATION_CLASS\n\n/**\nGenerated file:\n\npackage zlc.season.butterfly.compose\n\nimport android.os.Bundle\nimport androidx.compose.runtime.Composable\nimport kotlin.Any\nimport kotlin.String\nimport kotlin.Unit\nimport zlc.season.butterfly.compose.ComposeDestination\nimport zlc.season.compose.dashboard.DashboardScreen\n\npublic class DashboardScreenComposeDestination : ComposeDestination() {\npublic override val composable: @Composable (() -> Unit)? =\n@Composable {\nDashboardScreen()\n}\n\npublic override val paramsComposable: @Composable ((Bundle) -> Unit)? =\n@Composable { bundle ->\nDashboardScreen(bundle)\n}\n\npublic override val viewModelComposable: @Composable ((Any) -> Unit)? =\n@Composable { viewModel ->\nDashboardScreen(viewModel as zlc.season.compose.dashboard.DashboardViewModel)\n}\n\npublic override val paramsViewModelComposable: @Composable ((Bundle, Any) -> Unit)? =\n@Composable { bundle, viewModel ->\nDashboardScreen(\nbundle, viewModel as zlc.season.compose.dashboard.DashboardViewModel\n)\n}\n\npublic override val viewModelClass: String = \"zlc.season.compose.dashboard.DashboardViewModel\"\n}\n */\ninternal object ComposableHelper {\n    private val composeAnnotationCls = ClassName(\"androidx.compose.runtime\", \"Composable\")\n    private val composeAnnotation = AnnotationSpec.builder(composeAnnotationCls).build()\n\n    private val bundleCls = ClassName(\"android.os\", \"Bundle\")\n    private val bundleParams = ParameterSpec.unnamed(bundleCls)\n\n    private val anyParams = ParameterSpec.unnamed(Any::class)\n\n    private val unitType = Unit::class.asTypeName()\n\n    val composableLambdaType = get(returnType = unitType).copy(annotations = arrayListOf(composeAnnotation), nullable = true)\n\n    val viewModelComposableLambdaType = get(parameters = listOf(anyParams), returnType = unitType)\n        .copy(annotations = arrayListOf(composeAnnotation), nullable = true)\n\n    val paramsComposableLambdaType = get(parameters = listOf(bundleParams), returnType = unitType)\n        .copy(annotations = arrayListOf(composeAnnotation), nullable = true)\n\n    val paramsViewModelComposableLambdaType =\n        get(parameters = listOf(bundleParams, anyParams), returnType = unitType)\n            .copy(annotations = arrayListOf(composeAnnotation), nullable = true)\n\n    val superCls = ClassName(DEFAULT_GENERATE_COMPOSABLE_PACKAGE_NAME, COMPOSE_DESTINATION_CLASS)\n}\n\ninternal class ComposableGenerator {\n    fun createFileSpec(composeDestinationInfo: ComposeDestinationInfo): FileSpec {\n        val classBuilder = TypeSpec.classBuilder(composeDestinationClassName(composeDestinationInfo.methodName))\n            .superclass(ComposableHelper.superCls)\n            .apply {\n                if (composeDestinationInfo.hasBundle) {\n                    if (composeDestinationInfo.viewModelName.isNotEmpty()) {\n                        addProperty(\n                            PropertySpec.builder(\"paramsViewModelComposable\", paramsViewModelComposableLambdaType)\n                                .addModifiers(KModifier.OVERRIDE)\n                                .initializer(\n                                    \"\"\"@Composable { bundle, viewModel -> ${composeDestinationInfo.methodName}(bundle, viewModel as ${composeDestinationInfo.viewModelName}) }\"\"\".trimIndent()\n                                )\n                                .build()\n                        )\n                        addProperty(\n                            PropertySpec.builder(\"viewModelClass\", String::class)\n                                .addModifiers(KModifier.OVERRIDE)\n                                .initializer(\n                                    \"\"\" \"${composeDestinationInfo.viewModelName}\" \"\"\".trimIndent()\n                                )\n                                .build()\n                        )\n                    } else {\n                        addProperty(\n                            PropertySpec.builder(\"paramsComposable\", paramsComposableLambdaType)\n                                .addModifiers(KModifier.OVERRIDE)\n                                .initializer(\n                                    \"\"\"@Composable { bundle -> ${composeDestinationInfo.methodName}(bundle) }\"\"\".trimIndent()\n                                )\n                                .build()\n                        )\n                    }\n                } else {\n                    if (composeDestinationInfo.viewModelName.isNotEmpty()) {\n                        addProperty(\n                            PropertySpec.builder(\"viewModelComposable\", viewModelComposableLambdaType)\n                                .addModifiers(KModifier.OVERRIDE)\n                                .initializer(\n                                    \"\"\"@Composable { viewModel -> ${composeDestinationInfo.methodName}(viewModel as ${composeDestinationInfo.viewModelName}) }\"\"\".trimIndent()\n                                )\n                                .build()\n                        )\n                        addProperty(\n                            PropertySpec.builder(\"viewModelClass\", String::class)\n                                .addModifiers(KModifier.OVERRIDE)\n                                .initializer(\n                                    \"\"\" \"${composeDestinationInfo.viewModelName}\" \"\"\".trimIndent()\n                                )\n                                .build()\n                        )\n                    } else {\n                        addProperty(\n                            PropertySpec.builder(\"composable\", composableLambdaType)\n                                .addModifiers(KModifier.OVERRIDE)\n                                .initializer(\n                                    \"\"\"@Composable { ${composeDestinationInfo.methodName}() }\"\"\".trimIndent()\n                                )\n                                .build()\n                        )\n                    }\n                }\n            }\n\n        return FileSpec.builder(DEFAULT_GENERATE_COMPOSABLE_PACKAGE_NAME, composeDestinationClassName(composeDestinationInfo.methodName))\n            .addType(classBuilder.build())\n            .addImport(ClassName(composeDestinationInfo.packageName, composeDestinationInfo.methodName), \"\")\n            .addImport(ComposableHelper.superCls, \"\")\n            .build()\n    }\n}"
  },
  {
    "path": "compiler/src/main/java/zlc/season/butterfly/compiler/generator/ModuleClassGenerator.kt",
    "content": "package zlc.season.butterfly.compiler.generator\n\nimport com.squareup.kotlinpoet.CodeBlock\nimport com.squareup.kotlinpoet.FileSpec\nimport com.squareup.kotlinpoet.FunSpec\nimport com.squareup.kotlinpoet.KModifier\nimport com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy\nimport com.squareup.kotlinpoet.PropertySpec\nimport com.squareup.kotlinpoet.TypeSpec\nimport com.squareup.kotlinpoet.asClassName\nimport com.squareup.kotlinpoet.typeNameOf\nimport zlc.season.butterfly.annotation.EvadeData\nimport zlc.season.butterfly.compiler.EvadeImplInfo\nimport zlc.season.butterfly.module.Module\n\n/**\n * public class ButterflyModuleApp() : Module {\n *  public val destinationMap: HashMap<String, String> = hashMapOf<String, String>()\n *  public val evadeMap: HashMap<String, String> = hashMapOf<String, String>()\n *  public val evadeImplMap: HashMap<String, EvadeData> = hashMapOf<String, EvadeData>()\n *\n *  init {\n *      destinationMap[\"/path/foo\"] = \"zlc.season.butterflydemo.MainActivity\"\n *      destinationMap[\"/path/bar\"] = \"zlc.season.butterflydemo.TestActivity\"\n *  }\n *\n *   *  init {\n *      evadeMap[\"/path/foo\"] = \"zlc.season.butterflydemo.Foo\"\n *      evadeMap[\"path/bar\"] = \"zlc.season.butterflydemo.Bar\"\n *  }\n *\n *   *  init {\n *      evadeImplMap[\"/path/foo\"] = EvadeData(\"zlc.season.butterflydemo.Foo\",false)\n *      evadeImplMap[\"/path/bar\"] = EvadeData(\"zlc.season.butterflydemo.Bar\",false)\n *  }\n *\n *  public override fun getDestination(): HashMap<String, String> = destinationMap\n *  public override fun getEvade(): HashMap<String, String> = evadeMap\n *  public override fun getEvadeImpl(): HashMap<String, EvadeData> = evadeImplMap\n * }\n */\ninternal class ModuleClassGenerator(\n    private val packageName: String,\n    private val className: String,\n    private val destinationMap: Map<String, String>,\n    private val evadeMap: Map<String, String>,\n    private val evadeImplMap: Map<String, EvadeImplInfo>,\n) {\n    private val moduleClass = Module::class.asClassName()\n\n    private val mapClass = typeNameOf<HashMap<String, Class<*>>>()\n    private val mapDataClass = HashMap::class.asClassName().parameterizedBy(String::class.asClassName(), EvadeData::class.asClassName())\n\n    fun generate(): FileSpec {\n        val companion = TypeSpec.companionObjectBuilder()\n            .addFunction(\n                FunSpec.builder(\"doNothing\")\n                    .returns(moduleClass)\n                    .addStatement(\"return ${className}()\")\n                    .build()\n            )\n            .build()\n        val classBuilder = TypeSpec.classBuilder(className)\n            .addSuperinterface(moduleClass)\n            .primaryConstructor(FunSpec.constructorBuilder().build())\n            .addProperty(\n                PropertySpec.builder(\"destinationMap\", mapClass)\n                    .initializer(\"hashMapOf<String,  Class<*>>()\")\n                    .build()\n            )\n            .addProperty(\n                PropertySpec.builder(\"evadeMap\", mapClass)\n                    .initializer(\"hashMapOf<String, Class<*>>()\")\n                    .build()\n            )\n            .addProperty(\n                PropertySpec.builder(\"evadeImplMap\", mapDataClass)\n                    .initializer(\"hashMapOf<String, EvadeData>()\")\n                    .build()\n            )\n            .addInitializerBlock(\n                generateDestinationMapBlock()\n            )\n            .addInitializerBlock(\n                generateEvadeMapBlock()\n            )\n            .addInitializerBlock(\n                generateEvadeImplMapBlock()\n            )\n            .addFunction(\n                FunSpec.builder(\"getDestination\")\n                    .addModifiers(KModifier.OVERRIDE)\n                    .addStatement(\"return destinationMap\")\n                    .returns(mapClass)\n                    .build()\n            )\n            .addFunction(\n                FunSpec.builder(\"getEvade\")\n                    .addModifiers(KModifier.OVERRIDE)\n                    .addStatement(\"return evadeMap\")\n                    .returns(mapClass)\n                    .build()\n            )\n            .addFunction(\n                FunSpec.builder(\"getEvadeImpl\")\n                    .addModifiers(KModifier.OVERRIDE)\n                    .addStatement(\"return evadeImplMap\")\n                    .returns(mapDataClass)\n                    .build()\n            )\n            .addType(companion)\n\n        return FileSpec.builder(packageName, className)\n            .addType(classBuilder.build())\n            .build()\n    }\n\n    private fun generateDestinationMapBlock(): CodeBlock {\n        val builder = CodeBlock.Builder()\n        destinationMap.forEach { (k, v) ->\n            builder.addStatement(\"\"\"destinationMap[\"$k\"] = ${v}::class.java \"\"\")\n        }\n        return builder.build()\n    }\n\n    private fun generateEvadeMapBlock(): CodeBlock {\n        val builder = CodeBlock.Builder()\n        evadeMap.forEach { (k, v) ->\n            builder.addStatement(\"\"\"evadeMap[\"$k\"] = ${v}::class.java \"\"\")\n        }\n        return builder.build()\n    }\n\n    private fun generateEvadeImplMapBlock(): CodeBlock {\n        val builder = CodeBlock.Builder()\n        evadeImplMap.forEach { (k, v) ->\n            builder.addStatement(\"\"\"evadeImplMap[\"$k\"] = EvadeData(cls=${(v.className)}::class.java, singleton=${v.singleton}) \"\"\")\n        }\n        return builder.build()\n    }\n}"
  },
  {
    "path": "compiler/src/main/java/zlc/season/butterfly/compiler/utils/KspUtils.kt",
    "content": "package zlc.season.butterfly.compiler.utils\n\nimport com.google.devtools.ksp.symbol.KSAnnotation\nimport com.google.devtools.ksp.symbol.KSClassDeclaration\nimport com.google.devtools.ksp.symbol.KSFunctionDeclaration\nimport com.google.devtools.ksp.symbol.KSType\n\nconst val BUTTERFLY_LOG_ENABLE = \"butterfly.log.enable\"\n\n/**\n * Get class full name by KSType.\n * eg: com.example.Test\n */\ninternal fun KSType.getClassFullName(): String {\n    val ksClassDeclaration = declaration as KSClassDeclaration\n    val packageName = ksClassDeclaration.packageName.asString()\n    val className = ksClassDeclaration.simpleName.asString()\n    return \"$packageName.$className\"\n}\n\n/**\n * Get class full name by KSClassDeclaration.\n * eg: com.example.Test\n */\ninternal fun KSClassDeclaration.getClassFullName(): String {\n    return \"${packageName.asString()}.${simpleName.asString()}\"\n}\n\n/**\n * Get annotation's value by key\n */\n@Suppress(\"UNCHECKED_CAST\")\ninternal fun <T> KSAnnotation.getValue(key: String, defaultValue: T): T {\n    return arguments.find { it.name?.asString() == key }?.value as? T ?: defaultValue\n}\n\ninternal fun KSFunctionDeclaration.getAnnotationByName(name: String): KSAnnotation? {\n    return annotations.toList().find { it.shortName.asString() == name }\n}\n\ninternal fun KSClassDeclaration.getAnnotationByName(name: String): KSAnnotation? {\n    return annotations.toList().find { it.shortName.asString() == name }\n}"
  },
  {
    "path": "compiler/src/main/java/zlc/season/butterfly/compiler/utils/Util.kt",
    "content": "package zlc.season.butterfly.compiler.utils\n\nimport java.util.Locale\nimport java.io.File.separatorChar as s\n\n/**\n * default package name for generate module class.\n */\nconst val DEFAULT_GENERATE_MODULE_PACKAGE = \"zlc.season.butterfly.module\"\nconst val DEFAULT_GENERATE_COMPOSABLE_PACKAGE_NAME = \"zlc.season.butterfly.compose\"\n\nconst val TEMP_FILE_NAME = \"Temp\"\n\nconst val BUNDLE_CLASS_NAME = \"android.os.Bundle\"\nconst val VIEW_MODEL_CLASS_NAME = \"androidx.lifecycle.ViewModel\"\n\nconst val DESTINATION_NAME = \"Destination\"\nconst val EVADE_NAME = \"Evade\"\nconst val EVADE_IMPL_NAME = \"EvadeImpl\"\n\nconst val COMPOSE_DESTINATION_CLASS = \"ComposeDestination\"\n\nconst val DESTINATION_ROUTE_KEY = \"route\"\nconst val EVADE_IDENTITY_KEY = \"identity\"\nconst val EVADE_SINGLETON_KEY = \"singleton\"\n\nconst val EVADE_IMPL_SUFFIX = \"Impl\"\n\n/**\n * get generate module class name. eg: ButterflyHomeModule\n */\ninternal fun getGenerateModuleClassName(generateDir: String): String {\n    return try {\n        val kspGenDir = \"${s}build${s}generated${s}ksp\"\n        val pathIndex = generateDir.lastIndexOf(kspGenDir)\n        val subStr = generateDir.substring(0, pathIndex)\n        val lastIndex = subStr.lastIndexOf(s)\n        val result = subStr.substring(lastIndex + 1)\n        \"Butterfly${result.camelCase()}Module\"\n    } catch (e: Exception) {\n        \"ButterflyDefaultModule\"\n    }\n}\n\n\n/**\n * The name of each Composable function Class.\n */\ninternal fun composeDestinationFullClassName(methodName: String): String {\n    return \"$DEFAULT_GENERATE_COMPOSABLE_PACKAGE_NAME.${composeDestinationClassName(methodName)}\"\n}\n\n/**\n * The name of generated ComposeDestination class.\n * eg:\n * @Destination(\"path\")\n * @Composable\n * fun Test(){}\n *\n * will generate class:\n * class TestComposeDestination: ComposeDestination {}\n */\ninternal fun composeDestinationClassName(methodName: String): String {\n    return \"${methodName}${COMPOSE_DESTINATION_CLASS}\"\n}\n\ninternal fun String.camelCase(): String {\n    val words: List<String> = split(\"[\\\\W_]+\".toRegex())\n    val builder = StringBuilder()\n    words.forEach {\n        val word = if (it.isEmpty()) it else it[0].uppercase() + it.substring(1).lowercase()\n        builder.append(word)\n    }\n\n    return builder.toString()\n}\n\ninternal fun String.cap(): String {\n    return replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }\n}"
  },
  {
    "path": "compiler/src/main/java/zlc/season/butterfly/compiler/visitor/DestinationAnnotationVisitor.kt",
    "content": "package zlc.season.butterfly.compiler.visitor\n\nimport com.google.devtools.ksp.getClassDeclarationByName\nimport com.google.devtools.ksp.processing.Resolver\nimport com.google.devtools.ksp.processing.SymbolProcessorEnvironment\nimport com.google.devtools.ksp.symbol.KSClassDeclaration\nimport com.google.devtools.ksp.symbol.KSFile\nimport com.google.devtools.ksp.symbol.KSFunctionDeclaration\nimport com.google.devtools.ksp.symbol.KSVisitorVoid\nimport zlc.season.butterfly.compiler.ComposeDestinationInfo\nimport zlc.season.butterfly.compiler.logc\nimport zlc.season.butterfly.compiler.loge\nimport zlc.season.butterfly.compiler.utils.BUNDLE_CLASS_NAME\nimport zlc.season.butterfly.compiler.utils.DESTINATION_NAME\nimport zlc.season.butterfly.compiler.utils.DESTINATION_ROUTE_KEY\nimport zlc.season.butterfly.compiler.utils.VIEW_MODEL_CLASS_NAME\nimport zlc.season.butterfly.compiler.utils.composeDestinationFullClassName\nimport zlc.season.butterfly.compiler.utils.getAnnotationByName\nimport zlc.season.butterfly.compiler.utils.getClassFullName\nimport zlc.season.butterfly.compiler.utils.getValue\n\nclass DestinationAnnotationVisitor(\n    private val environment: SymbolProcessorEnvironment,\n    private val resolver: Resolver,\n    private val destinationMap: MutableMap<String, String>,\n    private val composeList: MutableList<ComposeDestinationInfo>,\n    private val sourcesFile: MutableList<KSFile>\n) : KSVisitorVoid() {\n\n    private val bundleClassType = resolver.getClassDeclarationByName(BUNDLE_CLASS_NAME)!!.asStarProjectedType()\n    private val viewModelClassType = resolver.getClassDeclarationByName(VIEW_MODEL_CLASS_NAME)!!.asStarProjectedType()\n\n    override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {\n        environment.logc(\"process destination: $classDeclaration\")\n        val annotation = classDeclaration.getAnnotationByName(DESTINATION_NAME)\n        if (annotation != null) {\n            val routeValue = annotation.getValue(DESTINATION_ROUTE_KEY, \"\")\n            val className = classDeclaration.getClassFullName()\n            if (routeValue.isNotEmpty()) {\n                environment.logc(\"destination processed: [route='$routeValue', target='$className']\")\n                destinationMap[routeValue] = className\n\n                // add file to dependency\n                sourcesFile.add(classDeclaration.containingFile!!)\n            } else {\n                environment.loge(\"[$classDeclaration] route not found!\")\n            }\n        }\n    }\n\n    override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) {\n        environment.logc(\"process compose destination: $function\")\n        val annotation = function.getAnnotationByName(DESTINATION_NAME)\n        if (annotation != null) {\n            val packageName = function.packageName.asString()\n            val methodName = function.simpleName.asString()\n            val routeValue = annotation.getValue(DESTINATION_ROUTE_KEY, \"\")\n            if (routeValue.isNotEmpty()) {\n                if (function.parameters.isNotEmpty()) {\n                    when (function.parameters.size) {\n                        1 -> {\n                            val parameterKsType = function.parameters[0].type.resolve()\n                            if (bundleClassType.isAssignableFrom(parameterKsType)) {\n                                composeList.add(ComposeDestinationInfo(packageName, methodName, true, \"\"))\n                            } else if (viewModelClassType.isAssignableFrom(parameterKsType)) {\n                                val viewModelClassName = parameterKsType.getClassFullName()\n                                composeList.add(ComposeDestinationInfo(packageName, methodName, false, viewModelClassName))\n                            } else {\n                                environment.loge(\"[$function] invalid parameter! Compose only support Bundle or ViewModel type!\")\n                            }\n                        }\n\n                        2 -> {\n                            val firstParameterKsType = function.parameters[0].type.resolve()\n                            val secondParameterKsType = function.parameters[1].type.resolve()\n                            val isBundleFirst = bundleClassType.isAssignableFrom(firstParameterKsType)\n                            val isViewModelSecond = viewModelClassType.isAssignableFrom(secondParameterKsType)\n\n                            if (isBundleFirst && isViewModelSecond) {\n                                val viewModelClassName = secondParameterKsType.getClassFullName()\n                                composeList.add(ComposeDestinationInfo(packageName, methodName, true, viewModelClassName))\n                            } else {\n                                if (!isBundleFirst) {\n                                    environment.loge(\"[$function] first parameter type must be Bundle!\")\n                                } else {\n                                    environment.loge(\"[$function] second parameter type must be ViewModel!\")\n                                }\n                            }\n                        }\n\n                        else -> {\n                            environment.loge(\"[$function] invalid parameter size! Compose only support max 2 parameters!\")\n                        }\n                    }\n                } else {\n                    composeList.add(ComposeDestinationInfo(packageName, methodName, false, \"\"))\n                }\n\n                val targetClassName = composeDestinationFullClassName(methodName)\n                environment.logc(\"compose destination processed: [route='$routeValue', target='$targetClassName']\")\n                destinationMap[routeValue] = targetClassName\n\n                // add file to dependency\n                sourcesFile.add(function.containingFile!!)\n            } else {\n                environment.loge(\"[$function] route not found!\")\n            }\n        }\n    }\n}"
  },
  {
    "path": "compiler/src/main/java/zlc/season/butterfly/compiler/visitor/EvadeAnnotationVisitor.kt",
    "content": "package zlc.season.butterfly.compiler.visitor\n\nimport com.google.devtools.ksp.processing.SymbolProcessorEnvironment\nimport com.google.devtools.ksp.symbol.ClassKind\nimport com.google.devtools.ksp.symbol.KSClassDeclaration\nimport com.google.devtools.ksp.symbol.KSFile\nimport com.google.devtools.ksp.symbol.KSVisitorVoid\nimport zlc.season.butterfly.compiler.utils.EVADE_IDENTITY_KEY\nimport zlc.season.butterfly.compiler.utils.getClassFullName\nimport zlc.season.butterfly.compiler.utils.getValue\nimport zlc.season.butterfly.compiler.logc\nimport zlc.season.butterfly.compiler.loge\nimport zlc.season.butterfly.compiler.utils.EVADE_NAME\nimport zlc.season.butterfly.compiler.utils.getAnnotationByName\n\nclass EvadeAnnotationVisitor(\n    private val environment: SymbolProcessorEnvironment,\n    private val evadeMap: MutableMap<String, String>,\n    private val sourcesFile: MutableList<KSFile>\n) : KSVisitorVoid() {\n    override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {\n        if (classDeclaration.classKind == ClassKind.INTERFACE) {\n            environment.logc(\"process evade: $classDeclaration\")\n            val annotation = classDeclaration.getAnnotationByName(EVADE_NAME)\n            if (annotation != null) {\n                val identityValue = annotation.getValue(EVADE_IDENTITY_KEY, \"\")\n                val realKey = identityValue.ifEmpty { classDeclaration.simpleName.asString() }\n                val targetClassName = classDeclaration.getClassFullName()\n\n                environment.logc(\"evade processed: [identity='$realKey', target='$targetClassName']\")\n                evadeMap[realKey] = targetClassName\n\n                // add file to dependency\n                sourcesFile.add(classDeclaration.containingFile!!)\n            }\n        } else {\n            environment.loge(\"[$classDeclaration] invalid evade. @Evade must be annotated at an interface!\")\n        }\n    }\n}"
  },
  {
    "path": "compiler/src/main/java/zlc/season/butterfly/compiler/visitor/EvadeImplAnnotationVisitor.kt",
    "content": "package zlc.season.butterfly.compiler.visitor\n\nimport com.google.devtools.ksp.processing.SymbolProcessorEnvironment\nimport com.google.devtools.ksp.symbol.ClassKind\nimport com.google.devtools.ksp.symbol.KSClassDeclaration\nimport com.google.devtools.ksp.symbol.KSFile\nimport com.google.devtools.ksp.symbol.KSVisitorVoid\nimport zlc.season.butterfly.compiler.utils.EVADE_IDENTITY_KEY\nimport zlc.season.butterfly.compiler.utils.EVADE_IMPL_SUFFIX\nimport zlc.season.butterfly.compiler.utils.EVADE_SINGLETON_KEY\nimport zlc.season.butterfly.compiler.EvadeImplInfo\nimport zlc.season.butterfly.compiler.utils.getClassFullName\nimport zlc.season.butterfly.compiler.utils.getValue\nimport zlc.season.butterfly.compiler.logc\nimport zlc.season.butterfly.compiler.loge\nimport zlc.season.butterfly.compiler.utils.EVADE_IMPL_NAME\nimport zlc.season.butterfly.compiler.utils.EVADE_NAME\nimport zlc.season.butterfly.compiler.utils.getAnnotationByName\n\nclass EvadeImplAnnotationVisitor(\n    private val environment: SymbolProcessorEnvironment,\n    private val evadeImplMap: MutableMap<String, EvadeImplInfo>,\n    private val sourcesFile: MutableList<KSFile>\n) : KSVisitorVoid() {\n    override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {\n        if (classDeclaration.classKind == ClassKind.CLASS) {\n            environment.logc(\"process evade impl: $classDeclaration\")\n            val annotation = classDeclaration.getAnnotationByName(EVADE_IMPL_NAME)\n            if (annotation != null) {\n                val isSingleton = annotation.getValue(EVADE_SINGLETON_KEY, true)\n                val identityValue = annotation.getValue(EVADE_IDENTITY_KEY, \"\")\n                val classSimpleName = classDeclaration.simpleName.asString()\n\n                if (identityValue.isEmpty() && !classSimpleName.endsWith(EVADE_IMPL_SUFFIX)) {\n                    environment.loge(\"[$classDeclaration] invalid evade impl. If your @EvadeImpl class does not provide identity value, then the class name must end with Impl!\")\n                } else {\n                    val realKey = identityValue.ifEmpty {\n                        val index = classSimpleName.lastIndexOf(EVADE_IMPL_SUFFIX)\n                        classSimpleName.substring(0, index)\n                    }\n                    val targetClassName = classDeclaration.getClassFullName()\n                    environment.logc(\"evade impl processed: [identity='$realKey', target='$targetClassName']\")\n                    evadeImplMap[realKey] = EvadeImplInfo(targetClassName, isSingleton)\n\n                    // add file to dependency\n                    sourcesFile.add(classDeclaration.containingFile!!)\n                }\n            }\n        } else {\n            environment.loge(\"[$classDeclaration] invalid evade impl. @EvadeImpl must be annotated at an class!\")\n        }\n    }\n}"
  },
  {
    "path": "compiler/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider",
    "content": "zlc.season.butterfly.compiler.ProcessorProvider"
  },
  {
    "path": "gradle/libs.versions.toml",
    "content": "[versions]\n# android\nsdk-min-version = \"23\"\nsdk-target-version = \"34\"\nsdk-compile-version = \"34\"\n\n# library\ngradle = \"8.1.4\"\nkover = \"0.7.3\"\ndetekt = \"1.23.0\"\n\nksp = \"1.9.20-1.0.14\"\npoet = \"1.11.0\"\nauto-service = \"1.0\"\n\n# kotlin\nkotlin = \"1.9.20\"\nkotlinx-coroutines = \"1.7.2\"\nkotlinx-serialization-json = \"1.5.1\"\n\n# android\nappcompat = \"1.6.1\"\nfragment-ktx = \"1.6.2\"\nactivity-ktx = \"1.8.1\"\ncore-ktx = \"1.12.0\"\narch-lifecycle = \"2.6.2\"\narch-lifecycle-extension = \"2.2.0\"\nhilt = \"2.46.1\"\n\n# ui\nmaterial = \"1.10.0\"\nconstraint-layout = \"2.1.4\"\n\n# compose\n# compose compiler\ncompose-compiler = \"1.5.5\"\n\nactivity-compose = \"1.8.1\"\ncompose = \"1.5.4\"\ncompose-material3 = \"1.1.2\"\naccompanist = \"0.31.3-beta\"\n\n# http\nretrofit = \"2.9.0\"\nretrofit-kotlinx-serialization-json = \"1.0.0\"\nokhttp-logging = \"4.11.0\"\ncoil = \"2.4.0\"\nexoplayer = \"2.19.1\"\n\n# leak\nleakcanary = \"2.9.1\"\n\n# season\nbutterfly = \"1.0.1\"\nclarity = \"1.0.6\"\nyasha = \"1.1.4\"\nbracer = \"1.0.7\"\n\n# test\narch-test = \"2.2.0\"\njunit = \"4.13.2\"\nmockk = \"1.13.5\"\ntruth = \"1.1.5\"\nrobolectric = \"4.10.3\"\nturbine = \"1.0.0\"\nslf4j = \"2.0.7\"\n\n# android test\nandroidx-junit = \"1.1.5\"\nandroidx-test-core = \"1.5.0\"\nandroidx-test-runner = \"1.5.2\"\nandroidx-test-rules = \"1.5.0\"\nespresso-core = \"3.5.1\"\n\n[libraries]\n# tools\nkotlin-poet = { module = \"com.squareup:kotlinpoet\", version.ref = \"poet\" }\nauto-service = { module = \"com.google.auto.service:auto-service\", version.ref = \"auto-service\" }\n\n\n# plugin\nandroid-gradle = { module = \"com.android.tools.build:gradle\", version.ref = \"gradle\" }\nandroid-gradle-api = { module = \"com.android.tools.build:gradle-api\", version.ref = \"gradle\" }\nkotlin-gradle-plugin = { module = \"org.jetbrains.kotlin:kotlin-gradle-plugin\", version.ref = \"kotlin\" }\n\n# kotlin\nkotlin-stdlib = { module = \"org.jetbrains.kotlin:kotlin-stdlib-jdk8\", version.ref = \"kotlin\" }\nkotlin-reflect = { module = \"org.jetbrains.kotlin:kotlin-reflect\", version.ref = \"kotlin\" }\nkotlin-coroutines = { module = \"org.jetbrains.kotlinx:kotlinx-coroutines-android\", version.ref = \"kotlinx-coroutines\" }\nkotlin-serialization-json = { module = \"org.jetbrains.kotlinx:kotlinx-serialization-json\", version.ref = \"kotlinx-serialization-json\" }\n\n# android\nappcompat = { module = \"androidx.appcompat:appcompat\", version.ref = \"appcompat\" }\ncore-ktx = { module = \"androidx.core:core-ktx\", version.ref = \"core-ktx\" }\nactivity-ktx = { module = \"androidx.activity:activity-ktx\", version.ref = \"activity-ktx\" }\nfragment-ktx = { module = \"androidx.fragment:fragment-ktx\", version.ref = \"fragment-ktx\" }\nconstraintlayout = { module = \"androidx.constraintlayout:constraintlayout\", version.ref = \"constraint-layout\" }\nmaterial = { module = \"com.google.android.material:material\", version.ref = \"material\" }\naccompanist-systemuicontroller = { module = \"com.google.accompanist:accompanist-systemuicontroller\", version.ref = \"accompanist\" }\naccompanist-flowlayout = { module = \"com.google.accompanist:accompanist-flowlayout\", version.ref = \"accompanist\" }\naccompanist-permission = { module = \"com.google.accompanist:accompanist-permissions\", version.ref = \"accompanist\" }\n\n# lifecycle\nlifecycle-runtime-ktx = { module = \"androidx.lifecycle:lifecycle-runtime-ktx\", version.ref = \"arch-lifecycle\" }\nlifecycle-viewmodel-ktx = { module = \"androidx.lifecycle:lifecycle-viewmodel-ktx\", version.ref = \"arch-lifecycle\" }\nlifecycle-extensions = { module = \"androidx.lifecycle:lifecycle-extensions\", version.ref = \"arch-lifecycle-extension\" }\n\n# compose\ncompose-activity = { module = \"androidx.activity:activity-compose\", version.ref = \"activity-compose\" }\ncompose-viewmodel = { module = \"androidx.lifecycle:lifecycle-viewmodel-compose\", version.ref = \"arch-lifecycle\" }\ncompose-lifecycle = { module = \"androidx.lifecycle:lifecycle-runtime-compose\", version.ref = \"arch-lifecycle\" }\ncompose-animation = { module = \"androidx.compose.animation:animation\", version.ref = \"compose\" }\ncompose-foundation = { module = \"androidx.compose.foundation:foundation\", version.ref = \"compose\" }\ncompose-material = { module = \"androidx.compose.material:material\", version.ref = \"compose\" }\ncompose-material-icons-core = { module = \"androidx.compose.material:material-icons-core\", version.ref = \"compose\" }\ncompose-material-icons-extended = { module = \"androidx.compose.material:material-icons-extended\", version.ref = \"compose\" }\ncompose-runtime = { module = \"androidx.compose.runtime:runtime\", version.ref = \"compose\" }\ncompose-runtime-livedata = { module = \"androidx.compose.runtime:runtime-livedata\", version.ref = \"compose\" }\ncompose-runtime-saveable = { module = \"androidx.compose.runtime:runtime-saveable\", version.ref = \"compose\" }\ncompose-ui = { module = \"androidx.compose.ui:ui\", version.ref = \"compose\" }\ncompose-ui-viewbinding = { module = \"androidx.compose.ui:ui-viewbinding\", version.ref = \"compose\" }\ncompose-ui-tooling = { module = \"androidx.compose.ui:ui-tooling\", version.ref = \"compose\" }\ncompose-ui-tooling-preview = { module = \"androidx.compose.ui:ui-tooling-preview\", version.ref = \"compose\" }\ncompose-ui-test = { module = \"androidx.compose.ui:ui-test\", version.ref = \"compose\" }\ncompose-ui-test-junit = { module = \"androidx.compose.ui:ui-test-junit4\", version.ref = \"compose\" }\ncompose-ui-test-manifest = { module = \"androidx.compose.ui:ui-test-manifest\", version.ref = \"compose\" }\ncompose-material3 = { module = \"androidx.compose.material3:material3\", version.ref = \"compose-material3\" }\n\n# hilt\nhilt-android = { module = \"com.google.dagger:hilt-android\", version.ref = \"hilt\" }\nhilt-compiler = { module = \"com.google.dagger:hilt-android-compiler\", version.ref = \"hilt\" }\n\n# retrofit\nretrofit-core = { module = \"com.squareup.retrofit2:retrofit\", version.ref = \"retrofit\" }\nretrofit-kotlin-serialization = { module = \"com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter\", version.ref = \"retrofit-kotlinx-serialization-json\" }\nokhttp-logging = { module = \"com.squareup.okhttp3:logging-interceptor\", version.ref = \"okhttp-logging\" }\n\n# detekt\ndetekt-formatting = { module = \"io.gitlab.arturbosch.detekt:detekt-formatting\", version.ref = \"detekt\" }\n\n# Coil\ncoil-kt = { module = \"io.coil-kt:coil\", version.ref = \"coil\" }\ncoil-kt-compose = { module = \"io.coil-kt:coil-compose\", version.ref = \"coil\" }\ncoil-kt-svg = { module = \"io.coil-kt:coil-svg\", version.ref = \"coil\" }\n\n# video\nexoplayer = { module = \"com.google.android.exoplayer:exoplayer\", version.ref = \"exoplayer\" }\n\n# leak\nleakcanary = { module = \"com.squareup.leakcanary:leakcanary-android\", version.ref = \"leakcanary\" }\n\n# season\nyasha = { module = \"com.github.ssseasonnn:Yasha\", version.ref = \"yasha\" }\nbracer = { module = \"com.github.ssseasonnn:Bracer\", version.ref = \"bracer\" }\nclarity = { module = \"com.github.ssseasonnn:ClarityPotion\", version.ref = \"clarity\" }\n\n# test\njunit4 = { module = \"junit:junit\", version.ref = \"junit\" }\nkotlin-coroutines-test = { module = \"org.jetbrains.kotlinx:kotlinx-coroutines-test\", version.ref = \"kotlinx-coroutines\" }\nmockk = { module = \"io.mockk:mockk\", version.ref = \"mockk\" }\nmockk-agent = { module = \"io.mockk:mockk-agent-jvm\", version.ref = \"mockk\" }\ntruth = { module = \"com.google.truth:truth\", version.ref = \"truth\" }\nrobolectric = { module = \"org.robolectric:robolectric\", version.ref = \"robolectric\" }\nturbine = { module = \"app.cash.turbine:turbine\", version.ref = \"turbine\" }\nslf4j = { module = \"org.slf4j:slf4j-nop\", version.ref = \"slf4j\" }\n\n# android test\ncore-testing = { module = \"androidx.arch.core:core-testing\", version.ref = \"arch-test\" }\nhilt-testing = { module = \"com.google.dagger:hilt-android-testing\", version.ref = \"hilt\" }\nandroidx-junit = { module = \"androidx.test.ext:junit\", version.ref = \"androidx-junit\" }\nandroidx-junit-ktx = { module = \"androidx.test.ext:junit-ktx\", version.ref = \"androidx-junit\" }\nandroidx-test-core = { module = \"androidx.test:core\", version.ref = \"androidx-test-core\" }\nandroidx-test-runner = { module = \"androidx.test:runner\", version.ref = \"androidx-test-runner\" }\nandroidx-test-rules = { module = \"androidx.test:rules\", version.ref = \"androidx-test-rules\" }\nespresso-core = { module = \"androidx.test.espresso:espresso-core\", version.ref = \"espresso-core\" }\n\n[bundles]\nseason = [\n    \"yasha\",\n    \"bracer\",\n    \"clarity\"\n]\n\ncompose = [\n    \"compose-activity\",\n    \"compose-lifecycle\",\n    \"compose-runtime\",\n    \"compose-viewmodel\",\n    \"compose-ui\",\n    \"compose-material3\",\n    \"compose-ui-tooling\",\n    \"compose-ui-tooling-preview\"\n]\n\nkotlin = [\n    \"kotlin-stdlib\",\n    \"kotlin-coroutines\",\n    \"kotlin-serialization-json\"\n]\nandroid = [\n    \"appcompat\",\n    \"core-ktx\",\n    \"activity-ktx\",\n    \"fragment-ktx\",\n    \"constraintlayout\",\n    \"material\",\n    \"lifecycle-runtime-ktx\",\n    \"lifecycle-viewmodel-ktx\",\n    \"accompanist-systemuicontroller\"\n]\nretrofit = [\n    \"retrofit-core\",\n    \"retrofit-kotlin-serialization\",\n    \"okhttp-logging\"\n]\ncoil = [\n    \"coil-kt\",\n    \"coil-kt-compose\",\n    \"coil-kt-svg\"\n]\nunit-test = [\n    \"junit4\",\n    \"kotlin-coroutines-test\",\n    \"mockk\",\n    \"mockk-agent\",\n    \"truth\",\n    \"turbine\",\n    \"slf4j\"\n]\nandroid-test = [\n    \"core-testing\",\n    \"compose-ui-test\",\n    \"compose-ui-test-junit\",\n    \"compose-ui-test-manifest\",\n    \"androidx-junit\",\n    \"androidx-junit-ktx\",\n    \"androidx-test-core\",\n    \"androidx-test-runner\",\n    \"androidx-test-rules\",\n    \"espresso-core\",\n    \"mockk\",\n    \"mockk-agent\",\n    \"truth\",\n    \"slf4j\"\n]\n\n[plugins]\napplication = { id = \"com.android.application\", version.ref = \"gradle\" }\nlibrary = { id = \"com.android.library\", version.ref = \"gradle\" }\nkotlin-jvm = { id = \"org.jetbrains.kotlin.jvm\", version.ref = \"kotlin\" }\nkotlin = { id = \"org.jetbrains.kotlin.android\", version.ref = \"kotlin\" }\nkotlin-serialization = { id = \"org.jetbrains.kotlin.plugin.serialization\", version.ref = \"kotlin\" }\nkotlin-parcelize = { id = \"org.jetbrains.kotlin.plugin.parcelize\", version.ref = \"kotlin\" }\nkotlin-kover = { id = \"org.jetbrains.kotlinx.kover\", version.ref = \"kover\" }\nkapt = { id = \"org.jetbrains.kotlin.kapt\", version.ref = \"kotlin\" }\nksp = { id = \"com.google.devtools.ksp\", version.ref = \"ksp\" }\nhilt = { id = \"com.google.dagger.hilt.android\", version.ref = \"hilt\" }\nbutterfly = { id = \"io.github.ssseasonnn.butterfly\", version.ref = \"butterfly\" }"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Mon Feb 07 17:16:51 CST 2022\ndistributionBase=GRADLE_USER_HOME\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.0-bin.zip\ndistributionPath=wrapper/dists\nzipStorePath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\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# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app\"s APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n# Enables namespacing of each library's R class so that its R class includes only the\n# resources declared in the library itself and none from the library's dependencies,\n# thereby reducing the size of the R class for that library\nandroid.nonTransitiveRClass=true"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or 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#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS 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##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif [ \"$cygwin\" = \"true\" -o \"$msys\" = \"true\" ] ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\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=`expr $i + 1`\n    done\n    case $i in\n        0) set -- ;;\n        1) set -- \"$args0\" ;;\n        2) set -- \"$args0\" \"$args1\" ;;\n        3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=`save \"$@\"`\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\n@rem you may not use this file except in compliance with the License.\n@rem You may obtain a copy of the License at\n@rem\n@rem      https://www.apache.org/licenses/LICENSE-2.0\n@rem\n@rem Unless required by applicable law or agreed to in writing, software\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n@rem See the License for the specific language governing permissions and\n@rem limitations under the License.\n@rem\n\n@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\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\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=\"-Xmx64m\" \"-Xms64m\"\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 execute\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 execute\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:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\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 %*\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": "jitpack.yml",
    "content": "jdk:\n  - openjdk17"
  },
  {
    "path": "plugin/.gitignore",
    "content": "/build"
  },
  {
    "path": "plugin/build.gradle.kts",
    "content": "plugins {\n    id(\"kotlin\")\n    id(\"groovy\")\n    id(\"java-gradle-plugin\")\n    id(\"maven-publish\")\n    id(\"com.gradle.plugin-publish\") version (\"0.18.0\")\n}\n\ndependencies {\n    implementation(gradleApi())\n    implementation(localGroovy())\n    implementation(libs.android.gradle)\n    implementation(libs.android.gradle.api)\n}\n\ngroup = \"io.github.ssseasonnn\"\nversion = \"1.0.1\"\n\ngradlePlugin {\n    plugins {\n        create(\"butterflyPlugin\") {\n            id = \"io.github.ssseasonnn.butterfly\"\n            displayName = \"Butterfly plugin\"\n            description = \"Butterfly plugin\"\n            implementationClass = \"zlc.season.butterfly.plugin.ButterflyPlugin\"\n        }\n    }\n}\n\npluginBundle {\n    website = \"https://github.com/ssseasonnn/Butterfly\"\n    vcsUrl = \"https://github.com/ssseasonnn/Butterfly.git\"\n    tags = listOf(\"Butterfly\", \"Plugin\")\n}\n\ntasks.withType<Copy>().all {\n    duplicatesStrategy = DuplicatesStrategy.EXCLUDE\n}\n\npublishing {\n    publications {\n        create<MavenPublication>(\"gradle\") {\n            groupId = \"io.github.ssseasonnn\"\n            artifactId = \"io.github.ssseasonnn.butterfly\"\n            version = \"1.0.1\"\n\n            from(components.getByName(\"java\"))\n        }\n    }\n    repositories {\n        mavenLocal()\n    }\n}"
  },
  {
    "path": "plugin/src/main/java/zlc/season/butterfly/plugin/ButterflyPlugin.kt",
    "content": "package zlc.season.butterfly.plugin\n\nimport com.android.build.api.instrumentation.FramesComputationMode\nimport com.android.build.api.instrumentation.InstrumentationScope\nimport com.android.build.api.variant.AndroidComponentsExtension\nimport com.android.build.gradle.internal.tasks.factory.dependsOn\nimport com.android.build.gradle.internal.utils.setDisallowChanges\nimport org.gradle.api.DefaultTask\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.tasks.TaskAction\nimport java.util.Locale\n\nclass ButterflyPlugin : Plugin<Project> {\n    override fun apply(project: Project) {\n        project.pluginManager.withPlugin(\"com.android.application\") {\n            val androidComponentsExtension = project.extensions.getByType(AndroidComponentsExtension::class.java)\n            androidComponentsExtension.onVariants { variant ->\n\n                val task = project.tasks.register(\"clean${variant.name.cap()}ButterflyModule\", CleanModuleMapTask::class.java) {}\n                val cleanTask = project.tasks.named(\"clean\")\n                cleanTask.dependsOn(task)\n\n                variant.instrumentation.transformClassesWith(ModuleClassVisitorFactory::class.java, InstrumentationScope.ALL) {\n                    it.invalidate.setDisallowChanges(System.currentTimeMillis())\n                }\n                variant.instrumentation.setAsmFramesComputationMode(FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS)\n            }\n        }\n    }\n\n    private fun String.cap(): String {\n        return replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }\n    }\n}\n\nabstract class CleanModuleMapTask : DefaultTask() {\n    @TaskAction\n    fun action() {\n        ModuleHolder.clearModule()\n    }\n}"
  },
  {
    "path": "plugin/src/main/java/zlc/season/butterfly/plugin/ModuleClassVisitorFactory.kt",
    "content": "package zlc.season.butterfly.plugin\n\nimport com.android.build.api.instrumentation.AsmClassVisitorFactory\nimport com.android.build.api.instrumentation.ClassContext\nimport com.android.build.api.instrumentation.ClassData\nimport com.android.build.api.instrumentation.InstrumentationParameters\nimport org.gradle.api.provider.Property\nimport org.gradle.api.tasks.Input\nimport org.gradle.api.tasks.Optional\nimport org.objectweb.asm.ClassVisitor\nimport org.objectweb.asm.MethodVisitor\nimport org.objectweb.asm.Opcodes.*\nimport java.util.concurrent.ConcurrentHashMap\n\n\n/**\n * Save all module name\n */\nobject ModuleHolder {\n    private val modulesMap = ConcurrentHashMap<String, String>()\n\n    fun printCurrentModules() {\n        \"Current module info ${modulesMap.values}\".log()\n    }\n\n    fun addModule(moduleName: String) {\n        modulesMap[moduleName] = moduleName\n        \"Found module $moduleName\".log()\n    }\n\n    fun forEach(block: (String) -> Unit) {\n        modulesMap.values.forEach {\n            \"Auto register module $it\".log()\n            block(it)\n        }\n    }\n\n    fun clearModule() {\n        \"Clear module info...\".log()\n        modulesMap.clear()\n    }\n}\n\nabstract class ModuleClassVisitorFactory : AsmClassVisitorFactory<ModuleClassVisitorFactory.ModuleInstrumentation> {\n    interface ModuleInstrumentation : InstrumentationParameters {\n        @get:Input\n        @get:Optional\n        val invalidate: Property<Long>\n    }\n\n    override fun createClassVisitor(classContext: ClassContext, nextClassVisitor: ClassVisitor): ClassVisitor {\n        return ModuleClassVisitor(nextClassVisitor)\n    }\n\n    override fun isInstrumentable(classData: ClassData): Boolean {\n        if (classData.interfaces.contains(\"zlc.season.butterfly.module.Module\")) {\n            ModuleHolder.addModule(classData.className)\n        }\n\n        return if (classData.superClasses.contains(\"android.app.Application\")) {\n            \"Found application: ${classData.className}\".log()\n            true\n        } else {\n            false\n        }\n    }\n}\n\nclass ModuleClassVisitor(nextClassVisitor: ClassVisitor) : ClassVisitor(ASM7, nextClassVisitor) {\n    override fun visitMethod(\n        access: Int,\n        name: String,\n        descriptor: String?,\n        signature: String?,\n        exceptions: Array<out String>?\n    ): MethodVisitor? {\n        var mv = super.visitMethod(access, name, descriptor, signature, exceptions)\n        if (name == \"onCreate\") {\n            mv = ModuleMethodVisitor(mv)\n        }\n        return mv\n    }\n}\n\nclass ModuleMethodVisitor(methodVisitor: MethodVisitor) : MethodVisitor(ASM7, methodVisitor) {\n    override fun visitCode() {\n        super.visitCode()\n\n        ModuleHolder.forEach {\n            mv.visitFieldInsn(GETSTATIC, \"zlc/season/butterfly/ButterflyCore\", \"INSTANCE\", \"Lzlc/season/butterfly/ButterflyCore;\")\n            mv.visitLdcInsn(it)\n            mv.visitMethodInsn(INVOKEVIRTUAL, \"zlc/season/butterfly/ButterflyCore\", \"addModuleName\", \"(Ljava/lang/String;)V\", false)\n        }\n    }\n}"
  },
  {
    "path": "plugin/src/main/java/zlc/season/butterfly/plugin/Utils.kt",
    "content": "package zlc.season.butterfly.plugin\n\nfun String.log() {\n    println(\"[Butterfly Plugin] $this\")\n}"
  },
  {
    "path": "plugin/src/main/resources/META-INF/gradle-plugins/io.github.ssseasonnn.butterfly.properties",
    "content": "implementation-class=zlc.season.butterfly.plugin.ButterflyPlugin"
  },
  {
    "path": "samples/app/.gitignore",
    "content": "/build"
  },
  {
    "path": "samples/app/build.gradle.kts",
    "content": "@file:Suppress(\"DSL_SCOPE_VIOLATION\", \"UnstableApiUsage\")\n\nimport zlc.season.buildlogic.base.androidApplication\nimport zlc.season.buildlogic.base.enableCompose\n\nplugins {\n    alias(libs.plugins.application)\n    alias(libs.plugins.kotlin)\n    alias(libs.plugins.ksp)\n    alias(libs.plugins.butterfly)\n}\n\n\nandroidApplication {\n    namespace = \"zlc.season.butterflydemo\"\n\n    viewBinding { enable = true }\n\n    enableCompose()\n}\n\n\nksp {\n    arg(\"butterfly.log.enable\", \"true\")\n}\n\ndependencies {\n    implementation(project(\":samples:modules:normal:home\"))\n    implementation(project(\":samples:modules:normal:dashboard\"))\n    implementation(project(\":samples:modules:normal:notifications\"))\n\n    implementation(project(\":samples:modules:compose:compose_home\"))\n    implementation(project(\":samples:modules:compose:compose_dashboard\"))\n    implementation(project(\":samples:modules:compose:compose_notifications\"))\n\n    implementation(project(\":samples:modules:base\"))\n    implementation(project(\":samples:modules:feature1\"))\n    implementation(project(\":samples:modules:feature2\"))\n\n    ksp(project(\":compiler\"))\n    implementation(project(\":butterfly\"))\n    implementation(project(\":butterfly-compose\"))\n\n    implementation(libs.bundles.android)\n    implementation(libs.bundles.compose)\n    implementation(libs.bundles.kotlin)\n    implementation(libs.bundles.season)\n\n    debugImplementation(libs.leakcanary)\n\n}"
  },
  {
    "path": "samples/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.kts.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n\n\n-keep public class zlc.season.butterfly.module.**\n-keep public class zlc.season.butterfly.annotation.**\n-keep public class zlc.season.butterfly.ButterflyCore {*;}\n-keep public class * extends zlc.season.butterfly.action.Action\n\n-keep @zlc.season.butterfly.annotation.Destination class * {*;}\n-keep @zlc.season.butterfly.annotation.Evade class * {*;}\n-keep @zlc.season.butterfly.annotation.EvadeImpl class * {*;}\n"
  },
  {
    "path": "samples/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.POST_NOTIFICATIONS\" />\n\n    <application\n        android:name=\".DemoApplication\"\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:networkSecurityConfig=\"@xml/network_security_config\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/Theme.ButterflyDemo\">\n\n        <activity\n            android:name=\".MainActivity\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\".ComposeBottomNavigationActivity\"\n            android:exported=\"false\" />\n        <activity\n            android:name=\".ComposeDemoActivity\"\n            android:exported=\"false\" />\n        <activity\n            android:name=\".FragmentBottomNavigationActivity\"\n            android:exported=\"false\" />\n        <activity\n            android:name=\".EvadeTestActivity\"\n            android:exported=\"false\" />\n        <activity\n            android:name=\".DestinationTestActivity\"\n            android:exported=\"false\" />\n        <activity\n            android:name=\".FragmentDemoActivity\"\n            android:exported=\"false\" />\n    </application>\n\n</manifest>"
  },
  {
    "path": "samples/app/src/main/java/zlc/season/butterflydemo/ComposeBottomNavigationActivity.kt",
    "content": "package zlc.season.butterflydemo\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport zlc.season.base.Destinations\nimport zlc.season.butterfly.Butterfly\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.butterflydemo.databinding.ActivityComposeBottomNavigationBinding\n\n@Destination(Destinations.COMPOSE_BOTTOM_NAVIGATION)\nclass ComposeBottomNavigationActivity : AppCompatActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        val binding = ActivityComposeBottomNavigationBinding.inflate(layoutInflater)\n        setContentView(binding.root)\n\n        binding.navView.setOnItemSelectedListener {\n            when (it.itemId) {\n                R.id.navigation_home -> {\n                    Butterfly.of(this)\n                        .container(R.id.container)\n                        .group(\"\")\n                        .navigate(Destinations.COMPOSE_HOME)\n                }\n\n                R.id.navigation_dashboard -> {\n                    Butterfly.of(this)\n                        .container(R.id.container)\n                        .group(\"\")\n                        .navigate(Destinations.COMPOSE_DASHBOARD)\n                }\n\n                R.id.navigation_notifications -> {\n                    Butterfly.of(this)\n                        .container(R.id.container)\n                        .group(\"\")\n                        .navigate(Destinations.COMPOSE_NOTIFICATION)\n                }\n            }\n            true\n        }\n        binding.navView.selectedItemId = R.id.navigation_home\n    }\n}"
  },
  {
    "path": "samples/app/src/main/java/zlc/season/butterflydemo/ComposeDemoActivity.kt",
    "content": "package zlc.season.butterflydemo\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport zlc.season.base.Destinations\nimport zlc.season.butterfly.Butterfly\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.butterflydemo.databinding.ActivityComposeDemoBinding\n\n\n@Destination(Destinations.COMPOSE_DEMO)\nclass ComposeDemoActivity : AppCompatActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        val binding = ActivityComposeDemoBinding.inflate(layoutInflater)\n        setContentView(binding.root)\n\n        binding.btnStartA.setOnClickListener {\n            Butterfly.of(this).asRoot().navigate(Destinations.COMPOSE_A)\n        }\n\n        binding.btnStartB.setOnClickListener {\n            Butterfly.of(this).navigate(Destinations.COMPOSE_B)\n        }\n        binding.btnStartC.setOnClickListener {\n            Butterfly.of(this).navigate(Destinations.COMPOSE_C)\n        }\n    }\n\n    override fun onBackPressed() {\n//        super.onBackPressed()\n        Butterfly.of(this).popBack(\"result\" to \"Result from Activity\")\n    }\n}\n\n"
  },
  {
    "path": "samples/app/src/main/java/zlc/season/butterflydemo/DemoApplication.kt",
    "content": "package zlc.season.butterflydemo\n\nimport android.app.Application\n\n//import zlc.season.compose.TestModule\n\nclass DemoApplication : Application() {\n    override fun onCreate() {\n        super.onCreate()\n//        ButterflyCore.addModule(TestModule())\n    }\n}"
  },
  {
    "path": "samples/app/src/main/java/zlc/season/butterflydemo/DestinationTestActivity.kt",
    "content": "package zlc.season.butterflydemo\n\nimport android.annotation.SuppressLint\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport zlc.season.base.Destinations\nimport zlc.season.bracer.params\nimport zlc.season.butterfly.Butterfly\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.butterflydemo.databinding.ActivityDestinationTestBinding\n\n@Destination(Destinations.DESTINATION_TEST)\nclass DestinationTestActivity : AppCompatActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        val binding = ActivityDestinationTestBinding.inflate(layoutInflater)\n        setContentView(binding.root)\n\n        binding.startActivity.setOnClickListener {\n            Butterfly.of(this)\n                .params(\n                    \"intValue\" to 1,\n                    \"booleanValue\" to true,\n                    \"stringValue\" to \"test value\"\n                )\n                .navigate(Destinations.TEST + \"?a=1&b=2\")\n        }\n\n        binding.startActivityForResult.setOnClickListener {\n            Butterfly.of(this)\n                .params(\n                    \"intValue\" to 1,\n                    \"booleanValue\" to true,\n                    \"stringValue\" to \"test value\"\n                )\n                .navigate(Destinations.TEST_RESULT + \"?a=1&b=2\") {\n                    if (it.isSuccess) {\n                        val bundle = it.getOrDefault(Bundle.EMPTY)\n                        val result by bundle.params<String>()\n                        binding.tvResult.text = result\n                    }\n                }\n        }\n\n        binding.startAction.setOnClickListener {\n            Butterfly.of(this).navigate(Destinations.ACTION + \"?a=1&b=2\")\n        }\n\n        binding.startFragment.setOnClickListener {\n            Butterfly.of(this).navigate(Destinations.FRAGMENT) {\n                if (it.isSuccess) {\n                    val bundle = it.getOrDefault(Bundle.EMPTY)\n                    val abc by bundle.params<String>()\n                    binding.tvResult.text = abc\n                }\n            }\n        }\n\n        binding.startDialogFragment.setOnClickListener {\n            Butterfly.of(this).navigate(Destinations.DIALOG_FRAGMbENT)\n        }\n\n        binding.startBottomSheetDialogFragment.setOnClickListener {\n            Butterfly.of(this).navigate(Destinations.BOTTOM_SHEET_DIALOG_FRAGMENT)\n        }\n    }\n\n    @SuppressLint(\"MissingSuperCall\")\n    override fun onBackPressed() {\n//        super.onBackPressed()\n        Butterfly.of(this).popBack()\n    }\n}"
  },
  {
    "path": "samples/app/src/main/java/zlc/season/butterflydemo/EvadeTestActivity.kt",
    "content": "package zlc.season.butterflydemo\n\nimport android.os.Bundle\nimport androidx.activity.compose.setContent\nimport androidx.appcompat.app.AppCompatActivity\nimport zlc.season.base.Destinations\nimport zlc.season.butterfly.Butterfly\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.butterflydemo.databinding.ActivityEvadeTestBinding\n\n@Destination(Destinations.EVADE_TEST)\nclass EvadeTestActivity : AppCompatActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        val binding = ActivityEvadeTestBinding.inflate(layoutInflater)\n        setContentView(binding.root)\n\n        val home = Butterfly.evade<Home>()\n\n        binding.showFragment.setOnClickListener {\n            home.showHome(supportFragmentManager, R.id.container)\n        }\n\n        binding.testCompose.setOnClickListener {\n            setContent {\n                home.testCompose().composable?.invoke()\n            }\n        }\n    }\n\n    override fun onBackPressed() {\n        val home = Butterfly.evade<Home>()\n        if (home.isHomeShowing(supportFragmentManager)) {\n            home.hideHome(supportFragmentManager)\n            return\n        }\n        super.onBackPressed()\n    }\n}"
  },
  {
    "path": "samples/app/src/main/java/zlc/season/butterflydemo/FragmentBottomNavigationActivity.kt",
    "content": "package zlc.season.butterflydemo\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport zlc.season.base.Destinations\nimport zlc.season.butterfly.Butterfly\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.butterflydemo.databinding.ActivityFragmentBottomNavigationBinding\n\n@Destination(Destinations.FRAGMENT_BOTTOM_NAVIGATION)\nclass FragmentBottomNavigationActivity : AppCompatActivity() {\n\n    private lateinit var binding: ActivityFragmentBottomNavigationBinding\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        binding = ActivityFragmentBottomNavigationBinding.inflate(layoutInflater)\n        setContentView(binding.root)\n\n        val groupId = \"test_group\"\n        binding.navView.setOnItemSelectedListener {\n            when (it.itemId) {\n                R.id.navigation_home -> {\n                    Butterfly.of(this)\n                        .container(R.id.container)\n                        .group(groupId)\n                        .navigate(Destinations.HOME)\n                }\n\n                R.id.navigation_dashboard -> {\n                    Butterfly.of(this)\n                        .container(R.id.container)\n                        .group(groupId)\n                        .navigate(Destinations.DASHBOARD)\n                }\n\n                R.id.navigation_notifications -> {\n                    Butterfly.of(this)\n                        .container(R.id.container)\n                        .group(groupId)\n                        .navigate(Destinations.NOTIFICATION)\n                }\n            }\n            true\n        }\n        binding.navView.selectedItemId = R.id.navigation_home\n\n    }\n}"
  },
  {
    "path": "samples/app/src/main/java/zlc/season/butterflydemo/FragmentDemoActivity.kt",
    "content": "package zlc.season.butterflydemo\n\nimport android.annotation.SuppressLint\nimport android.os.Bundle\nimport zlc.season.base.BaseActivity\nimport zlc.season.base.Destinations\nimport zlc.season.bracer.params\nimport zlc.season.butterfly.Butterfly\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.butterflydemo.databinding.ActivityFragmentDemoBinding\n\n@Destination(Destinations.FRAGMENT_DEMO)\nclass FragmentDemoActivity : BaseActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        val binding = ActivityFragmentDemoBinding.inflate(layoutInflater)\n        setContentView(binding.root)\n\n        binding.btnStartA.setOnClickListener {\n            Butterfly.of(this)\n                .asRoot()\n                .navigate(Destinations.FRAGMENT_A) {\n                    if (it.isSuccess) {\n                        val bundle = it.getOrDefault(Bundle.EMPTY)\n                        val result by bundle.params<String>()\n                        binding.tvResult.text = result\n                    }\n                }\n        }\n\n        binding.btnStartB.setOnClickListener {\n            Butterfly.of(this)\n                .navigate(Destinations.FRAGMENT_B) {\n                    if (it.isSuccess) {\n                        val bundle = it.getOrDefault(Bundle.EMPTY)\n                        val result by bundle.params<String>()\n                        binding.tvResult.text = result\n                    }\n                }\n        }\n        binding.btnStartC.setOnClickListener {\n            Butterfly.of(this)\n                .navigate(Destinations.FRAGMENT_C) {\n                    if (it.isSuccess) {\n                        val bundle = it.getOrDefault(Bundle.EMPTY)\n                        val result by bundle.params<String>()\n                        binding.tvResult.text = result\n                    }\n                }\n        }\n    }\n\n    @SuppressLint(\"MissingSuperCall\")\n    override fun onBackPressed() {\n        Butterfly.of(this).popBack(\"result\" to \"Result from Activity\")\n    }\n}"
  },
  {
    "path": "samples/app/src/main/java/zlc/season/butterflydemo/Home.kt",
    "content": "package zlc.season.butterflydemo\n\nimport androidx.fragment.app.FragmentManager\nimport zlc.season.butterfly.annotation.Evade\nimport zlc.season.butterfly.compose.ComposeDestination\n\n@Evade\ninterface Home {\n    fun isHomeShowing(fragmentManager: FragmentManager): Boolean\n    fun showHome(fragmentManager: FragmentManager, container: Int)\n    fun hideHome(fragmentManager: FragmentManager)\n\n    fun testCompose(): ComposeDestination\n}"
  },
  {
    "path": "samples/app/src/main/java/zlc/season/butterflydemo/MainActivity.kt",
    "content": "package zlc.season.butterflydemo\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport zlc.season.base.Destinations\nimport zlc.season.butterfly.Butterfly\nimport zlc.season.butterflydemo.databinding.ActivityMainBinding\n\nclass MainActivity : AppCompatActivity() {\n    val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContentView(binding.root)\n\n        binding.startDestinationTest.setOnClickListener {\n            Butterfly.of(this).navigate(Destinations.DESTINATION_TEST)\n        }\n        binding.btnFragmentTest.setOnClickListener {\n            Butterfly.of(this).navigate(Destinations.FRAGMENT_DEMO)\n        }\n        binding.btnBottomNavigationTest.setOnClickListener {\n            Butterfly.of(this).navigate(Destinations.FRAGMENT_BOTTOM_NAVIGATION)\n        }\n        binding.btnComposeTest.setOnClickListener {\n            Butterfly.of(this).navigate(Destinations.COMPOSE_DEMO)\n        }\n        binding.btnComposeBottomNavigationTest.setOnClickListener {\n            Butterfly.of(this).navigate(Destinations.COMPOSE_BOTTOM_NAVIGATION)\n        }\n        binding.startEvadeTest.setOnClickListener {\n            Butterfly.of(this).navigate(Destinations.EVADE_TEST)\n        }\n    }\n}"
  },
  {
    "path": "samples/app/src/main/res/drawable/ic_dashboard_black_24dp.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24.0\"\n    android:viewportHeight=\"24.0\">\n    <path\n        android:fillColor=\"#FF000000\"\n        android:pathData=\"M3,13h8L11,3L3,3v10zM3,21h8v-6L3,15v6zM13,21h8L21,11h-8v10zM13,3v6h8L21,3h-8z\" />\n</vector>\n"
  },
  {
    "path": "samples/app/src/main/res/drawable/ic_home_black_24dp.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24.0\"\n    android:viewportHeight=\"24.0\">\n    <path\n        android:fillColor=\"#FF000000\"\n        android:pathData=\"M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z\" />\n</vector>\n"
  },
  {
    "path": "samples/app/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": "samples/app/src/main/res/drawable/ic_notifications_black_24dp.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24.0\"\n    android:viewportHeight=\"24.0\">\n    <path\n        android:fillColor=\"#FF000000\"\n        android:pathData=\"M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z\" />\n</vector>\n"
  },
  {
    "path": "samples/app/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": "samples/app/src/main/res/layout/activity_compose_bottom_navigation.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.google.android.material.bottomnavigation.BottomNavigationView\n        android:id=\"@+id/nav_view\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"0dp\"\n        android:layout_marginEnd=\"0dp\"\n        android:background=\"@android:color/white\"\n        app:itemIconTint=\"@android:color/black\"\n        app:itemTextColor=\"@android:color/black\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:menu=\"@menu/bottom_nav_menu\" />\n\n    <FrameLayout\n        android:id=\"@+id/container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:layout_constraintBottom_toTopOf=\"@id/nav_view\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "samples/app/src/main/res/layout/activity_compose_demo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/root\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    android:paddingStart=\"20dp\"\n    android:paddingTop=\"20dp\"\n    android:paddingEnd=\"20dp\"\n    android:paddingBottom=\"20dp\"\n    tools:ignore=\"HardcodedText\">\n\n    <Button\n        android:id=\"@+id/btnStartA\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"10dp\"\n        android:layout_marginTop=\"10dp\"\n        android:layout_marginEnd=\"10dp\"\n        android:text=\"Start Screen A\"\n        android:textColor=\"@android:color/black\" />\n\n    <Button\n        android:id=\"@+id/btnStartB\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"10dp\"\n        android:layout_marginTop=\"10dp\"\n        android:layout_marginEnd=\"10dp\"\n        android:text=\"Start Screen B\"\n        android:textColor=\"@android:color/black\" />\n\n    <Button\n        android:id=\"@+id/btnStartC\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"10dp\"\n        android:layout_marginTop=\"10dp\"\n        android:layout_marginEnd=\"10dp\"\n        android:text=\"Start Screen C\"\n        android:textColor=\"@android:color/black\" />\n\n    <TextView\n        android:id=\"@+id/tvResult\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:layout_marginTop=\"10dp\" />\n</LinearLayout>"
  },
  {
    "path": "samples/app/src/main/res/layout/activity_destination_test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:ignore=\"HardcodedText\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\"\n        android:paddingStart=\"20dp\"\n        android:paddingTop=\"20dp\"\n        android:paddingEnd=\"20dp\"\n        android:paddingBottom=\"20dp\">\n\n        <Button\n            android:id=\"@+id/startActivity\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginEnd=\"10dp\"\n            android:text=\"start activity\"\n            android:textColor=\"@android:color/black\" />\n\n        <Button\n            android:id=\"@+id/startActivityForResult\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginEnd=\"10dp\"\n            android:text=\"start activity for result\"\n            android:textColor=\"@android:color/black\" />\n\n        <Button\n            android:id=\"@+id/startAction\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginEnd=\"10dp\"\n            android:text=\"start action\"\n            android:textColor=\"@android:color/black\"\n            tools:ignore=\"HardcodedText\" />\n\n        <Button\n            android:id=\"@+id/startFragment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginEnd=\"10dp\"\n            android:text=\"start fragment\"\n            android:textColor=\"@android:color/black\"\n            tools:ignore=\"HardcodedText\" />\n\n        <Button\n            android:id=\"@+id/startDialogFragment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginEnd=\"10dp\"\n            android:text=\"start dialog fragment\"\n            android:textColor=\"@android:color/black\"\n            tools:ignore=\"HardcodedText\" />\n\n        <Button\n            android:id=\"@+id/startBottomSheetDialogFragment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginEnd=\"10dp\"\n            android:text=\"start bottom sheet dialog fragment\"\n            android:textColor=\"@android:color/black\"\n            tools:ignore=\"HardcodedText\" />\n\n        <TextView\n            android:id=\"@+id/tv_result\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_horizontal\"\n            android:layout_marginTop=\"20dp\"\n            android:textColor=\"@android:color/black\" />\n\n    </LinearLayout>\n</FrameLayout>"
  },
  {
    "path": "samples/app/src/main/res/layout/activity_evade_test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\"\n        android:paddingStart=\"20dp\"\n        android:paddingTop=\"20dp\"\n        android:paddingEnd=\"20dp\"\n        android:paddingBottom=\"20dp\">\n\n        <Button\n            android:id=\"@+id/showFragment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginEnd=\"10dp\"\n            android:text=\"show fragment\"\n            android:textColor=\"@android:color/black\" />\n\n        <Button\n            android:id=\"@+id/testCompose\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginEnd=\"10dp\"\n            android:text=\"test Compose\"\n            android:textColor=\"@android:color/black\" />\n    </LinearLayout>\n\n    <FrameLayout\n        android:id=\"@+id/container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n</FrameLayout>"
  },
  {
    "path": "samples/app/src/main/res/layout/activity_fragment_bottom_navigation.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.google.android.material.bottomnavigation.BottomNavigationView\n        android:id=\"@+id/nav_view\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"0dp\"\n        android:layout_marginEnd=\"0dp\"\n        android:background=\"@android:color/white\"\n        app:itemIconTint=\"@android:color/black\"\n        app:itemTextColor=\"@android:color/black\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:menu=\"@menu/bottom_nav_menu\" />\n\n    <FrameLayout\n        android:id=\"@+id/container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:layout_constraintBottom_toTopOf=\"@id/nav_view\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "samples/app/src/main/res/layout/activity_fragment_demo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    android:paddingStart=\"20dp\"\n    android:paddingTop=\"20dp\"\n    android:paddingEnd=\"20dp\"\n    android:paddingBottom=\"20dp\"\n    tools:ignore=\"HardcodedText\">\n\n    <Button\n        android:id=\"@+id/btnStartA\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"10dp\"\n        android:layout_marginTop=\"10dp\"\n        android:layout_marginEnd=\"10dp\"\n        android:text=\"Start Fragment A\"\n        android:textColor=\"@android:color/black\" />\n\n    <Button\n        android:id=\"@+id/btnStartB\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"10dp\"\n        android:layout_marginTop=\"10dp\"\n        android:layout_marginEnd=\"10dp\"\n        android:text=\"Start Fragment B\"\n        android:textColor=\"@android:color/black\" />\n\n    <Button\n        android:id=\"@+id/btnStartC\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"10dp\"\n        android:layout_marginTop=\"10dp\"\n        android:layout_marginEnd=\"10dp\"\n        android:text=\"Start Fragment C\"\n        android:textColor=\"@android:color/black\" />\n\n    <TextView\n        android:id=\"@+id/tvResult\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:layout_marginTop=\"10dp\" />\n</LinearLayout>"
  },
  {
    "path": "samples/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\"\n        android:paddingStart=\"20dp\"\n        android:paddingTop=\"20dp\"\n        android:paddingEnd=\"20dp\"\n        android:paddingBottom=\"20dp\">\n\n        <Button\n            android:id=\"@+id/startDestinationTest\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginEnd=\"10dp\"\n            android:text=\"Destination Demo\"\n            android:textColor=\"@color/black\" />\n\n        <Button\n            android:id=\"@+id/btnFragmentTest\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginEnd=\"10dp\"\n            android:text=\"Fragment Demo\"\n            android:textColor=\"@color/black\" />\n\n        <Button\n            android:id=\"@+id/btnBottomNavigationTest\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginEnd=\"10dp\"\n            android:text=\"BottomNavigation DEMO\"\n            android:textColor=\"@color/black\" />\n\n        <Button\n            android:id=\"@+id/btnComposeTest\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginEnd=\"10dp\"\n            android:text=\"Compose DEMO\"\n            android:textColor=\"@color/black\" />\n\n        <Button\n            android:id=\"@+id/btnComposeBottomNavigationTest\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginEnd=\"10dp\"\n            android:text=\"Compose BottomNavigation DEMO\"\n            android:textColor=\"@color/black\" />\n\n        <Button\n            android:id=\"@+id/startEvadeTest\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginEnd=\"10dp\"\n            android:text=\"evade test\"\n            android:textColor=\"@color/black\" />\n    </LinearLayout>\n</FrameLayout>"
  },
  {
    "path": "samples/app/src/main/res/menu/bottom_nav_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item\n        android:id=\"@+id/navigation_home\"\n        android:icon=\"@drawable/ic_home_black_24dp\"\n        android:title=\"@string/title_home\" />\n\n    <item\n        android:id=\"@+id/navigation_dashboard\"\n        android:icon=\"@drawable/ic_dashboard_black_24dp\"\n        android:title=\"@string/title_dashboard\" />\n\n    <item\n        android:id=\"@+id/navigation_notifications\"\n        android:icon=\"@drawable/ic_notifications_black_24dp\"\n        android:title=\"@string/title_notifications\" />\n\n</menu>"
  },
  {
    "path": "samples/app/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": "samples/app/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": "samples/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"black\">#FF000000</color>\n    <color name=\"white\">#FFFFFFFF</color>\n    <color name=\"background\">#FCFCFC</color>\n</resources>\n\n"
  },
  {
    "path": "samples/app/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n</resources>"
  },
  {
    "path": "samples/app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">ButterflyDemo</string>\n\n    <string name=\"title_home\">Home</string>\n    <string name=\"title_dashboard\">Dashboard</string>\n    <string name=\"title_notifications\">Notifications</string>\n</resources>"
  },
  {
    "path": "samples/app/src/main/res/values/themes.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- Base application theme. -->\n    <style name=\"Theme.ButterflyDemo\" parent=\"Theme.MaterialComponents.Light.NoActionBar\">\n        <!-- Primary brand color. -->\n        <item name=\"colorPrimary\">@color/white</item>\n        <item name=\"colorPrimaryVariant\">@color/background</item>\n        <item name=\"colorOnPrimary\">@color/background</item>\n        <!-- Secondary brand color. -->\n        <item name=\"colorSecondary\">@color/black</item>\n        <item name=\"colorSecondaryVariant\">@color/black</item>\n        <item name=\"colorOnSecondary\">@color/black</item>\n        <!-- Status bar color. -->\n        <item name=\"android:statusBarColor\" tools:targetApi=\"l\">?attr/colorPrimaryVariant</item>\n        <item name=\"android:windowLightStatusBar\">true</item>\n    </style>\n</resources>"
  },
  {
    "path": "samples/app/src/main/res/xml/network_security_config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<network-security-config>\n    <base-config cleartextTrafficPermitted=\"true\">\n        <trust-anchors>\n            <certificates src=\"system\" />\n            <certificates src=\"user\" />\n        </trust-anchors>\n    </base-config>\n</network-security-config>"
  },
  {
    "path": "samples/modules/base/.gitignore",
    "content": "/build"
  },
  {
    "path": "samples/modules/base/build.gradle.kts",
    "content": "import zlc.season.buildlogic.base.androidLibrary\n\n@Suppress(\"DSL_SCOPE_VIOLATION\")\nplugins {\n    alias(libs.plugins.library)\n    alias(libs.plugins.kotlin)\n    alias(libs.plugins.kotlin.serialization)\n    alias(libs.plugins.butterfly)\n    alias(libs.plugins.ksp)\n}\n\nandroidLibrary {\n    namespace = \"zlc.season.base\"\n}\n\ndependencies {\n    ksp(project(\":compiler\"))\n    implementation(project(\":butterfly\"))\n    implementation(libs.bundles.android)\n    implementation(libs.bundles.kotlin)\n}"
  },
  {
    "path": "samples/modules/base/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "samples/modules/base/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.kts.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "samples/modules/base/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest>\n\n</manifest>"
  },
  {
    "path": "samples/modules/base/src/main/java/zlc/season/base/BaseActivity.kt",
    "content": "package zlc.season.base\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\n\nopen class BaseActivity : AppCompatActivity() {\n    open val name: String = \"$this\"\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        println(\"$name onCreate: $savedInstanceState\")\n    }\n\n    override fun onRestart() {\n        super.onRestart()\n        println(\"$name onRestart\")\n    }\n\n\n    override fun onDestroy() {\n        super.onDestroy()\n        println(\"$name onDestroy\")\n    }\n}"
  },
  {
    "path": "samples/modules/base/src/main/java/zlc/season/base/BaseFragment.kt",
    "content": "package zlc.season.base\n\nimport android.content.Context\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.fragment.app.Fragment\n\nopen class BaseFragment : Fragment() {\n    open val name: String\n        get() = \"$this tag: ${this.tag}\"\n\n    override fun onAttach(context: Context) {\n        super.onAttach(context)\n        println(\"$name onAttach\")\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        println(\"$name onCreate: $savedInstanceState\")\n    }\n\n    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {\n        println(\"$name onCreateView: $savedInstanceState\")\n        return super.onCreateView(inflater, container, savedInstanceState)\n    }\n\n    override fun onStart() {\n        super.onStart()\n        println(\"$name onStart\")\n    }\n\n    override fun onResume() {\n        super.onResume()\n        println(\"$name onResume\")\n    }\n\n    override fun onPause() {\n        super.onPause()\n        println(\"$name onPause\")\n    }\n\n    override fun onStop() {\n        super.onStop()\n        println(\"$name onStop\")\n    }\n\n    override fun onDestroyView() {\n        super.onDestroyView()\n        println(\"$name onDestroyView\")\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        println(\"$name onDestroy\")\n    }\n\n    override fun onDetach() {\n        super.onDetach()\n        println(\"$name onDetach\")\n    }\n}"
  },
  {
    "path": "samples/modules/base/src/main/java/zlc/season/base/Destinations.kt",
    "content": "package zlc.season.base\n\nobject Destinations {\n    private const val HOST = \"butterfly_demo\"\n\n    const val DESTINATION_TEST = \"destination/test\"\n    const val EVADE_TEST = \"evade/test\"\n\n    const val FRAGMENT_DEMO = \"fragment/demo\"\n    const val FRAGMENT_BOTTOM_NAVIGATION = \"fragment/bottom_navigation\"\n    const val COMPOSE_DEMO = \"compose/demo\"\n    const val COMPOSE_BOTTOM_NAVIGATION = \"compose/bottom_navigation\"\n\n    const val TEST = \"${HOST}://test\"\n    const val TEST_RESULT = \"${HOST}://test_result\"\n    const val FRAGMENT = \"${HOST}://test_fragment\"\n    const val DIALOG_FRAGMbENT = \"${HOST}://test_dialog_fragment\"\n    const val BOTTOM_SHEET_DIALOG_FRAGMENT = \"${HOST}://test_bottom_sheet_dialog_fragment\"\n    const val ACTION = \"${HOST}://action\"\n\n    const val FRAGMENT_A = \"${HOST}://a_fragment\"\n    const val FRAGMENT_B = \"${HOST}://b_fragment\"\n    const val FRAGMENT_C = \"${HOST}://c_fragment\"\n\n    const val COMPOSE_A = \"${HOST}://a_screen\"\n    const val COMPOSE_B = \"${HOST}://b_screen\"\n    const val COMPOSE_C = \"${HOST}://c_screen\"\n\n    const val DASHBOARD = \"${HOST}://dashboard\"\n    const val NOTIFICATION = \"${HOST}://notification\"\n    const val HOME = \"${HOST}://home\"\n\n    const val COMPOSE_DASHBOARD = \"${HOST}://compose/dashboard\"\n    const val COMPOSE_NOTIFICATION = \"${HOST}://compose/notification\"\n    const val COMPOSE_HOME = \"${HOST}://compose/home\"\n}"
  },
  {
    "path": "samples/modules/compose/compose_dashboard/.gitignore",
    "content": "/build"
  },
  {
    "path": "samples/modules/compose/compose_dashboard/build.gradle.kts",
    "content": "import zlc.season.buildlogic.base.androidLibrary\nimport zlc.season.buildlogic.base.enableCompose\n\n@Suppress(\"DSL_SCOPE_VIOLATION\")\nplugins {\n    alias(libs.plugins.library)\n    alias(libs.plugins.kotlin)\n    alias(libs.plugins.kotlin.serialization)\n    alias(libs.plugins.ksp)\n    alias(libs.plugins.butterfly)\n}\n\nandroidLibrary {\n    namespace = \"zlc.season.compose.dashboard\"\n    enableCompose()\n}\n\ndependencies {\n    ksp(project(\":compiler\"))\n    implementation(project(\":butterfly\"))\n    implementation(project(\":butterfly-compose\"))\n\n    implementation(project(\":samples:modules:base\"))\n    implementation(libs.bundles.android)\n    implementation(libs.bundles.kotlin)\n    implementation(libs.bundles.season)\n    implementation(libs.bundles.compose)\n}"
  },
  {
    "path": "samples/modules/compose/compose_dashboard/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "samples/modules/compose/compose_dashboard/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.kts.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "samples/modules/compose/compose_dashboard/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest>\n\n</manifest>"
  },
  {
    "path": "samples/modules/compose/compose_dashboard/src/main/java/zlc/season/compose/dashboard/DashboardScreen.kt",
    "content": "package zlc.season.compose.dashboard\n\nimport android.os.Bundle\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.collectAsState\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.unit.dp\nimport androidx.core.os.bundleOf\nimport zlc.season.base.Destinations\nimport zlc.season.butterfly.annotation.Destination\n\n@Destination(Destinations.COMPOSE_DASHBOARD)\n@Composable\nfun DashboardScreen(test: Bundle = bundleOf(), viewModel: DashboardViewModel = DashboardViewModel()) {\n    val textFromViewModel = viewModel.text.collectAsState(initial = \"\")\n\n    Surface(modifier = Modifier.fillMaxSize()) {\n        Box {\n            Text(\n                modifier = Modifier.align(Alignment.Center),\n                text = \"This is dashboard screen!\"\n            )\n            Text(\n                modifier = Modifier\n                    .align(Alignment.BottomCenter)\n                    .padding(bottom = 200.dp),\n                text = textFromViewModel.value\n            )\n        }\n    }\n}"
  },
  {
    "path": "samples/modules/compose/compose_dashboard/src/main/java/zlc/season/compose/dashboard/DashboardViewModel.kt",
    "content": "package zlc.season.compose.dashboard\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.launch\n\nclass DashboardViewModel : ViewModel() {\n\n    val text = MutableStateFlow(\"\")\n\n    init {\n        viewModelScope.launch {\n            delay(1500)\n            text.value = \"This is text from dashboard viewModel\"\n        }\n    }\n}"
  },
  {
    "path": "samples/modules/compose/compose_home/.gitignore",
    "content": "/build"
  },
  {
    "path": "samples/modules/compose/compose_home/build.gradle.kts",
    "content": "import zlc.season.buildlogic.base.androidLibrary\nimport zlc.season.buildlogic.base.enableCompose\n\n@Suppress(\"DSL_SCOPE_VIOLATION\")\nplugins {\n    alias(libs.plugins.library)\n    alias(libs.plugins.kotlin)\n    alias(libs.plugins.kotlin.serialization)\n    alias(libs.plugins.ksp)\n    alias(libs.plugins.butterfly)\n}\n\nandroidLibrary {\n    namespace = \"zlc.season.compose.home\"\n    enableCompose()\n}\n\ndependencies {\n    ksp(project(\":compiler\"))\n    implementation(project(\":butterfly\"))\n    implementation(project(\":butterfly-compose\"))\n\n    implementation(project(\":samples:modules:base\"))\n    implementation(libs.bundles.android)\n    implementation(libs.bundles.kotlin)\n    implementation(libs.bundles.season)\n    implementation(libs.bundles.compose)\n}"
  },
  {
    "path": "samples/modules/compose/compose_home/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "samples/modules/compose/compose_home/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.kts.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "samples/modules/compose/compose_home/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest>\n\n    <application>\n\n    </application>\n\n</manifest>"
  },
  {
    "path": "samples/modules/compose/compose_home/src/main/java/zlc/season/compose/home/HomeScreen.kt",
    "content": "package zlc.season.compose.home\n\nimport android.os.Bundle\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport zlc.season.base.Destinations\nimport zlc.season.butterfly.annotation.Destination\n\n@Destination(Destinations.COMPOSE_HOME)\n@Composable\nfun HomeScreen(test: Bundle) {\n    Surface(modifier = Modifier.fillMaxSize()) {\n        Box {\n            Text(\n                modifier = Modifier.align(Alignment.Center),\n                text = \"This is home screen!\"\n            )\n        }\n    }\n}"
  },
  {
    "path": "samples/modules/compose/compose_notifications/.gitignore",
    "content": "/build"
  },
  {
    "path": "samples/modules/compose/compose_notifications/build.gradle.kts",
    "content": "import zlc.season.buildlogic.base.androidLibrary\nimport zlc.season.buildlogic.base.enableCompose\n\n@Suppress(\"DSL_SCOPE_VIOLATION\")\nplugins {\n    alias(libs.plugins.library)\n    alias(libs.plugins.kotlin)\n    alias(libs.plugins.kotlin.serialization)\n    alias(libs.plugins.ksp)\n    alias(libs.plugins.butterfly)\n}\n\nandroidLibrary {\n    namespace = \"zlc.season.compose.notifications\"\n    enableCompose()\n}\n\ndependencies {\n    ksp(project(\":compiler\"))\n    implementation(project(\":butterfly\"))\n    implementation(project(\":butterfly-compose\"))\n\n    implementation(project(\":samples:modules:base\"))\n    implementation(libs.bundles.android)\n    implementation(libs.bundles.kotlin)\n    implementation(libs.bundles.season)\n    implementation(libs.bundles.compose)\n}"
  },
  {
    "path": "samples/modules/compose/compose_notifications/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "samples/modules/compose/compose_notifications/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.kts.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "samples/modules/compose/compose_notifications/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest>\n\n</manifest>"
  },
  {
    "path": "samples/modules/compose/compose_notifications/src/main/java/zlc/season/compose/notifications/NotificationsScreen.kt",
    "content": "package zlc.season.compose.notifications\n\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.collectAsState\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.unit.dp\nimport zlc.season.base.Destinations\nimport zlc.season.butterfly.annotation.Destination\n\n@Destination(Destinations.COMPOSE_NOTIFICATION)\n@Composable\nfun NotificationsScreen(viewModel: NotificationsViewModel) {\n    val textFromViewModel = viewModel.text.collectAsState(initial = \"\")\n\n    Surface(modifier = Modifier.fillMaxSize()) {\n        Box {\n            Text(\n                modifier = Modifier.align(Alignment.Center),\n                text = \"This is notification screen!\"\n            )\n            Text(\n                modifier = Modifier\n                    .align(Alignment.BottomCenter)\n                    .padding(bottom = 200.dp),\n                text = textFromViewModel.value\n            )\n        }\n    }\n}"
  },
  {
    "path": "samples/modules/compose/compose_notifications/src/main/java/zlc/season/compose/notifications/NotificationsViewModel.kt",
    "content": "package zlc.season.compose.notifications\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.launch\n\nclass NotificationsViewModel : ViewModel() {\n\n    val text = MutableStateFlow(\"\")\n\n    init {\n        viewModelScope.launch {\n            delay(1500)\n            text.value = \"This is text from notification viewModel\"\n        }\n    }\n}"
  },
  {
    "path": "samples/modules/feature1/.gitignore",
    "content": "/build"
  },
  {
    "path": "samples/modules/feature1/build.gradle.kts",
    "content": "import zlc.season.buildlogic.base.androidLibrary\n\n@Suppress(\"DSL_SCOPE_VIOLATION\")\nplugins {\n    alias(libs.plugins.library)\n    alias(libs.plugins.kotlin)\n    alias(libs.plugins.kotlin.serialization)\n    alias(libs.plugins.ksp)\n    alias(libs.plugins.butterfly)\n}\n\nandroidLibrary {\n    namespace = \"zlc.season.feature1\"\n\n    viewBinding { enable = true }\n}\n\ndependencies {\n    ksp(project(\":compiler\"))\n    implementation(project(\":butterfly\"))\n\n    implementation(project(\":samples:modules:base\"))\n\n    implementation(libs.bundles.android)\n    implementation(libs.bundles.kotlin)\n    implementation(libs.bundles.season)\n}"
  },
  {
    "path": "samples/modules/feature1/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "samples/modules/feature1/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.kts.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "samples/modules/feature1/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        <activity\n            android:name=\".TestActivity\"\n            android:exported=\"false\" />\n        <activity\n            android:name=\".TestResultActivity\"\n            android:exported=\"false\" />\n    </application>\n\n</manifest>"
  },
  {
    "path": "samples/modules/feature1/src/main/java/zlc/season/feature1/AFragment.kt",
    "content": "package zlc.season.feature1\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport zlc.season.base.BaseFragment\nimport zlc.season.base.Destinations\nimport zlc.season.bracer.params\nimport zlc.season.butterfly.Butterfly\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.feature1.databinding.FragmentCommonBinding\n\n@Destination(Destinations.FRAGMENT_A)\nclass AFragment : BaseFragment() {\n    override fun onCreateView(\n        inflater: LayoutInflater, container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View? {\n        return FragmentCommonBinding.inflate(inflater, container, false).also {\n            it.setup()\n        }.root\n    }\n\n    private fun FragmentCommonBinding.setup() {\n        root.setBackgroundResource(R.color.blue)\n        tvContent.text = \"Fragment A ${hashCode()}\"\n        btnBack.setOnClickListener {\n            Butterfly.of(requireContext()).popBack(\"result\" to \"Result from FragmentA\")\n        }\n        btnDialog.setOnClickListener {\n            Butterfly.of(requireContext())\n                .navigate(Destinations.BOTTOM_SHEET_DIALOG_FRAGMENT) {\n                    if (it.isSuccess) {\n                        val bundle = it.getOrDefault(Bundle.EMPTY)\n                        val result by bundle.params<String>()\n                        tvResult.text = result\n                    }\n                }\n        }\n        btnNextA.setOnClickListener {\n            Butterfly.of(requireContext())\n                .run {\n                    if (cbClearTop.isChecked) {\n                        clearTop()\n                    } else if (cbSingleTop.isChecked) {\n                        singleTop()\n                    } else {\n                        this\n                    }\n                }\n                .navigate(Destinations.FRAGMENT_A) {\n                    if (it.isSuccess) {\n                        val bundle = it.getOrDefault(Bundle.EMPTY)\n                        val result by bundle.params<String>()\n                        tvResult.text = result\n                    }\n                }\n        }\n        btnNextB.setOnClickListener {\n            Butterfly.of(requireContext())\n                .run {\n                    if (cbClearTop.isChecked) {\n                        clearTop()\n                    } else if (cbSingleTop.isChecked) {\n                        singleTop()\n                    } else {\n                        this\n                    }\n                }\n                .navigate(Destinations.FRAGMENT_B) {\n                    if (it.isSuccess) {\n                        val bundle = it.getOrDefault(Bundle.EMPTY)\n                        val result by bundle.params<String>()\n                        tvResult.text = result\n                    }\n                }\n        }\n        btnNextC.setOnClickListener {\n            Butterfly.of(requireContext())\n                .run {\n                    if (cbClearTop.isChecked) {\n                        clearTop()\n                    } else if (cbSingleTop.isChecked) {\n                        singleTop()\n                    } else {\n                        this\n                    }\n                }\n                .navigate(Destinations.FRAGMENT_C) {\n                    if (it.isSuccess) {\n                        val bundle = it.getOrDefault(Bundle.EMPTY)\n                        val result by bundle.params<String>()\n                        tvResult.text = result\n                    }\n                }\n        }\n    }\n}"
  },
  {
    "path": "samples/modules/feature1/src/main/java/zlc/season/feature1/BFragment.kt",
    "content": "package zlc.season.feature1\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport zlc.season.base.BaseFragment\nimport zlc.season.base.Destinations\nimport zlc.season.bracer.params\nimport zlc.season.butterfly.Butterfly\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.feature1.databinding.FragmentCommonBinding\n\n@Destination(Destinations.FRAGMENT_B)\nclass BFragment : BaseFragment() {\n    override fun onCreateView(\n        inflater: LayoutInflater, container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View? {\n        return FragmentCommonBinding.inflate(inflater, container, false).also {\n            it.setup()\n        }.root\n    }\n\n    private fun FragmentCommonBinding.setup() {\n        root.setBackgroundResource(R.color.green)\n        tvContent.text = \"Fragment B ${hashCode()}\"\n        btnBack.setOnClickListener {\n            Butterfly.of(requireContext()).popBack(\"result\" to \"Result from FragmentB\")\n        }\n        btnDialog.setOnClickListener {\n            Butterfly.of(requireContext())\n                .navigate(Destinations.BOTTOM_SHEET_DIALOG_FRAGMENT) {\n                    if (it.isSuccess) {\n                        val bundle = it.getOrDefault(Bundle.EMPTY)\n                        val result by bundle.params<String>()\n                        tvResult.text = result\n                    }\n                }\n        }\n        btnNextA.setOnClickListener {\n            Butterfly.of(requireContext())\n                .run {\n                    if (cbClearTop.isChecked) {\n                        clearTop()\n                    } else if (cbSingleTop.isChecked) {\n                        singleTop()\n                    } else {\n                        this\n                    }\n                }\n                .navigate(Destinations.FRAGMENT_A) {\n                    if (it.isSuccess) {\n                        val bundle = it.getOrDefault(Bundle.EMPTY)\n                        val result by bundle.params<String>()\n                        tvResult.text = result\n                    }\n                }\n        }\n        btnNextB.setOnClickListener {\n            Butterfly.of(requireContext())\n                .run {\n                    if (cbClearTop.isChecked) {\n                        clearTop()\n                    } else if (cbSingleTop.isChecked) {\n                        singleTop()\n                    } else {\n                        this\n                    }\n                }\n                .navigate(Destinations.FRAGMENT_B) {\n                    if (it.isSuccess) {\n                        val bundle = it.getOrDefault(Bundle.EMPTY)\n                        val result by bundle.params<String>()\n                        tvResult.text = result\n                    }\n                }\n        }\n        btnNextC.setOnClickListener {\n            Butterfly.of(requireContext())\n                .run {\n                    if (cbClearTop.isChecked) {\n                        clearTop()\n                    } else if (cbSingleTop.isChecked) {\n                        singleTop()\n                    } else {\n                        this\n                    }\n                }\n                .navigate(Destinations.FRAGMENT_C) {\n                    if (it.isSuccess) {\n                        val bundle = it.getOrDefault(Bundle.EMPTY)\n                        val result by bundle.params<String>()\n                        tvResult.text = result\n                    }\n                }\n        }\n    }\n}"
  },
  {
    "path": "samples/modules/feature1/src/main/java/zlc/season/feature1/CFragment.kt",
    "content": "package zlc.season.feature1\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport zlc.season.base.BaseFragment\nimport zlc.season.base.Destinations\nimport zlc.season.bracer.params\nimport zlc.season.butterfly.Butterfly\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.feature1.databinding.FragmentCommonBinding\n\n@Destination(Destinations.FRAGMENT_C)\nclass CFragment : BaseFragment() {\n    override fun onCreateView(\n        inflater: LayoutInflater, container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View? {\n        return FragmentCommonBinding.inflate(inflater, container, false).also {\n            it.setup()\n        }.root\n    }\n\n    private fun FragmentCommonBinding.setup() {\n        root.setBackgroundResource(R.color.yellow)\n        tvContent.text = \"Fragment C ${hashCode()}\"\n\n        btnBack.setOnClickListener {\n            Butterfly.of(requireContext()).popBack(\"result\" to \"Result from FragmentC\")\n        }\n\n        btnDialog.setOnClickListener {\n            Butterfly.of(requireContext())\n                .navigate(Destinations.BOTTOM_SHEET_DIALOG_FRAGMENT) {\n                    if (it.isSuccess) {\n                        val bundle = it.getOrDefault(Bundle.EMPTY)\n                        val result by bundle.params<String>()\n                        tvResult.text = result\n                    }\n                }\n        }\n        btnNextA.setOnClickListener {\n            Butterfly.of(requireContext())\n                .run {\n                    if (cbClearTop.isChecked) {\n                        clearTop()\n                    } else if (cbSingleTop.isChecked) {\n                        singleTop()\n                    } else {\n                        this\n                    }\n                }\n                .navigate(Destinations.FRAGMENT_A) {\n                    if (it.isSuccess) {\n                        val bundle = it.getOrDefault(Bundle.EMPTY)\n                        val result by bundle.params<String>()\n                        tvResult.text = result\n                    }\n                }\n        }\n        btnNextB.setOnClickListener {\n            Butterfly.of(requireContext())\n                .run {\n                    if (cbClearTop.isChecked) {\n                        clearTop()\n                    } else if (cbSingleTop.isChecked) {\n                        singleTop()\n                    } else {\n                        this\n                    }\n                }\n                .navigate(Destinations.FRAGMENT_B) {\n                    if (it.isSuccess) {\n                        val bundle = it.getOrDefault(Bundle.EMPTY)\n                        val result by bundle.params<String>()\n                        tvResult.text = result\n                    }\n                }\n        }\n        btnNextC.setOnClickListener {\n            Butterfly.of(requireContext())\n                .run {\n                    if (cbClearTop.isChecked) {\n                        clearTop()\n                    } else if (cbSingleTop.isChecked) {\n                        singleTop()\n                    } else {\n                        this\n                    }\n                }\n                .navigate(Destinations.FRAGMENT_C) {\n                    if (it.isSuccess) {\n                        val bundle = it.getOrDefault(Bundle.EMPTY)\n                        val result by bundle.params<String>()\n                        tvResult.text = result\n                    }\n                }\n        }\n    }\n}\n"
  },
  {
    "path": "samples/modules/feature1/src/main/java/zlc/season/feature1/TestAction.kt",
    "content": "package zlc.season.feature1\n\nimport android.content.Context\nimport android.os.Bundle\nimport android.widget.Toast\nimport zlc.season.base.Destinations\nimport zlc.season.butterfly.action.Action\nimport zlc.season.butterfly.annotation.Destination\n\n@Destination(Destinations.ACTION)\nclass TestAction : Action {\n    override fun doAction(context: Context, route: String, data: Bundle) {\n        Toast.makeText(context, \"This is an Action\", Toast.LENGTH_SHORT).show()\n    }\n}"
  },
  {
    "path": "samples/modules/feature1/src/main/java/zlc/season/feature1/TestActivity.kt",
    "content": "package zlc.season.feature1\n\nimport android.annotation.SuppressLint\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport zlc.season.base.Destinations\nimport zlc.season.bracer.params\nimport zlc.season.butterfly.Butterfly\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.feature1.databinding.ActivityTestBinding\n\n@Destination(Destinations.TEST)\nclass TestActivity : AppCompatActivity() {\n    val intValue by params<Int>()\n    val booleanValue by params<Boolean>()\n    val stringValue by params<String>()\n\n    val a by params<String>()\n    val b by params<String>()\n\n    @SuppressLint(\"SetTextI18n\")\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        val binding = ActivityTestBinding.inflate(layoutInflater)\n        setContentView(binding.root)\n\n        binding.tvParam.text = \"\"\"\n            intValue = $intValue \n            booleanValue = $booleanValue\n            stringValue = $stringValue\n            a = $a\n            b = $b\n        \"\"\".trimIndent()\n\n        binding.btnFinish.setOnClickListener {\n            Butterfly.of(this).popBack(\"result\" to \"asb\")\n        }\n    }\n}"
  },
  {
    "path": "samples/modules/feature1/src/main/java/zlc/season/feature1/TestBottomSheetDialogFragment.kt",
    "content": "package zlc.season.feature1\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport com.google.android.material.bottomsheet.BottomSheetDialogFragment\nimport zlc.season.base.Destinations\nimport zlc.season.base.Destinations.BOTTOM_SHEET_DIALOG_FRAGMENT\nimport zlc.season.butterfly.Butterfly\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.feature1.databinding.DialogTestBinding\n\n@Destination(BOTTOM_SHEET_DIALOG_FRAGMENT)\nclass TestBottomSheetDialogFragment : BottomSheetDialogFragment() {\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View {\n        return DialogTestBinding.inflate(inflater, container, false).also {\n            it.btnNext.setOnClickListener {\n                Butterfly.of(requireContext()).navigate(Destinations.FRAGMENT)\n                Butterfly.of(requireContext()).popBack()\n            }\n            it.btnBack.setOnClickListener {\n                Butterfly.of(requireContext()).popBack(\"result\" to \"Result from BottomSheetDialog!\")\n            }\n        }.root\n    }\n}"
  },
  {
    "path": "samples/modules/feature1/src/main/java/zlc/season/feature1/TestDialogFragment.kt",
    "content": "package zlc.season.feature1\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.fragment.app.DialogFragment\nimport zlc.season.base.Destinations\nimport zlc.season.base.Destinations.DIALOG_FRAGMbENT\nimport zlc.season.butterfly.Butterfly\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.feature1.databinding.DialogTestBinding\n\n\n@Destination(DIALOG_FRAGMbENT)\nclass TestDialogFragment : DialogFragment() {\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setStyle(STYLE_NO_TITLE, android.R.style.ThemeOverlay_Material_Dialog_Alert)\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View {\n        return DialogTestBinding.inflate(inflater, container, false).also {\n            it.btnNext.setOnClickListener {\n                Butterfly.of(requireContext()).navigate(Destinations.FRAGMENT)\n                dismiss()\n            }\n            it.btnBack.setOnClickListener {\n                Butterfly.of(requireContext()).popBack(\"result\" to \"Result from dialog!\")\n            }\n        }.root\n    }\n}"
  },
  {
    "path": "samples/modules/feature1/src/main/java/zlc/season/feature1/TestFragment.kt",
    "content": "package zlc.season.feature1\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.fragment.app.Fragment\nimport zlc.season.base.Destinations.FRAGMENT\nimport zlc.season.bracer.params\nimport zlc.season.butterfly.Butterfly\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.feature1.databinding.FragmentTestBinding\n\n@Destination(FRAGMENT)\nclass TestFragment : Fragment() {\n    val number by params<Int>()\n\n    var binding: FragmentTestBinding? = null\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View? {\n        return FragmentTestBinding.inflate(inflater, container, false).also { binding = it }.root\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n        binding?.apply {\n            tvContent.text = \"This is Fragment $number\"\n            btnSetResult.setOnClickListener {\n                Butterfly.of(requireContext()).popBack(\"abc\" to \"123\")\n            }\n        }\n    }\n}"
  },
  {
    "path": "samples/modules/feature1/src/main/java/zlc/season/feature1/TestResultActivity.kt",
    "content": "package zlc.season.feature1\n\nimport android.annotation.SuppressLint\nimport android.app.Activity\nimport android.content.Intent\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.core.os.bundleOf\nimport zlc.season.base.Destinations\nimport zlc.season.bracer.params\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.feature1.databinding.ActivityTestResultBinding\n\n@Destination(Destinations.TEST_RESULT)\nclass TestResultActivity : AppCompatActivity() {\n    val intValue by params<Int>()\n    val booleanValue by params<Boolean>()\n    val stringValue by params<String>()\n    val a by params<String>()\n    val b by params<String>()\n\n    @SuppressLint(\"SetTextI18n\")\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        val binding = ActivityTestResultBinding.inflate(layoutInflater)\n        setContentView(binding.root)\n\n        binding.tvParam.text = \"\"\"\n            intValue = $intValue \n            booleanValue = $booleanValue\n            stringValue = $stringValue\n            a = $a\n            b = $b\n        \"\"\".trimIndent()\n\n        binding.btnFinish.setOnClickListener {\n            setResult(Activity.RESULT_OK, Intent().apply {\n                putExtras(\n                    bundleOf(\n                        \"result\" to \"This is result\"\n                    )\n                )\n            })\n            finish()\n\n            // or use\n            // Butterfly.retreat(\"result\" to \"aaa\")\n        }\n    }\n}"
  },
  {
    "path": "samples/modules/feature1/src/main/res/layout/activity_test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".FooActivity\">\n\n    <TextView\n        android:id=\"@+id/tv_param\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\" />\n\n    <Button\n        android:id=\"@+id/btn_finish\"\n        android:layout_width=\"100dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:layout_marginTop=\"150dp\"\n        android:text=\"Finish\"\n        android:textColor=\"@android:color/black\" />\n</FrameLayout>"
  },
  {
    "path": "samples/modules/feature1/src/main/res/layout/activity_test_result.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <TextView\n        android:id=\"@+id/tv_param\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\" />\n\n    <Button\n        android:id=\"@+id/btn_finish\"\n        android:layout_width=\"100dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:layout_marginTop=\"150dp\"\n        android:text=\"Finish\"\n        android:textColor=\"@android:color/black\" />\n</FrameLayout>"
  },
  {
    "path": "samples/modules/feature1/src/main/res/layout/dialog_test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"300dp\">\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:text=\"This is a dialog fragment!\"\n            android:textColor=\"@android:color/black\" />\n\n        <Button\n            android:id=\"@+id/btnBack\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:layout_marginTop=\"40dp\"\n            android:text=\"Back\"\n            android:textColor=\"@android:color/black\" />\n\n        <Button\n            android:id=\"@+id/btnNext\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:layout_marginTop=\"90dp\"\n            android:text=\"Next\"\n            android:textColor=\"@android:color/black\" />\n    </FrameLayout>\n</FrameLayout>"
  },
  {
    "path": "samples/modules/feature1/src/main/res/layout/fragment_common.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/root\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    android:paddingTop=\"150dp\">\n\n    <TextView\n        android:id=\"@+id/tvContent\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:text=\"This is Fragment B!\"\n        android:textColor=\"@android:color/white\" />\n\n    <Button\n        android:id=\"@+id/btnBack\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:layout_marginTop=\"20dp\"\n        android:text=\"Back\"\n        android:textColor=\"@android:color/black\" />\n\n    <Button\n        android:id=\"@+id/btnDialog\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:text=\"Show Dialog\"\n        android:textColor=\"@android:color/black\" />\n\n    <Button\n        android:id=\"@+id/btnNextA\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:text=\"Next to A\"\n        android:textColor=\"@android:color/black\" />\n\n    <Button\n        android:id=\"@+id/btnNextB\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:text=\"Next to B\"\n        android:textColor=\"@android:color/black\" />\n\n    <Button\n        android:id=\"@+id/btnNextC\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:text=\"Next to C\"\n        android:textColor=\"@android:color/black\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:layout_marginTop=\"10dp\"\n        android:gravity=\"center\"\n        android:orientation=\"horizontal\">\n\n        <CheckBox\n            android:id=\"@+id/cbSingleTop\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"SingleTop\" />\n\n        <CheckBox\n            android:id=\"@+id/cbClearTop\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"ClearTop\" />\n    </LinearLayout>\n\n    <TextView\n        android:id=\"@+id/tvResult\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:layout_marginTop=\"40dp\"\n        android:textColor=\"@android:color/white\" />\n</LinearLayout>"
  },
  {
    "path": "samples/modules/feature1/src/main/res/layout/fragment_test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/design_default_color_primary\">\n\n    <TextView\n        android:id=\"@+id/tvContent\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:text=\"This is Fragment 0!\"\n        android:textColor=\"@android:color/white\" />\n\n    <Button\n        android:id=\"@+id/btnSetResult\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:layout_marginTop=\"50dp\"\n        android:text=\"Set result\"\n        android:textColor=\"@android:color/black\" />\n</FrameLayout>"
  },
  {
    "path": "samples/modules/feature1/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"blue\">#2196F3</color>\n    <color name=\"green\">#00BCD4</color>\n    <color name=\"yellow\">#FF9800</color>\n    <color name=\"purple200\">#FFBB86FC</color>\n    <color name=\"purple500\">#FF6200EE</color>\n    <color name=\"purple700\">#FF3700B3</color>\n</resources>"
  },
  {
    "path": "samples/modules/feature1/src/main/res/values/strings.xml",
    "content": "<resources></resources>"
  },
  {
    "path": "samples/modules/feature2/.gitignore",
    "content": "/build"
  },
  {
    "path": "samples/modules/feature2/build.gradle.kts",
    "content": "import zlc.season.buildlogic.base.androidLibrary\nimport zlc.season.buildlogic.base.enableCompose\n\n@Suppress(\"DSL_SCOPE_VIOLATION\")\nplugins {\n    alias(libs.plugins.library)\n    alias(libs.plugins.kotlin)\n    alias(libs.plugins.kotlin.serialization)\n    alias(libs.plugins.ksp)\n    alias(libs.plugins.butterfly)\n}\n\nandroidLibrary {\n    namespace = \"zlc.season.feature2\"\n    enableCompose()\n}\n\ndependencies {\n    ksp(project(\":compiler\"))\n    implementation(project(\":butterfly\"))\n    implementation(project(\":butterfly-compose\"))\n\n    implementation(project(\":samples:modules:base\"))\n\n    implementation(libs.bundles.android)\n    implementation(libs.bundles.kotlin)\n    implementation(libs.bundles.compose)\n    implementation(libs.bundles.season)\n}"
  },
  {
    "path": "samples/modules/feature2/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "samples/modules/feature2/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.kts.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "samples/modules/feature2/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest>\n\n</manifest>"
  },
  {
    "path": "samples/modules/feature2/src/main/java/zlc/season/bar/Colors.kt",
    "content": "package zlc.season.bar\n\nimport androidx.compose.ui.graphics.Color\n\nobject Colors {\n    val YELLOW = Color(0xFFFF5722)\n    val PURPLE = Color(0xFF673AB7)\n    val GREEN = Color(0xFF009688)\n}"
  },
  {
    "path": "samples/modules/feature2/src/main/java/zlc/season/bar/ComposeScreenA.kt",
    "content": "package zlc.season.bar\n\nimport android.os.Bundle\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.Checkbox\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.*\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.core.os.bundleOf\nimport androidx.lifecycle.viewmodel.compose.viewModel\nimport zlc.season.base.Destinations\nimport zlc.season.bracer.params\nimport zlc.season.butterfly.Butterfly\nimport zlc.season.butterfly.annotation.Destination\n\n@Destination(Destinations.COMPOSE_A)\n@Composable\nfun ComposeScreenA(bundle: Bundle = bundleOf()) {\n    val id by bundle.params<Int>()\n    val viewModel = viewModel<AScreenViewModel>()\n    val textFromViewModel = viewModel.text.collectAsState(initial = \"\")\n\n    val ctx = LocalContext.current\n    Surface(\n        modifier = Modifier\n            .fillMaxSize(),\n        color = Colors.YELLOW\n    ) {\n        Box {\n            Column(\n                modifier = Modifier\n                    .wrapContentSize()\n                    .align(Alignment.Center),\n                horizontalAlignment = Alignment.CenterHorizontally\n            ) {\n                Text(text = \"This is ComposeScreen A ${id}\")\n                Text(text = textFromViewModel.value)\n\n                Button(onClick = {\n                    Butterfly.of(ctx).popBack()\n                }) {\n                    Text(text = \"Back\")\n                }\n                Button(onClick = {\n                    Butterfly.of(ctx).navigate(Destinations.BOTTOM_SHEET_DIALOG_FRAGMENT)\n                }) {\n                    Text(text = \"Show Dialog\")\n                }\n\n                var singleTop by remember { mutableStateOf(false) }\n                var clearTop by remember { mutableStateOf(false) }\n\n                Button(onClick = {\n                    Butterfly.of(ctx)\n                        .params(bundleOf(\"id\" to id + 1))\n                        .run {\n                            if (clearTop) {\n                                clearTop()\n                            } else if (singleTop) {\n                                singleTop()\n                            } else {\n                                this\n                            }\n                        }\n                        .navigate(Destinations.COMPOSE_A)\n                }) {\n                    Text(text = \"Next To A\")\n                }\n                Button(onClick = {\n                    Butterfly.of(ctx)\n                        .run {\n                            if (clearTop) {\n                                clearTop()\n                            } else if (singleTop) {\n                                singleTop()\n                            } else {\n                                this\n                            }\n                        }\n                        .navigate(Destinations.COMPOSE_B)\n                }) {\n                    Text(text = \"Next To B\")\n                }\n                Button(onClick = {\n                    Butterfly.of(ctx)\n                        .run {\n                            if (clearTop) {\n                                clearTop()\n                            } else if (singleTop) {\n                                singleTop()\n                            } else {\n                                this\n                            }\n                        }\n                        .navigate(Destinations.COMPOSE_C)\n                }) {\n                    Text(text = \"Next to C\")\n                }\n\n                Row(verticalAlignment = Alignment.CenterVertically) {\n                    Checkbox(checked = singleTop, onCheckedChange = { singleTop = it })\n                    Text(text = \"SingleTop\")\n                    Checkbox(checked = clearTop, onCheckedChange = { clearTop = it })\n                    Text(text = \"ClearTop\")\n                }\n\n                Text(text = \"result\")\n            }\n        }\n    }\n}"
  },
  {
    "path": "samples/modules/feature2/src/main/java/zlc/season/bar/ComposeScreenB.kt",
    "content": "package zlc.season.bar\n\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.wrapContentSize\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.Checkbox\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.collectAsState\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.lifecycle.viewmodel.compose.viewModel\nimport zlc.season.base.Destinations\nimport zlc.season.butterfly.Butterfly\nimport zlc.season.butterfly.annotation.Destination\n\n@Destination(Destinations.COMPOSE_B)\n@Composable\nfun ComposeScreenB() {\n    val ctx = LocalContext.current\n    val viewModel = viewModel<BScreenViewModel>()\n    val textFromViewModel = viewModel.text.collectAsState(initial = \"\")\n\n    Surface(modifier = Modifier.fillMaxSize(), color = Colors.PURPLE) {\n        Box {\n            Column(\n                modifier = Modifier\n                    .wrapContentSize()\n                    .align(Alignment.Center),\n                horizontalAlignment = Alignment.CenterHorizontally\n            ) {\n                Text(text = \"This is ComposeScreen B\")\n                Text(text = textFromViewModel.value)\n                Button(onClick = {\n                    Butterfly.of(ctx).popBack()\n                }) {\n                    Text(text = \"Back\")\n                }\n                Button(onClick = {\n                    Butterfly.of(ctx).navigate(Destinations.BOTTOM_SHEET_DIALOG_FRAGMENT)\n                }) {\n                    Text(text = \"Show Dialog\")\n                }\n\n                var singleTop by remember { mutableStateOf(false) }\n                var clearTop by remember { mutableStateOf(false) }\n\n                Button(onClick = {\n                    Butterfly.of(ctx)\n                        .run {\n                            if (clearTop) {\n                                clearTop()\n                            } else if (singleTop) {\n                                singleTop()\n                            } else {\n                                this\n                            }\n                        }\n                        .navigate(Destinations.COMPOSE_A)\n                }) {\n                    Text(text = \"Next To A\")\n                }\n                Button(onClick = {\n                    Butterfly.of(ctx)\n                        .run {\n                            if (clearTop) {\n                                clearTop()\n                            } else if (singleTop) {\n                                singleTop()\n                            } else {\n                                this\n                            }\n                        }\n                        .navigate(Destinations.COMPOSE_B)\n                }) {\n                    Text(text = \"Next To B\")\n                }\n                Button(onClick = {\n                    Butterfly.of(ctx)\n                        .run {\n                            if (clearTop) {\n                                clearTop()\n                            } else if (singleTop) {\n                                singleTop()\n                            } else {\n                                this\n                            }\n                        }\n                        .navigate(Destinations.COMPOSE_C)\n                }) {\n                    Text(text = \"Next to C\")\n                }\n\n                Row(verticalAlignment = Alignment.CenterVertically) {\n                    Checkbox(checked = singleTop, onCheckedChange = { singleTop = it })\n                    Text(text = \"SingleTop\")\n                    Checkbox(checked = clearTop, onCheckedChange = { clearTop = it })\n                    Text(text = \"ClearTop\")\n                }\n\n                Text(text = \"result\")\n            }\n        }\n    }\n}"
  },
  {
    "path": "samples/modules/feature2/src/main/java/zlc/season/bar/ComposeScreenC.kt",
    "content": "package zlc.season.bar\n\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.wrapContentSize\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.Checkbox\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.collectAsState\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.lifecycle.viewmodel.compose.viewModel\nimport zlc.season.base.Destinations\nimport zlc.season.butterfly.Butterfly\nimport zlc.season.butterfly.annotation.Destination\n\n@Destination(Destinations.COMPOSE_C)\n@Composable\nfun ComposeScreenC() {\n    val viewModel = viewModel<CScreenViewModel>()\n    val textFromViewModel = viewModel.text.collectAsState(initial = \"\")\n    val ctx = LocalContext.current\n\n    Surface(modifier = Modifier.fillMaxSize(), color = Colors.GREEN) {\n        Box {\n            Column(\n                modifier = Modifier\n                    .wrapContentSize()\n                    .align(Alignment.Center),\n                horizontalAlignment = Alignment.CenterHorizontally\n            ) {\n                Text(text = \"This is ComposeScreen C\")\n                Text(text = textFromViewModel.value)\n                Button(onClick = {\n                    Butterfly.of(ctx).popBack()\n                }) {\n                    Text(text = \"Back\")\n                }\n                Button(onClick = {\n                    Butterfly.of(ctx).navigate(Destinations.BOTTOM_SHEET_DIALOG_FRAGMENT)\n                }) {\n                    Text(text = \"Show Dialog\")\n                }\n\n                var singleTop by remember { mutableStateOf(false) }\n                var clearTop by remember { mutableStateOf(false) }\n\n                Button(onClick = {\n                    Butterfly.of(ctx)\n                        .run {\n                            if (clearTop) {\n                                clearTop()\n                            } else if (singleTop) {\n                                singleTop()\n                            } else {\n                                this\n                            }\n                        }\n                        .navigate(Destinations.COMPOSE_A)\n                }) {\n                    Text(text = \"Next To A\")\n                }\n                Button(onClick = {\n                    Butterfly.of(ctx)\n                        .run {\n                            if (clearTop) {\n                                clearTop()\n                            } else if (singleTop) {\n                                singleTop()\n                            } else {\n                                this\n                            }\n                        }\n                        .navigate(Destinations.COMPOSE_B)\n                }) {\n                    Text(text = \"Next To B\")\n                }\n                Button(onClick = {\n                    Butterfly.of(ctx)\n                        .run {\n                            if (clearTop) {\n                                clearTop()\n                            } else if (singleTop) {\n                                singleTop()\n                            } else {\n                                this\n                            }\n                        }\n                        .navigate(Destinations.COMPOSE_C)\n                }) {\n                    Text(text = \"Next to C\")\n                }\n\n                Row(verticalAlignment = Alignment.CenterVertically) {\n                    Checkbox(checked = singleTop, onCheckedChange = { singleTop = it })\n                    Text(text = \"SingleTop\")\n                    Checkbox(checked = clearTop, onCheckedChange = { clearTop = it })\n                    Text(text = \"ClearTop\")\n                }\n\n                Text(text = \"result\")\n            }\n        }\n    }\n}"
  },
  {
    "path": "samples/modules/feature2/src/main/java/zlc/season/bar/ScreenViewModels.kt",
    "content": "package zlc.season.bar\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.launch\n\nclass AScreenViewModel : ViewModel() {\n\n    val text = MutableStateFlow(\"\")\n\n    var count = 0\n\n    init {\n        viewModelScope.launch {\n            while (isActive) {\n                delay(1000)\n                text.value = \"This is count: ${count++} from A screen viewModel\"\n            }\n        }\n    }\n\n    override fun onCleared() {\n        super.onCleared()\n        println(\"A screen view model is cleared!\")\n    }\n}\n\nclass BScreenViewModel : ViewModel() {\n\n    val text = MutableStateFlow(\"\")\n    var count = 0\n\n    init {\n        viewModelScope.launch {\n            while (isActive) {\n                delay(1000)\n                text.value = \"This is count: ${count++} from B screen viewModel\"\n            }\n        }\n    }\n\n    override fun onCleared() {\n        super.onCleared()\n        println(\"B screen view model is cleared!\")\n    }\n}\n\nclass CScreenViewModel : ViewModel() {\n\n    val text = MutableStateFlow(\"\")\n    var count = 0\n\n    init {\n        viewModelScope.launch {\n            while (isActive) {\n                delay(1000)\n                text.value = \"This is count: ${count++} from C screen viewModel\"\n            }\n        }\n    }\n\n    override fun onCleared() {\n        super.onCleared()\n        println(\"C screen view model is cleared!\")\n    }\n}"
  },
  {
    "path": "samples/modules/normal/dashboard/.gitignore",
    "content": "/build"
  },
  {
    "path": "samples/modules/normal/dashboard/build.gradle.kts",
    "content": "import zlc.season.buildlogic.base.androidLibrary\n\n@Suppress(\"DSL_SCOPE_VIOLATION\")\nplugins {\n    alias(libs.plugins.library)\n    alias(libs.plugins.kotlin)\n    alias(libs.plugins.kotlin.serialization)\n    alias(libs.plugins.ksp)\n    alias(libs.plugins.butterfly)\n}\n\nandroidLibrary {\n    namespace = \"zlc.season.dashboard\"\n\n    viewBinding { enable = true }\n}\n\ndependencies {\n    ksp(project(\":compiler\"))\n    implementation(project(\":butterfly\"))\n\n    implementation(project(\":samples:modules:base\"))\n    implementation(libs.bundles.android)\n    implementation(libs.bundles.kotlin)\n    implementation(libs.bundles.season)\n}"
  },
  {
    "path": "samples/modules/normal/dashboard/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "samples/modules/normal/dashboard/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.kts.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "samples/modules/normal/dashboard/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</manifest>"
  },
  {
    "path": "samples/modules/normal/dashboard/src/main/java/zlc/season/dashboard/DashboardFragment.kt",
    "content": "package zlc.season.dashboard\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.TextView\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.viewModels\nimport zlc.season.base.Destinations\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.dashboard.databinding.FragmentDashboardBinding\n\n@Destination(Destinations.DASHBOARD)\nclass DashboardFragment : Fragment() {\n\n    private var _binding: FragmentDashboardBinding? = null\n\n    // This property is only valid between onCreateView and\n    // onDestroyView.\n    private val binding get() = _binding!!\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View {\n        val dashboardViewModel by viewModels<DashboardViewModel>()\n\n        _binding = FragmentDashboardBinding.inflate(inflater, container, false)\n        val root: View = binding.root\n\n        val textView: TextView = binding.textDashboard\n        dashboardViewModel.text.observe(viewLifecycleOwner) {\n            textView.text = it\n        }\n        return root\n    }\n\n    override fun onDestroyView() {\n        super.onDestroyView()\n        _binding = null\n    }\n}"
  },
  {
    "path": "samples/modules/normal/dashboard/src/main/java/zlc/season/dashboard/DashboardViewModel.kt",
    "content": "package zlc.season.dashboard\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.ViewModel\n\nclass DashboardViewModel : ViewModel() {\n\n    private val _text = MutableLiveData<String>().apply {\n        value = \"This is dashboard Fragment\"\n    }\n    val text: LiveData<String> = _text\n}"
  },
  {
    "path": "samples/modules/normal/dashboard/src/main/res/layout/fragment_dashboard.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".ui.dashboard.DashboardFragment\">\n\n    <TextView\n        android:id=\"@+id/text_dashboard\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"8dp\"\n        android:layout_marginTop=\"8dp\"\n        android:layout_marginEnd=\"8dp\"\n        android:textAlignment=\"center\"\n        android:textSize=\"20sp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "samples/modules/normal/home/.gitignore",
    "content": "/build"
  },
  {
    "path": "samples/modules/normal/home/build.gradle.kts",
    "content": "import zlc.season.buildlogic.base.androidLibrary\n\n@Suppress(\"DSL_SCOPE_VIOLATION\")\nplugins {\n    alias(libs.plugins.library)\n    alias(libs.plugins.kotlin)\n    alias(libs.plugins.kotlin.serialization)\n    alias(libs.plugins.ksp)\n    alias(libs.plugins.butterfly)\n}\n\nandroidLibrary {\n    namespace = \"zlc.season.home\"\n\n    viewBinding { enable = true }\n}\n\ndependencies {\n    ksp(project(\":compiler\"))\n    implementation(project(\":butterfly\"))\n    implementation(project(\":butterfly-compose\"))\n\n    implementation(project(\":samples:modules:base\"))\n    implementation(libs.bundles.android)\n    implementation(libs.bundles.kotlin)\n    implementation(libs.bundles.season)\n    implementation(libs.bundles.coil)\n}"
  },
  {
    "path": "samples/modules/normal/home/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "samples/modules/normal/home/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.kts.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "samples/modules/normal/home/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\n    </application>\n\n</manifest>"
  },
  {
    "path": "samples/modules/normal/home/src/main/java/zlc/season/home/HomeFragment.kt",
    "content": "package zlc.season.home\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.TextView\nimport androidx.fragment.app.Fragment\nimport zlc.season.base.Destinations\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.home.databinding.FragmentHomeBinding\n\n@Destination(Destinations.HOME)\nclass HomeFragment : Fragment() {\n    var binding: FragmentHomeBinding? = null\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View {\n        binding = FragmentHomeBinding.inflate(inflater, container, false)\n        val root: View = binding!!.root\n\n        val textView: TextView = binding!!.textHome\n        textView.text = \"This is HomeFragment\"\n        return root\n    }\n}\n"
  },
  {
    "path": "samples/modules/normal/home/src/main/java/zlc/season/home/HomeImpl.kt",
    "content": "package zlc.season.home\n\nimport androidx.compose.foundation.text.BasicText\nimport androidx.compose.runtime.Composable\nimport androidx.fragment.app.FragmentManager\nimport zlc.season.butterfly.annotation.EvadeImpl\nimport zlc.season.butterfly.compose.ComposeDestination\n\n@EvadeImpl\nclass HomeImpl {\n    val TAG = \"home_tag\"\n\n    var homeFragment: HomeFragment? = null\n\n    fun showHome(fragmentManager: FragmentManager, container: Int) {\n        if (homeFragment == null) {\n            homeFragment = HomeFragment()\n        }\n        homeFragment?.let {\n            fragmentManager.beginTransaction()\n                .replace(container, it, TAG)\n                .commit()\n        }\n    }\n\n    fun isHomeShowing(fragmentManager: FragmentManager): Boolean {\n        val find = fragmentManager.findFragmentByTag(TAG)\n        return find != null\n    }\n\n    fun hideHome(fragmentManager: FragmentManager) {\n        homeFragment?.let {\n            fragmentManager.beginTransaction()\n                .remove(it)\n                .commit()\n        }\n    }\n\n    fun testCompose(): ComposeDestination {\n        return ComposeDestination(composable = @Composable {\n            BasicText(\"test compose\")\n        })\n    }\n}"
  },
  {
    "path": "samples/modules/normal/home/src/main/res/layout/fragment_home.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <TextView\n        android:id=\"@+id/text_home\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"8dp\"\n        android:layout_marginTop=\"8dp\"\n        android:layout_marginEnd=\"8dp\"\n        android:textAlignment=\"center\"\n        android:textSize=\"20sp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "samples/modules/normal/notifications/.gitignore",
    "content": "/build"
  },
  {
    "path": "samples/modules/normal/notifications/build.gradle.kts",
    "content": "import zlc.season.buildlogic.base.androidLibrary\n\n@Suppress(\"DSL_SCOPE_VIOLATION\")\nplugins {\n    alias(libs.plugins.library)\n    alias(libs.plugins.kotlin)\n    alias(libs.plugins.kotlin.serialization)\n    alias(libs.plugins.ksp)\n    alias(libs.plugins.butterfly)\n}\n\nandroidLibrary {\n    namespace = \"zlc.season.notifications\"\n    viewBinding { enable = true }\n}\n\ndependencies {\n    ksp(project(\":compiler\"))\n    implementation(project(\":butterfly\"))\n\n    implementation(project(\":samples:modules:base\"))\n    implementation(libs.bundles.android)\n    implementation(libs.bundles.kotlin)\n    implementation(libs.bundles.season)\n}"
  },
  {
    "path": "samples/modules/normal/notifications/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "samples/modules/normal/notifications/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.kts.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "samples/modules/normal/notifications/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</manifest>"
  },
  {
    "path": "samples/modules/normal/notifications/src/main/java/zlc/season/notifications/NotificationsFragment.kt",
    "content": "package zlc.season.notifications\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.TextView\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.viewModels\nimport zlc.season.base.Destinations\nimport zlc.season.butterfly.annotation.Destination\nimport zlc.season.notifications.databinding.FragmentNotificationsBinding\n\n@Destination(Destinations.NOTIFICATION)\nclass NotificationsFragment : Fragment() {\n\n    private var _binding: FragmentNotificationsBinding? = null\n\n    // This property is only valid between onCreateView and\n    // onDestroyView.\n    private val binding get() = _binding!!\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View {\n        val notificationsViewModel by viewModels<NotificationsViewModel>()\n\n        _binding = FragmentNotificationsBinding.inflate(inflater, container, false)\n        val root: View = binding.root\n\n        val textView: TextView = binding.textNotifications\n        notificationsViewModel.text.observe(viewLifecycleOwner) {\n            textView.text = it\n        }\n        return root\n    }\n\n    override fun onDestroyView() {\n        super.onDestroyView()\n        _binding = null\n    }\n}"
  },
  {
    "path": "samples/modules/normal/notifications/src/main/java/zlc/season/notifications/NotificationsViewModel.kt",
    "content": "package zlc.season.notifications\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.ViewModel\n\nclass NotificationsViewModel : ViewModel() {\n\n    private val _text = MutableLiveData<String>().apply {\n        value = \"This is notifications Fragment\"\n    }\n    val text: LiveData<String> = _text\n}"
  },
  {
    "path": "samples/modules/normal/notifications/src/main/res/layout/fragment_notifications.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <TextView\n        android:id=\"@+id/text_notifications\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"8dp\"\n        android:layout_marginTop=\"8dp\"\n        android:layout_marginEnd=\"8dp\"\n        android:textAlignment=\"center\"\n        android:textSize=\"20sp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "settings.gradle.kts",
    "content": "@file:Suppress(\"DSL_SCOPE_VIOLATION\", \"UnstableApiUsage\")\n\npluginManagement {\n    includeBuild(\"buildLogic\")\n\n    repositories {\n        mavenLocal()\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n        maven(\"https://jitpack.io\")\n    }\n}\n\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)\n    repositories {\n        mavenLocal()\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n        maven(\"https://jitpack.io\")\n    }\n}\n\nrootProject.name = \"ButterflyDemo\"\ninclude(\":annotation\")\ninclude(\":butterfly\")\ninclude(\":butterfly-compose\")\ninclude(\":compiler\")\ninclude(\":plugin\")\n\n\ninclude(\":samples\")\ninclude(\":samples:app\")\ninclude(\":samples:modules\")\n\ninclude(\":samples:modules:base\")\ninclude(\":samples:modules:feature2\")\ninclude(\":samples:modules:feature1\")\n\ninclude(\":samples:modules:compose\")\ninclude(\":samples:modules:compose:compose_home\")\ninclude(\":samples:modules:compose:compose_dashboard\")\ninclude(\":samples:modules:compose:compose_notifications\")\n\ninclude(\":samples:modules:normal\")\ninclude(\":samples:modules:normal:home\")\ninclude(\":samples:modules:normal:dashboard\")\ninclude(\":samples:modules:normal:notifications\")\n"
  }
]